@@ -1,1339 +0,0 @@
3−#if ! MIN_VERSION_base(4,6,0)
4−import Prelude hiding (catch)
7−import Data.Binary (Binary(..))
8−import Data.Typeable (Typeable)
9−import Data.Foldable (forM_)
10−import Control.Concurrent (forkIO, threadDelay, myThreadId, throwTo, ThreadId)
11−import Control.Concurrent.MVar
18−import Control.Monad (replicateM_, replicateM, forever, void)
19−import Control.Exception (SomeException, throwIO)
20−import qualified Control.Exception as Ex (catch)
21−import Control.Applicative ((<$>), (<*>), pure, (<|>))
22−import qualified Network.Transport as NT (Transport, closeEndPoint)
23−import Network.Socket (sClose)
24−import Network.Transport.TCP
25− ( createTransportExposeInternals
26− , TransportInternals(socketBetween)
27− , defaultTCPParameters
29−import Control.Distributed.Process
30−import Control.Distributed.Process.Internal.Types
31− ( NodeId(nodeAddress)
32− , LocalNode(localEndPoint)
33− , ProcessExitException(..)
36−import Control.Distributed.Process.Node
37−import Control.Distributed.Process.Serializable (Serializable)
39−import Test.HUnit (Assertion, assertFailure)
40−import Test.Framework (Test, defaultMain, testGroup)
41−import Test.Framework.Providers.HUnit (testCase)
42−import Control.Rematch hiding (match)
43−import Control.Rematch.Run (Match(..))
45−newtype Ping = Ping ProcessId
46− deriving (Typeable, Binary, Show)
48−newtype Pong = Pong ProcessId
49− deriving (Typeable, Binary, Show)
51−--------------------------------------------------------------------------------
52−-- Supporting definitions --
53−--------------------------------------------------------------------------------
55−expectThat :: a -> Matcher a -> Assertion
56−expectThat a matcher = case res of
57− MatchSuccess -> return ()
58− (MatchFailure msg) -> assertFailure msg
59− where res = runMatch matcher a
61−-- | Like fork, but throw exceptions in the child thread to the parent
62−forkTry :: IO () -> IO ThreadId
65− forkIO $ Ex.catch p (\e -> throwTo tid (e :: SomeException))
67−-- | The ping server from the paper
70− Pong partner <- expect
72− send partner (Ping self)
75−-- | Quick and dirty synchronous version of whereisRemoteAsync
76−whereisRemote :: NodeId -> String -> Process (Maybe ProcessId)
77−whereisRemote nid string = do
78− whereisRemoteAsync nid string
79− WhereIsReply _ mPid <- expect
82−data Add = Add ProcessId Double Double deriving (Typeable)
83−data Divide = Divide ProcessId Double Double deriving (Typeable)
84−data DivByZero = DivByZero deriving (Typeable)
86−instance Binary Add where
87− put (Add pid x y) = put pid >> put x >> put y
88− get = Add <$> get <*> get <*> get
90−instance Binary Divide where
91− put (Divide pid x y) = put pid >> put x >> put y
92− get = Divide <$> get <*> get <*> get
94−instance Binary DivByZero where
95− put DivByZero = return ()
96− get = return DivByZero
98−-- The math server from the paper
102− [ match (\(Add pid x y) -> send pid (x + y))
103− , matchIf (\(Divide _ _ y) -> y /= 0)
104− (\(Divide pid x y) -> send pid (x / y))
105− , match (\(Divide pid _ _) -> send pid DivByZero)
109−-- | Monitor or link to a remote node
110−monitorOrLink :: Bool -- ^ 'True' for monitor, 'False' for link
111− -> ProcessId -- Process to monitor/link to
112− -> Maybe (MVar ()) -- MVar to signal on once the monitor has been set up
113− -> Process (Maybe MonitorRef)
114−monitorOrLink mOrL pid mSignal = do
115− result <- if mOrL then Just <$> monitor pid
116− else link pid >> return Nothing
117− -- Monitor is asynchronous, which usually does not matter but if we want a
118− -- *specific* signal then it does. Therefore we wait an arbitrary delay and
119− -- hope that this means the monitor has been set up
120− forM_ mSignal $ \signal -> liftIO . forkIO $ threadDelay 100000 >> putMVar signal ()
123−monitorTestProcess :: ProcessId -- Process to monitor/link to
124− -> Bool -- 'True' for monitor, 'False' for link
125− -> Bool -- Should we unmonitor?
126− -> DiedReason -- Expected cause of death
127− -> Maybe (MVar ()) -- Signal for 'monitor set up'
128− -> MVar () -- Signal for successful termination
130−monitorTestProcess theirAddr mOrL un reason monitorSetup done =
131− catch (do mRef <- monitorOrLink mOrL theirAddr monitorSetup
133− (True, Nothing) -> do
135− liftIO $ putMVar done ()
136− (True, Just ref) -> do
138− liftIO $ putMVar done ()
140− ProcessMonitorNotification ref' pid reason' <- expect
141− True <- return $ Just ref' == ref && pid == theirAddr && mOrL && reason == reason'
142− liftIO $ putMVar done ()
144− (\(ProcessLinkException pid reason') -> do
145− True <- return $ pid == theirAddr && not mOrL && not un && reason == reason'
146− liftIO $ putMVar done ()
149−--------------------------------------------------------------------------------
150−-- The tests proper --
151−--------------------------------------------------------------------------------
154−testPing :: NT.Transport -> Assertion
155−testPing transport = do
156− serverAddr <- newEmptyMVar
157− clientDone <- newEmptyMVar
161− localNode <- newLocalNode transport initRemoteTable
162− addr <- forkProcess localNode ping
163− putMVar serverAddr addr
167− localNode <- newLocalNode transport initRemoteTable
168− pingServer <- readMVar serverAddr
170− let numPings = 10000
172− runProcess localNode $ do
174− replicateM_ numPings $ do
175− send pingServer (Pong pid)
179− putMVar clientDone ()
183−-- | Monitor a process on an unreachable node
184−testMonitorUnreachable :: NT.Transport -> Bool -> Bool -> Assertion
185−testMonitorUnreachable transport mOrL un = do
186− deadProcess <- newEmptyMVar
187− done <- newEmptyMVar
190− localNode <- newLocalNode transport initRemoteTable
191− addr <- forkProcess localNode . liftIO $ threadDelay 1000000
192− closeLocalNode localNode
193− putMVar deadProcess addr
196− localNode <- newLocalNode transport initRemoteTable
197− theirAddr <- readMVar deadProcess
198− runProcess localNode $
199− monitorTestProcess theirAddr mOrL un DiedDisconnect Nothing done
203−-- | Monitor a process which terminates normally
204−testMonitorNormalTermination :: NT.Transport -> Bool -> Bool -> Assertion
205−testMonitorNormalTermination transport mOrL un = do
206− monitorSetup <- newEmptyMVar
207− monitoredProcess <- newEmptyMVar
208− done <- newEmptyMVar
211− localNode <- newLocalNode transport initRemoteTable
212− addr <- forkProcess localNode $
213− liftIO $ readMVar monitorSetup
214− putMVar monitoredProcess addr
217− localNode <- newLocalNode transport initRemoteTable
218− theirAddr <- readMVar monitoredProcess
219− runProcess localNode $
220− monitorTestProcess theirAddr mOrL un DiedNormal (Just monitorSetup) done
224−-- | Monitor a process which terminates abnormally
225−testMonitorAbnormalTermination :: NT.Transport -> Bool -> Bool -> Assertion
226−testMonitorAbnormalTermination transport mOrL un = do
227− monitorSetup <- newEmptyMVar
228− monitoredProcess <- newEmptyMVar
229− done <- newEmptyMVar
231− let err = userError "Abnormal termination"
234− localNode <- newLocalNode transport initRemoteTable
235− addr <- forkProcess localNode . liftIO $ do
236− readMVar monitorSetup
238− putMVar monitoredProcess addr
241− localNode <- newLocalNode transport initRemoteTable
242− theirAddr <- readMVar monitoredProcess
243− runProcess localNode $
244− monitorTestProcess theirAddr mOrL un (DiedException (show err)) (Just monitorSetup) done
248−-- | Monitor a local process that is already dead
249−testMonitorLocalDeadProcess :: NT.Transport -> Bool -> Bool -> Assertion
250−testMonitorLocalDeadProcess transport mOrL un = do
251− processDead <- newEmptyMVar
252− processAddr <- newEmptyMVar
253− localNode <- newLocalNode transport initRemoteTable
254− done <- newEmptyMVar
257− addr <- forkProcess localNode . liftIO $ putMVar processDead ()
258− putMVar processAddr addr
261− theirAddr <- readMVar processAddr
262− readMVar processDead
263− runProcess localNode $ do
264− monitorTestProcess theirAddr mOrL un DiedUnknownId Nothing done
268−-- | Monitor a remote process that is already dead
269−testMonitorRemoteDeadProcess :: NT.Transport -> Bool -> Bool -> Assertion
270−testMonitorRemoteDeadProcess transport mOrL un = do
271− processDead <- newEmptyMVar
272− processAddr <- newEmptyMVar
273− done <- newEmptyMVar
276− localNode <- newLocalNode transport initRemoteTable
277− addr <- forkProcess localNode . liftIO $ putMVar processDead ()
278− putMVar processAddr addr
281− localNode <- newLocalNode transport initRemoteTable
282− theirAddr <- readMVar processAddr
283− readMVar processDead
284− runProcess localNode $ do
285− monitorTestProcess theirAddr mOrL un DiedUnknownId Nothing done
289−-- | Monitor a process that becomes disconnected
290−testMonitorDisconnect :: NT.Transport -> Bool -> Bool -> Assertion
291−testMonitorDisconnect transport mOrL un = do
292− processAddr <- newEmptyMVar
293− monitorSetup <- newEmptyMVar
294− done <- newEmptyMVar
297− localNode <- newLocalNode transport initRemoteTable
298− addr <- forkProcess localNode . liftIO $ threadDelay 1000000
299− putMVar processAddr addr
300− readMVar monitorSetup
301− NT.closeEndPoint (localEndPoint localNode)
304− localNode <- newLocalNode transport initRemoteTable
305− theirAddr <- readMVar processAddr
306− runProcess localNode $ do
307− monitorTestProcess theirAddr mOrL un DiedDisconnect (Just monitorSetup) done
311−-- | Test the math server (i.e., receiveWait)
312−testMath :: NT.Transport -> Assertion
313−testMath transport = do
314− serverAddr <- newEmptyMVar
315− clientDone <- newEmptyMVar
319− localNode <- newLocalNode transport initRemoteTable
320− addr <- forkProcess localNode math
321− putMVar serverAddr addr
325− localNode <- newLocalNode transport initRemoteTable
326− mathServer <- readMVar serverAddr
328− runProcess localNode $ do
330− send mathServer (Add pid 1 2)
331− 3 <- expect :: Process Double
332− send mathServer (Divide pid 8 2)
333− 4 <- expect :: Process Double
334− send mathServer (Divide pid 8 0)
336− liftIO $ putMVar clientDone ()
340−-- | Send first message (i.e. connect) to an already terminated process
341−-- (without monitoring); then send another message to a second process on
342−-- the same remote node (we're checking that the remote node did not die)
343−testSendToTerminated :: NT.Transport -> Assertion
344−testSendToTerminated transport = do
345− serverAddr1 <- newEmptyMVar
346− serverAddr2 <- newEmptyMVar
347− clientDone <- newEmptyMVar
350− terminated <- newEmptyMVar
351− localNode <- newLocalNode transport initRemoteTable
352− addr1 <- forkProcess localNode $ liftIO $ putMVar terminated ()
353− addr2 <- forkProcess localNode $ ping
355− putMVar serverAddr1 addr1
356− putMVar serverAddr2 addr2
359− localNode <- newLocalNode transport initRemoteTable
360− server1 <- readMVar serverAddr1
361− server2 <- readMVar serverAddr2
362− runProcess localNode $ do
365− send server2 (Pong pid)
367− True <- return $ pid' == server2
368− liftIO $ putMVar clientDone ()
372−-- | Test (non-zero) timeout
373−testTimeout :: NT.Transport -> Assertion
374−testTimeout transport = do
375− localNode <- newLocalNode transport initRemoteTable
376− done <- newEmptyMVar
378− runProcess localNode $ do
379− Nothing <- receiveTimeout 1000000 [match (\(Add _ _ _) -> return ())]
380− liftIO $ putMVar done ()
384−-- | Test zero timeout
385−testTimeout0 :: NT.Transport -> Assertion
386−testTimeout0 transport = do
387− serverAddr <- newEmptyMVar
388− clientDone <- newEmptyMVar
389− messagesSent <- newEmptyMVar
392− localNode <- newLocalNode transport initRemoteTable
393− addr <- forkProcess localNode $ do
394− liftIO $ readMVar messagesSent >> threadDelay 1000000
395− -- Variation on the venerable ping server which uses a zero timeout
396− -- Since we wait for all messages to be sent before doing this receive,
397− -- we should nevertheless find the right message immediately
398− Just partner <- receiveTimeout 0 [match (\(Pong partner) -> return partner)]
400− send partner (Ping self)
401− putMVar serverAddr addr
404− localNode <- newLocalNode transport initRemoteTable
405− server <- readMVar serverAddr
406− runProcess localNode $ do
408− -- Send a bunch of messages. A large number of messages that the server
409− -- is not interested in, and then a single message that it wants
410− replicateM_ 10000 $ send server "Irrelevant message"
411− send server (Pong pid)
412− liftIO $ putMVar messagesSent ()
414− liftIO $ putMVar clientDone ()
418−-- | Test typed channels
419−testTypedChannels :: NT.Transport -> Assertion
420−testTypedChannels transport = do
421− serverChannel <- newEmptyMVar :: IO (MVar (SendPort (SendPort Bool, Int)))
422− clientDone <- newEmptyMVar
425− localNode <- newLocalNode transport initRemoteTable
426− forkProcess localNode $ do
427− (serverSendPort, rport) <- newChan
428− liftIO $ putMVar serverChannel serverSendPort
429− (clientSendPort, i) <- receiveChan rport
430− sendChan clientSendPort (even i)
434− localNode <- newLocalNode transport initRemoteTable
435− serverSendPort <- readMVar serverChannel
436− runProcess localNode $ do
437− (clientSendPort, rport) <- newChan
438− sendChan serverSendPort (clientSendPort, 5)
439− False <- receiveChan rport
440− liftIO $ putMVar clientDone ()
444−-- | Test merging receive ports
445−testMergeChannels :: NT.Transport -> Assertion
446−testMergeChannels transport = do
447− localNode <- newLocalNode transport initRemoteTable
448− testFlat localNode True "aaabbbccc"
449− testFlat localNode False "abcabcabc"
450− testNested localNode True True "aaabbbcccdddeeefffggghhhiii"
451− testNested localNode True False "adgadgadgbehbehbehcficficfi"
452− testNested localNode False True "abcabcabcdefdefdefghighighi"
453− testNested localNode False False "adgbehcfiadgbehcfiadgbehcfi"
454− testBlocked localNode True
455− testBlocked localNode False
457− -- Single layer of merging
458− testFlat :: LocalNode -> Bool -> String -> IO ()
459− testFlat localNode biased expected = do
460− done <- newEmptyMVar
461− forkProcess localNode $ do
462− rs <- mapM charChannel "abc"
463− m <- mergePorts biased rs
464− xs <- replicateM 9 $ receiveChan m
465− True <- return $ xs == expected
466− liftIO $ putMVar done ()
469− -- Two layers of merging
470− testNested :: LocalNode -> Bool -> Bool -> String -> IO ()
471− testNested localNode biasedInner biasedOuter expected = do
472− done <- newEmptyMVar
473− forkProcess localNode $ do
474− rss <- mapM (mapM charChannel) ["abc", "def", "ghi"]
475− ms <- mapM (mergePorts biasedInner) rss
476− m <- mergePorts biasedOuter ms
477− xs <- replicateM (9 * 3) $ receiveChan m
478− True <- return $ xs == expected
479− liftIO $ putMVar done ()
482− -- Test that if no messages are (immediately) available, the scheduler makes no difference
483− testBlocked :: LocalNode -> Bool -> IO ()
484− testBlocked localNode biased = do
485− vs <- replicateM 3 newEmptyMVar
486− done <- newEmptyMVar
488− forkProcess localNode $ do
489− [sa, sb, sc] <- liftIO $ mapM readMVar vs
490− mapM_ ((>> liftIO (threadDelay 10000)) . uncurry sendChan)
517− forkProcess localNode $ do
518− (ss, rs) <- unzip <$> replicateM 3 newChan
519− liftIO $ mapM_ (uncurry putMVar) $ zip vs ss
520− m <- mergePorts biased rs
521− xs <- replicateM (6 * 3) $ receiveChan m
522− True <- return $ xs == "abcacbbacbcacabcba"
523− liftIO $ putMVar done ()
527− mergePorts :: Serializable a => Bool -> [ReceivePort a] -> Process (ReceivePort a)
528− mergePorts True = mergePortsBiased
529− mergePorts False = mergePortsRR
531− charChannel :: Char -> Process (ReceivePort Char)
533− (sport, rport) <- newChan
534− replicateM_ 3 $ sendChan sport c
535− liftIO $ threadDelay 10000 -- Make sure messages have been sent
538−testTerminate :: NT.Transport -> Assertion
539−testTerminate transport = do
540− localNode <- newLocalNode transport initRemoteTable
541− done <- newEmptyMVar
543− pid <- forkProcess localNode $ do
544− liftIO $ threadDelay 100000
547− runProcess localNode $ do
549− ProcessMonitorNotification ref' pid' (DiedException ex) <- expect
550− True <- return $ ref == ref' && pid == pid' && ex == show ProcessTerminationException
551− liftIO $ putMVar done ()
555−testMonitorNode :: NT.Transport -> Assertion
556−testMonitorNode transport = do
557− [node1, node2] <- replicateM 2 $ newLocalNode transport initRemoteTable
558− done <- newEmptyMVar
560− closeLocalNode node1
562− runProcess node2 $ do
563− ref <- monitorNode (localNodeId node1)
564− NodeMonitorNotification ref' nid DiedDisconnect <- expect
565− True <- return $ ref == ref' && nid == localNodeId node1
566− liftIO $ putMVar done ()
570−testMonitorLiveNode :: NT.Transport -> Assertion
571−testMonitorLiveNode transport = do
572− [node1, node2] <- replicateM 2 $ newLocalNode transport initRemoteTable
573− ready <- newEmptyMVar
574− done <- newEmptyMVar
576− forkProcess node2 $ do
577− ref <- monitorNode (localNodeId node1)
578− liftIO $ putMVar ready ()
579− NodeMonitorNotification ref' nid _ <- expect
580− True <- return $ ref == ref' && nid == localNodeId node1
581− liftIO $ putMVar done ()
584− closeLocalNode node1
588−testMonitorChannel :: NT.Transport -> Assertion
589−testMonitorChannel transport = do
590− [node1, node2] <- replicateM 2 $ newLocalNode transport initRemoteTable
591− gotNotification <- newEmptyMVar
593− pid <- forkProcess node1 $ do
594− sport <- expect :: Process (SendPort ())
595− ref <- monitorPort sport
596− PortMonitorNotification ref' port' reason <- expect
597− -- reason might be DiedUnknownId if the receive port is GCed before the
598− -- monitor is established (TODO: not sure that this is reasonable)
599− return $ ref' == ref && port' == sendPortId sport && (reason == DiedNormal || reason == DiedUnknownId)
600− liftIO $ putMVar gotNotification ()
602− runProcess node2 $ do
603− (sport, _) <- newChan :: Process (SendPort (), ReceivePort ())
605− liftIO $ threadDelay 100000
607− takeMVar gotNotification
609−testRegistry :: NT.Transport -> Assertion
610−testRegistry transport = do
611− node <- newLocalNode transport initRemoteTable
612− done <- newEmptyMVar
614− pingServer <- forkProcess node ping
616− runProcess node $ do
617− register "ping" pingServer
618− Just pid <- whereis "ping"
619− True <- return $ pingServer == pid
621− nsend "ping" (Pong us)
623− True <- return $ pingServer == pid'
624− liftIO $ putMVar done ()
628−testRemoteRegistry :: NT.Transport -> Assertion
629−testRemoteRegistry transport = do
630− node1 <- newLocalNode transport initRemoteTable
631− node2 <- newLocalNode transport initRemoteTable
632− done <- newEmptyMVar
634− pingServer <- forkProcess node1 ping
636− runProcess node2 $ do
637− let nid1 = localNodeId node1
638− registerRemoteAsync nid1 "ping" pingServer
640− matchIf (\(RegisterReply label' _) -> "ping" == label')
641− (\(RegisterReply _ _) -> return ()) ]
643− Just pid <- whereisRemote nid1 "ping"
644− True <- return $ pingServer == pid
646− nsendRemote nid1 "ping" (Pong us)
648− True <- return $ pingServer == pid'
649− liftIO $ putMVar done ()
653−testSpawnLocal :: NT.Transport -> Assertion
654−testSpawnLocal transport = do
655− node <- newLocalNode transport initRemoteTable
656− done <- newEmptyMVar
658− runProcess node $ do
661− pid <- spawnLocal $ do
663− sendChan sport (1234 :: Int)
665− sport <- spawnChannelLocal $ \rport -> do
666− (1234 :: Int) <- receiveChan rport
671− liftIO $ putMVar done ()
675−testReconnect :: NT.Transport -> TransportInternals -> Assertion
676−testReconnect transport transportInternals = do
677− [node1, node2] <- replicateM 2 $ newLocalNode transport initRemoteTable
678− let nid1 = localNodeId node1
679− nid2 = localNodeId node2
680− processA <- newEmptyMVar
681− [sendTestOk, registerTestOk] <- replicateM 2 newEmptyMVar
683− forkProcess node1 $ do
685− liftIO $ putMVar processA us
688− True <- return $ msg1 == "message 1" && msg2 == "message 3"
689− liftIO $ putMVar sendTestOk ()
691− forkProcess node2 $ do
693− - Make sure there is no implicit reconnect on normal message sending
696− them <- liftIO $ readMVar processA
697− send them "message 1" >> liftIO (threadDelay 100000)
699− -- Simulate network failure
701− sock <- socketBetween transportInternals (nodeAddress nid1) (nodeAddress nid2)
705− -- Should not arrive
706− send them "message 2"
710− send them "message 3"
712− liftIO $ takeMVar sendTestOk
715− - Test that there *is* implicit reconnect on node controller messages
719− registerRemoteAsync nid1 "a" us -- registerRemote is asynchronous
721− matchIf (\(RegisterReply label' _) -> "a" == label')
722− (\(RegisterReply _ _) -> return ()) ]
724− Just _ <- whereisRemote nid1 "a"
727− -- Simulate network failure
729− sock <- socketBetween transportInternals (nodeAddress nid1) (nodeAddress nid2)
733− -- This will happen due to implicit reconnect
734− registerRemoteAsync nid1 "b" us
736− matchIf (\(RegisterReply label' _) -> "b" == label')
737− (\(RegisterReply _ _) -> return ()) ]
740− registerRemoteAsync nid1 "c" us
742− matchIf (\(RegisterReply label' _) -> "c" == label')
743− (\(RegisterReply _ _) -> return ()) ]
746− Nothing <- whereisRemote nid1 "a" -- this will fail because the name is removed when the node is disconnected
747− Just _ <- whereisRemote nid1 "b" -- this will suceed because the value is set after thereconnect
748− Just _ <- whereisRemote nid1 "c"
750− liftIO $ putMVar registerTestOk ()
752− takeMVar registerTestOk
754−-- | Test 'matchAny'. This repeats the 'testMath' but with a proxy server
756−testMatchAny :: NT.Transport -> Assertion
757−testMatchAny transport = do
758− proxyAddr <- newEmptyMVar
759− clientDone <- newEmptyMVar
763− localNode <- newLocalNode transport initRemoteTable
764− mathServer <- forkProcess localNode math
765− proxyServer <- forkProcess localNode $ forever $ do
766− msg <- receiveWait [ matchAny return ]
767− forward msg mathServer
768− putMVar proxyAddr proxyServer
772− localNode <- newLocalNode transport initRemoteTable
773− mathServer <- readMVar proxyAddr
775− runProcess localNode $ do
777− send mathServer (Add pid 1 2)
778− 3 <- expect :: Process Double
779− send mathServer (Divide pid 8 2)
780− 4 <- expect :: Process Double
781− send mathServer (Divide pid 8 0)
783− liftIO $ putMVar clientDone ()
787−-- | Test 'matchAny'. This repeats the 'testMath' but with a proxy server
788−-- in between, however we block 'Divide' requests ....
789−testMatchAnyHandle :: NT.Transport -> Assertion
790−testMatchAnyHandle transport = do
791− proxyAddr <- newEmptyMVar
792− clientDone <- newEmptyMVar
796− localNode <- newLocalNode transport initRemoteTable
797− mathServer <- forkProcess localNode math
798− proxyServer <- forkProcess localNode $ forever $ do
800− matchAny (maybeForward mathServer)
802− putMVar proxyAddr proxyServer
806− localNode <- newLocalNode transport initRemoteTable
807− mathServer <- readMVar proxyAddr
809− runProcess localNode $ do
811− send mathServer (Add pid 1 2)
812− 3 <- expect :: Process Double
813− send mathServer (Divide pid 8 2)
814− Nothing <- (expectTimeout 100000) :: Process (Maybe Double)
815− liftIO $ putMVar clientDone ()
818− where maybeForward :: ProcessId -> Message -> Process (Maybe ())
819− maybeForward s msg =
820− handleMessage msg (\m@(Add _ _ _) -> send s m)
822−testMatchAnyNoHandle :: NT.Transport -> Assertion
823−testMatchAnyNoHandle transport = do
824− addr <- newEmptyMVar
825− clientDone <- newEmptyMVar
826− serverDone <- newEmptyMVar
830− localNode <- newLocalNode transport initRemoteTable
831− server <- forkProcess localNode $ forever $ do
834− -- the condition has type `Add -> Bool`
835− (\(Add _ _ _) -> True)
836− -- the match `AbstractMessage -> Process ()` will succeed!
838− -- `String -> Process ()` does *not* match the input types however
839− r <- (handleMessage m (\(_ :: String) -> die "NONSENSE" ))
841− Nothing -> return ()
842− Just _ -> die "NONSENSE")
844− -- we *must* have removed the message from our mailbox though!!!
845− Nothing <- receiveTimeout 100000 [ match (\(Add _ _ _) -> return ()) ]
846− liftIO $ putMVar serverDone ()
851− localNode <- newLocalNode transport initRemoteTable
852− server <- readMVar addr
854− runProcess localNode $ do
856− send server (Add pid 1 2)
857− -- we only care about the client having sent a message, so we're done
858− liftIO $ putMVar clientDone ()
863−-- | Test 'matchAnyIf'. We provide an /echo/ server, but it ignores requests
864−-- unless the text body @/= "bar"@ - this case should time out rather than
865−-- removing the message from the process mailbox.
866−testMatchAnyIf :: NT.Transport -> Assertion
867−testMatchAnyIf transport = do
868− echoAddr <- newEmptyMVar
869− clientDone <- newEmptyMVar
873− localNode <- newLocalNode transport initRemoteTable
874− echoServer <- forkProcess localNode $ forever $ do
876− matchAnyIf (\(_ :: ProcessId, (s :: String)) -> s /= "bar")
879− putMVar echoAddr echoServer
883− localNode <- newLocalNode transport initRemoteTable
884− server <- readMVar echoAddr
886− runProcess localNode $ do
888− send server (pid, "foo")
890− send server (pid, "baz")
892− send server (pid, "bar")
893− Nothing <- (expectTimeout 100000) :: Process (Maybe Double)
894− liftIO $ putMVar clientDone ()
897− where tryHandleMessage :: Message -> Process (Maybe ())
898− tryHandleMessage msg =
899− handleMessage msg (\(pid :: ProcessId, (m :: String))
900− -> do { send pid m; return () })
902−testMatchMessageWithUnwrap :: NT.Transport -> Assertion
903−testMatchMessageWithUnwrap transport = do
904− echoAddr <- newEmptyMVar
905− clientDone <- newEmptyMVar
909− localNode <- newLocalNode transport initRemoteTable
910− echoServer <- forkProcess localNode $ forever $ do
911− msg <- receiveWait [
912− matchMessage (\(m :: Message) -> do
915− unwrapped <- unwrapMessage msg :: Process (Maybe (ProcessId, Message))
917− (Just (p, msg')) -> forward msg' p
918− Nothing -> die "unable to unwrap the message"
919− putMVar echoAddr echoServer
923− localNode <- newLocalNode transport initRemoteTable
924− server <- readMVar echoAddr
926− runProcess localNode $ do
928− send server (pid, wrapMessage ("foo" :: String))
930− send server (pid, wrapMessage ("baz" :: String))
932− liftIO $ putMVar clientDone ()
936−-- Test 'receiveChanTimeout'
937−testReceiveChanTimeout :: NT.Transport -> Assertion
938−testReceiveChanTimeout transport = do
939− done <- newEmptyMVar
940− sendPort <- newEmptyMVar
943− localNode <- newLocalNode transport initRemoteTable
944− runProcess localNode $ do
945− -- Create a typed channel
946− (sp, rp) <- newChan :: Process (SendPort Bool, ReceivePort Bool)
947− liftIO $ putMVar sendPort sp
949− -- Wait for a message with a delay. No message arrives, we should get Nothing after 1 second
950− Nothing <- receiveChanTimeout 1000000 rp
952− -- Wait for a message with a delay again. Now a message arrives after 0.5 seconds
953− Just True <- receiveChanTimeout 1000000 rp
955− -- Wait for a message with zero timeout: non-blocking check. No message is available, we get Nothing
956− Nothing <- receiveChanTimeout 0 rp
958− -- Again, but now there is a message available
959− liftIO $ threadDelay 1000000
960− Just False <- receiveChanTimeout 0 rp
962− liftIO $ putMVar done ()
965− localNode <- newLocalNode transport initRemoteTable
966− runProcess localNode $ do
967− sp <- liftIO $ readMVar sendPort
969− liftIO $ threadDelay 1500000
972− liftIO $ threadDelay 500000
977−-- | Test Functor, Applicative, Alternative and Monad instances for ReceiveChan
978−testReceiveChanFeatures :: NT.Transport -> Assertion
979−testReceiveChanFeatures transport = do
980− done <- newEmptyMVar
983− localNode <- newLocalNode transport initRemoteTable
984− runProcess localNode $ do
985− (spInt, rpInt) <- newChan :: Process (SendPort Int, ReceivePort Int)
986− (spBool, rpBool) <- newChan :: Process (SendPort Bool, ReceivePort Bool)
988− -- Test Functor instance
991− sendChan spBool False
993− rp1 <- mergePortsBiased [even <$> rpInt, rpBool]
995− True <- receiveChan rp1
996− False <- receiveChan rp1
998− -- Test Applicative instance
1003− let rp2 = pure (+) <*> rpInt <*> rpInt
1005− 7 <- receiveChan rp2
1007− -- Test Alternative instance
1010− sendChan spBool True
1012− let rp3 = (even <$> rpInt) <|> rpBool
1014− False <- receiveChan rp3
1015− True <- receiveChan rp3
1017− -- Test Monad instance
1019− sendChan spBool True
1020− sendChan spBool False
1023− let rp4 :: ReceivePort Int
1024− rp4 = do b <- rpBool
1029− 5 <- receiveChan rp4
1030− 7 <- receiveChan rp4
1032− liftIO $ putMVar done ()
1036−testKillLocal :: NT.Transport -> Assertion
1037−testKillLocal transport = do
1038− localNode <- newLocalNode transport initRemoteTable
1039− done <- newEmptyMVar
1041− pid <- forkProcess localNode $ do
1042− liftIO $ threadDelay 1000000
1044− runProcess localNode $ do
1047− kill pid "TestKill"
1048− ProcessMonitorNotification ref' pid' (DiedException ex) <- expect
1049− True <- return $ ref == ref' && pid == pid' && ex == "killed-by=" ++ show us ++ ",reason=TestKill"
1050− liftIO $ putMVar done ()
1054−testKillRemote :: NT.Transport -> Assertion
1055−testKillRemote transport = do
1056− node1 <- newLocalNode transport initRemoteTable
1057− node2 <- newLocalNode transport initRemoteTable
1058− done <- newEmptyMVar
1060− pid <- forkProcess node1 $ do
1061− liftIO $ threadDelay 1000000
1063− runProcess node2 $ do
1066− kill pid "TestKill"
1067− ProcessMonitorNotification ref' pid' (DiedException reason) <- expect
1068− True <- return $ ref == ref' && pid == pid' && reason == "killed-by=" ++ show us ++ ",reason=TestKill"
1069− liftIO $ putMVar done ()
1073−testCatchesExit :: NT.Transport -> Assertion
1074−testCatchesExit transport = do
1075− localNode <- newLocalNode transport initRemoteTable
1076− done <- newEmptyMVar
1078− _ <- forkProcess localNode $ do
1079− (die ("foobar", 123 :: Int))
1081− (\_ m -> handleMessage m (\(_ :: String) -> return ()))
1082− , (\_ m -> handleMessage m (\(_ :: Maybe Int) -> return ()))
1083− , (\_ m -> handleMessage m (\(_ :: String, _ :: Int)
1084− -> (liftIO $ putMVar done ()) >> return ()))
1089−testHandleMessageIf :: NT.Transport -> Assertion
1090−testHandleMessageIf transport = do
1091− localNode <- newLocalNode transport initRemoteTable
1092− done <- newEmptyMVar
1093− _ <- forkProcess localNode $ do
1095− send self (5 :: Integer, 10 :: Integer)
1096− msg <- receiveWait [ matchMessage return ]
1097− Nothing <- handleMessageIf msg (\() -> True) (\() -> die $ "whoops")
1098− handleMessageIf msg (\(x :: Integer, y :: Integer) -> x == 5 && y == 10)
1099− (\input -> liftIO $ putMVar done input)
1102− result <- takeMVar done
1103− expectThat result $ equalTo (5, 10)
1105−testCatches :: NT.Transport -> Assertion
1106−testCatches transport = do
1107− localNode <- newLocalNode transport initRemoteTable
1108− done <- newEmptyMVar
1110− _ <- forkProcess localNode $ do
1111− node <- getSelfNode
1112− (liftIO $ throwIO (ProcessLinkException (nullProcessId node) DiedNormal))
1114− Handler (\(ProcessLinkException _ _) -> liftIO $ putMVar done ())
1119−testMaskRestoreScope :: NT.Transport -> Assertion
1120−testMaskRestoreScope transport = do
1121− localNode <- newLocalNode transport initRemoteTable
1122− parentPid <- newEmptyMVar :: IO (MVar ProcessId)
1123− spawnedPid <- newEmptyMVar :: IO (MVar ProcessId)
1125− void $ runProcess localNode $ mask $ \unmask -> do
1126− getSelfPid >>= liftIO . putMVar parentPid
1127− void $ spawnLocal $ unmask (getSelfPid >>= liftIO . putMVar spawnedPid)
1129− parent <- liftIO $ takeMVar parentPid
1130− child <- liftIO $ takeMVar spawnedPid
1131− expectThat parent $ isNot $ equalTo child
1133−testDie :: NT.Transport -> Assertion
1134−testDie transport = do
1135− localNode <- newLocalNode transport initRemoteTable
1136− done <- newEmptyMVar
1138− _ <- forkProcess localNode $ do
1139− (die ("foobar", 123 :: Int))
1140− `catchExit` \_from reason -> do
1141− -- TODO: should verify that 'from' has the right value
1142− True <- return $ reason == ("foobar", 123 :: Int)
1143− liftIO $ putMVar done ()
1147−testPrettyExit :: NT.Transport -> Assertion
1148−testPrettyExit transport = do
1149− localNode <- newLocalNode transport initRemoteTable
1150− done <- newEmptyMVar
1152− _ <- forkProcess localNode $ do
1154− `catch` \ex@(ProcessExitException from _) ->
1155− let expected = "exit-from=" ++ (show from)
1157− True <- return $ (show ex) == expected
1158− liftIO $ putMVar done ()
1162−testExitLocal :: NT.Transport -> Assertion
1163−testExitLocal transport = do
1164− localNode <- newLocalNode transport initRemoteTable
1165− supervisedDone <- newEmptyMVar
1166− supervisorDone <- newEmptyMVar
1168− pid <- forkProcess localNode $ do
1169− (liftIO $ threadDelay 100000)
1170− `catchExit` \_from reason -> do
1171− -- TODO: should verify that 'from' has the right value
1172− True <- return $ reason == "TestExit"
1173− liftIO $ putMVar supervisedDone ()
1175− runProcess localNode $ do
1177− exit pid "TestExit"
1178− -- This time the client catches the exception, so it dies normally
1179− ProcessMonitorNotification ref' pid' DiedNormal <- expect
1180− True <- return $ ref == ref' && pid == pid'
1181− liftIO $ putMVar supervisorDone ()
1183− takeMVar supervisedDone
1184− takeMVar supervisorDone
1186−testExitRemote :: NT.Transport -> Assertion
1187−testExitRemote transport = do
1188− node1 <- newLocalNode transport initRemoteTable
1189− node2 <- newLocalNode transport initRemoteTable
1190− supervisedDone <- newEmptyMVar
1191− supervisorDone <- newEmptyMVar
1193− pid <- forkProcess node1 $ do
1194− (liftIO $ threadDelay 100000)
1195− `catchExit` \_from reason -> do
1196− -- TODO: should verify that 'from' has the right value
1197− True <- return $ reason == "TestExit"
1198− liftIO $ putMVar supervisedDone ()
1200− runProcess node2 $ do
1202− exit pid "TestExit"
1203− ProcessMonitorNotification ref' pid' DiedNormal <- expect
1204− True <- return $ ref == ref' && pid == pid'
1205− liftIO $ putMVar supervisorDone ()
1207− takeMVar supervisedDone
1208− takeMVar supervisorDone
1210−testUnsafeSend :: NT.Transport -> Assertion
1211−testUnsafeSend transport = do
1212− serverAddr <- newEmptyMVar
1213− clientDone <- newEmptyMVar
1215− localNode <- newLocalNode transport initRemoteTable
1216− void $ forkProcess localNode $ do
1218− liftIO $ putMVar serverAddr self
1219− clientAddr <- expect
1220− unsafeSend clientAddr ()
1222− void $ forkProcess localNode $ do
1223− serverPid <- liftIO $ takeMVar serverAddr
1224− getSelfPid >>= unsafeSend serverPid
1226− liftIO $ putMVar clientDone ()
1228− takeMVar clientDone
1230−testUnsafeNSend :: NT.Transport -> Assertion
1231−testUnsafeNSend transport = do
1232− clientDone <- newEmptyMVar
1234− localNode <- newLocalNode transport initRemoteTable
1236− pid <- forkProcess localNode $ do
1238− liftIO $ putMVar clientDone ()
1240− void $ runProcess localNode $ do
1241− register "foobar" pid
1242− unsafeNSend "foobar" ()
1244− takeMVar clientDone
1246−testUnsafeSendChan :: NT.Transport -> Assertion
1247−testUnsafeSendChan transport = do
1248− serverAddr <- newEmptyMVar
1249− clientDone <- newEmptyMVar
1251− localNode <- newLocalNode transport initRemoteTable
1252− void $ forkProcess localNode $ do
1254− liftIO $ putMVar serverAddr self
1256− unsafeSendChan sp ()
1258− void $ forkProcess localNode $ do
1259− serverPid <- liftIO $ takeMVar serverAddr
1260− (sp, rp) <- newChan
1261− unsafeSend serverPid sp
1262− () <- receiveChan rp
1263− liftIO $ putMVar clientDone ()
1265− takeMVar clientDone
1267−tests :: (NT.Transport, TransportInternals) -> [Test]
1268−tests (transport, transportInternals) = [
1269− testGroup "Basic features" [
1270− testCase "Ping" (testPing transport)
1271− , testCase "Math" (testMath transport)
1272− , testCase "Timeout" (testTimeout transport)
1273− , testCase "Timeout0" (testTimeout0 transport)
1274− , testCase "SendToTerminated" (testSendToTerminated transport)
1275− , testCase "TypedChannnels" (testTypedChannels transport)
1276− , testCase "MergeChannels" (testMergeChannels transport)
1277− , testCase "Terminate" (testTerminate transport)
1278− , testCase "Registry" (testRegistry transport)
1279− , testCase "RemoteRegistry" (testRemoteRegistry transport)
1280− , testCase "SpawnLocal" (testSpawnLocal transport)
1281− , testCase "HandleMessageIf" (testHandleMessageIf transport)
1282− , testCase "MatchAny" (testMatchAny transport)
1283− , testCase "MatchAnyHandle" (testMatchAnyHandle transport)
1284− , testCase "MatchAnyNoHandle" (testMatchAnyNoHandle transport)
1285− , testCase "MatchAnyIf" (testMatchAnyIf transport)
1286− , testCase "MatchMessageUnwrap" (testMatchMessageWithUnwrap transport)
1287− , testCase "ReceiveChanTimeout" (testReceiveChanTimeout transport)
1288− , testCase "ReceiveChanFeatures" (testReceiveChanFeatures transport)
1289− , testCase "KillLocal" (testKillLocal transport)
1290− , testCase "KillRemote" (testKillRemote transport)
1291− , testCase "Die" (testDie transport)
1292− , testCase "PrettyExit" (testPrettyExit transport)
1293− , testCase "CatchesExit" (testCatchesExit transport)
1294− , testCase "Catches" (testCatches transport)
1295− , testCase "MaskRestoreScope" (testMaskRestoreScope transport)
1296− , testCase "ExitLocal" (testExitLocal transport)
1297− , testCase "ExitRemote" (testExitRemote transport)
1298− -- Unsafe Primitives
1299− , testCase "TestUnsafeSend" (testUnsafeSend transport)
1300− , testCase "TestUnsafeNSend" (testUnsafeNSend transport)
1301− , testCase "TestUnsafeSendChan" (testUnsafeSendChan transport)
1303− , testGroup "Monitoring and Linking" [
1304− -- Monitoring processes
1306− -- The "missing" combinations in the list below don't make much sense, as
1307− -- we cannot guarantee that the monitor reply or link exception will not
1308− -- happen before the unmonitor or unlink
1309− testCase "MonitorUnreachable" (testMonitorUnreachable transport True False)
1310− , testCase "MonitorNormalTermination" (testMonitorNormalTermination transport True False)
1311− , testCase "MonitorAbnormalTermination" (testMonitorAbnormalTermination transport True False)
1312− , testCase "MonitorLocalDeadProcess" (testMonitorLocalDeadProcess transport True False)
1313− , testCase "MonitorRemoteDeadProcess" (testMonitorRemoteDeadProcess transport True False)
1314− , testCase "MonitorDisconnect" (testMonitorDisconnect transport True False)
1315− , testCase "LinkUnreachable" (testMonitorUnreachable transport False False)
1316− , testCase "LinkNormalTermination" (testMonitorNormalTermination transport False False)
1317− , testCase "LinkAbnormalTermination" (testMonitorAbnormalTermination transport False False)
1318− , testCase "LinkLocalDeadProcess" (testMonitorLocalDeadProcess transport False False)
1319− , testCase "LinkRemoteDeadProcess" (testMonitorRemoteDeadProcess transport False False)
1320− , testCase "LinkDisconnect" (testMonitorDisconnect transport False False)
1321− , testCase "UnmonitorNormalTermination" (testMonitorNormalTermination transport True True)
1322− , testCase "UnmonitorAbnormalTermination" (testMonitorAbnormalTermination transport True True)
1323− , testCase "UnmonitorDisconnect" (testMonitorDisconnect transport True True)
1324− , testCase "UnlinkNormalTermination" (testMonitorNormalTermination transport False True)
1325− , testCase "UnlinkAbnormalTermination" (testMonitorAbnormalTermination transport False True)
1326− , testCase "UnlinkDisconnect" (testMonitorDisconnect transport False True)
1327− -- Monitoring nodes and channels
1328− , testCase "MonitorNode" (testMonitorNode transport)
1329− , testCase "MonitorLiveNode" (testMonitorLiveNode transport)
1330− , testCase "MonitorChannel" (testMonitorChannel transport)
1332− , testCase "Reconnect" (testReconnect transport transportInternals)
1338− Right transport <- createTransportExposeInternals "127.0.0.1" "8080" defaultTCPParameters
1339− defaultMain (tests transport)