
io.iteratee.testing.EnumeratorSuite.scala Maven / Gradle / Ivy
The newest version!
package io.iteratee.testing
import cats.{Eval, Monad}
import cats.kernel.laws.discipline.MonoidTests
import cats.laws.discipline.{MonadTests, SemigroupalTests}
import io.iteratee.Enumerator
import io.iteratee.modules.{EnumerateeModule, EnumeratorModule, IterateeModule, Module}
import scala.Predef._
abstract class EnumeratorSuite[F[_]: Monad] extends ModuleSuite[F] {
this: Module[F] with EnumerateeModule[F] with EnumeratorModule[F] with IterateeModule[F] =>
type EnumeratorF[E] = Enumerator[F, E]
implicit val isomorphisms: SemigroupalTests.Isomorphisms[EnumeratorF] =
SemigroupalTests.Isomorphisms.invariant[EnumeratorF]
checkLaws(s"Enumerator[$monadName, Int]", MonoidTests[Enumerator[F, Int]].monoid)
checkLaws(s"Enumerator[$monadName, Int]", MonadTests[EnumeratorF].monad[Int, Int, Int])
"liftToEnumerator" should "lift a value in a context into an enumerator" in forAll { (i: Int) =>
assert(liftToEnumerator(F.pure(i)).toVector === F.pure(Vector(i)))
}
"liftMEval" should "lift a value in a context into an enumerator" in forAll { (i: Int) =>
var counter = 0
val eval = Eval.later {
counter += i
F.pure(i)
}
val enumerator = Enumerator.liftMEval(eval)
assert(counter === 0)
assert(enumerator.toVector === F.pure(Vector(i)))
assert(counter === i)
}
"enumerate" should "enumerate varargs values" in forAll { (xs: List[Int]) =>
assert(enumerate(xs: _*).toVector === F.pure(xs.toVector))
}
"empty" should "not enumerate any values" in {
assert(empty[Int].toVector === F.pure(Vector.empty))
}
"enumOne" should "enumerate a single value" in forAll { (i: Int) =>
assert(enumOne(i).toVector === F.pure(Vector(i)))
}
"enumIterable" should "enumerate values from an iterable" in forAll { (xs: Iterable[Int], chunkSize: Int) =>
assert(enumIterable(xs, chunkSize).toVector === F.pure(xs.toVector))
}
"enumStream" should "enumerate values from a stream" in forAll { (xs: Stream[Int], chunkSize: Int) =>
assert(enumStream(xs, chunkSize).toVector === F.pure(xs.toVector))
}
"enumList" should "enumerate values from a list" in forAll { (xs: List[Int]) =>
assert(enumList(xs).toVector === F.pure(xs.toVector))
}
"enumVector" should "enumerate values from a vector" in forAll { (xs: Vector[Int]) =>
assert(enumVector(xs).toVector === F.pure(xs))
}
it should "enumerate a vector with a single element" in forAll { (x: Int) =>
assert(enumVector(Vector(x)).toVector === F.pure(Vector(x)))
}
"enumIndexedSeq" should "enumerate a slice of values from an indexed sequence" in {
forAll { (xs: Vector[Int], start: Int, count: Int) =>
// Check for overflow (workaround for #11990 in 2.13.2).
val until = if (start + count < 0) Int.MaxValue else start + count
assert(enumIndexedSeq(xs, start, until).toVector === F.pure(xs.slice(start, until)))
}
}
it should "enumerate a slice of the first hundred values from an indexed sequence" in {
forAll { (xs: Vector[Int]) =>
assert(enumIndexedSeq(xs, 0, 100).toVector === F.pure(xs.slice(0, 100)))
}
}
"repeat" should "repeat a value" in forAll { (i: Int, count: Short) =>
assert(repeat(i).into(takeI(count.toInt)) === F.pure(Vector.fill(count.toInt)(i)))
}
"iterate" should "enumerate values by applying a function iteratively" in forAll { (n: Int, count: Short) =>
assert(iterate(n)(_ + 1).into(takeI(count.toInt)) === F.pure(Vector.iterate(n, count.toInt)(_ + 1)))
}
"iterateM" should "enumerate values by applying a pure function iteratively" in {
forAll { (n: Int, count: Short) =>
assert(iterateM(n)(i => F.pure(i + 1)).into(takeI(count.toInt)) === F.pure(Vector.iterate(n, count.toInt)(_ + 1)))
}
}
"iterateUntil" should "apply a function until it returns an empty result" in forAll { (n: Short) =>
val count = math.abs(n.toInt)
val enumerator = iterateUntil(0)(i => if (i == count) None else Some(i + 1))
assert(enumerator.toVector === F.pure((0 to count).toVector))
}
it should "work with finished iteratee (#71)" in forAll { (n: Short, fewer: Byte) =>
val count = math.abs(n.toInt)
val taken = n - math.abs(fewer.toInt)
val enumerator = iterateUntil(0)(i => if (i == count) None else Some(i + 1))
assert(enumerator.into(takeI(taken)) === F.pure((0 to count).toVector.take(taken)))
}
"iterateUntilM" should "apply a pure function until it returns an empty result" in forAll { (n: Short) =>
val count = math.abs(n.toInt)
val enumerator = iterateUntilM(0)(i => F.pure(if (i == count) None else Some(i + 1)))
assert(enumerator.toVector === F.pure((0 to count).toVector))
}
it should "work with finished iteratee (#71)" in forAll { (n: Short, fewer: Byte) =>
val count = math.abs(n.toInt)
val taken = n - math.abs(fewer.toInt)
val enumerator = iterateUntilM(0)(i => F.pure(if (i == count) None else Some(i + 1)))
assert(enumerator.into(takeI(taken)) === F.pure((0 to count).toVector.take(taken)))
}
"toVector" should "collect all the values in the stream" in forAll { (eav: EnumeratorAndValues[Int]) =>
assert(eav.enumerator.toVector === F.pure(eav.values))
}
"prepend" should "prepend a value to a stream" in forAll { (eav: EnumeratorAndValues[Int], v: Int) =>
assert(eav.enumerator.prepend(v).toVector === F.pure(v +: eav.values))
}
it should "work with a done iteratee" in {
assert(enumOne(0).append(enumOne(2).prepend(1)).into(head) === F.pure((Some(0))))
}
"bindM" should "bind through Option" in forAll { (eav: EnumeratorAndValues[Int]) =>
val enumeratorF: F[Option[Enumerator[F, String]]] = eav.enumerator.bindM(v => Option(enumOne(v.toString)))
assert(enumeratorF.map(_.map(_.toVector)) === F.pure(Option(F.pure(eav.values.map(_.toString)))))
}
"intoEnumerator" should "be available on values in a context" in forAll { (i: Int) =>
import syntax._
assert(F.pure(i).intoEnumerator.toVector === F.pure(Vector(i)))
}
"flatten" should "collapse enumerated values in the context" in forAll { (v: Int) =>
assert(enumOne(F.pure(v)).flatten[Int].toVector === F.pure(Vector(v)))
}
"reduced" should "reduce the stream with a function" in forAll { (eav: EnumeratorAndValues[Int]) =>
assert(eav.enumerator.reduced(Vector.empty[Int])(_ :+ _).toVector === F.pure(Vector(eav.values)))
}
it should "reduce the stream with a pure function" in forAll { (eav: EnumeratorAndValues[Int]) =>
assert(eav.enumerator.reducedM(Vector.empty[Int])((i, s) => F.pure(i :+ s)).toVector === F.pure(Vector(eav.values)))
}
"map" should "transform the stream" in forAll { (eav: EnumeratorAndValues[Int]) =>
assert(eav.enumerator.map(_ + 1).toVector === F.pure(eav.values.map(_ + 1)))
}
"flatMapM" should "transform the stream with a pure effectful function" in forAll { (eav: EnumeratorAndValues[Int]) =>
assert(eav.enumerator.flatMapM(i => F.pure(i + 1)).toVector === F.pure(eav.values.map(_ + 1)))
}
"flatMap" should "transform the stream with a function into enumerators" in {
forAll { (eav: EnumeratorAndValues[Int]) =>
val enumerator = eav.enumerator.flatMap(v => enumVector(Vector(v, v)))
assert(enumerator.toVector === F.pure(eav.values.flatMap(v => Vector(v, v))))
}
}
"take" should "match using an Enumeratee directly" in forAll { (eav: EnumeratorAndValues[Int], n: Long) =>
assert(eav.enumerator.take(n) === eav.enumerator.through(take(n)))
}
"takeWhile" should "match using an Enumeratee directly" in {
forAll { (eav: EnumeratorAndValues[Int], p: Int => Boolean) =>
assert(eav.enumerator.takeWhile(p) === eav.enumerator.through(takeWhile(p)))
}
}
"takeWhileM" should "match using an Enumeratee directly" in {
forAll { (eav: EnumeratorAndValues[Int], p: Int => Boolean) =>
assert(eav.enumerator.takeWhileM(p.andThen(F.pure)) === eav.enumerator.through(takeWhileM(p.andThen(F.pure))))
}
}
"drop" should "match using an Enumeratee directly" in forAll { (eav: EnumeratorAndValues[Int], n: Long) =>
assert(eav.enumerator.drop(n) === eav.enumerator.through(drop(n)))
}
"dropWhile" should "match using an Enumeratee directly" in {
forAll { (eav: EnumeratorAndValues[Int], p: Int => Boolean) =>
assert(eav.enumerator.dropWhile(p) === eav.enumerator.through(dropWhile(p)))
}
}
"dropWhileM" should "match using an Enumeratee directly" in {
forAll { (eav: EnumeratorAndValues[Int], p: Int => Boolean) =>
assert(eav.enumerator.dropWhileM(p.andThen(F.pure)) === eav.enumerator.through(dropWhileM(p.andThen(F.pure))))
}
}
"scan" should "match using an Enumeratee directly" in {
forAll { (eav: EnumeratorAndValues[Int], init: String, f: (String, Int) => String) =>
assert(eav.enumerator.scan(init)(f) === eav.enumerator.through(scan(init)(f)))
}
}
"scanM" should "match using an Enumeratee directly" in {
forAll { (eav: EnumeratorAndValues[Int], init: String, f: (String, Int) => String) =>
val ff: (String, Int) => F[String] = (s, i) => F.pure(f(s, i))
assert(eav.enumerator.scanM(init)(ff) === eav.enumerator.through(scanM(init)(ff)))
}
}
"collect" should "match using an Enumeratee directly" in {
forAll { (eav: EnumeratorAndValues[Int], f: Int => Option[String]) =>
val pf: PartialFunction[Int, String] = {
case x if f(x).isDefined => f(x).get
}
assert(eav.enumerator.collect(pf) === eav.enumerator.through(collect(pf)))
}
}
"filter" should "match using an Enumeratee directly" in forAll { (eav: EnumeratorAndValues[Int], p: Int => Boolean) =>
assert(eav.enumerator.filter(p) === eav.enumerator.through(filter(p)))
}
"filterM" should "match using an Enumeratee directly" in {
forAll { (eav: EnumeratorAndValues[Int], p: Int => Boolean) =>
assert(eav.enumerator.filterM(p.andThen(F.pure)) === eav.enumerator.through(filterM(p.andThen(F.pure))))
}
}
"sequenceI" should "match using an Enumeratee directly" in forAll { (eav: EnumeratorAndValues[Int]) =>
assert(eav.enumerator.sequenceI(takeI(2)) === eav.enumerator.through(sequenceI(takeI(2))))
}
"uniq" should "match using an Enumeratee directly" in forAll { (eav: EnumeratorAndValues[Int]) =>
assert(eav.enumerator.uniq === eav.enumerator.through(uniq))
}
"zipWithIndex" should "match using an Enumeratee directly" in forAll { (eav: EnumeratorAndValues[Int]) =>
assert(eav.enumerator.zipWithIndex === eav.enumerator.through(zipWithIndex))
}
"grouped" should "match using an Enumeratee directly" in forAll { (eav: EnumeratorAndValues[Int]) =>
assert(eav.enumerator.grouped(2) === eav.enumerator.through(grouped(2)))
}
"splitOn" should "match using an Enumeratee directly" in {
forAll { (eav: EnumeratorAndValues[Int], p: Int => Boolean) =>
assert(eav.enumerator.splitOn(p) === eav.enumerator.through(splitOn(p)))
}
}
"cross" should "match using an Enumeratee directly" in forAll { (eav: EnumeratorAndValues[Int]) =>
assert(eav.enumerator.cross(enumList(List(1, 2))) === eav.enumerator.through(cross(enumList(List(1, 2)))))
}
"intersperse" should "match using an Enumeratee directly" in forAll { (eav: EnumeratorAndValues[Int]) =>
assert(eav.enumerator.intersperse(-1) === eav.enumerator.through(intersperse(-1)))
}
}
abstract class StackSafeEnumeratorSuite[F[_]: Monad] extends EnumeratorSuite[F] {
this: Module[F] with EnumerateeModule[F] with EnumeratorModule[F] with IterateeModule[F] =>
"StackUnsafe.enumStream" should "be consistent with enumStream" in forAll { (xs: Stream[Int]) =>
val expected = enumStream(xs).toVector
assert(Enumerator.StackUnsafe.enumStream[F, Int](xs).toVector === expected)
}
"StackUnsafe.repeat" should "be consistent with repeat" in forAll { (i: Int, count: Short) =>
val expected = repeat(i).into(takeI(count.toInt))
assert(Enumerator.StackUnsafe.repeat[F, Int](i).into(takeI(count.toInt)) === expected)
}
"StackUnsafe.iterate" should "be consistent with iterate" in forAll { (n: Int, count: Short) =>
val expected = iterate(n)(_ + 1).into(takeI(count.toInt))
assert(Enumerator.StackUnsafe.iterate[F, Int](n)(_ + 1).into(takeI(count.toInt)) === expected)
}
"StackUnsafe.iterateM" should "be consistent with iterateM" in forAll { (n: Int, count: Short) =>
val expected = iterateM(n)(i => F.pure(i + 1)).into(takeI(count.toInt))
assert(Enumerator.StackUnsafe.iterateM[F, Int](n)(i => F.pure(i + 1)).into(takeI(count.toInt)) === expected)
}
"StackUnsafe.iterateUntil" should "be consistent with iterateUntil" in forAll { (n: Short) =>
val count = math.abs(n.toInt)
val expected = iterateUntil(0)(i => if (i == count) None else Some(i + 1)).toVector
val enumerator = Enumerator.StackUnsafe.iterateUntil[F, Int](0)(i => if (i == count) None else Some(i + 1))
assert(enumerator.toVector === expected)
}
"StackUnsafe.iterateUntilM" should "be consistent with iterateUntilM" in forAll { (n: Short) =>
val count = math.abs(n.toInt)
val expected = iterateUntilM(0)(i => F.pure(if (i == count) None else Some(i + 1))).toVector
val enumerator = Enumerator.StackUnsafe.iterateUntilM[F, Int](0)(i => F.pure(if (i == count) None else Some(i + 1)))
assert(enumerator.toVector === expected)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy