Created
August 2, 2010 23:47
-
-
Save etorreborre/505536 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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