* str #20262 reduce should fail explicitly on empty stream * str #20262 document reduce behaviour on empty stream
This commit is contained in:
parent
503a77f515
commit
b9ab232cac
8 changed files with 67 additions and 14 deletions
|
|
@ -54,6 +54,24 @@ class FlowReduceSpec extends AkkaSpec {
|
||||||
the[Exception] thrownBy Await.result(future, 3.seconds) should be(error)
|
the[Exception] thrownBy Await.result(future, 3.seconds) should be(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"fail on empty stream using Source.runReduce" in assertAllStagesStopped {
|
||||||
|
val result = Source.empty[Int].runReduce(_ + _)
|
||||||
|
val ex = intercept[NoSuchElementException] { Await.result(result, 3.seconds) }
|
||||||
|
ex.getMessage should include("empty stream")
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail on empty stream using Flow.reduce" in assertAllStagesStopped {
|
||||||
|
val result = Source.empty[Int].via(reduceFlow).runWith(Sink.fold(0)(_ + _))
|
||||||
|
val ex = intercept[NoSuchElementException] { Await.result(result, 3.seconds) }
|
||||||
|
ex.getMessage should include("empty stream")
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail on empty stream using Sink.reduce" in assertAllStagesStopped {
|
||||||
|
val result = Source.empty[Int].runWith(reduceSink)
|
||||||
|
val ex = intercept[NoSuchElementException] { Await.result(result, 3.seconds) }
|
||||||
|
ex.getMessage should include("empty stream")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1158,31 +1158,36 @@ private[stream] final class DropWithin[T](timeout: FiniteDuration) extends Simpl
|
||||||
private[stream] final class Reduce[T](f: (T, T) ⇒ T) extends SimpleLinearGraphStage[T] {
|
private[stream] final class Reduce[T](f: (T, T) ⇒ T) extends SimpleLinearGraphStage[T] {
|
||||||
override def initialAttributes: Attributes = DefaultAttributes.reduce
|
override def initialAttributes: Attributes = DefaultAttributes.reduce
|
||||||
|
|
||||||
override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) {
|
override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with InHandler with OutHandler { self ⇒
|
||||||
override def toString = s"Reduce.Logic(aggregator=$aggregator)"
|
override def toString = s"Reduce.Logic(aggregator=$aggregator)"
|
||||||
|
|
||||||
var aggregator: T = _
|
var aggregator: T = _
|
||||||
|
|
||||||
|
// Initial input handler
|
||||||
setHandler(in, new InHandler {
|
setHandler(in, new InHandler {
|
||||||
override def onPush(): Unit = {
|
override def onPush(): Unit = {
|
||||||
aggregator = grab(in)
|
aggregator = grab(in)
|
||||||
pull(in)
|
pull(in)
|
||||||
setHandler(in, rest)
|
setHandler(in, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def onUpstreamFinish(): Unit =
|
||||||
|
failStage(new NoSuchElementException("reduce over empty stream"))
|
||||||
})
|
})
|
||||||
def rest = new InHandler {
|
|
||||||
override def onPush(): Unit = {
|
override def onPush(): Unit = {
|
||||||
aggregator = f(aggregator, grab(in))
|
aggregator = f(aggregator, grab(in))
|
||||||
pull(in)
|
pull(in)
|
||||||
}
|
|
||||||
override def onUpstreamFinish(): Unit = {
|
|
||||||
push(out, aggregator)
|
|
||||||
completeStage()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setHandler(out, new OutHandler {
|
override def onPull(): Unit = pull(in)
|
||||||
override def onPull(): Unit = pull(in)
|
|
||||||
})
|
override def onUpstreamFinish(): Unit = {
|
||||||
|
push(out, aggregator)
|
||||||
|
completeStage()
|
||||||
|
}
|
||||||
|
|
||||||
|
setHandler(out, self)
|
||||||
}
|
}
|
||||||
override def toString = "Reduce"
|
override def toString = "Reduce"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -573,6 +573,11 @@ final class Flow[-In, +Out, +Mat](delegate: scaladsl.Flow[In, Out, Mat]) extends
|
||||||
* Applies the given function towards its current and next value,
|
* Applies the given function towards its current and next value,
|
||||||
* yielding the next current value.
|
* yielding the next current value.
|
||||||
*
|
*
|
||||||
|
* If the stream is empty (i.e. completes before signalling any elements),
|
||||||
|
* the reduce stage will fail its downstream with a [[NoSuchElementException]],
|
||||||
|
* which is semantically in-line with that Scala's standard library collections
|
||||||
|
* do in such situations.
|
||||||
|
*
|
||||||
* '''Emits when''' upstream completes
|
* '''Emits when''' upstream completes
|
||||||
*
|
*
|
||||||
* '''Backpressures when''' downstream backpressures
|
* '''Backpressures when''' downstream backpressures
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,11 @@ object Sink {
|
||||||
* The returned [[java.util.concurrent.CompletionStage]] will be completed with value of the final
|
* The returned [[java.util.concurrent.CompletionStage]] will be completed with value of the final
|
||||||
* function evaluation when the input stream ends, or completed with `Failure`
|
* function evaluation when the input stream ends, or completed with `Failure`
|
||||||
* if there is a failure signaled in the stream.
|
* if there is a failure signaled in the stream.
|
||||||
|
*
|
||||||
|
* If the stream is empty (i.e. completes before signalling any elements),
|
||||||
|
* the reduce stage will fail its downstream with a [[NoSuchElementException]],
|
||||||
|
* which is semantically in-line with that Scala's standard library collections
|
||||||
|
* do in such situations.
|
||||||
*/
|
*/
|
||||||
def reduce[In](f: function.Function2[In, In, In]): Sink[In, CompletionStage[In]] =
|
def reduce[In](f: function.Function2[In, In, In]): Sink[In, CompletionStage[In]] =
|
||||||
new Sink(scaladsl.Sink.reduce[In](f.apply).toCompletionStage())
|
new Sink(scaladsl.Sink.reduce[In](f.apply).toCompletionStage())
|
||||||
|
|
|
||||||
|
|
@ -498,6 +498,11 @@ final class Source[+Out, +Mat](delegate: scaladsl.Source[Out, Mat]) extends Grap
|
||||||
* The returned [[java.util.concurrent.CompletionStage]] will be completed with value of the final
|
* The returned [[java.util.concurrent.CompletionStage]] will be completed with value of the final
|
||||||
* function evaluation when the input stream ends, or completed with `Failure`
|
* function evaluation when the input stream ends, or completed with `Failure`
|
||||||
* if there is a failure is signaled in the stream.
|
* if there is a failure is signaled in the stream.
|
||||||
|
*
|
||||||
|
* If the stream is empty (i.e. completes before signalling any elements),
|
||||||
|
* the reduce stage will fail its downstream with a [[NoSuchElementException]],
|
||||||
|
* which is semantically in-line with that Scala's standard library collections
|
||||||
|
* do in such situations.
|
||||||
*/
|
*/
|
||||||
def runReduce[U >: Out](f: function.Function2[U, U, U], materializer: Materializer): CompletionStage[U] =
|
def runReduce[U >: Out](f: function.Function2[U, U, U], materializer: Materializer): CompletionStage[U] =
|
||||||
runWith(Sink.reduce(f), materializer)
|
runWith(Sink.reduce(f), materializer)
|
||||||
|
|
|
||||||
|
|
@ -773,6 +773,11 @@ trait FlowOps[+Out, +Mat] {
|
||||||
* Applies the given function towards its current and next value,
|
* Applies the given function towards its current and next value,
|
||||||
* yielding the next current value.
|
* yielding the next current value.
|
||||||
*
|
*
|
||||||
|
* If the stream is empty (i.e. completes before signalling any elements),
|
||||||
|
* the reduce stage will fail its downstream with a [[NoSuchElementException]],
|
||||||
|
* which is semantically in-line with that Scala's standard library collections
|
||||||
|
* do in such situations.
|
||||||
|
*
|
||||||
* '''Emits when''' upstream completes
|
* '''Emits when''' upstream completes
|
||||||
*
|
*
|
||||||
* '''Backpressures when''' downstream backpressures
|
* '''Backpressures when''' downstream backpressures
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,11 @@ object Sink {
|
||||||
* The returned [[scala.concurrent.Future]] will be completed with value of the final
|
* The returned [[scala.concurrent.Future]] will be completed with value of the final
|
||||||
* function evaluation when the input stream ends, or completed with `Failure`
|
* function evaluation when the input stream ends, or completed with `Failure`
|
||||||
* if there is a failure signaled in the stream.
|
* if there is a failure signaled in the stream.
|
||||||
|
*
|
||||||
|
* If the stream is empty (i.e. completes before signalling any elements),
|
||||||
|
* the reduce stage will fail its downstream with a [[NoSuchElementException]],
|
||||||
|
* which is semantically in-line with that Scala's standard library collections
|
||||||
|
* do in such situations.
|
||||||
*/
|
*/
|
||||||
def reduce[T](f: (T, T) ⇒ T): Sink[T, Future[T]] =
|
def reduce[T](f: (T, T) ⇒ T): Sink[T, Future[T]] =
|
||||||
Flow[T].reduce(f).toMat(Sink.head)(Keep.right).named("reduceSink")
|
Flow[T].reduce(f).toMat(Sink.head)(Keep.right).named("reduceSink")
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,11 @@ final class Source[+Out, +Mat](private[stream] override val module: Module)
|
||||||
* The returned [[scala.concurrent.Future]] will be completed with value of the final
|
* The returned [[scala.concurrent.Future]] will be completed with value of the final
|
||||||
* function evaluation when the input stream ends, or completed with `Failure`
|
* function evaluation when the input stream ends, or completed with `Failure`
|
||||||
* if there is a failure signaled in the stream.
|
* if there is a failure signaled in the stream.
|
||||||
|
*
|
||||||
|
* If the stream is empty (i.e. completes before signalling any elements),
|
||||||
|
* the reduce stage will fail its downstream with a [[NoSuchElementException]],
|
||||||
|
* which is semantically in-line with that Scala's standard library collections
|
||||||
|
* do in such situations.
|
||||||
*/
|
*/
|
||||||
def runReduce[U >: Out](f: (U, U) ⇒ U)(implicit materializer: Materializer): Future[U] =
|
def runReduce[U >: Out](f: (U, U) ⇒ U)(implicit materializer: Materializer): Future[U] =
|
||||||
runWith(Sink.reduce(f))
|
runWith(Sink.reduce(f))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue