From 5d746b40b1416e398c6c2951e2ea684c8905f154 Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Fri, 10 Jul 2015 00:13:07 +0200 Subject: [PATCH] =act,per additional test for onTransition behaviour when initialize() called --- .../scala/akka/actor/FSMTransitionSpec.scala | 27 +++++++++++--- .../src/main/scala/akka/actor/FSM.scala | 2 ++ .../main/scala/akka/persistence/fsm/FSM.scala | 4 ++- .../persistence/PersistentActorSpec.scala | 2 +- .../fsm/PersistentFSMActorSpec.scala | 36 +++++++++++++++++++ 5 files changed, 65 insertions(+), 6 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/actor/FSMTransitionSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/FSMTransitionSpec.scala index ceae3c2c5b..baa60e49e0 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/FSMTransitionSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/FSMTransitionSpec.scala @@ -3,10 +3,10 @@ */ package akka.actor -import language.postfixOps - import akka.testkit._ + import scala.concurrent.duration._ +import scala.language.postfixOps object FSMTransitionSpec { @@ -14,6 +14,17 @@ object FSMTransitionSpec { def receive = { case _ ⇒ } } + class SendAnyTransitionFSM(target: ActorRef) extends Actor with FSM[Int, Int] { + startWith(0, 0) + when(0) { + case Event("stay", _) ⇒ stay() + case Event(_, _) ⇒ goto(0) + } + onTransition { case from -> to ⇒ target ! (from -> to) } + + initialize() + } + class MyFSM(target: ActorRef) extends Actor with FSM[Int, Unit] { startWith(0, Unit) when(0) { @@ -57,8 +68,17 @@ class FSMTransitionSpec extends AkkaSpec with ImplicitSender { "A FSM transition notifier" must { + "not trigger onTransition for stay" in { + val fsm = system.actorOf(Props(new SendAnyTransitionFSM(testActor))) + expectMsg(0 -> 0) // caused by initialize(), OK. + fsm ! "stay" // no transition event + expectNoMsg(500.millis) + fsm ! "goto" // goto(current state) + expectMsg(0 -> 0) + } + "notify listeners" in { - import FSM.{ SubscribeTransitionCallBack, CurrentState, Transition } + import FSM.{ CurrentState, SubscribeTransitionCallBack, Transition } val fsm = system.actorOf(Props(new MyFSM(testActor))) within(1 second) { @@ -113,7 +133,6 @@ class FSMTransitionSpec extends AkkaSpec with ImplicitSender { } "not trigger transition event on stay()" in { - import FSM.Transition val forward = system.actorOf(Props(new Forwarder(testActor))) val fsm = system.actorOf(Props(new OtherFSM(testActor))) diff --git a/akka-actor/src/main/scala/akka/actor/FSM.scala b/akka-actor/src/main/scala/akka/actor/FSM.scala index 9acc38d1a3..a785dcb600 100644 --- a/akka-actor/src/main/scala/akka/actor/FSM.scala +++ b/akka-actor/src/main/scala/akka/actor/FSM.scala @@ -511,6 +511,8 @@ trait FSM[S, D] extends Actor with Listeners with ActorLogging { * last call within the constructor, or [[akka.actor.Actor#preStart]] and * [[akka.actor.Actor#postRestart]] * + * An initial `currentState -> currentState` notification will be triggered by calling this method. + * * @see [[#startWith]] */ final def initialize(): Unit = makeTransition(currentState) diff --git a/akka-persistence/src/main/scala/akka/persistence/fsm/FSM.scala b/akka-persistence/src/main/scala/akka/persistence/fsm/FSM.scala index 9aefbe0917..6ca95cb5c1 100644 --- a/akka-persistence/src/main/scala/akka/persistence/fsm/FSM.scala +++ b/akka-persistence/src/main/scala/akka/persistence/fsm/FSM.scala @@ -503,7 +503,9 @@ trait FSM[S, D, E] extends Actor with Listeners with ActorLogging { /** * Verify existence of initial state and setup timers. This should be the * last call within the constructor, or [[akka.actor.Actor#preStart]] and - * [[akka.actor.Actor#postRestart]] + * [[akka.actor.Actor#postRestart]]. + * + * An initial `currentState -> currentState` notification will be triggered by calling this method. * * @see [[#startWith]] */ diff --git a/akka-persistence/src/test/scala/akka/persistence/PersistentActorSpec.scala b/akka-persistence/src/test/scala/akka/persistence/PersistentActorSpec.scala index cc7f976d24..a30f559e60 100644 --- a/akka-persistence/src/test/scala/akka/persistence/PersistentActorSpec.scala +++ b/akka-persistence/src/test/scala/akka/persistence/PersistentActorSpec.scala @@ -678,7 +678,7 @@ abstract class PersistentActorSpec(config: Config) extends PersistenceSpec(confi persistentActor ! GetState expectMsg(List("a-1", "a-2", "b-10", "b-11", "b-12", "c-10", "c-11", "c-12")) } - "recover on command failure xoxo" in { + "recover on command failure" in { val persistentActor = namedPersistentActor[Behavior3PersistentActor] persistentActor ! Cmd("b") persistentActor ! "boom" diff --git a/akka-persistence/src/test/scala/akka/persistence/fsm/PersistentFSMActorSpec.scala b/akka-persistence/src/test/scala/akka/persistence/fsm/PersistentFSMActorSpec.scala index 1abd66d037..6541898209 100644 --- a/akka-persistence/src/test/scala/akka/persistence/fsm/PersistentFSMActorSpec.scala +++ b/akka-persistence/src/test/scala/akka/persistence/fsm/PersistentFSMActorSpec.scala @@ -223,6 +223,20 @@ abstract class PersistentFSMActorSpec(config: Config) extends PersistenceSpec(co expectTerminated(recoveredFsmRef) } } + + "not trigger onTransition for stay()" taggedAs TimingTest in { + val persistenceId = name + val probe = TestProbe() + val fsmRef = system.actorOf(SimpleTransitionFSMActor.props(persistenceId, probe.ref)) + + probe.expectMsg(3.seconds, "LookingAround -> LookingAround") // caused by initialize(), OK + + fsmRef ! "goto(the same state)" // causes goto() + probe.expectMsg(3.seconds, "LookingAround -> LookingAround") + + fsmRef ! "stay" // causes stay() + probe.expectNoMsg(3.seconds) + } } } @@ -281,6 +295,28 @@ object PersistentFSMActorSpec { case class PurchaseWasMade(items: Seq[Item]) extends ReportEvent case object ShoppingCardDiscarded extends ReportEvent + class SimpleTransitionFSMActor(_persistenceId: String, reportActor: ActorRef)(implicit val domainEventClassTag: ClassTag[DomainEvent]) extends PersistentFsmActor[UserState, ShoppingCart, DomainEvent] { + override val persistenceId = _persistenceId + + startWith(LookingAround, EmptyShoppingCart) + + when(LookingAround) { + case Event("stay", _) ⇒ stay + case Event(e, _) ⇒ goto(LookingAround) + } + + onTransition { + case (from, to) ⇒ reportActor ! s"$from -> $to" + } + + override def applyEvent(domainEvent: DomainEvent, currentData: ShoppingCart): ShoppingCart = + currentData + } + object SimpleTransitionFSMActor { + def props(persistenceId: String, reportActor: ActorRef) = + Props(new SimpleTransitionFSMActor(persistenceId, reportActor)) + } + class WebStoreCustomerFSMActor(_persistenceId: String, reportActor: ActorRef)(implicit val domainEventClassTag: ClassTag[DomainEvent]) extends PersistentFsmActor[UserState, ShoppingCart, DomainEvent] { override def persistenceId = _persistenceId