@@ -1,11 +1,24 @@
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−
8+import Control.Exception (SomeException)
9+import Control.Distributed.Process hiding (bracket, finally, try)
7−import Control.Distributed.Process
10+import Control.Distributed.Process.Internal.Types
11+ ( ProcessExitException(..)
12+ , unsafeCreateUnencodedMessage
8import Control.Distributed.Process.Node
14import Control.Distributed.Process.Node
15+import qualified Control.Distributed.Process.UnsafePrimitives as Unsafe
9import Control.Distributed.Process.Management
22import Control.Distributed.Process.Management
10 ( MxEvent(..)
23 ( MxEvent(..)
11 , MxAgentId(..)
24 , MxAgentId(..)
@@ -22,12 +35,13 @@ import Control.Distributed.Process.Management
22 , mxBroadcast
35 , mxBroadcast
24import Control.Monad (void, unless)
37import Control.Monad (void, unless)
38+import Control.Monad.Catch(finally, bracket, try)
25import Control.Rematch (equalTo)
39import Control.Rematch (equalTo)
26import Data.Binary
40import Data.Binary
27−import Data.List (find, sort)
41+import Data.List (find, sort, intercalate)
28−import Data.Maybe (isJust, isNothing)
42+import Data.Maybe (isJust, fromJust, isNothing, fromMaybe)
29import Data.Typeable
43import Data.Typeable
30−import GHC.Generics
44+import GHC.Generics hiding (from)
31#if ! MIN_VERSION_base(4,6,0)
45#if ! MIN_VERSION_base(4,6,0)
32import Prelude hiding (catch, log)
46import Prelude hiding (catch, log)
@@ -36,6 +50,7 @@ import Test.Framework
37 , testGroup
51 , testGroup
53+import Test.HUnit (Assertion)
39import Test.Framework.Providers.HUnit (testCase)
54import Test.Framework.Providers.HUnit (testCase)
41data Publish = Publish
56data Publish = Publish
@@ -51,7 +66,7 @@ awaitExit pid =
51 (\_ -> return ())
66 (\_ -> return ())
54− withMonitorRef pid = bracket (monitor pid) unmonitor
69+ withMonitorRef p = bracket (monitor p) unmonitor
56testAgentBroadcast :: TestResult () -> Process ()
71testAgentBroadcast :: TestResult () -> Process ()
57testAgentBroadcast result = do
72testAgentBroadcast result = do
@@ -313,43 +328,204 @@ testMxRegMon remoteNode result = do
313ensure :: Process () -> Process () -> Process ()
328ensure :: Process () -> Process () -> Process ()
314ensure = flip finally
329ensure = flip finally
331+testNSend :: (String -> () -> Process ())
334+testNSend op n = testMxSend n $ \p1 sink -> do
336+ let label = "testMxSend"
337+ let isValid = isValidLabel n label
340+ reg1 <- receiveChanTimeout delay sink
342+ Just (MxRegistered pd lb)
343+ | pd == p1 && isValid lb -> return ()
344+ _ -> die $ "Reg-Failed: " ++ show reg1
349+ sent <- receiveChanTimeout delay sink
351+ Just (MxSentToName lb by _)
352+ | by == us && isValid lb -> return True
353+ _ -> die $ "Send-Failed: " ++ show sent
356+ isValidLabel n l1 l2
358+ | isJust n = l2 == l1 ++ "@" ++ show (localNodeId $ fromJust n)
361+testSend :: (ProcessId -> () -> Process ())
364+testSend op n = testMxSend n $ \p1 sink -> do
368+ -- verify the management event
370+ sent <- receiveChanTimeout 5000000 sink
372+ Just (MxSent pidTo pidFrom _)
373+ | pidTo == p1 && pidFrom == us -> return True
376+testChan :: (SendPort () -> () -> Process ())
379+testChan op n = testMxSend n $ \p1 sink -> do
384+ cleared <- receiveChan sink
386+ (MxSent pidTo pidFrom _)
387+ | pidTo == p1 && pidFrom == us -> return ()
388+ _ -> die "Received uncleared Mx Event"
390+ chan <- receiveTimeout 5000000 [ match return ]
391+ let ch' = fromMaybe (error "No reply chan received") chan
396+ -- verify the management event
397+ sent <- receiveChanTimeout 5000000 sink
399+ Just (MxSentToPort sId spId _)
400+ | sId == us && spId == sendPortId ch' -> return True
403+type SendTest = ProcessId -> ReceivePort MxEvent -> Process Bool
405+testMxSend :: Maybe LocalNode -> SendTest -> Process ()
406+testMxSend mNode test = do
409+ (chan, sink) <- newChan
410+ agent <- mxAgent (MxAgentId $ agentLabel us) () [
413+ m@(MxSentToPort _ cid _)
414+ | cid /= sendPortId chan
415+ && cid /= sendPortId sp -> liftMX $ sendChan chan m
416+ m@(MxSent _ fromPid _)
417+ | fromPid == us -> liftMX $ sendChan chan m
418+ m@(MxSentToName _ fromPid _)
419+ | fromPid == us -> liftMX $ sendChan chan m
420+ m@(MxRegistered _ name)
421+ | name == label -> liftMX $ sendChan chan m
427+ Nothing -> void $ spawnLocal (proc sp)
428+ Just remoteNode -> void $ liftIO $ forkProcess remoteNode $ proc sp
430+ p1 <- receiveChan rp
431+ res <- try (test p1 sink)
437+ Left (ProcessExitException _ m) -> (liftIO $ putStrLn $ "SomeException-" ++ show m) >> die m
438+ Right tr -> tr `shouldBe` equalTo True
442+ label = "testMxSend"
443+ agentLabel s = "mx-unsafe-check-agent-" ++ show s
444+ proc sp' = getSelfPid >>= sendChan sp' >> go Nothing
446+ go :: Maybe (ReceivePort ()) -> Process ()
447+ go Nothing = receiveTimeout 5000000 [ match replyChannel ] >>= go
448+ go c@(Just c') = receiveWait [ matchChan c' return ] >> go c
450+ replyChannel p' = do
316tests :: TestTransport -> IO [Test]
455tests :: TestTransport -> IO [Test]
317−tests TestTransport{..} = do
456+tests tt@TestTransport{..} = do
318 node1 <- newLocalNode testTransport initRemoteTable
457 node1 <- newLocalNode testTransport initRemoteTable
319 node2 <- newLocalNode testTransport initRemoteTable
458 node2 <- newLocalNode testTransport initRemoteTable
321− testGroup "Mx Agents" [
460+ testGroup "MxAgents" [
322− testCase "Event Handling"
461+ testCase "EventHandling"
323 (delayedAssertion
462 (delayedAssertion
324 "expected True, but events where not as expected"
463 "expected True, but events where not as expected"
325 node1 True testAgentEventHandling)
464 node1 True testAgentEventHandling)
326− , testCase "Inter-Agent Broadcast"
465+ , testCase "InterAgentBroadcast"
327 (delayedAssertion
466 (delayedAssertion
328 "expected (), but no broadcast was received"
467 "expected (), but no broadcast was received"
329 node1 () testAgentBroadcast)
468 node1 () testAgentBroadcast)
330− , testCase "Agent Mailbox Handling"
469+ , testCase "AgentMailboxHandling"
331 (delayedAssertion
470 (delayedAssertion
332 "expected (Just ()), but no regular (mailbox) input was handled"
471 "expected (Just ()), but no regular (mailbox) input was handled"
333 node1 (Just ()) testAgentMailboxHandling)
472 node1 (Just ()) testAgentMailboxHandling)
334− , testCase "Agent Dual Input Handling"
473+ , testCase "AgentDualInputHandling"
335 (delayedAssertion
474 (delayedAssertion
336 "expected sum = 15, but the result was Nothing"
475 "expected sum = 15, but the result was Nothing"
337 node1 (Just 15 :: Maybe Int) testAgentDualInput)
476 node1 (Just 15 :: Maybe Int) testAgentDualInput)
338− , testCase "Agent Input Prioritisation"
477+ , testCase "AgentInputPrioritisation"
339 (delayedAssertion
478 (delayedAssertion
340 "expected [first, second, third, fourth, fifth], but result diverged"
479 "expected [first, second, third, fourth, fifth], but result diverged"
341 node1 (sort ["first", "second",
480 node1 (sort ["first", "second",
342 "third", "fourth",
481 "third", "fourth",
343 "fifth"]) testAgentPrioritisation)
482 "fifth"]) testAgentPrioritisation)
345− , testGroup "Mx Events" [
484+ , testGroup "MxEvents" [
346− testCase "Name Registration Events"
485+ testCase "NameRegistrationEvents"
347 (delayedAssertion
486 (delayedAssertion
348 "expected registration events to map to the correct ProcessId"
487 "expected registration events to map to the correct ProcessId"
349 node1 () testMxRegEvents)
488 node1 () testMxRegEvents)
350− , testCase "Post Death Name UnRegistration Events"
489+ , testCase "PostDeathNameUnRegistrationEvents"
351 (delayedAssertion
490 (delayedAssertion
352 "expected process deaths to result in unregistration events"
491 "expected process deaths to result in unregistration events"
353 node1 () (testMxRegMon node2))
492 node1 () (testMxRegMon node2))
493+ , testGroup "SendEvents" $ buildTestCases tt node2
497+ buildTestCases tt' n2 = let nid = localNodeId n2 in build tt' n2 [
499+ ("nsend", testNSend nsend)
500+ , ("Unsafe.nsend", testNSend Unsafe.nsend)
501+ , ("nsendRemote", testNSend (nsendRemote nid))
502+ , ("Unsafe.nsendRemote", testNSend (Unsafe.nsendRemote nid))
505+ ("send", testSend send)
506+ , ("Unsafe.send", testSend Unsafe.send)
507+ , ("usend", testSend usend)
508+ , ("Unsafe.usend", testSend Unsafe.usend)
509+ , ("sendChan", testChan sendChan)
510+ , ("Unsafe.sendChan", testChan Unsafe.sendChan)
513+ ("forward", testSend (\p m -> forward (unsafeCreateUnencodedMessage m) p))
514+ , ("uforward", testSend (\p m -> uforward (unsafeCreateUnencodedMessage m) p))
518+ build :: TestTransport
520+ -> [(String, [(String, (Maybe LocalNode -> Process ()))])]
522+ build TestTransport{..} ln specs =
523+ [ testGroup (intercalate "-" [groupName, caseSuffix]) [
524+ testCase (intercalate "-" [caseName, caseSuffix])
525+ (newLocalNode testTransport initRemoteTable >>= \n ->
526+ tryRunProcess n (caseImpl caseNode))
527+ | (caseName, caseImpl) <- groupCases
529+ | (groupName, groupCases) <- specs
530+ , (caseSuffix, caseNode) <- [("RemotePid", Just ln), ("LocalPid", Nothing)]