@@ -1,9 +1,10 @@
1−{-# LANGUAGE DeriveGeneric #-}
1+{-# LANGUAGE DeriveGeneric #-}
2+{-# LANGUAGE ParallelListComp #-}
2module Control.Distributed.Process.Tests.Mx (tests) where
4module Control.Distributed.Process.Tests.Mx (tests) where
4import Control.Distributed.Process.Tests.Internal.Utils
6import Control.Distributed.Process.Tests.Internal.Utils
5import Network.Transport.Test (TestTransport(..))
7import Network.Transport.Test (TestTransport(..))
6−import Control.Exception (SomeException)
7import Control.Distributed.Process hiding (bracket, finally, try)
8import Control.Distributed.Process hiding (bracket, finally, try)
8import Control.Distributed.Process.Internal.Types
9import Control.Distributed.Process.Internal.Types
9 ( ProcessExitException(..)
10 ( ProcessExitException(..)
@@ -15,6 +16,7 @@ import qualified Control.Distributed.Process.UnsafePrimitives as Unsafe
16 , nsendRemote
17 , nsendRemote
19import Control.Distributed.Process.Management
21import Control.Distributed.Process.Management
20 ( MxEvent(..)
22 ( MxEvent(..)
@@ -35,8 +37,8 @@ import Control.Monad (void, unless)
35import Control.Monad.Catch(finally, bracket, try)
37import Control.Monad.Catch(finally, bracket, try)
36import Control.Rematch (equalTo)
38import Control.Rematch (equalTo)
37import Data.Binary
39import Data.Binary
38−import Data.List (find, sort)
40+import Data.List (find, sort, intercalate)
39−import Data.Maybe (isJust, fromJust, isNothing)
41+import Data.Maybe (isJust, fromJust, isNothing, fromMaybe, catMaybes)
40import Data.Typeable
42import Data.Typeable
41import GHC.Generics hiding (from)
43import GHC.Generics hiding (from)
42#if ! MIN_VERSION_base(4,6,0)
44#if ! MIN_VERSION_base(4,6,0)
@@ -64,7 +66,7 @@ awaitExit pid =
65 withMonitorRef p = bracket (monitor p) unmonitor
67 withMonitorRef p = bracket (monitor p) unmonitor
67−testAgentBroadcast :: TestResult () -> Process ()
69+testAgentBroadcast :: TestResult (Maybe ()) -> Process ()
68testAgentBroadcast result = do
70testAgentBroadcast result = do
69 (resultSP, resultRP) <- newChan :: Process (SendPort (), ReceivePort ())
71 (resultSP, resultRP) <- newChan :: Process (SendPort (), ReceivePort ())
@@ -79,10 +81,10 @@ testAgentBroadcast result = do
79 mxNotify ()
81 mxNotify ()
80 -- Once the publisher has seen our message, it will broadcast the Publish
82 -- Once the publisher has seen our message, it will broadcast the Publish
81 -- and the consumer will see that and send the result to our typed channel.
83 -- and the consumer will see that and send the result to our typed channel.
82− stash result =<< receiveChan resultRP
84+ stash result =<< receiveChanTimeout 10000000 resultRP
84− kill publisher "finished" >> awaitExit publisher
86+ kill publisher "finished"
85− kill consumer "finished" >> awaitExit consumer
87+ kill consumer "finished"
87testAgentDualInput :: TestResult (Maybe Int) -> Process ()
89testAgentDualInput :: TestResult (Maybe Int) -> Process ()
88testAgentDualInput result = do
90testAgentDualInput result = do
@@ -97,6 +99,8 @@ testAgentDualInput result = do
97 else mxReady)
99 else mxReady)
100 mxNotify (1 :: Int)
104 mxNotify (1 :: Int)
101 nsend "sum-agent" (3 :: Int)
105 nsend "sum-agent" (3 :: Int)
102 mxNotify (2 :: Int)
106 mxNotify (2 :: Int)
@@ -104,7 +108,10 @@ testAgentDualInput result = do
104 mxNotify (5 :: Int)
108 mxNotify (5 :: Int)
106 stash result =<< receiveChanTimeout 10000000 rp
110 stash result =<< receiveChanTimeout 10000000 rp
107− awaitExit s
111+ died <- receiveTimeout 10000000 [
112+ matchIf (\(ProcessMonitorNotification r _ _) -> r == mRef) (const $ return True)
114+ died `shouldBe` equalTo (Just True)
109testAgentPrioritisation :: TestResult [String] -> Process ()
116testAgentPrioritisation :: TestResult [String] -> Process ()
110testAgentPrioritisation result = do
117testAgentPrioritisation result = do
@@ -118,10 +125,10 @@ testAgentPrioritisation result = do
118 (sp, rp) <- newChan
125 (sp, rp) <- newChan
119 s <- mxAgent (MxAgentId name) ["first"] [
126 s <- mxAgent (MxAgentId name) ["first"] [
120 mxSink (\(s :: String) -> do
127 mxSink (\(s :: String) -> do
121− mxUpdateLocal ((s:))
128+ mxUpdateLocal (s:)
122 st <- mxGetLocal
129 st <- mxGetLocal
123 case length st of
130 case length st of
124− n | n == 5 -> do liftMX $ sendChan sp st
131+ n | n == 5 -> do liftMX $ sendChan sp (sort st)
125 mxDeactivate "finished"
132 mxDeactivate "finished"
126 _ -> mxReceive -- go to the mailbox
133 _ -> mxReceive -- go to the mailbox
@@ -132,7 +139,7 @@ testAgentPrioritisation result = do
132 mxNotify "fourth"
139 mxNotify "fourth"
133 nsend name "fifth"
140 nsend name "fifth"
135− stash result . sort =<< receiveChan rp
142+ stash result . fromMaybe [] =<< receiveChanTimeout 10000000 rp
136 awaitExit s
143 awaitExit s
138testAgentMailboxHandling :: TestResult (Maybe ()) -> Process ()
145testAgentMailboxHandling :: TestResult (Maybe ()) -> Process ()
@@ -145,12 +152,19 @@ testAgentMailboxHandling result = do
145 nsend "mailbox-agent" ()
152 nsend "mailbox-agent" ()
147 stash result =<< receiveChanTimeout 1000000 rp
154 stash result =<< receiveChanTimeout 1000000 rp
148− kill agent "finished" >> awaitExit agent
155+ kill agent "finished"
150testAgentEventHandling :: TestResult Bool -> Process ()
157testAgentEventHandling :: TestResult Bool -> Process ()
151testAgentEventHandling result = do
158testAgentEventHandling result = do
152− let initState = [] :: [MxEvent]
159+ us <- getSelfPid
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]
154 (rc, rs) <- newChan
168 (rc, rs) <- newChan
155 agentPid <- mxAgent (MxAgentId "lifecycle-listener-agent") initState [
169 agentPid <- mxAgent (MxAgentId "lifecycle-listener-agent") initState [
156 (mxSink $ \ev -> do
170 (mxSink $ \ev -> do
@@ -183,8 +197,7 @@ testAgentEventHandling result = do
183 -- TODO: yes, this is racy, but we're at the mercy of the scheduler here...
197 -- TODO: yes, this is racy, but we're at the mercy of the scheduler here...
184 faff 2000000 rs
198 faff 2000000 rs
186− _ <- monitor agentPid
187− (sp, rp) <- newChan
200+ (sp, rp) <- newChan :: Process (SendPort (), ReceivePort ())
189 pid <- spawnLocal $ expect >>= sendChan sp
202 pid <- spawnLocal $ expect >>= sendChan sp
@@ -193,9 +206,11 @@ testAgentEventHandling result = do
193 monitor pid
206 monitor pid
195 send pid ()
208 send pid ()
196− () <- receiveChan rp
209+ rct <- receiveChanTimeout 10000000 rp
210+ unless (isJust rct) $ die "No response on channel"
198− receiveWait [ match (\(ProcessMonitorNotification _ _ _) -> return ()) ]
212+ pmn <- receiveTimeout 2000000 [ match (\ProcessMonitorNotification{} -> return ()) ]
213+ unless (isJust pmn) $ die "No monitor notification arrived"
200 -- TODO: yes, this is racy, but we're at the mercy of the scheduler here...
215 -- TODO: yes, this is racy, but we're at the mercy of the scheduler here...
201 faff 2000000 rs
216 faff 2000000 rs
@@ -204,17 +219,16 @@ testAgentEventHandling result = do
204 mxNotify (MxSpawned pid, replyTo)
219 mxNotify (MxSpawned pid, replyTo)
205 mxNotify (MxProcessDied pid DiedNormal, replyTo)
220 mxNotify (MxProcessDied pid DiedNormal, replyTo)
207− seenAlive <- receiveChan reply
222+ seenAlive <- receiveChanTimeout 10000000 reply
208− seenDead <- receiveChan reply
223+ seenDead <- receiveChanTimeout 10000000 reply
210− stash result $ seenAlive && seenDead
225+ stash result $ foldr (&&) True $ catMaybes [seenAlive, seenDead]
211− kill agentPid "test-complete" >> awaitExit agentPid
226+ kill timer "test-complete"
227+ kill agentPid "test-complete"
213 faff delay port = do
229 faff delay port = do
214 res <- receiveChanTimeout delay port
230 res <- receiveChanTimeout delay port
215− unless (isNothing res) $ do
231+ unless (isNothing res) $ pause delay
219testMxRegEvents :: TestResult () -> Process ()
233testMxRegEvents :: TestResult () -> Process ()
220testMxRegEvents result = do
234testMxRegEvents result = do
@@ -261,7 +275,6 @@ testMxRegEvents result = do
261 reg3 `shouldBe` equalTo (Just (label, p1))
275 reg3 `shouldBe` equalTo (Just (label, p1))
263 mapM_ (flip kill $ "test-complete") [agent, p1, p2]
277 mapM_ (flip kill $ "test-complete") [agent, p1, p2]
266testMxRegMon :: LocalNode -> TestResult () -> Process ()
279testMxRegMon :: LocalNode -> TestResult () -> Process ()
267testMxRegMon remoteNode result = do
280testMxRegMon remoteNode result = do
@@ -319,67 +332,99 @@ testMxRegMon remoteNode result = do
319 evts `shouldContain` (Just (label1, p1))
332 evts `shouldContain` (Just (label1, p1))
320 evts `shouldContain` (Just (label2, p1))
333 evts `shouldContain` (Just (label2, p1))
322− kill agent "test-complete" >> awaitExit agent
335+ kill agent "test-complete"
324ensure :: Process () -> Process () -> Process ()
337ensure :: Process () -> Process () -> Process ()
325ensure = flip finally
338ensure = flip finally
327testNSend :: (String -> () -> Process ())
340testNSend :: (String -> () -> Process ())
328 -> Maybe LocalNode
341 -> Maybe LocalNode
330 -> Process ()
342 -> Process ()
331−testNSend op n r = testMxSend n r $ \p1 sink -> do
343+testNSend op n = do
332 let delay = 5000000
345 let delay = 5000000
333− let label = "testMxSend"
346+ let label = "testMxSend" ++ (show us)
334 let isValid = isValidLabel n label
347 let isValid = isValidLabel n label
349+ testMxSend n label $ \p1 sink -> do
336− register label p1
350+ register label p1
337− reg1 <- receiveChanTimeout delay sink
351+ reg1 <- receiveChanTimeout delay sink
338− case reg1 of
352+ case reg1 of
339− Just (MxRegistered pd lb)
353+ Just (MxRegistered pd lb)
340− | pd == p1 && isValid lb -> return ()
354+ | pd == p1 && isValid lb -> return ()
341− _ -> die $ "Reg-Failed: " ++ show reg1
355+ _ -> die $ "Reg-Failed: " ++ show reg1
343− op label ()
357+ op label ()
346− sent <- receiveChanTimeout delay sink
359+ sent <- receiveChanTimeout delay sink
347− case sent of
360+ case sent of
348− Just (MxSentToName lb by _)
361+ Just (MxSentToName lb by _)
349− | by == us && isValid lb -> return True
362+ | by == us && isValid lb -> return True
350− _ -> die $ "Send-Failed: " ++ show sent
363+ _ -> die $ "Send-Failed: " ++ show sent
353− isValidLabel n l1 l2
366+ isValidLabel nd l1 l2
354 | l2 == l2 = True
367 | l2 == l2 = True
355− | isJust n = l2 == l1 ++ "@" ++ show (localNodeId $ fromJust n)
368+ | isJust nd = l2 == l1 ++ "@" ++ show (localNodeId $ fromJust nd)
356 | otherwise = False
369 | otherwise = False
358testSend :: (ProcessId -> () -> Process ())
371testSend :: (ProcessId -> () -> Process ())
359 -> Maybe LocalNode
372 -> Maybe LocalNode
361 -> Process ()
373 -> Process ()
362−testSend op n r = testMxSend n r $ \p1 sink -> do
374+testSend op n = do
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
363 -- initiate a send
405 -- initiate a send
364− op p1 ()
406+ op ch' ()
366 -- verify the management event
408 -- verify the management event
368 sent <- receiveChanTimeout 5000000 sink
409 sent <- receiveChanTimeout 5000000 sink
369 case sent of
410 case sent of
370− Just (MxSent pidTo pidFrom _)
411+ Just (MxSentToPort sId spId _)
371− | pidTo == p1 && pidFrom == us -> return True
412+ | sId == us && spId == sendPortId ch' -> return True
372− _ -> return False
413+ _ -> return False
374type SendTest = ProcessId -> ReceivePort MxEvent -> Process Bool
415type SendTest = ProcessId -> ReceivePort MxEvent -> Process Bool
376−testMxSend :: Maybe LocalNode -> TestResult Bool -> SendTest -> Process ()
417+testMxSend :: Maybe LocalNode -> String -> SendTest -> Process ()
377−testMxSend mNode result test = do
418+testMxSend mNode label test = do
378 us <- getSelfPid
419 us <- getSelfPid
379 (chan, sink) <- newChan
421 (chan, sink) <- newChan
380 agent <- mxAgent (MxAgentId $ agentLabel us) () [
422 agent <- mxAgent (MxAgentId $ agentLabel us) () [
381 mxSink $ \ev -> do
423 mxSink $ \ev -> do
382 case ev of
424 case ev of
425+ m@(MxSentToPort _ cid _)
426+ | cid /= sendPortId chan
427+ && cid /= sendPortId sp -> liftMX $ sendChan chan m
383 m@(MxSent _ fromPid _)
428 m@(MxSent _ fromPid _)
384 | fromPid == us -> liftMX $ sendChan chan m
429 | fromPid == us -> liftMX $ sendChan chan m
385 m@(MxSentToName _ fromPid _)
430 m@(MxSentToName _ fromPid _)
@@ -390,29 +435,39 @@ testMxSend mNode result test = do
394 case mNode of
438 case mNode of
395 Nothing -> void $ spawnLocal (proc sp)
439 Nothing -> void $ spawnLocal (proc sp)
396 Just remoteNode -> void $ liftIO $ forkProcess remoteNode $ proc sp
440 Just remoteNode -> void $ liftIO $ forkProcess remoteNode $ proc sp
398− p1 <- receiveChan rp
442+ p1 <- receiveChanTimeout 2000000 rp
443+ unless (isJust p1) $ die "Timed out waiting for ProcessId"
399− res <- try (test p1 sink)
444+ res <- try $ test (fromJust p1) sink
401− Left (ProcessExitException _ m) -> (liftIO $ putStrLn $ "SomeException-" ++ show m) >> stash result False
402− Right tr -> stash result tr
403 kill agent "bye"
446 kill agent "bye"
404− kill p1 "bye"
447+ kill (fromJust p1) "bye"
450+ Left (ProcessExitException _ m) -> (liftIO $ putStrLn $ "SomeException-" ++ show m) >> die m
451+ Right tr -> tr `shouldBe` equalTo True
407− label = "testMxSend"
408 agentLabel s = "mx-unsafe-check-agent-" ++ show s
455 agentLabel s = "mx-unsafe-check-agent-" ++ show s
409− proc sp' = getSelfPid >>= sendChan sp' >> expect :: Process ()
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
411tests :: TestTransport -> IO [Test]
467tests :: TestTransport -> IO [Test]
412tests TestTransport{..} = do
468tests TestTransport{..} = do
413 node1 <- newLocalNode testTransport initRemoteTable
469 node1 <- newLocalNode testTransport initRemoteTable
414 node2 <- newLocalNode testTransport initRemoteTable
470 node2 <- newLocalNode testTransport initRemoteTable
415− let nid = localNodeId node2
417 testGroup "MxAgents" [
472 testGroup "MxAgents" [
418 testCase "EventHandling"
473 testCase "EventHandling"
@@ -422,7 +477,7 @@ tests TestTransport{..} = do
422 , testCase "InterAgentBroadcast"
477 , testCase "InterAgentBroadcast"
423 (delayedAssertion
478 (delayedAssertion
424 "expected (), but no broadcast was received"
479 "expected (), but no broadcast was received"
425− node1 () testAgentBroadcast)
480+ node1 (Just ()) testAgentBroadcast)
426 , testCase "AgentMailboxHandling"
481 , testCase "AgentMailboxHandling"
427 (delayedAssertion
482 (delayedAssertion
428 "expected (Just ()), but no regular (mailbox) input was handled"
483 "expected (Just ()), but no regular (mailbox) input was handled"
@@ -447,65 +502,41 @@ tests TestTransport{..} = do
447 (delayedAssertion
502 (delayedAssertion
448 "expected process deaths to result in unregistration events"
503 "expected process deaths to result in unregistration events"
449 node1 () (testMxRegMon node2))
504 node1 () (testMxRegMon node2))
450− , testGroup "SentEvents" [
505+ , testGroup "SendEvents" $ buildTestCases node1 node2
451− testGroup "RemoteTargets" [
506+ ]
452− testCase "Unsafe.nsend"
507+ ]
453− (delayedAssertion "expected mx events failed"
508+ where
454− node1 True (testNSend Unsafe.nsend $ Just node2))
509+ buildTestCases n1 n2 = let nid = localNodeId n2 in build n1 n2 [
455− , testCase "Unsafe.nsendRemote"
510+ ("NSend", [
456− (delayedAssertion "expected mx events failed"
511+ ("nsend", testNSend nsend)
457− node1 True (testNSend (Unsafe.nsendRemote nid) $ Just node2))
512+ , ("Unsafe.nsend", testNSend Unsafe.nsend)
458− , testCase "Unsafe.send"
513+ , ("nsendRemote", testNSend (nsendRemote nid))
459− (delayedAssertion "expected mx events failed"
514+ , ("Unsafe.nsendRemote", testNSend (Unsafe.nsendRemote nid))
460− node1 True (testSend Unsafe.send $ Just node2))
515+ ])
461− , testCase "Unsafe.usend"
516+ , ("Send", [
462− (delayedAssertion "expected mx events failed"
517+ ("send", testSend send)
463− node1 True (testSend Unsafe.usend $ Just node2))
518+ , ("Unsafe.send", testSend Unsafe.send)
464− , testCase "nsend"
519+ , ("usend", testSend usend)
465− (delayedAssertion "expected mx events failed"
520+ , ("Unsafe.usend", testSend Unsafe.usend)
466− node1 True (testNSend nsend $ Just node2))
521+ , ("sendChan", testChan sendChan)
467− , testCase "nsendRemote"
522+ , ("Unsafe.sendChan", testChan Unsafe.sendChan)
468− (delayedAssertion "expected mx events failed"
523+ ])
469− node1 True (testNSend (nsendRemote nid) $ Just node2))
524+ , ("Forward", [
471− (delayedAssertion "expected mx events failed"
472− node1 True (testSend send $ Just node2))
474− (delayedAssertion "expected mx events failed"
475− node1 True (testSend usend $ Just node2))
476− , testCase "forward"
477− (delayedAssertion "expected mx events failed"
478− node1 True (testSend (\p m -> forward (unsafeCreateUnencodedMessage m) p) $ Just node2))
479− , testCase "uforward"
480− (delayedAssertion "expected mx events failed"
481− node1 True (testSend (\p m -> uforward (unsafeCreateUnencodedMessage m) p) $ Just node2))
483− , testGroup "LocalTargets" [
484− testCase "Unsafe.nsend"
485− (delayedAssertion "expected mx events failed"
486− node1 True (testNSend Unsafe.nsend Nothing))
487− , testCase "Unsafe.send"
488− (delayedAssertion "expected mx events failed"
489− node1 True (testSend Unsafe.send Nothing))
490− , testCase "Unsafe.usend"
491− (delayedAssertion "expected mx events failed"
492− node1 True (testSend Unsafe.usend Nothing))
494− (delayedAssertion "expected mx events failed"
495− node1 True (testNSend nsend Nothing))
497− (delayedAssertion "expected mx events failed"
498− node1 True (testSend send Nothing))
500− (delayedAssertion "expected mx events failed"
501− node1 True (testSend usend Nothing))
502− , testCase "forward"
503− (delayedAssertion "expected mx events failed"
504− node1 True (testSend (\p m -> forward (unsafeCreateUnencodedMessage m) p) Nothing))
525+ ("forward", testSend (\p m -> forward (unsafeCreateUnencodedMessage m) p))
505− , testCase "uforward"
506− (delayedAssertion "expected mx events failed"
507− node1 True (testSend (\p m -> uforward (unsafeCreateUnencodedMessage m) p) Nothing))
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
510− ]
540+ | (groupName, groupCases) <- specs
541+ , (caseSuffix, caseNode) <- [("RemotePid", Just ln), ("LocalPid", Nothing)]