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