|
Play Framework/Scala example source code file (IterateesSpec.scala)
The IterateesSpec.scala Play Framework example source code
/*
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
*/
package play.api.libs.iteratee
import org.specs2.mutable._
import java.io.OutputStream
import java.util.concurrent.{ CountDownLatch, TimeUnit }
import scala.concurrent.{ ExecutionContext, Promise, Future, Await }
import scala.util.{ Failure, Success, Try }
object IterateesSpec extends Specification
with IterateeSpecification with ExecutionSpecification {
def checkFoldResult[A, E](i: Iteratee[A, E], expected: Step[A, E]) = {
mustExecute(1) { foldEC =>
await(i.fold(s => Future.successful(s))(foldEC)) must equalTo(expected)
}
}
def checkUnflattenResult[A, E](i: Iteratee[A, E], expected: Step[A, E]) = {
await(i.unflatten) must equalTo(expected)
}
def checkImmediateFoldFailure[A, E](i: Iteratee[A, E]) = {
mustExecute(1) { foldEC =>
val e = new Exception("exception")
val result = ready(i.fold(_ => throw e)(foldEC))
result.value must equalTo(Some(Failure(e)))
}
}
def checkFutureFoldFailure[A, E](i: Iteratee[A, E]) = {
mustExecute(1, 1) { (foldEC, folderEC) =>
val e = new Exception("exception")
val preparedFolderEC = folderEC.prepare()
val result = ready(i.fold(_ => Future(throw e)(preparedFolderEC))(foldEC))
result.value must equalTo(Some(Failure(e)))
}
}
def mustTranslate3To(x: Int)(f: Iteratee[Int, Int] => Iteratee[Int, Int]) = {
await(f(Done(3)).unflatten) must equalTo(Step.Done(x, Input.Empty))
}
"Flattened iteratees" should {
val i = Iteratee.flatten(Future.successful(Done(1, Input.El("x"))))
"delegate folding to their promised iteratee" in {
checkFoldResult(i, Step.Done(1, Input.El("x")))
}
"return immediate fold errors in promise" in {
checkImmediateFoldFailure(i)
}
"return future fold errors in promise" in {
checkFutureFoldFailure(i)
}
"support unflattening their state" in {
checkUnflattenResult(i, Step.Done(1, Input.El("x")))
}
}
"Done iteratees" should {
val i = Done(1, Input.El("x"))
"fold with their result and unused input" in {
checkFoldResult(i, Step.Done(1, Input.El("x")))
}
"return immediate fold errors in promise" in {
checkImmediateFoldFailure(i)
}
"return future fold errors in promise" in {
checkFutureFoldFailure(i)
}
"fold input with fold1" in {
mustExecute(1) { foldEC =>
mustTranslate3To(5)(it => Iteratee.flatten(it.fold1(
(a, i) => Future.successful(Done(a + 2, i)),
_ => ???,
(_, _) => ???)(foldEC)))
}
}
"fold input with pureFold" in {
mustExecute(1) { foldEC =>
mustTranslate3To(9)(it => Iteratee.flatten(it.pureFold(_ => Done[Int, Int](9))(foldEC)))
}
}
"fold input with pureFlatFold" in {
mustExecute(1) { foldEC =>
mustTranslate3To(9)(_.pureFlatFold(_ => Done[Int, Int](9))(foldEC))
}
}
"fold input with flatFold0" in {
mustExecute(1) { foldEC =>
mustTranslate3To(9)(_.flatFold0(_ => Future.successful(Done[Int, Int](9)))(foldEC))
}
}
"fold input with flatFold" in {
mustExecute(1) { foldEC =>
mustTranslate3To(9)(_.flatFold(
(_, _) => Future.successful(Done[Int, Int](9)),
_ => ???,
(_, _) => ???)(foldEC))
}
}
"support unflattening their state" in {
checkUnflattenResult(i, Step.Done(1, Input.El("x")))
}
"flatMap directly to result when no remaining input" in {
mustExecute(1) { flatMapEC =>
await(Done(3).flatMap((x: Int) => Done[Int, Int](x * 2))(flatMapEC).unflatten) must equalTo(Step.Done(6, Input.Empty))
}
}
"flatMap result and process remaining input with Done" in {
mustExecute(1) { flatMapEC =>
await(Done(3, Input.El("remaining")).flatMap((x: Int) => Done[String, Int](x * 2))(flatMapEC).unflatten) must equalTo(Step.Done(6, Input.El("remaining")))
}
}
"flatMap result and process remaining input with Cont" in {
mustExecute(1) { flatMapEC =>
await(Done(3, Input.El("remaining")).flatMap((x: Int) => Cont(in => Done[String, Int](x * 2, in)))(flatMapEC).unflatten) must equalTo(Step.Done(6, Input.El("remaining")))
}
}
"flatMap result and process remaining input with Error" in {
mustExecute(1) { flatMapEC =>
await(Done(3, Input.El("remaining")).flatMap((x: Int) => Error("error", Input.El("bad")))(flatMapEC).unflatten) must equalTo(Step.Error("error", Input.El("bad")))
}
}
"flatMap result with flatMapM" in {
mustExecute(1) { flatMapEC =>
mustTranslate3To(6)(_.flatMapM((x: Int) => Future.successful(Done[Int, Int](x * 2)))(flatMapEC))
}
}
"fold result with flatMapInput" in {
mustExecute(1) { flatMapEC =>
mustTranslate3To(1)(_.flatMapInput(_ => Done(1))(flatMapEC))
}
}
"concatenate unused input with flatMapTraversable" in {
mustExecute(1) { flatMapEC =>
await(Done(3, Input.El(List(1, 2))).flatMapTraversable(_ => Done[List[Int], Int](4, Input.El(List(3, 4))))(
implicitly[List[Int] => scala.collection.TraversableLike[Int, List[Int]]],
implicitly[scala.collection.generic.CanBuildFrom[List[Int], Int, List[Int]]],
flatMapEC).unflatten) must equalTo(Step.Done(4, Input.El(List(1, 2, 3, 4))))
}
}
}
"Cont iteratees" should {
val k: Input[String] => Iteratee[String, Int] = x => ???
val i = Cont(k)
"fold with their continuation" in {
checkFoldResult(i, Step.Cont(k))
}
"return immediate fold errors in promise" in {
checkImmediateFoldFailure(i)
}
"return future fold errors in promise" in {
checkFutureFoldFailure(i)
}
"support unflattening their state" in {
checkUnflattenResult(i, Step.Cont(k))
}
"flatMap recursively" in {
mustExecute(1) { flatMapEC =>
await(Iteratee.flatten(Cont[Int, Int](_ => Done(3)).flatMap((x: Int) => Done[Int, Int](x * 2))(flatMapEC).feed(Input.El(11))).unflatten) must equalTo(Step.Done(6, Input.Empty))
}
}
}
"Error iteratees" should {
val i = Error("msg", Input.El("x"))
"fold with their message and the input that caused the error" in {
checkFoldResult(i, Step.Error("msg", Input.El("x")))
}
"return immediate fold errors in promise" in {
checkImmediateFoldFailure(i)
}
"return future fold errors in promise" in {
checkFutureFoldFailure(i)
}
"support unflattening their state" in {
checkUnflattenResult(i, Step.Error("msg", Input.El("x")))
}
"flatMap to an error" in {
mustExecute(0) { flatMapEC =>
await(Error("msg", Input.El("bad")).flatMap((x: Int) => Done("done"))(flatMapEC).unflatten) must equalTo(Step.Error("msg", Input.El("bad")))
}
}
}
"Iteratees fed multiple inputs" should {
"map the final iteratee's result (with map)" in {
mustExecute(4, 1) { (foldEC, mapEC) =>
await(Enumerator(1, 2, 3, 4) |>>> Iteratee.fold[Int, Int](0)(_ + _)(foldEC).map(_ * 2)(mapEC)) must equalTo(20)
}
}
"map the final iteratee's result (with mapM)" in {
mustExecute(4, 1) { (foldEC, mapEC) =>
await(Enumerator(1, 2, 3, 4) |>>> Iteratee.fold[Int, Int](0)(_ + _)(foldEC).mapM(x => Future.successful(x * 2))(mapEC)) must equalTo(20)
}
}
}
"Iteratee.fold" should {
"fold input" in {
mustExecute(4) { foldEC =>
await(Enumerator(1, 2, 3, 4) |>>> Iteratee.fold[Int, Int](0)(_ + _)(foldEC)) must equalTo(10)
}
}
}
"Iteratee.foldM" should {
"fold input" in {
mustExecute(4) { foldEC =>
await(Enumerator(1, 2, 3, 4) |>>> Iteratee.foldM[Int, Int](0)((x, y) => Future.successful(x + y))(foldEC)) must equalTo(10)
}
}
}
"Iteratee.fold2" should {
"fold input" in {
mustExecute(4) { foldEC =>
val folder = (x: Int, y: Int) => Future.successful((x + y, false))
await(Enumerator(1, 2, 3, 4) |>>> Iteratee.fold2[Int, Int](0)(folder)(foldEC)) must equalTo(10)
}
}
"fold input, stopping early" in {
mustExecute(3) { foldEC =>
val folder = (x: Int, y: Int) => Future.successful((x + y, (y > 2)))
await(Enumerator(1, 2, 3, 4) |>>> Iteratee.fold2[Int, Int](0)(folder)(foldEC)) must equalTo(6)
}
}
}
"Iteratee.foldM" should {
"fold input" in {
mustExecute(4) { foldEC =>
await(Enumerator(1, 2, 3, 4) |>>> Iteratee.fold1[Int, Int](Future.successful(0))((x, y) => Future.successful(x + y))(foldEC)) must equalTo(10)
}
}
}
"Iteratee.recover" should {
val expected = "expected"
val unexpected = "should not be returned"
"do nothing on a Done iteratee" in {
mustExecute(1) { implicit foldEC =>
val it = done(expected).recover { case t: Throwable => unexpected }
val actual = await(Enumerator(unexpected) |>>> it)
actual must equalTo(expected)
}
}
"do nothing on an eventually Done iteratee" in {
mustExecute(1) { implicit foldEC =>
val it = delayed(done(expected)).recover { case t: Throwable => unexpected }
val actual = await(Enumerator(unexpected) |>>> it)
actual must equalTo(expected)
}
}
"recover with the expected fallback value from an Error iteratee" in {
mustExecute(2) { implicit foldEC =>
val it = error(unexpected).recover { case t: Throwable => expected }
val actual = await(Enumerator(unexpected) |>>> it)
actual must equalTo(expected)
}
}
"leave the Error iteratee unchanged if the Exception type doesn't match the partial function" in {
mustExecute(1) { implicit foldEC =>
val it = error(expected).recover { case t: IllegalArgumentException => unexpected }
val actual = await((Enumerator(unexpected) |>>> it).failed)
actual.getMessage must equalTo(expected)
}
}
"recover with the expected fallback value from an eventually Error iteratee" in {
mustExecute(2) { implicit foldEC =>
val it = delayed(error(unexpected)).recover { case t: Throwable => expected }
val actual = await(Enumerator(unexpected) |>>> it)
actual must equalTo(expected)
}
}
"recover with the expected fallback value from an iteratee that eventually throws an exception" in {
mustExecute(2) { implicit foldEC =>
val it = delayed(throw new RuntimeException(unexpected)).recover { case t: Throwable => expected }
val actual = await(Enumerator(unexpected) |>>> it)
actual must equalTo(expected)
}
}
"do nothing on a Cont iteratee that becomes Done with input" in {
mustExecute(1) { implicit foldEC =>
val it = cont(input => done(input)).recover { case t: Throwable => unexpected }
val actual = await(Enumerator(expected) |>>> it)
actual must equalTo(expected)
}
}
"do nothing on an eventually Cont iteratee that becomes Done with input" in {
mustExecute(1) { implicit foldEC =>
val it = delayed(cont(input => done(input))).recover { case t: Throwable => unexpected }
val actual = await(Enumerator(expected) |>>> it)
actual must equalTo(expected)
}
}
"do nothing on a Cont iteratee that eventually becomes Done with input" in {
mustExecute(1) { implicit foldEC =>
val it = cont(input => delayed(done(input))).recover { case t: Throwable => unexpected }
val actual = await(Enumerator(expected) |>>> it)
actual must equalTo(expected)
}
}
"do nothing on an Cont iteratee that eventually becomes Done with input after several steps" in {
mustExecute(3) { implicit foldEC =>
val it = delayed(
cont(input1 => delayed(
cont(input2 => delayed(
cont(input3 => delayed(
done(input1 + input2 + input3)
))
))
))
).recover { case t: Throwable => unexpected }
val actual = await(Enumerator(expected, expected, expected) |>>> it)
actual must equalTo(expected * 3)
}
}
"recover with the expected fallback value from a Cont iteratee that eventually becomes an Error iteratee after several steps" in {
mustExecute(4) { implicit foldEC =>
val it = delayed(
cont(input1 => delayed(
cont(input2 => delayed(
cont(input3 => delayed(
error(input1 + input2 + input3)
))
))
))
).recover { case t: Throwable => expected }
val actual = await(Enumerator(unexpected, unexpected, unexpected) |>>> it)
actual must equalTo(expected)
}
}
}
"Iteratee.recoverM" should {
val expected = "expected"
val unexpected = "should not be returned"
"do nothing on a Done iteratee" in {
mustExecute(1) { implicit foldEC =>
val it = done(expected).recoverM { case t: Throwable => Future.successful(unexpected) }
val actual = await(Enumerator(unexpected) |>>> it)
actual must equalTo(expected)
}
}
"do nothing on a Done iteratee even if the recover block gets a failed Future" in {
mustExecute(1) { implicit foldEC =>
val it = done(expected).recoverM { case t: Throwable => Future.failed(new RuntimeException(unexpected)) }
val actual = await(Enumerator(unexpected) |>>> it)
actual must equalTo(expected)
}
}
"recover with the expected fallback Future from an Error iteratee" in {
mustExecute(2) { implicit foldEC =>
val it = error(unexpected).recoverM { case t: Throwable => Future.successful(expected) }
val actual = await(Enumerator(unexpected) |>>> it)
actual must equalTo(expected)
}
}
"leave the Error iteratee unchanged if the Exception type doesn't match the partial function" in {
mustExecute(1) { implicit foldEC =>
val it = error(expected).recoverM { case t: IllegalArgumentException => Future.successful(unexpected) }
val actual = await((Enumerator(unexpected) |>>> it).failed)
actual.getMessage must equalTo(expected)
}
}
"return a failed Future if you try to recover from an Error iteratee with a failed Future" in {
mustExecute(2) { implicit foldEC =>
val exception = new RuntimeException(expected)
val it = error(unexpected).recoverM { case t: Throwable => Future.failed(exception) }
val actual = await((Enumerator(unexpected) |>>> it).failed)
actual must equalTo(exception)
}
}
}
"Iteratee.recoverWith" should {
val expected = "expected"
val unexpected = "should not be returned"
"do nothing on a Done iteratee" in {
mustExecute(1) { implicit foldEC =>
val it = done(expected).recoverWith { case t: Throwable => done(unexpected) }
val actual = await(Enumerator(unexpected) |>>> it)
actual must equalTo(expected)
}
}
"do nothing on a Done iteratee even if the recover block gets an error Iteratee" in {
mustExecute(1) { implicit foldEC =>
val it = done(expected).recoverWith { case t: Throwable => error(unexpected) }
val actual = await(Enumerator(unexpected) |>>> it)
actual must equalTo(expected)
}
}
"recover with the expected fallback Iteratee from an Error iteratee" in {
mustExecute(1) { implicit foldEC =>
val it = error(unexpected).recoverWith { case t: Throwable => done(expected) }
val actual = await(Enumerator(unexpected) |>>> it)
actual must equalTo(expected)
}
}
"leave the Error iteratee unchanged if the Exception type doesn't match the partial function" in {
mustExecute(1) { implicit foldEC =>
val it = error(expected).recoverWith { case t: IllegalArgumentException => done(unexpected) }
val actual = await((Enumerator(unexpected) |>>> it).failed)
actual.getMessage must equalTo(expected)
}
}
"return a failed Future if you try to recover from an Error iteratee with an Error iteratee" in {
mustExecute(1) { implicit foldEC =>
val it = error(unexpected).recoverWith { case t: Throwable => error(expected) }
val actual = await((Enumerator(unexpected) |>>> it).failed)
actual.getMessage must equalTo(expected)
}
}
}
"Iteratee.consume" should {
"return its concatenated input" in {
val s = List(List(1, 2), List(3), List(4, 5))
val r = List(1, 2, 3, 4, 5)
await(Enumerator.enumerateSeq1(s) |>>> Iteratee.consume[List[Int]]()) must equalTo(r)
}
}
"Iteratee.getChunks" should {
"return its input as a list" in {
val s = List(1, 2, 3, 4, 5)
await(Enumerator.enumerateSeq1(s) |>>> Iteratee.getChunks[Int]) must equalTo(s)
}
}
"Iteratee.ignore" should {
"never throw an OutOfMemoryError when consuming large input" in {
// Work out how many arrays we'd need to create to trigger an OutOfMemoryError
val arraySize = 1000000
val tooManyArrays = (Runtime.getRuntime.maxMemory / arraySize).toInt + 1
val iterator = Iterator.range(0, tooManyArrays).map(_ => new Array[Byte](arraySize))
import play.api.libs.iteratee.Execution.Implicits.defaultExecutionContext
await(Enumerator.enumerate(iterator) |>>> Iteratee.ignore[Array[Byte]]) must_== (())
}
}
}
Other Play Framework source code examplesHere is a short list of links related to this Play Framework IterateesSpec.scala source code file: |
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.