Skip to content

Commit

Permalink
ZIO Test: Migrate ChunkSpec (zio#1651)
Browse files Browse the repository at this point in the history
* Add test framework

* Migrate ChunkSpec

* fmt
  • Loading branch information
Regis Kuckaertz authored Sep 12, 2019
1 parent 2798463 commit b99b0b0
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 188 deletions.
2 changes: 2 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,9 @@ lazy val streamsTests = crossProject(JSPlatform, JVMPlatform)
.in(file("streams-tests"))
.dependsOn(streams)
.dependsOn(coreTests % "test->test;compile->compile")
.dependsOn(testRunner % "test->test;compile->compile")
.settings(stdSettings("zio-streams-tests"))
.settings(testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework"))
.settings(buildInfoSettings)
.settings(streamReplSettings)
.enablePlugins(BuildInfoPlugin)
Expand Down
345 changes: 157 additions & 188 deletions streams-tests/jvm/src/test/scala/zio/stream/ChunkSpec.scala
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 streams-tests/jvm/src/test/scala/zio/stream/ChunkUtils.scala
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
Loading

0 comments on commit b99b0b0

Please sign in to comment.