2015-05-11 00:09:59 +02:00
/* *
2016-02-23 12:58:39 +01:00
* Copyright ( C ) 2009 - 2016 Lightbend Inc . < http : //www.lightbend.com>
2015-05-11 00:09:59 +02:00
*/
package akka.stream.impl.fusing
2016-04-17 20:33:56 -07:00
import akka.stream.Attributes
import akka.stream.impl.fusing.GraphStages.SimpleLinearGraphStage
2015-10-31 14:46:10 +01:00
import akka.stream.stage._
2016-02-25 14:27:45 +01:00
import akka.testkit.AkkaSpec
2015-05-11 00:09:59 +02:00
import akka.stream.testkit.Utils.TE
import scala.concurrent.duration._
2015-11-03 18:56:46 +01:00
class LifecycleInterpreterSpec extends AkkaSpec with GraphInterpreterSpecKit {
2015-05-11 00:09:59 +02:00
import akka.stream.Supervision._
"Interpreter" must {
2016-04-17 20:33:56 -07:00
"call preStart in order on stages" in new OneBoundedSetup [ String ] (
PreStartAndPostStopIdentity ( onStart = ( ) ⇒ testActor ! "start-a" ) ,
PreStartAndPostStopIdentity ( onStart = ( ) ⇒ testActor ! "start-b" ) ,
PreStartAndPostStopIdentity ( onStart = ( ) ⇒ testActor ! "start-c" ) ) {
2015-05-11 00:09:59 +02:00
expectMsg ( "start-a" )
expectMsg ( "start-b" )
expectMsg ( "start-c" )
expectNoMsg ( 300. millis )
upstream . onComplete ( )
}
2016-04-17 20:33:56 -07:00
"call postStop in order on stages - when upstream completes" in new OneBoundedSetup [ String ] (
2015-05-11 00:09:59 +02:00
PreStartAndPostStopIdentity ( onUpstreamCompleted = ( ) ⇒ testActor ! "complete-a" , onStop = ( ) ⇒ testActor ! "stop-a" ) ,
PreStartAndPostStopIdentity ( onUpstreamCompleted = ( ) ⇒ testActor ! "complete-b" , onStop = ( ) ⇒ testActor ! "stop-b" ) ,
2016-04-17 20:33:56 -07:00
PreStartAndPostStopIdentity ( onUpstreamCompleted = ( ) ⇒ testActor ! "complete-c" , onStop = ( ) ⇒ testActor ! "stop-c" ) ) {
2015-05-11 00:09:59 +02:00
upstream . onComplete ( )
expectMsg ( "complete-a" )
expectMsg ( "stop-a" )
expectMsg ( "complete-b" )
expectMsg ( "stop-b" )
expectMsg ( "complete-c" )
expectMsg ( "stop-c" )
expectNoMsg ( 300. millis )
}
2016-04-17 20:33:56 -07:00
"call postStop in order on stages - when upstream onErrors" in new OneBoundedSetup [ String ] (
2015-05-11 00:09:59 +02:00
PreStartAndPostStopIdentity (
onUpstreamFailed = ex ⇒ testActor ! ex . getMessage ,
2016-04-17 20:33:56 -07:00
onStop = ( ) ⇒ testActor ! "stop-c" ) ) {
2015-05-11 00:09:59 +02:00
val msg = "Boom! Boom! Boom!"
upstream . onError ( TE ( msg ) )
expectMsg ( msg )
expectMsg ( "stop-c" )
expectNoMsg ( 300. millis )
}
2016-04-17 20:33:56 -07:00
"call postStop in order on stages - when downstream cancels" in new OneBoundedSetup [ String ] (
2015-05-11 00:09:59 +02:00
PreStartAndPostStopIdentity ( onStop = ( ) ⇒ testActor ! "stop-a" ) ,
PreStartAndPostStopIdentity ( onStop = ( ) ⇒ testActor ! "stop-b" ) ,
2016-04-17 20:33:56 -07:00
PreStartAndPostStopIdentity ( onStop = ( ) ⇒ testActor ! "stop-c" ) ) {
2015-05-11 00:09:59 +02:00
downstream . cancel ( )
expectMsg ( "stop-c" )
expectMsg ( "stop-b" )
expectMsg ( "stop-a" )
expectNoMsg ( 300. millis )
}
2016-04-17 20:33:56 -07:00
"call preStart before postStop" in new OneBoundedSetup [ String ] (
PreStartAndPostStopIdentity ( onStart = ( ) ⇒ testActor ! "start-a" , onStop = ( ) ⇒ testActor ! "stop-a" ) ) {
2015-05-11 00:09:59 +02:00
expectMsg ( "start-a" )
expectNoMsg ( 300. millis )
upstream . onComplete ( )
expectMsg ( "stop-a" )
expectNoMsg ( 300. millis )
}
2016-04-17 20:33:56 -07:00
"onError when preStart fails" in new OneBoundedSetup [ String ] (
PreStartFailer ( ( ) ⇒ throw TE ( "Boom!" ) ) ) {
2015-05-11 00:09:59 +02:00
lastEvents ( ) should === ( Set ( Cancel , OnError ( TE ( "Boom!" ) ) ) )
}
2016-04-17 20:33:56 -07:00
"not blow up when postStop fails" in new OneBoundedSetup [ String ] (
PostStopFailer ( ( ) ⇒ throw TE ( "Boom!" ) ) ) {
2015-05-11 00:09:59 +02:00
upstream . onComplete ( )
lastEvents ( ) should === ( Set ( OnComplete ) )
}
2016-04-17 20:33:56 -07:00
"onError when preStart fails with stages after" in new OneBoundedSetup [ String ] (
Map ( ( x : Int ) ⇒ x , stoppingDecider ) . toGS ,
2015-05-11 00:09:59 +02:00
PreStartFailer ( ( ) ⇒ throw TE ( "Boom!" ) ) ,
2016-04-17 20:33:56 -07:00
Map ( ( x : Int ) ⇒ x , stoppingDecider ) . toGS ) {
2015-05-11 00:09:59 +02:00
lastEvents ( ) should === ( Set ( Cancel , OnError ( TE ( "Boom!" ) ) ) )
}
2016-04-17 20:33:56 -07:00
"continue with stream shutdown when postStop fails" in new OneBoundedSetup [ String ] (
PostStopFailer ( ( ) ⇒ throw TE ( "Boom!" ) ) ) {
2015-05-11 00:09:59 +02:00
lastEvents ( ) should === ( Set ( ) )
upstream . onComplete ( )
lastEvents should === ( Set ( OnComplete ) )
}
2016-04-17 20:33:56 -07:00
"postStop when pushAndFinish called if upstream completes with pushAndFinish" in new OneBoundedSetup [ String ] (
new PushFinishStage ( onPostStop = ( ) ⇒ testActor ! "stop" ) ) {
2015-05-11 00:09:59 +02:00
lastEvents ( ) should be ( Set . empty )
downstream . requestOne ( )
lastEvents ( ) should be ( Set ( RequestOne ) )
upstream . onNextAndComplete ( "foo" )
lastEvents ( ) should be ( Set ( OnNext ( "foo" ) , OnComplete ) )
expectMsg ( "stop" )
}
2016-04-17 20:33:56 -07:00
"postStop when pushAndFinish called with pushAndFinish if indirect upstream completes with pushAndFinish" in new OneBoundedSetup [ String ] (
Map ( ( x : Any ) ⇒ x , stoppingDecider ) . toGS ,
2015-05-11 00:09:59 +02:00
new PushFinishStage ( onPostStop = ( ) ⇒ testActor ! "stop" ) ,
2016-04-17 20:33:56 -07:00
Map ( ( x : Any ) ⇒ x , stoppingDecider ) . toGS ) {
2015-05-11 00:09:59 +02:00
lastEvents ( ) should be ( Set . empty )
downstream . requestOne ( )
lastEvents ( ) should be ( Set ( RequestOne ) )
upstream . onNextAndComplete ( "foo" )
lastEvents ( ) should be ( Set ( OnNext ( "foo" ) , OnComplete ) )
expectMsg ( "stop" )
}
2016-04-17 20:33:56 -07:00
"postStop when pushAndFinish called with pushAndFinish if upstream completes with pushAndFinish and downstream immediately pulls" in new OneBoundedSetup [ String ] (
2015-05-11 00:09:59 +02:00
new PushFinishStage ( onPostStop = ( ) ⇒ testActor ! "stop" ) ,
2016-07-08 14:22:18 +02:00
Fold ( "" , ( x : String , y : String ) ⇒ x + y ) ) {
2015-05-11 00:09:59 +02:00
lastEvents ( ) should be ( Set . empty )
downstream . requestOne ( )
lastEvents ( ) should be ( Set ( RequestOne ) )
upstream . onNextAndComplete ( "foo" )
lastEvents ( ) should be ( Set ( OnNext ( "foo" ) , OnComplete ) )
expectMsg ( "stop" )
}
}
2015-10-31 14:46:10 +01:00
private [ akka ] case class PreStartAndPostStopIdentity [ T ] (
2016-06-02 14:06:57 +02:00
onStart : ( ) ⇒ Unit = ( ) ⇒ ( ) ,
onStop : ( ) ⇒ Unit = ( ) ⇒ ( ) ,
onUpstreamCompleted : ( ) ⇒ Unit = ( ) ⇒ ( ) ,
onUpstreamFailed : Throwable ⇒ Unit = ex ⇒ ( ) ) extends SimpleLinearGraphStage [ T ] {
2015-10-31 14:46:10 +01:00
2016-04-17 20:33:56 -07:00
override def createLogic ( attributes : Attributes ) : GraphStageLogic =
new GraphStageLogic ( shape ) with InHandler with OutHandler {
override def preStart ( ) : Unit = onStart ( )
override def postStop ( ) : Unit = onStop ( )
2015-10-31 14:46:10 +01:00
2016-04-17 20:33:56 -07:00
override def onPush ( ) : Unit = push ( out , grab ( in ) )
override def onPull ( ) : Unit = pull ( in )
2015-10-31 14:46:10 +01:00
2016-04-17 20:33:56 -07:00
override def onUpstreamFinish ( ) : Unit = {
onUpstreamCompleted ( )
super . onUpstreamFinish ( )
}
override def onUpstreamFailure ( cause : Throwable ) : Unit = {
onUpstreamFailed ( cause )
super . onUpstreamFailure ( cause )
}
setHandlers ( in , out , this )
}
2015-10-31 14:46:10 +01:00
2016-04-17 20:33:56 -07:00
override def toString = "PreStartAndPostStopIdentity"
2015-10-31 14:46:10 +01:00
}
2016-04-17 20:33:56 -07:00
private [ akka ] case class PreStartFailer [ T ] ( pleaseThrow : ( ) ⇒ Unit ) extends SimpleLinearGraphStage [ T ] {
2015-10-31 14:46:10 +01:00
2016-04-17 20:33:56 -07:00
override def createLogic ( attributes : Attributes ) : GraphStageLogic =
new GraphStageLogic ( shape ) with InHandler with OutHandler {
override def preStart ( ) : Unit = pleaseThrow ( )
2015-10-31 14:46:10 +01:00
2016-04-17 20:33:56 -07:00
override def onPush ( ) : Unit = push ( out , grab ( in ) )
override def onPull ( ) : Unit = pull ( in )
setHandlers ( in , out , this )
}
override def toString = "PreStartFailer"
2015-10-31 14:46:10 +01:00
}
2016-04-17 20:33:56 -07:00
private [ akka ] case class PostStopFailer [ T ] ( pleaseThrow : ( ) ⇒ Unit ) extends SimpleLinearGraphStage [ T ] {
override def createLogic ( attributes : Attributes ) : GraphStageLogic =
new GraphStageLogic ( shape ) with InHandler with OutHandler {
override def onUpstreamFinish ( ) : Unit = completeStage ( )
2015-10-31 14:46:10 +01:00
2016-04-17 20:33:56 -07:00
override def onPush ( ) : Unit = push ( out , grab ( in ) )
override def onPull ( ) : Unit = pull ( in )
override def postStop ( ) : Unit = pleaseThrow ( )
setHandlers ( in , out , this )
}
override def toString = "PostStopFailer"
2015-10-31 14:46:10 +01:00
}
// This test is related to issue #17351
2016-04-17 20:33:56 -07:00
private [ akka ] class PushFinishStage ( onPostStop : ( ) ⇒ Unit = ( ) ⇒ ( ) ) extends SimpleLinearGraphStage [ Any ] {
2015-10-31 14:46:10 +01:00
2016-04-17 20:33:56 -07:00
override def createLogic ( attributes : Attributes ) : GraphStageLogic =
new GraphStageLogic ( shape ) with InHandler with OutHandler {
override def onPush ( ) : Unit = {
push ( out , grab ( in ) )
completeStage ( )
}
override def onPull ( ) : Unit = pull ( in )
2015-10-31 14:46:10 +01:00
2016-04-17 20:33:56 -07:00
override def onUpstreamFinish ( ) : Unit = failStage ( TE ( "Cannot happen" ) )
2015-10-31 14:46:10 +01:00
2016-04-17 20:33:56 -07:00
override def postStop ( ) : Unit = onPostStop ( )
setHandlers ( in , out , this )
}
override def toString = "PushFinish"
}
2015-05-11 00:09:59 +02:00
}