2015-08-19 23:04:20 -04:00
|
|
|
/**
|
2016-02-23 12:58:39 +01:00
|
|
|
* Copyright (C) 2015-2016 Lightbend Inc. <http://www.lightbend.com>
|
2015-08-19 23:04:20 -04:00
|
|
|
*/
|
|
|
|
|
package akka.stream.scaladsl
|
|
|
|
|
|
|
|
|
|
import akka.actor.Status
|
2015-12-04 09:37:32 -05:00
|
|
|
import akka.pattern.pipe
|
2016-02-15 13:38:37 +01:00
|
|
|
import akka.stream.Attributes.inputBuffer
|
2016-02-22 20:18:15 +01:00
|
|
|
import akka.stream.{ ActorMaterializer }
|
2015-08-19 23:04:20 -04:00
|
|
|
import akka.stream.testkit.Utils._
|
2016-02-25 14:27:45 +01:00
|
|
|
import akka.stream.testkit._
|
2016-02-02 19:38:00 +01:00
|
|
|
import org.scalatest.concurrent.ScalaFutures
|
2015-12-04 09:37:32 -05:00
|
|
|
import scala.concurrent.Await
|
2015-08-19 23:04:20 -04:00
|
|
|
import scala.concurrent.duration._
|
|
|
|
|
import scala.util.control.NoStackTrace
|
2016-02-25 14:27:45 +01:00
|
|
|
import akka.testkit.AkkaSpec
|
2015-08-19 23:04:20 -04:00
|
|
|
|
2016-02-25 14:27:45 +01:00
|
|
|
class QueueSinkSpec extends AkkaSpec {
|
2015-08-19 23:04:20 -04:00
|
|
|
implicit val ec = system.dispatcher
|
2015-12-11 14:45:24 +01:00
|
|
|
implicit val materializer = ActorMaterializer()
|
2015-08-19 23:04:20 -04:00
|
|
|
|
|
|
|
|
val ex = new RuntimeException("ex") with NoStackTrace
|
|
|
|
|
|
|
|
|
|
val noMsgTimeout = 300.millis
|
|
|
|
|
|
2015-12-04 09:37:32 -05:00
|
|
|
"An QueueSinkSpec" must {
|
2015-08-19 23:04:20 -04:00
|
|
|
|
|
|
|
|
"send the elements as result of future" in assertAllStagesStopped {
|
2015-12-09 23:16:48 +01:00
|
|
|
val expected = List(Some(1), Some(2), Some(3), None)
|
2015-12-04 09:37:32 -05:00
|
|
|
val queue = Source(expected.flatten).runWith(Sink.queue())
|
2015-12-09 23:16:48 +01:00
|
|
|
expected foreach { v ⇒
|
|
|
|
|
queue.pull() pipeTo testActor
|
|
|
|
|
expectMsg(v)
|
|
|
|
|
}
|
2015-08-19 23:04:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"allow to have only one future waiting for result in each point of time" in assertAllStagesStopped {
|
|
|
|
|
val probe = TestPublisher.manualProbe[Int]()
|
2015-12-18 11:51:17 +01:00
|
|
|
val queue = Source.fromPublisher(probe).runWith(Sink.queue())
|
2015-08-19 23:04:20 -04:00
|
|
|
val sub = probe.expectSubscription()
|
|
|
|
|
val future = queue.pull()
|
|
|
|
|
val future2 = queue.pull()
|
2016-04-05 15:57:47 +02:00
|
|
|
an[IllegalStateException] shouldBe thrownBy { Await.result(future2, remainingOrDefault) }
|
2015-08-19 23:04:20 -04:00
|
|
|
|
|
|
|
|
sub.sendNext(1)
|
|
|
|
|
future.pipeTo(testActor)
|
|
|
|
|
expectMsg(Some(1))
|
|
|
|
|
|
|
|
|
|
sub.sendComplete()
|
2015-12-04 09:37:32 -05:00
|
|
|
queue.pull()
|
2015-08-19 23:04:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"wait for next element from upstream" in assertAllStagesStopped {
|
|
|
|
|
val probe = TestPublisher.manualProbe[Int]()
|
2015-12-18 11:51:17 +01:00
|
|
|
val queue = Source.fromPublisher(probe).runWith(Sink.queue())
|
2015-08-19 23:04:20 -04:00
|
|
|
val sub = probe.expectSubscription()
|
|
|
|
|
|
|
|
|
|
queue.pull().pipeTo(testActor)
|
|
|
|
|
expectNoMsg(noMsgTimeout)
|
|
|
|
|
|
|
|
|
|
sub.sendNext(1)
|
|
|
|
|
expectMsg(Some(1))
|
|
|
|
|
sub.sendComplete()
|
2015-12-04 09:37:32 -05:00
|
|
|
queue.pull()
|
2015-08-19 23:04:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"fail future on stream failure" in assertAllStagesStopped {
|
|
|
|
|
val probe = TestPublisher.manualProbe[Int]()
|
2015-12-18 11:51:17 +01:00
|
|
|
val queue = Source.fromPublisher(probe).runWith(Sink.queue())
|
2015-08-19 23:04:20 -04:00
|
|
|
val sub = probe.expectSubscription()
|
|
|
|
|
|
|
|
|
|
queue.pull().pipeTo(testActor)
|
|
|
|
|
expectNoMsg(noMsgTimeout)
|
|
|
|
|
|
|
|
|
|
sub.sendError(ex)
|
|
|
|
|
expectMsg(Status.Failure(ex))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"fail future when stream failed" in assertAllStagesStopped {
|
|
|
|
|
val probe = TestPublisher.manualProbe[Int]()
|
2015-12-18 11:51:17 +01:00
|
|
|
val queue = Source.fromPublisher(probe).runWith(Sink.queue())
|
2015-08-19 23:04:20 -04:00
|
|
|
val sub = probe.expectSubscription()
|
2015-12-04 09:37:32 -05:00
|
|
|
sub.sendError(ex)
|
2015-08-19 23:04:20 -04:00
|
|
|
|
2016-04-05 15:57:47 +02:00
|
|
|
the[Exception] thrownBy { Await.result(queue.pull(), remainingOrDefault) } should be(ex)
|
2015-08-19 23:04:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"timeout future when stream cannot provide data" in assertAllStagesStopped {
|
|
|
|
|
val probe = TestPublisher.manualProbe[Int]()
|
2015-12-18 11:51:17 +01:00
|
|
|
val queue = Source.fromPublisher(probe).runWith(Sink.queue())
|
2015-08-19 23:04:20 -04:00
|
|
|
val sub = probe.expectSubscription()
|
|
|
|
|
|
|
|
|
|
queue.pull().pipeTo(testActor)
|
|
|
|
|
expectNoMsg(noMsgTimeout)
|
|
|
|
|
|
|
|
|
|
sub.sendNext(1)
|
|
|
|
|
expectMsg(Some(1))
|
|
|
|
|
sub.sendComplete()
|
2015-12-04 09:37:32 -05:00
|
|
|
queue.pull()
|
2015-08-19 23:04:20 -04:00
|
|
|
}
|
|
|
|
|
|
2016-01-16 12:17:19 -05:00
|
|
|
"fail pull future when stream is completed" in assertAllStagesStopped {
|
|
|
|
|
val probe = TestPublisher.manualProbe[Int]()
|
|
|
|
|
val queue = Source.fromPublisher(probe).runWith(Sink.queue())
|
|
|
|
|
val sub = probe.expectSubscription()
|
|
|
|
|
|
|
|
|
|
queue.pull().pipeTo(testActor)
|
|
|
|
|
sub.sendNext(1)
|
|
|
|
|
expectMsg(Some(1))
|
|
|
|
|
|
|
|
|
|
sub.sendComplete()
|
|
|
|
|
Await.result(queue.pull(), noMsgTimeout) should be(None)
|
|
|
|
|
|
|
|
|
|
queue.pull().onFailure { case e ⇒ e.isInstanceOf[IllegalStateException] should ===(true) }
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-02 19:38:00 +01:00
|
|
|
"keep on sending even after the buffer has been full" in assertAllStagesStopped {
|
2016-02-16 13:09:01 +01:00
|
|
|
val bufferSize = 16
|
|
|
|
|
val streamElementCount = bufferSize + 4
|
|
|
|
|
val sink = Sink.queue[Int]()
|
|
|
|
|
.withAttributes(inputBuffer(bufferSize, bufferSize))
|
|
|
|
|
val (probe, queue) = Source(1 to streamElementCount)
|
|
|
|
|
.alsoToMat(Flow[Int].take(bufferSize).watchTermination()(Keep.right).to(Sink.ignore))(Keep.right)
|
|
|
|
|
.toMat(sink)(Keep.both)
|
2016-02-02 19:38:00 +01:00
|
|
|
.run()
|
|
|
|
|
probe.futureValue should ===(akka.Done)
|
2016-02-16 13:09:01 +01:00
|
|
|
for (i ← 1 to streamElementCount) {
|
2016-02-02 19:38:00 +01:00
|
|
|
queue.pull() pipeTo testActor
|
|
|
|
|
expectMsg(Some(i))
|
|
|
|
|
}
|
|
|
|
|
queue.pull() pipeTo testActor
|
|
|
|
|
expectMsg(None)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-16 13:09:01 +01:00
|
|
|
"work with one element buffer" in assertAllStagesStopped {
|
|
|
|
|
val sink = Sink.queue[Int]().withAttributes(inputBuffer(1, 1))
|
|
|
|
|
val probe = TestPublisher.manualProbe[Int]()
|
|
|
|
|
val queue = Source.fromPublisher(probe).runWith(sink)
|
|
|
|
|
val sub = probe.expectSubscription()
|
|
|
|
|
|
|
|
|
|
queue.pull().pipeTo(testActor)
|
|
|
|
|
sub.sendNext(1) // should pull next element
|
|
|
|
|
expectMsg(Some(1))
|
|
|
|
|
|
|
|
|
|
queue.pull().pipeTo(testActor)
|
|
|
|
|
expectNoMsg() // element requested but buffer empty
|
|
|
|
|
sub.sendNext(2)
|
|
|
|
|
expectMsg(Some(2))
|
|
|
|
|
|
|
|
|
|
sub.sendComplete()
|
|
|
|
|
Await.result(queue.pull(), noMsgTimeout) should be(None)
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-15 13:38:37 +01:00
|
|
|
"fail to materialize with zero sized input buffer" in {
|
|
|
|
|
an[IllegalArgumentException] shouldBe thrownBy {
|
|
|
|
|
Source.single(()).runWith(Sink.queue().withAttributes(inputBuffer(0, 0)))
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-08-19 23:04:20 -04:00
|
|
|
}
|
|
|
|
|
}
|