@@ -1,11 +1,23 @@
1−{-# LANGUAGE DeriveGeneric #-}
1+{-# LANGUAGE DeriveGeneric #-}
2+{-# LANGUAGE ParallelListComp #-}
3+
24module Control.Distributed.Process.Tests.Mx (tests) where
35
46import Control.Distributed.Process.Tests.Internal.Utils
57import Network.Transport.Test (TestTransport(..))
6−
7−import Control.Distributed.Process
8+import Control.Exception (SomeException)
9+import Control.Distributed.Process hiding (bracket, finally, try)
10+import Control.Distributed.Process.Internal.Types
11+ ( ProcessExitException(..)
12+ , unsafeCreateUnencodedMessage
13+ )
814import Control.Distributed.Process.Node
15+import qualified Control.Distributed.Process.UnsafePrimitives as Unsafe
16+ ( send
17+ , nsend
18+ , nsendRemote
19+ , usend
20+ )
921import Control.Distributed.Process.Management
1022 ( MxEvent(..)
1123 , MxAgentId(..)
@@ -22,12 +34,13 @@ import Control.Distributed.Process.Management
2234 , mxBroadcast
2335 )
2436import Control.Monad (void, unless)
37+import Control.Monad.Catch(finally, bracket, try)
2538import Control.Rematch (equalTo)
2639import Data.Binary
27−import Data.List (find, sort)
28−import Data.Maybe (isJust, isNothing)
40+import Data.List (find, sort, intercalate)
41+import Data.Maybe (isJust, fromJust, isNothing)
2942import Data.Typeable
30−import GHC.Generics
43+import GHC.Generics hiding (from)
3144#if ! MIN_VERSION_base(4,6,0)
3245import Prelude hiding (catch, log)
3346#endif
@@ -51,7 +64,7 @@ awaitExit pid =
5164 (\_ -> return ())
5265 ]
5366 where
54− withMonitorRef pid = bracket (monitor pid) unmonitor
67+ withMonitorRef p = bracket (monitor p) unmonitor
5568
5669testAgentBroadcast :: TestResult () -> Process ()
5770testAgentBroadcast result = do
@@ -313,43 +326,162 @@ testMxRegMon remoteNode result = do
313326ensure :: Process () -> Process () -> Process ()
314327ensure = flip finally
315328
329+testNSend :: (String -> () -> Process ())
330+ -> Maybe LocalNode
331+ -> TestResult Bool
332+ -> Process ()
333+testNSend op n r = testMxSend n r $ \p1 sink -> do
334+ let delay = 5000000
335+ let label = "testMxSend"
336+ let isValid = isValidLabel n label
337+
338+ register label p1
339+ reg1 <- receiveChanTimeout delay sink
340+ case reg1 of
341+ Just (MxRegistered pd lb)
342+ | pd == p1 && isValid lb -> return ()
343+ _ -> die $ "Reg-Failed: " ++ show reg1
344+
345+ op label ()
346+
347+ us <- getSelfPid
348+ sent <- receiveChanTimeout delay sink
349+ case sent of
350+ Just (MxSentToName lb by _)
351+ | by == us && isValid lb -> return True
352+ _ -> die $ "Send-Failed: " ++ show sent
353+
354+ where
355+ isValidLabel n l1 l2
356+ | l2 == l2 = True
357+ | isJust n = l2 == l1 ++ "@" ++ show (localNodeId $ fromJust n)
358+ | otherwise = False
359+
360+testSend :: (ProcessId -> () -> Process ())
361+ -> Maybe LocalNode
362+ -> TestResult Bool
363+ -> Process ()
364+testSend op n r = testMxSend n r $ \p1 sink -> do
365+ -- initiate a send
366+ op p1 ()
367+
368+ -- verify the management event
369+ us <- getSelfPid
370+ sent <- receiveChanTimeout 5000000 sink
371+ case sent of
372+ Just (MxSent pidTo pidFrom _)
373+ | pidTo == p1 && pidFrom == us -> return True
374+ _ -> return False
375+
376+type SendTest = ProcessId -> ReceivePort MxEvent -> Process Bool
377+
378+testMxSend :: Maybe LocalNode -> TestResult Bool -> SendTest -> Process ()
379+testMxSend mNode result test = do
380+ us <- getSelfPid
381+ (chan, sink) <- newChan
382+ agent <- mxAgent (MxAgentId $ agentLabel us) () [
383+ mxSink $ \ev -> do
384+ case ev of
385+ m@(MxSent _ fromPid _)
386+ | fromPid == us -> liftMX $ sendChan chan m
387+ m@(MxSentToName _ fromPid _)
388+ | fromPid == us -> liftMX $ sendChan chan m
389+ m@(MxRegistered _ name)
390+ | name == label -> liftMX $ sendChan chan m
391+ _ -> return ()
392+ mxReady
393+ ]
394+
395+ (sp, rp) <- newChan
396+ case mNode of
397+ Nothing -> void $ spawnLocal (proc sp)
398+ Just remoteNode -> void $ liftIO $ forkProcess remoteNode $ proc sp
399+
400+ p1 <- receiveChan rp
401+ res <- try (test p1 sink)
402+ case res of
403+ Left (ProcessExitException _ m) -> (liftIO $ putStrLn $ "SomeException-" ++ show m) >> stash result False
404+ Right tr -> stash result tr
405+ kill agent "bye"
406+ kill p1 "bye"
407+
408+ where
409+ label = "testMxSend"
410+ agentLabel s = "mx-unsafe-check-agent-" ++ show s
411+ proc sp' = getSelfPid >>= sendChan sp' >> expect :: Process ()
412+
316413tests :: TestTransport -> IO [Test]
317414tests TestTransport{..} = do
318415 node1 <- newLocalNode testTransport initRemoteTable
319416 node2 <- newLocalNode testTransport initRemoteTable
320417 return [
321− testGroup "Mx Agents" [
322− testCase "Event Handling"
418+ testGroup "MxAgents" [
419+ testCase "EventHandling"
323420 (delayedAssertion
324421 "expected True, but events where not as expected"
325422 node1 True testAgentEventHandling)
326− , testCase "Inter-Agent Broadcast"
423+ , testCase "InterAgentBroadcast"
327424 (delayedAssertion
328425 "expected (), but no broadcast was received"
329426 node1 () testAgentBroadcast)
330− , testCase "Agent Mailbox Handling"
427+ , testCase "AgentMailboxHandling"
331428 (delayedAssertion
332429 "expected (Just ()), but no regular (mailbox) input was handled"
333430 node1 (Just ()) testAgentMailboxHandling)
334− , testCase "Agent Dual Input Handling"
431+ , testCase "AgentDualInputHandling"
335432 (delayedAssertion
336433 "expected sum = 15, but the result was Nothing"
337434 node1 (Just 15 :: Maybe Int) testAgentDualInput)
338− , testCase "Agent Input Prioritisation"
435+ , testCase "AgentInputPrioritisation"
339436 (delayedAssertion
340437 "expected [first, second, third, fourth, fifth], but result diverged"
341438 node1 (sort ["first", "second",
342439 "third", "fourth",
343440 "fifth"]) testAgentPrioritisation)
344441 ]
345− , testGroup "Mx Events" [
346− testCase "Name Registration Events"
442+ , testGroup "MxEvents" [
443+ testCase "NameRegistrationEvents"
347444 (delayedAssertion
348445 "expected registration events to map to the correct ProcessId"
349446 node1 () testMxRegEvents)
350− , testCase "Post Death Name UnRegistration Events"
447+ , testCase "PostDeathNameUnRegistrationEvents"
351448 (delayedAssertion
352449 "expected process deaths to result in unregistration events"
353450 node1 () (testMxRegMon node2))
451+ , testGroup "SendEvents" $ buildTestCases node1 node2
354452 ]
355453 ]
454+ where
455+ buildTestCases n1 n2 =
456+ let nid = localNodeId n2 in build n2 [
457+ ("NSend", [
458+ ("Unsafe.nsend", testNSend Unsafe.nsend)
459+ , ("Unsafe.nsendRemote", testNSend (Unsafe.nsendRemote nid))
460+ , ("nsend", testNSend nsend)
461+ , ("nsendRemote", testNSend (nsendRemote nid))
462+ ])
463+ , ("Send", [
464+ ("Unsafe.send", testSend Unsafe.send)
465+ , ("Unsafe.usend", testSend Unsafe.usend)
466+ , ("send", testSend send)
467+ , ("usend", testSend usend)
468+ ])
469+ , ("Forward", [
470+ ("forward", testSend (\p m -> forward (unsafeCreateUnencodedMessage m) p))
471+ , ("uforward", testSend (\p m -> uforward (unsafeCreateUnencodedMessage m) p))
472+ ])
473+ ]
474+
475+ build :: LocalNode
476+ -> [(String, [(String, (Maybe LocalNode -> TestResult Bool -> Process ()))])]
477+ -> [Test]
478+ build ln specs =
479+ [ testGroup (intercalate "-" [groupName, caseSuffix]) [
480+ testCase (intercalate "-" [caseName, caseSuffix])
481+ (delayedAssertion "expected mx events failed"
482+ ln True (caseImpl caseNode))
483+ | (caseName, caseImpl) <- groupCases
484+ ]
485+ | (groupName, groupCases) <- specs
486+ , (caseSuffix, caseNode) <- [("RemotePid", Just ln), ("LocalPid", Nothing)]
487+ ]