@@ -25,7 +25,7 @@ import Control.Rematch (equalTo)
2525#if ! MIN_VERSION_base(4,6,0)
2626import Prelude hiding (catch , drop )
2727#else
28- import Prelude hiding (drop )
28+ import Prelude hiding (drop , (*>) )
2929#endif
3030
3131import Test.Framework as TF (defaultMain , testGroup , Test )
@@ -89,14 +89,82 @@ switchFsmAlt =
8989 (pick (await (event :: Event Check ) (reply stateData))
9090 (await (event :: Event Reset ) (always $ \ Reset -> put initCount >> enter Off ))))
9191
92+ blockingFsm :: SendPort () -> Step State ()
93+ blockingFsm sp = initState Off ()
94+ ^. ((event :: Event () )
95+ *> (allState $ \ () -> (lift $ sleep (seconds 10 ) >> sendChan sp () ) >> resume))
96+ .| ((event :: Event Stop )
97+ ~> ( ((== ExitNormal ) ~? (\ _ -> (liftIO $ putStrLn " resuming..." ) >> resume) )
98+ {- let's verify that we can't override
99+ a normal shutdown sequence... -}
100+ .| ((== ExitShutdown ) ~? const resume)
101+ ))
102+
103+ deepFSM :: SendPort () -> SendPort () -> Step State ()
104+ deepFSM on off = initState Off ()
105+ ^. ((event :: Event State ) ~> (allState $ \ s -> enter s))
106+ .| ( (Off ~@ resume)
107+ |> ((event :: Event () )
108+ ~> (allState $ \ s -> (lift $ sendChan off s) >> resume))
109+ )
110+ .| ( (On ~@ resume)
111+ |> ((event :: Event () )
112+ ~> (allState $ \ s -> (lift $ sendChan on s) >> resume))
113+ )
114+
115+ waitForDown :: MonitorRef -> Process DiedReason
116+ waitForDown ref =
117+ receiveWait [ matchIf (\ (ProcessMonitorNotification ref' _ _) -> ref == ref')
118+ (\ (ProcessMonitorNotification _ _ dr) -> return dr) ]
119+
120+ verifyOuterStateHandler :: Process ()
121+ verifyOuterStateHandler = do
122+ (spOn, rpOn) <- newChan
123+ (spOff, rpOff) <- newChan
124+
125+ pid <- start Off () $ deepFSM spOn spOff
126+
127+ send pid On
128+ send pid ()
129+ Nothing <- receiveChanTimeout (asTimeout $ seconds 3 ) rpOff
130+ () <- receiveChan rpOn
131+
132+ send pid Off
133+ send pid ()
134+ Nothing <- receiveChanTimeout (asTimeout $ seconds 3 ) rpOn
135+ () <- receiveChan rpOff
136+
137+ kill pid " bye bye"
138+
139+ verifyMailboxHandling :: Process ()
140+ verifyMailboxHandling = do
141+ (sp, rp) <- newChan :: Process (SendPort () , ReceivePort () )
142+ pid <- start Off () (blockingFsm sp)
143+
144+ send pid ()
145+ exit pid ExitNormal
146+
147+ sleep $ seconds 5
148+ alive <- isProcessAlive pid
149+ alive `shouldBe` equalTo True
150+
151+ -- we should resume after the ExitNormal handler runs, and get back into the ()
152+ -- handler due to safeWait (*>) which adds a `safe` filter check for the given type
153+ () <- receiveChan rp
154+
155+ exit pid ExitShutdown
156+ monitor pid >>= waitForDown
157+ alive' <- isProcessAlive pid
158+ alive' `shouldBe` equalTo False
159+
92160verifyStopBehaviour :: Process ()
93161verifyStopBehaviour = do
94162 pid <- start Off initCount switchFsm
95163 alive <- isProcessAlive pid
96164 alive `shouldBe` equalTo True
97165
98166 exit pid $ ExitOther " foobar"
99- sleep $ seconds 5
167+ monitor pid >>= waitForDown
100168 alive' <- isProcessAlive pid
101169 alive' `shouldBe` equalTo False
102170
@@ -132,7 +200,7 @@ walkingAnFsmTree pid = do
132200 mrst' `shouldBe` equalTo On
133201
134202 exit pid ExitShutdown
135- sleep $ seconds 5
203+ monitor pid >>= waitForDown
136204 alive' <- isProcessAlive pid
137205 alive' `shouldBe` equalTo False
138206
@@ -151,6 +219,12 @@ tests transport = do
151219 (runProcess localNode quirkyOperators)
152220 , testCase " Traversing an FSM definition (functions)"
153221 (runProcess localNode notSoQuirkyDefinitions)
222+ , testCase " Traversing an FSM definition (exit handling)"
223+ (runProcess localNode verifyStopBehaviour)
224+ , testCase " Traversing an FSM definition (mailbox handling)"
225+ (runProcess localNode verifyMailboxHandling)
226+ , testCase " Traversing an FSM definition (nested definitions)"
227+ (runProcess localNode verifyOuterStateHandler)
154228 ]
155229 ]
156230
0 commit comments