Skip to content

Instantly share code, notes, and snippets.

@etorreborre
Created August 2, 2010 23:47
Show Gist options
  • Save etorreborre/505536 to your computer and use it in GitHub Desktop.
Save etorreborre/505536 to your computer and use it in GitHub Desktop.
import org.specs._
import org.scalacheck._
import com.codecommit.collection.Vector
/**
* An enhanced specification of the Vector class from http://github.com/djspiewak/scala-collections
*
* It uses "verifies" to remove the boilerplate declaration of a property which "must pass"
* It uses must throwAn[Exception] to specify that an exception must be thrown
*
* Current issues are:
* * an additional implicit def is required to transform a Prop to a Boolean otherwise verifies with an implication property doesn't
* typecheck
* * reading the code with "verifies" is not very natural: "a vector" should "pop elements" verifies { ... }
*/
object VectorSpecs extends Specification with ScalaCheck {
import Prop._
val vector = Vector[Int]()
implicit def propToBoolean(p: Prop): Boolean = true
implicit def arbitraryVector[A](implicit arb: Arbitrary[A]): Arbitrary[Vector[A]] = {
Arbitrary(for {
data <- Arbitrary.arbitrary[List[A]]
} yield data.foldLeft(Vector[A]()) { _ + _ })
}
"A Vector" should {
"store a single element" verifies { (i: Int, e: Int) =>
i >= 0 ==> ((vector(0) = e)(0) == e)
}
"replace single element" verifies { (vec: Vector[Int], i: Int) =>
(!vec.isEmpty && i > scala.Int.MinValue) ==> {
val idx = scala.math.abs(i) % vec.length
val newVector = (vec(idx) = "test")(idx) = "newTest"
newVector(idx) == "newTest"
}
}
"implement length" verifies { (list: List[Int]) =>
val vec = list.foldLeft(Vector[Int]()) { _ + _ }
vec.length == list.length
}
"fail on apply out-of-bounds" verifies { (vec: Vector[Int], i: Int) =>
!((0 until vec.length) contains i) ==> { vec(i) must throwAn[IndexOutOfBoundsException] }
}
"fail on update out-of-bounds" verifies { (vec: Vector[Int], i: Int) =>
!((0 to vec.length) contains i) ==> { (vec(i) = 42) must throwAn[IndexOutOfBoundsException] }
}
"pop elements" in {
val prop = forAll { vec: Vector[Int] =>
vec.length > 0 ==> {
val popped = vec.pop
var back = popped.length == vec.length - 1
for (i <- 0 until popped.length) {
back &&= popped(i) == vec(i)
}
back
}
}
prop must pass(set(maxSize -> 3000, minTestsOk -> 1000))
}
"fail on pop empty vector" in {
Vector.empty.pop must throwAn[IllegalStateException]
}
"store multiple elements in order" verifies { list: List[Int] =>
val newVector = list.foldLeft(vector) { _ + _ }
val res = for (i <- 0 until list.length) yield newVector(i) == list(i)
res forall { _ == true }
}
"store lots of elements" in {
val LENGTH = 100000
val vector = (0 until LENGTH).foldLeft(Vector[Int]()) { _ + _ }
vector.length mustEqual LENGTH
for (i <- 0 until LENGTH) {
vector(i) mustEqual i
}
}
"maintain both old and new versions after conj" in {
val prop = forAll { vec: Vector[Int] =>
val vec2 = vec + 42
for (i <- 0 until vec.length) {
vec2(i) aka ("Index " + i + " in derivative") mustEqual vec(i) aka ("Index " + i + " in origin")
}
vec2.last mustEqual 42
}
prop must pass(set(maxSize -> 3000, minTestsOk -> 1000))
}
"maintain both old and new versions after update" in {
val prop = forAll { (vec: Vector[Int], i: Int) =>
(!vec.isEmpty && i > scala.Int.MinValue) ==> {
val idx = scala.math.abs(i) % vec.length
val vec2 = vec(idx) = 42
for (i <- 0 until vec.length if i != idx) {
vec2(i) aka ("Index " + i + " in derivative") mustEqual vec(i) aka ("Index " + i + " in origin")
}
vec2(idx) mustEqual 42
}
}
prop must pass(set(maxSize -> 3000, minTestsOk -> 1000))
}
"maintain both old and new versions after pop" in {
val prop = forAll { vec: Vector[Int] =>
!vec.isEmpty ==> {
val vec2 = vec.pop
for (i <- 0 until vec.length - 1) {
vec2(i) aka ("Index " + i + " in derivative") mustEqual vec(i) aka ("Index " + i + " in origin")
}
vec2.length mustEqual vec.length - 1
}
}
prop must pass(set(maxSize -> 3000, minTestsOk -> 1000))
}
"implement filter" verifies { (vec: Vector[Int], f: (Int)=>Boolean) =>
val filtered = vec filter f
var back = filtered forall f
for (e <- vec) {
if (f(e)) {
back &&= filtered.contains(e)
}
}
back
}
"implement foldLeft" verifies { list: List[Int] =>
val vec = list.foldLeft(Vector[Int]()) { _ + _ }
vec.foldLeft(0) { _ + _ } == list.foldLeft(0) { _ + _ }
}
"implement forall" verifies { (vec: Vector[Int], f: (Int)=>Boolean) =>
val bool = vec forall f
var back = true
for (e <- vec) {
back &&= f(e)
}
(back && bool) || (!back && !bool)
}
"implement flatMap" in { (vec: Vector[Int], f: (Int)=>Vector[Int]) =>
val mapped = vec flatMap f
var back = true
var i = 0
var n = 0
while (i < vec.length) {
val res = f(vec(i))
var inner = 0
while (inner < res.length) {
back &&= mapped(n) == res(inner)
inner += 1
n += 1
}
i += 1
}
back
}
"implement map" verifies { (vec: Vector[Int], f: (Int)=>Int) =>
val mapped = vec map f
var back = vec.length == mapped.length
for (i <- 0 until vec.length) {
back &&= mapped(i) == f(vec(i))
}
back
}
"implement reverse" verifies { v: Vector[Int] =>
val reversed = v.reverse
var back = v.length == reversed.length
for (i <- 0 until v.length) {
back &&= reversed(i) == v(v.length - i - 1)
}
back
}
"append to reverse" verifies { (v: Vector[Int], n: Int) =>
val rev = v.reverse
val add = rev + n
var back = add.length == rev.length + 1
for (i <- 0 until rev.length) {
back &&= add(i) == rev(i)
}
back && add(rev.length) == n
}
"map on reverse" verifies { (v: Vector[Int], f: (Int)=>Int) =>
val rev = v.reverse
val mapped = rev map f
var back = mapped.length == rev.length
for (i <- 0 until rev.length) {
back &&= mapped(i) == f(rev(i))
}
back
}
"implement zip" verifies { (first: Vector[Int], second: Vector[Double]) =>
val zip = first zip second
var back = zip.length == scala.math.min(first.length, second.length)
for (i <- 0 until zip.length) {
var (left, right) = zip(i)
back &&= (left == first(i) && right == second(i))
}
back
}
"implement zipWithIndex" verifies { vec: Vector[Int] =>
val zip = vec.zipWithIndex
var back = zip.length == vec.length
for (i <- 0 until zip.length) {
val (elem, index) = zip(i)
back &&= (index == i && elem == vec(i))
}
back
}
"implement equals" >> {
"with same vectors" verifies { list: List[Int] =>
val vecA = list.foldLeft(Vector[Int]()) { _ + _ }
val vecB = list.foldLeft(Vector[Int]()) { _ + _ }
vecA == vecB
}
"with vectors of different lengths" verifies { (vecA: Vector[Int], vecB: Vector[Int]) =>
vecA.length != vecB.length ==> (vecA != vecB)
}
"with different vectors" verifies { (listA: List[Int], listB: List[Int]) =>
val vecA = listA.foldLeft(Vector[Int]()) { _ + _ }
val vecB = listB.foldLeft(Vector[Int]()) { _ + _ }
listA != listB ==> (vecA != vecB)
}
"with an object of a different class" verifies { (vec: Vector[Int], data: Int) => vec != data }
}
"implement hashCode" verifies { list: List[Int] =>
val vecA = list.foldLeft(Vector[Int]()) { _ + _ }
val vecB = list.foldLeft(Vector[Int]()) { _ + _ }
vecA.hashCode == vecB.hashCode
}
"implement extractor" in {
val vec1 = Vector(1, 2, 3)
vec1 must beLike {
case Vector(a, b, c) => (a, b, c) == (1, 2, 3)
}
val vec2 = Vector("daniel", "chris", "joseph")
vec2 must beLike {
case Vector(a, b, c) => (a, b, c) == ("daniel", "chris", "joseph")
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment