在FSM的实现中(旧故事:对Elevator / Lift进行建模),我试图看看我是否可以测试FSM是否遵循从同一状态转换到同一状态。看起来,它没有发生,但令我感到惊讶的是因为根据this对StackOverFlow的讨论,该解决方案在Akka 2.4.x中可用。
这是代码的相关部分:
class LiftCarriageWithMovingState extends Actor
with FSM[LiftState,LiftData]
with ActorLogging
{
import context._
val mxTimeToWaitStopping = 500 milliseconds
private var pendingPassengerRequests: Vector[NextStop] = Vector.empty
private var currentFloorID = 0 // Always start at Ground Floor
val timeToReachNextFloor = 1000 millis // up or down, same time taken
private val dontCare: StateFunction = { /* ... */ }
private val powerYourselfOff: StateFunction = { /* ... */ }
private val powerYourselfOn: StateFunction = { /* ... */ }
private val beReady: StateFunction = { /* ... */ }
private val moveToWaitingPassenger: StateFunction = { /* ... */ }
private val transportPassengerToDest: StateFunction = {/* ... */ }
private val actWhenStoppedForLongEnough: StateFunction = {
case Event(StateTimeout,_) =>
if (this.pendingPassengerRequests isEmpty) {
println("timeout in Stopped, no passenger requests pending")
goto (Stopped)
}
else {
println("timeout in Stopped, moving to floor(" + this.pendingPassengerRequests.head + ")")
simulateMovementTo(this.pendingPassengerRequests.head)
goto(Moving)
}
}
startWith(PoweredOff,InitialData)
when (PoweredOff) (powerYourselfOn orElse
dontCare)
when (PoweredOn) (powerYourselfOff orElse
beReady orElse
dontCare)
when (Ready) (moveToWaitingPassenger orElse
transportPassengerToDest )
when (Stopped) (actWhenStoppedForLongEnough orElse
moveToWaitingPassenger orElse
transportPassengerToDest )
when (Moving) {
case Event(ReachedFloor(floorID, PurposeOfMovement.ToWelcomeInAnWaitingPassenger),_) =>
currentFloorID = pendingPassengerRequests.head.floorID
pendingPassengerRequests = pendingPassengerRequests.tail
goto (Stopped) forMax(this.mxTimeToWaitStopping)
case Event(ReachedFloor(floorID,PurposeOfMovement.ToAllowATransportedPassengerAlight),_) =>
currentFloorID = pendingPassengerRequests.head.floorID
pendingPassengerRequests = pendingPassengerRequests.tail
goto (Stopped) forMax(this.mxTimeToWaitStopping)
// .... Other events
}
whenUnhandled {
// Event handlers...
}
onTransition {
case Stopped -> Stopped => {
println("Remaining in Stopped ...")
}
}
// Rest of the Actor
我已经修剪了不必要的部分,因为重点在于: 当演员收到
时Event(ReachedFloor(floorID, PurposeOfMovement.ToWelcomeInAnWaitingPassenger),_)
当它处于Moving状态时,它会切换到Stopping状态,并指定超时。当FSM停止时该计时器触发时,应该执行的处理程序被命名(如上所示):
actWhenStoppedForLongEnough
在这里,我正在检查某个退出条件,如果我不满意,我会再次切换回相同的停止状态。我现在正在使用 goto(),但原始代码 stay()。
我也设置了一个 onTransition 块(再次显示在上面),以证明过渡确实发生了。
onTransition {
case Stopped -> Stopped => {
println("Remaining in Stopped ...")
}
}
当我运行代码时, println 语句根本不会被执行。
我也有一个测试代码;相关的测试用例是这样的:
var carriage: ActorRef = _
override def beforeAll(): Unit = {
super.beforeAll()
carriage = TestActorRef(Props(new LiftCarriageWithMovingState))
carriage ! SubscribeTransitionCallBack(testActor)
}
"A LiftCarriage" must {
"be ready, when it settles down after being PoweredOn" in {
expectMsg(Duration(100, TimeUnit.MILLISECONDS), new CurrentState(carriage,PoweredOff))
carriage ! InstructedToPowerOn
expectMsg (new Transition(carriage,PoweredOff,PoweredOn))
carriage ! BeReady
expectMsg((new Transition(carriage,PoweredOn,Ready)))
}
"move to where a passenger is waiting, if ready" in {
carriage ! ReportCurrentFloor
expectMsg(StoppedAt(0))
carriage ! PassengerIsWaitingAt(3)
expectMsg(new Transition(carriage,Ready,Moving))
expectMsg(Duration(5000, TimeUnit.MILLISECONDS), new Transition(carriage,Moving, Stopped))
carriage ! ReportCurrentFloor
expectMsg(StoppedAt(3))
}
"let a passenger in and transport her to her destination" in {
carriage ! ReportCurrentFloor
expectMsg(StoppedAt(3))
carriage ! PassengerRequestsATransportTo(Vector(7))
expectMsg(new Transition(carriage,Stopped,Moving))
expectMsg(Duration(5000, TimeUnit.MILLISECONDS), new Transition(carriage,Moving,Stopped))
carriage ! ReportCurrentFloor
expectMsg(StoppedAt(7))
// This always times out and fails.
expectMsg(Duration(5000, TimeUnit.MILLISECONDS), new Transition(carriage,Stopped,Stopped))
}
我正在努力思考我理解中的差距是否存在,但是失败了。任何提示?
这是 build.sbt :
name := """akka-sample-fsm-scala"""
version := "2.4"
scalaVersion := "2.11.8"
libraryDependencies ++= Seq(
// https://mvnrepository.com/artifact/com.typesafe.akka/akka-actor_2.11
"com.typesafe.akka" % "akka-actor_2.11" % "2.4.12",
// https://mvnrepository.com/artifact/org.scalatest/scalatest_2.11
"org.scalatest" % "scalatest_2.11" % "2.1.3",
// https://mvnrepository.com/artifact/com.typesafe.akka/akka-testkit_2.11,
"com.typesafe.akka" % "akka-testkit_2.11" % "2.3.15"
)
另一个question - 与我的很相似 - 早些时候被问过,但我没有看到任何回复。可能是,答案已经找到但尚未分享。