-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ZIO Test: Migrate ChunkSpec (zio#1651)
* Add test framework * Migrate ChunkSpec * fmt
- Loading branch information
Regis Kuckaertz
authored
Sep 12, 2019
1 parent
2798463
commit b99b0b0
Showing
4 changed files
with
204 additions
and
188 deletions.
There are no files selected for viewing
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
345 changes: 157 additions & 188 deletions
345
streams-tests/jvm/src/test/scala/zio/stream/ChunkSpec.scala
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,191 +1,160 @@ | ||
package zio.stream | ||
|
||
import org.specs2._ | ||
import org.specs2.specification.core.SpecStructure | ||
import zio.Chunk | ||
|
||
class ChunkSpec extends Specification with ScalaCheck { | ||
def is: SpecStructure = | ||
"ChunkSpec".title ^ | ||
s2""" | ||
chunk apply $apply | ||
chunk length $length | ||
chunk equality prop $equality | ||
chunk inequality $inequality | ||
flatMap chunk $flatMap | ||
map chunk $map | ||
materialize chunk $materialize | ||
foldLeft chunk $foldLeft | ||
filter chunk $filter | ||
drop chunk $drop | ||
take chunk $take | ||
dropWhile chunk $dropWhile | ||
takeWhile chunk $takeWhile | ||
toArray $toArray | ||
foreach $foreach | ||
concat chunk $concat | ||
chunk transitivity $testTransitivity | ||
chunk symmetry $testSymmetry | ||
chunk reflexivity $testReflexivity | ||
chunk negation $testNegation | ||
chunk substitutivity $testSubstitutivity | ||
chunk consistency $hashConsistency | ||
chunk zipAllWith $testzipAllWith | ||
An Array-based chunk that is filtered empty and mapped must not throw NPEs. $nullArrayBug | ||
toArray on concat of a slice must work properly. $toArrayOnConcatOfSlice | ||
toArray on concat of empty and integers must work properly. $toArrayOnConcatOfEmptyAndInts | ||
Chunk.filter that results in an empty Chunk must use Chunk.empty $filterConstFalseResultsInEmptyChunk | ||
""" | ||
|
||
import ArbitraryChunk._ | ||
import org.scalacheck._ | ||
|
||
private def apply = { | ||
implicit val chunkGen: Gen[(Chunk[Int], Int)] = for { | ||
chunk <- Arbitrary.arbitrary[Chunk[Int]].filter(_.length > 0) | ||
len <- Gen.chooseNum(0, chunk.length - 1) | ||
} yield (chunk, len) | ||
|
||
prop { t: (Chunk[Int], Int) => | ||
t._1.apply(t._2) must_=== t._1.toSeq.apply(t._2) | ||
}.setArbitrary(Arbitrary(chunkGen)) | ||
} | ||
|
||
private def length = | ||
prop { chunk: Chunk[Int] => | ||
chunk.length must_=== chunk.toSeq.length | ||
} | ||
|
||
private def equality = | ||
prop((c1: Chunk[Int], c2: Chunk[Int]) => c1.equals(c2) must_=== c1.toSeq.equals(c2.toSeq)) | ||
|
||
private def inequality = | ||
Chunk(1, 2, 3, 4, 5) must_!== Chunk(1, 2, 3, 4, 5, 6) | ||
|
||
private def flatMap = | ||
prop { (c: Chunk[Int], f: Int => Chunk[Int]) => | ||
c.flatMap(f).toSeq must_=== c.toSeq.flatMap(f.andThen(_.toSeq)) | ||
} | ||
|
||
private def map = | ||
prop { (c: Chunk[Int], f: Int => String) => | ||
c.map(f).toSeq must_=== c.toSeq.map(f) | ||
} | ||
|
||
private def materialize = | ||
prop { c: Chunk[Int] => | ||
c.materialize.toSeq must_=== c.toSeq | ||
} | ||
|
||
private def foldLeft = | ||
prop { (s0: String, f: (String, Int) => String, c: Chunk[Int]) => | ||
c.foldLeft(s0)(f) must_=== c.toArray.foldLeft(s0)(f) | ||
} | ||
|
||
private def filter = | ||
prop { (chunk: Chunk[String], p: String => Boolean) => | ||
chunk.filter(p).toSeq must_=== chunk.toSeq.filter(p) | ||
} | ||
|
||
private def drop = | ||
prop { (chunk: Chunk[Int], n: Int) => | ||
chunk.drop(n).toSeq must_=== chunk.toSeq.drop(n) | ||
} | ||
|
||
private def take = | ||
prop { (c: Chunk[Int], n: Int) => | ||
c.take(n).toSeq must_=== c.toSeq.take(n) | ||
} | ||
|
||
private def nullArrayBug = { | ||
val c = Chunk.fromArray(Array(1, 2, 3, 4, 5)) | ||
|
||
// foreach should not throw | ||
c.foreach(_ => ()) | ||
|
||
c.filter(_ => false).map(_ * 2).length must_=== 0 | ||
} | ||
|
||
private def concat = prop { (c1: Chunk[Int], c2: Chunk[Int]) => | ||
(c1 ++ c2).toSeq must_=== (c1.toSeq ++ c2.toSeq) | ||
} | ||
|
||
private def toArrayOnConcatOfSlice = { | ||
val onlyOdd: Int => Boolean = _ % 2 != 0 | ||
val concat = Chunk(1, 1, 1).filter(onlyOdd) ++ | ||
Chunk(2, 2, 2).filter(onlyOdd) ++ | ||
Chunk(3, 3, 3).filter(onlyOdd) | ||
|
||
val array = concat.toArray | ||
|
||
array must_=== Array(1, 1, 1, 3, 3, 3) | ||
} | ||
|
||
private def toArrayOnConcatOfEmptyAndInts = | ||
(Chunk.empty ++ Chunk.fromArray(Array(1, 2, 3))).toArray must_=== Array(1, 2, 3) | ||
|
||
private def filterConstFalseResultsInEmptyChunk = | ||
Chunk.fromArray(Array(1, 2, 3)).filter(_ => false) must_=== Chunk.empty | ||
|
||
private def dropWhile = | ||
prop { (c: Chunk[Int], p: Int => Boolean) => | ||
c.dropWhile(p).toSeq must_=== c.toSeq.dropWhile(p) | ||
} | ||
|
||
private def takeWhile = prop { (c: Chunk[Int], p: Int => Boolean) => | ||
c.takeWhile(p).toSeq must_=== c.toSeq.takeWhile(p) | ||
} | ||
|
||
private def toArray = prop { (c: Chunk[Int]) => | ||
c.toArray.toSeq must_=== c.toSeq | ||
} | ||
|
||
private def foreach = prop { (c: Chunk[Int]) => | ||
var sum = 0 | ||
c.foreach(sum += _) | ||
|
||
sum must_=== c.toSeq.sum | ||
} | ||
|
||
def testTransitivity = { | ||
val c1 = Chunk(1, 2, 3) | ||
val c2 = Chunk(1, 2, 3) | ||
val c3 = Chunk(1, 2, 3) | ||
((c1 == c2) && (c2 == c3) && (c1 == c3)) must beTrue | ||
} | ||
|
||
def testSymmetry = { | ||
val c1 = Chunk(1, 2, 3) | ||
val c2 = Chunk(1, 2, 3) | ||
((c1 == c2) && (c2 == c1)) must beTrue | ||
} | ||
|
||
def testReflexivity = { | ||
val c1 = Chunk(1, 2, 3) | ||
((c1 == c1)) must beTrue | ||
} | ||
|
||
def testNegation = { | ||
val c1 = Chunk(1, 2, 3) | ||
val c2 = Chunk(1, 2, 3) | ||
(c1 != c2 == !(c1 == c2)) must beTrue | ||
} | ||
|
||
def testSubstitutivity = { | ||
val c1 = Chunk(1, 2, 3) | ||
val c2 = Chunk(1, 2, 3) | ||
((c1 == c2) && (c1.toString == c2.toString)) must beTrue | ||
} | ||
|
||
def hashConsistency = { | ||
val c1 = (1, 2, 3) | ||
val c2 = (1, 2, 3) | ||
((c1 == c2) && (c1.hashCode == c2.hashCode)) must beTrue | ||
} | ||
|
||
def testzipAllWith = | ||
(Chunk(1, 2, 3).zipAllWith(Chunk(3, 2, 1))(_ => 0, _ => 0)(_ + _) == Chunk(4, 4, 4)) && | ||
(Chunk(1, 2, 3).zipAllWith(Chunk(3, 2))(_ => 0, _ => 0)(_ + _) == Chunk(4, 4, 0)) && | ||
(Chunk(1, 2).zipAllWith(Chunk(3, 2, 1))(_ => 0, _ => 0)(_ + _)) == Chunk(4, 4, 0) | ||
|
||
} | ||
import zio.ZIOBaseSpec | ||
import zio.random.Random | ||
import zio.test._ | ||
import zio.test.Assertion.equalTo | ||
import ChunkUtils._ | ||
|
||
object ChunkSpec | ||
extends ZIOBaseSpec( | ||
suite("ChunkSpec")( | ||
testM("apply") { | ||
check(chunkIxGen(Gen.unit)) { | ||
case (chunk, i) => | ||
assert(chunk.apply(i), equalTo(chunk.toSeq.apply(i))) | ||
} | ||
}, | ||
testM("length") { | ||
check(chunkGen(intGen)) { chunk => | ||
assert(chunk.length, equalTo(chunk.toSeq.length)) | ||
} | ||
}, | ||
testM("equality") { | ||
check(chunkGen(intGen), chunkGen(intGen)) { (c1, c2) => | ||
assert(c1.equals(c2), equalTo(c1.toSeq.equals(c2.toSeq))) | ||
} | ||
}, | ||
test("inequality") { | ||
assert(Chunk(1, 2, 3, 4, 5), Assertion.not(equalTo(Chunk(1, 2, 3, 4, 5, 6)))) | ||
}, | ||
testM("materialize") { | ||
check(chunkGen(intGen)) { c => | ||
assert(c.materialize.toSeq, equalTo(c.toSeq)) | ||
} | ||
}, | ||
testM("foldLeft") { | ||
val fn = Gen.function[Random with Sized, (String, Int), String](stringGen) | ||
check(stringGen, fn, chunkGen(intGen)) { (s0, f, c) => | ||
assert(c.foldLeft(s0)(Function.untupled(f)), equalTo(c.toArray.foldLeft(s0)(Function.untupled(f)))) | ||
} | ||
}, | ||
testM("map") { | ||
val fn = Gen.function[Random with Sized, Int, String](stringGen) | ||
check(chunkGen(intGen), fn) { (c, f) => | ||
assert(c.map(f).toSeq, equalTo(c.toSeq.map(f))) | ||
} | ||
}, | ||
testM("flatMap") { | ||
val fn = Gen.function[Random with Sized, Int, Chunk[Int]](chunkGen(intGen)) | ||
check(chunkGen(intGen), fn) { (c, f) => | ||
assert(c.flatMap(f).toSeq, equalTo(c.toSeq.flatMap(f.andThen(_.toSeq)))) | ||
} | ||
}, | ||
testM("filter") { | ||
val fn = Gen.function[Random with Sized, String, Boolean](Gen.boolean) | ||
check(chunkGen(stringGen), fn) { (chunk, p) => | ||
assert(chunk.filter(p).toSeq, equalTo(chunk.toSeq.filter(p))) | ||
} | ||
}, | ||
testM("drop chunk") { | ||
check(chunkGen(intGen), intGen) { (chunk, n) => | ||
assert(chunk.drop(n).toSeq, equalTo(chunk.toSeq.drop(n))) | ||
} | ||
}, | ||
testM("take chunk") { | ||
check(chunkIxGen(Gen.unit)) { | ||
case (c, n) => | ||
assert(c.take(n).toSeq, equalTo(c.toSeq.take(n))) | ||
} | ||
}, | ||
testM("dropWhile chunk") { | ||
check(chunkGen(intGen), toBoolFn[Random, Int]) { (c, p) => | ||
assert(c.dropWhile(p).toSeq, equalTo(c.toSeq.dropWhile(p))) | ||
} | ||
}, | ||
testM("takeWhile chunk") { | ||
check(chunkGen(intGen), toBoolFn[Random, Int]) { (c, p) => | ||
assert(c.takeWhile(p).toSeq, equalTo(c.toSeq.takeWhile(p))) | ||
} | ||
}, | ||
testM("toArray") { | ||
check(chunkGen(intGen)) { c => | ||
assert(c.toArray.toSeq, equalTo(c.toSeq)) | ||
} | ||
}, | ||
testM("foreach") { | ||
check(chunkGen(intGen)) { c => | ||
var sum = 0 | ||
c.foreach(sum += _) | ||
|
||
assert(sum, equalTo(c.toSeq.sum)) | ||
} | ||
}, | ||
testM("concat chunk") { | ||
check(chunkGen(intGen), chunkGen(intGen)) { (c1, c2) => | ||
assert((c1 ++ c2).toSeq, equalTo(c1.toSeq ++ c2.toSeq)) | ||
} | ||
}, | ||
test("chunk transitivity") { | ||
val c1 = Chunk(1, 2, 3) | ||
val c2 = Chunk(1, 2, 3) | ||
val c3 = Chunk(1, 2, 3) | ||
assert(c1 == c2 && c2 == c3 && c1 == c3, Assertion.isTrue) | ||
}, | ||
test("chunk symmetry") { | ||
val c1 = Chunk(1, 2, 3) | ||
val c2 = Chunk(1, 2, 3) | ||
assert(c1 == c2 && c2 == c1, Assertion.isTrue) | ||
}, | ||
test("chunk reflexivity") { | ||
val c1 = Chunk(1, 2, 3) | ||
assert(c1 == c1, Assertion.isTrue) | ||
}, | ||
test("chunk negation") { | ||
val c1 = Chunk(1, 2, 3) | ||
val c2 = Chunk(1, 2, 3) | ||
assert(c1 != c2 == !(c1 == c2), Assertion.isTrue) | ||
}, | ||
test("chunk substitutivity") { | ||
val c1 = Chunk(1, 2, 3) | ||
val c2 = Chunk(1, 2, 3) | ||
assert(c1 == c2 && c1.toString == c2.toString, Assertion.isTrue) | ||
}, | ||
test("chunk consistency") { | ||
val c1 = Chunk(1, 2, 3) | ||
val c2 = Chunk(1, 2, 3) | ||
assert(c1 == c2 && c1.hashCode == c2.hashCode, Assertion.isTrue) | ||
}, | ||
test("nullArrayBug") { | ||
val c = Chunk.fromArray(Array(1, 2, 3, 4, 5)) | ||
|
||
// foreach should not throw | ||
c.foreach(_ => ()) | ||
|
||
assert(c.filter(_ => false).map(_ * 2).length, equalTo(0)) | ||
}, | ||
test("toArrayOnConcatOfSlice") { | ||
val onlyOdd: Int => Boolean = _ % 2 != 0 | ||
val concat = Chunk(1, 1, 1).filter(onlyOdd) ++ | ||
Chunk(2, 2, 2).filter(onlyOdd) ++ | ||
Chunk(3, 3, 3).filter(onlyOdd) | ||
|
||
val array = concat.toArray | ||
|
||
assert(array, equalTo(Array(1, 1, 1, 3, 3, 3))) | ||
}, | ||
test("toArrayOnConcatOfEmptyAndInts") { | ||
assert(Chunk.empty ++ Chunk.fromArray(Array(1, 2, 3)), equalTo(Chunk(1, 2, 3))) | ||
}, | ||
test("filterConstFalseResultsInEmptyChunk") { | ||
assert(Chunk.fromArray(Array(1, 2, 3)).filter(_ => false), equalTo[Chunk[Int]](Chunk.empty)) | ||
}, | ||
test("def testzipAllWith") { | ||
assert(Chunk(1, 2, 3).zipAllWith(Chunk(3, 2, 1))(_ => 0, _ => 0)(_ + _), equalTo(Chunk(4, 4, 4))) && | ||
assert(Chunk(1, 2, 3).zipAllWith(Chunk(3, 2))(_ => 0, _ => 0)(_ + _), equalTo(Chunk(4, 4, 0))) && | ||
assert(Chunk(1, 2).zipAllWith(Chunk(3, 2, 1))(_ => 0, _ => 0)(_ + _), equalTo(Chunk(4, 4, 0))) | ||
} | ||
) | ||
) |
33 changes: 33 additions & 0 deletions
33
streams-tests/jvm/src/test/scala/zio/stream/ChunkUtils.scala
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package zio.stream | ||
|
||
import scala.reflect.ClassTag | ||
import zio.Chunk | ||
import zio.random.Random | ||
import zio.test.{ Gen, Sized } | ||
|
||
trait ChunkUtils { | ||
def chunkIxGen[R <: Random, A](a: Gen[R, A]): Gen[R with Sized, (Chunk[A], Int)] = | ||
for { | ||
n <- Gen.int(1, 100) | ||
i <- Gen.int(0, n - 1) | ||
vector <- Gen.vectorOfN(n)(a) | ||
chunk = Chunk.fromIterable(vector) | ||
} yield (chunk, i) | ||
|
||
def chunkGen[R <: Random, A: ClassTag](a: Gen[R, A]): Gen[R with Sized, Chunk[A]] = | ||
Gen.oneOf( | ||
Gen.const(Chunk.empty), | ||
a.map(Chunk.succeed), | ||
chunkIxGen(a).map(_._1), | ||
Gen.suspend(for { | ||
arr <- chunkGen(a) | ||
left <- Gen.int(0, arr.length) | ||
} yield arr.take(left)), | ||
Gen.suspend(for { | ||
left <- chunkGen(a) | ||
right <- chunkGen(a) | ||
} yield left ++ right) | ||
) | ||
} | ||
|
||
object ChunkUtils extends ChunkUtils with GenUtils |
Oops, something went wrong.