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