@@ -1,23 +1,12 @@
1−{-# LANGUAGE DeriveGeneric #-}
1+{-# LANGUAGE DeriveGeneric #-}
2−{-# LANGUAGE ParallelListComp #-}
4module Control.Distributed.Process.Tests.Mx (tests) where
2module Control.Distributed.Process.Tests.Mx (tests) where
6import Control.Distributed.Process.Tests.Internal.Utils
4import Control.Distributed.Process.Tests.Internal.Utils
7import Network.Transport.Test (TestTransport(..))
5import Network.Transport.Test (TestTransport(..))
8−import Control.Distributed.Process hiding (bracket, finally, try)
6+
7+import Control.Concurrent (threadDelay)
9−import Control.Distributed.Process.Internal.Types
8+import Control.Distributed.Process
10− ( ProcessExitException(..)
11− , unsafeCreateUnencodedMessage
13import Control.Distributed.Process.Node
9import Control.Distributed.Process.Node
14−import qualified Control.Distributed.Process.UnsafePrimitives as Unsafe
21import Control.Distributed.Process.Management
10import Control.Distributed.Process.Management
22 ( MxEvent(..)
11 ( MxEvent(..)
23 , MxAgentId(..)
12 , MxAgentId(..)
@@ -32,15 +21,15 @@ import Control.Distributed.Process.Management
32 , mxUpdateLocal
21 , mxUpdateLocal
33 , mxNotify
22 , mxNotify
34 , mxBroadcast
23 , mxBroadcast
36import Control.Monad (void, unless)
26import Control.Monad (void, unless)
37−import Control.Monad.Catch(finally, bracket, try)
38import Control.Rematch (equalTo)
27import Control.Rematch (equalTo)
39import Data.Binary
28import Data.Binary
40−import Data.List (find, sort, intercalate)
29+import Data.List (find, sort)
41−import Data.Maybe (isJust, fromJust, isNothing, fromMaybe, catMaybes)
30+import Data.Maybe (isJust, isNothing)
42import Data.Typeable
31import Data.Typeable
43−import GHC.Generics hiding (from)
32+import GHC.Generics
44#if ! MIN_VERSION_base(4,6,0)
33#if ! MIN_VERSION_base(4,6,0)
45import Prelude hiding (catch, log)
34import Prelude hiding (catch, log)
@@ -64,9 +53,9 @@ awaitExit pid =
64 (\_ -> return ())
53 (\_ -> return ())
67− withMonitorRef p = bracket (monitor p) unmonitor
56+ withMonitorRef pid = bracket (monitor pid) unmonitor
69−testAgentBroadcast :: TestResult (Maybe ()) -> Process ()
58+testAgentBroadcast :: TestResult () -> Process ()
70testAgentBroadcast result = do
59testAgentBroadcast result = do
71 (resultSP, resultRP) <- newChan :: Process (SendPort (), ReceivePort ())
60 (resultSP, resultRP) <- newChan :: Process (SendPort (), ReceivePort ())
@@ -81,10 +70,10 @@ testAgentBroadcast result = do
81 mxNotify ()
70 mxNotify ()
82 -- Once the publisher has seen our message, it will broadcast the Publish
71 -- Once the publisher has seen our message, it will broadcast the Publish
83 -- and the consumer will see that and send the result to our typed channel.
72 -- and the consumer will see that and send the result to our typed channel.
84− stash result =<< receiveChanTimeout 10000000 resultRP
73+ stash result =<< receiveChan resultRP
86− kill publisher "finished"
75+ kill publisher "finished" >> awaitExit publisher
87− kill consumer "finished"
76+ kill consumer "finished" >> awaitExit consumer
89testAgentDualInput :: TestResult (Maybe Int) -> Process ()
78testAgentDualInput :: TestResult (Maybe Int) -> Process ()
90testAgentDualInput result = do
79testAgentDualInput result = do
@@ -99,8 +88,6 @@ testAgentDualInput result = do
99 else mxReady)
88 else mxReady)
104 mxNotify (1 :: Int)
91 mxNotify (1 :: Int)
105 nsend "sum-agent" (3 :: Int)
92 nsend "sum-agent" (3 :: Int)
106 mxNotify (2 :: Int)
93 mxNotify (2 :: Int)
@@ -108,10 +95,7 @@ testAgentDualInput result = do
108 mxNotify (5 :: Int)
95 mxNotify (5 :: Int)
110 stash result =<< receiveChanTimeout 10000000 rp
97 stash result =<< receiveChanTimeout 10000000 rp
111− died <- receiveTimeout 10000000 [
98+ awaitExit s
112− matchIf (\(ProcessMonitorNotification r _ _) -> r == mRef) (const $ return True)
114− died `shouldBe` equalTo (Just True)
116testAgentPrioritisation :: TestResult [String] -> Process ()
100testAgentPrioritisation :: TestResult [String] -> Process ()
117testAgentPrioritisation result = do
101testAgentPrioritisation result = do
@@ -125,10 +109,10 @@ testAgentPrioritisation result = do
125 (sp, rp) <- newChan
109 (sp, rp) <- newChan
126 s <- mxAgent (MxAgentId name) ["first"] [
110 s <- mxAgent (MxAgentId name) ["first"] [
127 mxSink (\(s :: String) -> do
111 mxSink (\(s :: String) -> do
128− mxUpdateLocal (s:)
112+ mxUpdateLocal ((s:))
129 st <- mxGetLocal
113 st <- mxGetLocal
130 case length st of
114 case length st of
131− n | n == 5 -> do liftMX $ sendChan sp (sort st)
115+ n | n == 5 -> do liftMX $ sendChan sp st
132 mxDeactivate "finished"
116 mxDeactivate "finished"
133 _ -> mxReceive -- go to the mailbox
117 _ -> mxReceive -- go to the mailbox
@@ -139,7 +123,7 @@ testAgentPrioritisation result = do
139 mxNotify "fourth"
123 mxNotify "fourth"
140 nsend name "fifth"
124 nsend name "fifth"
142− stash result . fromMaybe [] =<< receiveChanTimeout 10000000 rp
126+ stash result . sort =<< receiveChan rp
143 awaitExit s
127 awaitExit s
145testAgentMailboxHandling :: TestResult (Maybe ()) -> Process ()
129testAgentMailboxHandling :: TestResult (Maybe ()) -> Process ()
@@ -152,19 +136,12 @@ testAgentMailboxHandling result = do
152 nsend "mailbox-agent" ()
136 nsend "mailbox-agent" ()
154 stash result =<< receiveChanTimeout 1000000 rp
138 stash result =<< receiveChanTimeout 1000000 rp
155− kill agent "finished"
139+ kill agent "finished" >> awaitExit agent
157testAgentEventHandling :: TestResult Bool -> Process ()
141testAgentEventHandling :: TestResult Bool -> Process ()
158testAgentEventHandling result = do
142testAgentEventHandling result = do
160− -- because this test is a bit racy, let's ensure it can't run indefinitely
161− timer <- spawnLocal $ do
162− pause (1000000 * 60 * 5)
163− -- okay we've waited 5 mins, let's kill the test off if it's stuck...
165− kill us "Test Timed Out"
167 let initState = [] :: [MxEvent]
143 let initState = [] :: [MxEvent]
168 (rc, rs) <- newChan
145 (rc, rs) <- newChan
169 agentPid <- mxAgent (MxAgentId "lifecycle-listener-agent") initState [
146 agentPid <- mxAgent (MxAgentId "lifecycle-listener-agent") initState [
170 (mxSink $ \ev -> do
147 (mxSink $ \ev -> do
@@ -197,7 +174,8 @@ testAgentEventHandling result = do
197 -- TODO: yes, this is racy, but we're at the mercy of the scheduler here...
174 -- TODO: yes, this is racy, but we're at the mercy of the scheduler here...
198 faff 2000000 rs
175 faff 2000000 rs
177+ _ <- monitor agentPid
200− (sp, rp) <- newChan :: Process (SendPort (), ReceivePort ())
178+ (sp, rp) <- newChan
202 pid <- spawnLocal $ expect >>= sendChan sp
180 pid <- spawnLocal $ expect >>= sendChan sp
@@ -206,11 +184,9 @@ testAgentEventHandling result = do
206 monitor pid
184 monitor pid
208 send pid ()
186 send pid ()
209− rct <- receiveChanTimeout 10000000 rp
187+ () <- receiveChan rp
210− unless (isJust rct) $ die "No response on channel"
212− pmn <- receiveTimeout 2000000 [ match (\ProcessMonitorNotification{} -> return ()) ]
189+ receiveWait [ match (\(ProcessMonitorNotification _ _ _) -> return ()) ]
213− unless (isJust pmn) $ die "No monitor notification arrived"
215 -- TODO: yes, this is racy, but we're at the mercy of the scheduler here...
191 -- TODO: yes, this is racy, but we're at the mercy of the scheduler here...
216 faff 2000000 rs
192 faff 2000000 rs
@@ -219,16 +195,17 @@ testAgentEventHandling result = do
219 mxNotify (MxSpawned pid, replyTo)
195 mxNotify (MxSpawned pid, replyTo)
220 mxNotify (MxProcessDied pid DiedNormal, replyTo)
196 mxNotify (MxProcessDied pid DiedNormal, replyTo)
222− seenAlive <- receiveChanTimeout 10000000 reply
198+ seenAlive <- receiveChan reply
223− seenDead <- receiveChanTimeout 10000000 reply
199+ seenDead <- receiveChan reply
225− stash result $ foldr (&&) True $ catMaybes [seenAlive, seenDead]
201+ stash result $ seenAlive && seenDead
226− kill timer "test-complete"
202+ kill agentPid "test-complete" >> awaitExit agentPid
227− kill agentPid "test-complete"
229 faff delay port = do
204 faff delay port = do
230 res <- receiveChanTimeout delay port
205 res <- receiveChanTimeout delay port
231− unless (isNothing res) $ pause delay
206+ unless (isNothing res) $ do
233testMxRegEvents :: TestResult () -> Process ()
210testMxRegEvents :: TestResult () -> Process ()
234testMxRegEvents result = do
211testMxRegEvents result = do
@@ -275,6 +252,7 @@ testMxRegEvents result = do
275 reg3 `shouldBe` equalTo (Just (label, p1))
252 reg3 `shouldBe` equalTo (Just (label, p1))
277 mapM_ (flip kill $ "test-complete") [agent, p1, p2]
254 mapM_ (flip kill $ "test-complete") [agent, p1, p2]
279testMxRegMon :: LocalNode -> TestResult () -> Process ()
257testMxRegMon :: LocalNode -> TestResult () -> Process ()
280testMxRegMon remoteNode result = do
258testMxRegMon remoteNode result = do
@@ -332,211 +310,48 @@ testMxRegMon remoteNode result = do
332 evts `shouldContain` (Just (label1, p1))
310 evts `shouldContain` (Just (label1, p1))
333 evts `shouldContain` (Just (label2, p1))
311 evts `shouldContain` (Just (label2, p1))
335− kill agent "test-complete"
313+ kill agent "test-complete" >> awaitExit agent
337ensure :: Process () -> Process () -> Process ()
315ensure :: Process () -> Process () -> Process ()
338ensure = flip finally
316ensure = flip finally
340−testNSend :: (String -> () -> Process ())
346− let label = "testMxSend" ++ (show us)
347− let isValid = isValidLabel n label
349− testMxSend n label $ \p1 sink -> do
351− reg1 <- receiveChanTimeout delay sink
353− Just (MxRegistered pd lb)
354− | pd == p1 && isValid lb -> return ()
355− _ -> die $ "Reg-Failed: " ++ show reg1
359− sent <- receiveChanTimeout delay sink
361− Just (MxSentToName lb by _)
362− | by == us && isValid lb -> return True
363− _ -> die $ "Send-Failed: " ++ show sent
366− isValidLabel nd l1 l2
368− | isJust nd = l2 == l1 ++ "@" ++ show (localNodeId $ fromJust nd)
371−testSend :: (ProcessId -> () -> Process ())
376− let label = "testMxSend" ++ (show us)
377− testMxSend n label $ \p1 sink -> do
381− -- verify the management event
382− sent <- receiveChanTimeout 5000000 sink
384− Just (MxSent pidTo pidFrom _)
385− | pidTo == p1 && pidFrom == us -> return True
388−testChan :: (SendPort () -> () -> Process ())
391−testChan op n = testMxSend n "" $ \p1 sink -> do
396− cleared <- receiveChanTimeout 2000000 sink
398− Just (MxSent pidTo pidFrom _)
399− | pidTo == p1 && pidFrom == us -> return ()
400− _ -> die "Received uncleared Mx Event"
402− chan <- receiveTimeout 5000000 [ match return ]
403− let ch' = fromMaybe (error "No reply chan received") chan
408− -- verify the management event
409− sent <- receiveChanTimeout 5000000 sink
411− Just (MxSentToPort sId spId _)
412− | sId == us && spId == sendPortId ch' -> return True
415−type SendTest = ProcessId -> ReceivePort MxEvent -> Process Bool
417−testMxSend :: Maybe LocalNode -> String -> SendTest -> Process ()
418−testMxSend mNode label test = do
421− (chan, sink) <- newChan
422− agent <- mxAgent (MxAgentId $ agentLabel us) () [
425− m@(MxSentToPort _ cid _)
426− | cid /= sendPortId chan
427− && cid /= sendPortId sp -> liftMX $ sendChan chan m
428− m@(MxSent _ fromPid _)
429− | fromPid == us -> liftMX $ sendChan chan m
430− m@(MxSentToName _ fromPid _)
431− | fromPid == us -> liftMX $ sendChan chan m
432− m@(MxRegistered _ name)
433− | name == label -> liftMX $ sendChan chan m
439− Nothing -> void $ spawnLocal (proc sp)
440− Just remoteNode -> void $ liftIO $ forkProcess remoteNode $ proc sp
442− p1 <- receiveChanTimeout 2000000 rp
443− unless (isJust p1) $ die "Timed out waiting for ProcessId"
444− res <- try $ test (fromJust p1) sink
447− kill (fromJust p1) "bye"
450− Left (ProcessExitException _ m) -> (liftIO $ putStrLn $ "SomeException-" ++ show m) >> die m
451− Right tr -> tr `shouldBe` equalTo True
455− agentLabel s = "mx-unsafe-check-agent-" ++ show s
456− proc sp' = getSelfPid >>= sendChan sp' >> go Nothing
458− go :: Maybe (ReceivePort ()) -> Process ()
459− go Nothing = receiveTimeout 5000000 [ match replyChannel ] >>= go
460− go c@(Just c') = receiveWait [ matchChan c' return ] >> go c
462− replyChannel p' = do
467tests :: TestTransport -> IO [Test]
318tests :: TestTransport -> IO [Test]
468tests TestTransport{..} = do
319tests TestTransport{..} = do
469 node1 <- newLocalNode testTransport initRemoteTable
320 node1 <- newLocalNode testTransport initRemoteTable
470 node2 <- newLocalNode testTransport initRemoteTable
321 node2 <- newLocalNode testTransport initRemoteTable
472− testGroup "MxAgents" [
323+ testGroup "Mx Agents" [
473− testCase "EventHandling"
324+ testCase "Event Handling"
474 (delayedAssertion
325 (delayedAssertion
475 "expected True, but events where not as expected"
326 "expected True, but events where not as expected"
476 node1 True testAgentEventHandling)
327 node1 True testAgentEventHandling)
477− , testCase "InterAgentBroadcast"
328+ , testCase "Inter-Agent Broadcast"
478 (delayedAssertion
329 (delayedAssertion
479 "expected (), but no broadcast was received"
330 "expected (), but no broadcast was received"
480− node1 (Just ()) testAgentBroadcast)
331+ node1 () testAgentBroadcast)
481− , testCase "AgentMailboxHandling"
332+ , testCase "Agent Mailbox Handling"
482 (delayedAssertion
333 (delayedAssertion
483 "expected (Just ()), but no regular (mailbox) input was handled"
334 "expected (Just ()), but no regular (mailbox) input was handled"
484 node1 (Just ()) testAgentMailboxHandling)
335 node1 (Just ()) testAgentMailboxHandling)
485− , testCase "AgentDualInputHandling"
336+ , testCase "Agent Dual Input Handling"
486 (delayedAssertion
337 (delayedAssertion
487 "expected sum = 15, but the result was Nothing"
338 "expected sum = 15, but the result was Nothing"
488 node1 (Just 15 :: Maybe Int) testAgentDualInput)
339 node1 (Just 15 :: Maybe Int) testAgentDualInput)
489− , testCase "AgentInputPrioritisation"
340+ , testCase "Agent Input Prioritisation"
490 (delayedAssertion
341 (delayedAssertion
491 "expected [first, second, third, fourth, fifth], but result diverged"
342 "expected [first, second, third, fourth, fifth], but result diverged"
492 node1 (sort ["first", "second",
343 node1 (sort ["first", "second",
493 "third", "fourth",
344 "third", "fourth",
494 "fifth"]) testAgentPrioritisation)
345 "fifth"]) testAgentPrioritisation)
496− , testGroup "MxEvents" [
347+ , testGroup "Mx Events" [
497− testCase "NameRegistrationEvents"
348+ testCase "Name Registration Events"
498 (delayedAssertion
349 (delayedAssertion
499 "expected registration events to map to the correct ProcessId"
350 "expected registration events to map to the correct ProcessId"
500 node1 () testMxRegEvents)
351 node1 () testMxRegEvents)
501− , testCase "PostDeathNameUnRegistrationEvents"
352+ , testCase "Post Death Name UnRegistration Events"
502 (delayedAssertion
353 (delayedAssertion
503 "expected process deaths to result in unregistration events"
354 "expected process deaths to result in unregistration events"
504 node1 () (testMxRegMon node2))
355 node1 () (testMxRegMon node2))
505− , testGroup "SendEvents" $ buildTestCases node1 node2
509− buildTestCases n1 n2 = let nid = localNodeId n2 in build n1 n2 [
511− ("nsend", testNSend nsend)
512− , ("Unsafe.nsend", testNSend Unsafe.nsend)
513− , ("nsendRemote", testNSend (nsendRemote nid))
514− , ("Unsafe.nsendRemote", testNSend (Unsafe.nsendRemote nid))
517− ("send", testSend send)
518− , ("Unsafe.send", testSend Unsafe.send)
519− , ("usend", testSend usend)
520− , ("Unsafe.usend", testSend Unsafe.usend)
521− , ("sendChan", testChan sendChan)
522− , ("Unsafe.sendChan", testChan Unsafe.sendChan)
525− ("forward", testSend (\p m -> forward (unsafeCreateUnencodedMessage m) p))
526− , ("uforward", testSend (\p m -> uforward (unsafeCreateUnencodedMessage m) p))
532− -> [(String, [(String, (Maybe LocalNode -> Process ()))])]
535− [ testGroup (intercalate "-" [groupName, caseSuffix]) [
536− testCase (intercalate "-" [caseName, caseSuffix])
537− (runProcess n (caseImpl caseNode))
538− | (caseName, caseImpl) <- groupCases
540− | (groupName, groupCases) <- specs
541− , (caseSuffix, caseNode) <- [("RemotePid", Just ln), ("LocalPid", Nothing)]