1module Control.Distributed.Process.Tests.CH (tests) where
3#if ! MIN_VERSION_base(4,6,0)
4import Prelude hiding (catch)
7import Network.Transport.Test (TestTransport(..))
9import Data.Binary (Binary(..))
10import Data.Typeable (Typeable)
11import Data.Foldable (forM_)
12import Data.Function (fix)
18import Control.Concurrent (forkIO, threadDelay, myThreadId, throwTo, ThreadId, yield)
19import Control.Concurrent.MVar
26import Control.Monad (replicateM_, replicateM, forever, void, unless, join)
27import Control.Exception (SomeException, throwIO, ErrorCall(..))
28import qualified Control.Exception as Ex (catch)
29import Control.Applicative ((<$>), (<*>), pure, (<|>))
30import qualified Network.Transport as NT (closeEndPoint, EndPointAddress)
31import Control.Distributed.Process
32import Control.Distributed.Process.Internal.Types
34 , LocalNode(localEndPoint)
35 , ProcessExitException(..)
37 , createUnencodedMessage
39import Control.Distributed.Process.Node
40import Control.Distributed.Process.Serializable (Serializable)
42import Test.HUnit (Assertion, assertFailure)
43import Test.Framework (Test, testGroup)
44import Test.Framework.Providers.HUnit (testCase)
45import Control.Rematch hiding (match)
46import Control.Rematch.Run (Match(..))
48newtype Ping = Ping ProcessId
49 deriving (Typeable, Binary, Show)
51newtype Pong = Pong ProcessId
52 deriving (Typeable, Binary, Show)
54--------------------------------------------------------------------------------
55-- Supporting definitions --
56--------------------------------------------------------------------------------
58expectThat :: a -> Matcher a -> Assertion
59expectThat a matcher = case res of
60 MatchSuccess -> return ()
61 (MatchFailure msg) -> assertFailure msg
62 where res = runMatch matcher a
64-- | Like fork, but throw exceptions in the child thread to the parent
65forkTry :: IO () -> IO ThreadId
68 forkIO $ Ex.catch p (\e -> throwTo tid (e :: SomeException))
70-- | The ping server from the paper
73 Pong partner <- expect
75 send partner (Ping self)
78-- | Quick and dirty synchronous version of whereisRemoteAsync
79whereisRemote :: NodeId -> String -> Process (Maybe ProcessId)
80whereisRemote nid string = do
81 whereisRemoteAsync nid string
82 WhereIsReply _ mPid <- expect
85data Add = Add ProcessId Double Double deriving (Typeable)
86data Divide = Divide ProcessId Double Double deriving (Typeable)
87data DivByZero = DivByZero deriving (Typeable)
89instance Binary Add where
90 put (Add pid x y) = put pid >> put x >> put y
91 get = Add <$> get <*> get <*> get
93instance Binary Divide where
94 put (Divide pid x y) = put pid >> put x >> put y
95 get = Divide <$> get <*> get <*> get
97instance Binary DivByZero where
98 put DivByZero = return ()
99 get = return DivByZero
101-- The math server from the paper
105 [ match (\(Add pid x y) -> send pid (x + y))
106 , matchIf (\(Divide _ _ y) -> y /= 0)
107 (\(Divide pid x y) -> send pid (x / y))
108 , match (\(Divide pid _ _) -> send pid DivByZero)
112-- | Monitor or link to a remote node
113monitorOrLink :: Bool -- ^ 'True' for monitor, 'False' for link
114 -> ProcessId -- ^ Process to monitor/link to
115 -> Maybe (MVar ()) -- ^ MVar to signal on once the monitor has been set up
116 -> Process (Maybe MonitorRef)
117monitorOrLink mOrL pid mSignal = do
118 result <- if mOrL then Just <$> monitor pid
119 else link pid >> return Nothing
120 -- Monitor is asynchronous, which usually does not matter but if we want a
121 -- *specific* signal then it does. Therefore we wait until the MonitorRef is
122 -- listed in the ProcessInfo and hope that this means the monitor has been set
124 forM_ mSignal $ \signal -> do
128 liftIO $ threadDelay 100000
129 mpinfo <- getProcessInfo pid
131 Nothing -> waitForMOrL
134 unless (result == lookup self (infoMonitors pinfo)) waitForMOrL
136 unless (elem self $ infoLinks pinfo) waitForMOrL
138 liftIO $ putMVar signal ()
141monitorTestProcess :: ProcessId -- Process to monitor/link to
142 -> Bool -- 'True' for monitor, 'False' for link
143 -> Bool -- Should we unmonitor?
144 -> DiedReason -- Expected cause of death
145 -> Maybe (MVar ()) -- Signal for 'monitor set up'
146 -> MVar () -- Signal for successful termination
148monitorTestProcess theirAddr mOrL un reason monitorSetup done =
149 catch (do mRef <- monitorOrLink mOrL theirAddr monitorSetup
151 (True, Nothing) -> do
153 liftIO $ putMVar done ()
154 (True, Just ref) -> do
156 liftIO $ putMVar done ()
158 ProcessMonitorNotification ref' pid reason' <- expect
159 True <- return $ Just ref' == ref && pid == theirAddr && mOrL && reason == reason'
160 liftIO $ putMVar done ()
162 (\(ProcessLinkException pid reason') -> do
163 True <- return $ pid == theirAddr && not mOrL && not un && reason == reason'
164 liftIO $ putMVar done ()
167--------------------------------------------------------------------------------
168-- The tests proper --
169--------------------------------------------------------------------------------
172testPing :: TestTransport -> Assertion
173testPing TestTransport{..} = do
174 serverAddr <- newEmptyMVar
175 clientDone <- newEmptyMVar
179 localNode <- newLocalNode testTransport initRemoteTable
180 addr <- forkProcess localNode ping
181 putMVar serverAddr addr
185 localNode <- newLocalNode testTransport initRemoteTable
186 pingServer <- readMVar serverAddr
190 runProcess localNode $ do
192 replicateM_ numPings $ do
193 send pingServer (Pong pid)
197 putMVar clientDone ()
201-- | Monitor a process on an unreachable node
202testMonitorUnreachable :: TestTransport -> Bool -> Bool -> Assertion
203testMonitorUnreachable TestTransport{..} mOrL un = do
204 deadProcess <- newEmptyMVar
208 localNode <- newLocalNode testTransport initRemoteTable
209 addr <- forkProcess localNode expect
210 closeLocalNode localNode
211 putMVar deadProcess addr
214 localNode <- newLocalNode testTransport initRemoteTable
215 theirAddr <- readMVar deadProcess
216 runProcess localNode $
217 monitorTestProcess theirAddr mOrL un DiedDisconnect Nothing done
221-- | Monitor a process which terminates normally
222testMonitorNormalTermination :: TestTransport -> Bool -> Bool -> Assertion
223testMonitorNormalTermination TestTransport{..} mOrL un = do
224 monitorSetup <- newEmptyMVar
225 monitoredProcess <- newEmptyMVar
229 localNode <- newLocalNode testTransport initRemoteTable
230 addr <- forkProcess localNode $
231 liftIO $ readMVar monitorSetup
232 putMVar monitoredProcess addr
235 localNode <- newLocalNode testTransport initRemoteTable
236 theirAddr <- readMVar monitoredProcess
237 runProcess localNode $
238 monitorTestProcess theirAddr mOrL un DiedNormal (Just monitorSetup) done
242-- | Monitor a process which terminates abnormally
243testMonitorAbnormalTermination :: TestTransport -> Bool -> Bool -> Assertion
244testMonitorAbnormalTermination TestTransport{..} mOrL un = do
245 monitorSetup <- newEmptyMVar
246 monitoredProcess <- newEmptyMVar
249 let err = userError "Abnormal termination"
252 localNode <- newLocalNode testTransport initRemoteTable
253 addr <- forkProcess localNode . liftIO $ do
254 readMVar monitorSetup
256 putMVar monitoredProcess addr
259 localNode <- newLocalNode testTransport initRemoteTable
260 theirAddr <- readMVar monitoredProcess
261 runProcess localNode $
262 monitorTestProcess theirAddr mOrL un (DiedException (show err)) (Just monitorSetup) done
266-- | Monitor a local process that is already dead
267testMonitorLocalDeadProcess :: TestTransport -> Bool -> Bool -> Assertion
268testMonitorLocalDeadProcess TestTransport{..} mOrL un = do
269 processAddr <- newEmptyMVar
270 localNode <- newLocalNode testTransport initRemoteTable
274 addr <- forkProcess localNode $ return ()
275 putMVar processAddr addr
278 theirAddr <- readMVar processAddr
279 runProcess localNode $ do
281 -- wait for the process to die
282 ProcessMonitorNotification _ _ _ <- expect
283 monitorTestProcess theirAddr mOrL un DiedUnknownId Nothing done
287-- | Monitor a remote process that is already dead
288testMonitorRemoteDeadProcess :: TestTransport -> Bool -> Bool -> Assertion
289testMonitorRemoteDeadProcess TestTransport{..} mOrL un = do
290 processDead <- newEmptyMVar
291 processAddr <- newEmptyMVar
295 localNode <- newLocalNode testTransport initRemoteTable
296 addr <- forkProcess localNode . liftIO $ putMVar processDead ()
297 putMVar processAddr addr
300 localNode <- newLocalNode testTransport initRemoteTable
301 theirAddr <- readMVar processAddr
303 runProcess localNode $ do
304 monitorTestProcess theirAddr mOrL un DiedUnknownId Nothing done
308-- | Monitor a process that becomes disconnected
309testMonitorDisconnect :: TestTransport -> Bool -> Bool -> Assertion
310testMonitorDisconnect TestTransport{..} mOrL un = do
311 processAddr <- newEmptyMVar
312 processAddr2 <- newEmptyMVar
313 monitorSetup <- newEmptyMVar
317 localNode <- newLocalNode testTransport initRemoteTable
318 addr <- forkProcess localNode $ expect
319 addr2 <- forkProcess localNode $ return ()
320 putMVar processAddr addr
321 readMVar monitorSetup
322 NT.closeEndPoint (localEndPoint localNode)
323 putMVar processAddr2 addr2
326 localNode <- newLocalNode testTransport initRemoteTable
327 theirAddr <- readMVar processAddr
328 forkProcess localNode $ do
329 lc <- liftIO $ readMVar processAddr2
331 runProcess localNode $ do
332 monitorTestProcess theirAddr mOrL un DiedDisconnect (Just monitorSetup) done
336-- | Test the math server (i.e., receiveWait)
337testMath :: TestTransport -> Assertion
338testMath TestTransport{..} = do
339 serverAddr <- newEmptyMVar
340 clientDone <- newEmptyMVar
344 localNode <- newLocalNode testTransport initRemoteTable
345 addr <- forkProcess localNode math
346 putMVar serverAddr addr
350 localNode <- newLocalNode testTransport initRemoteTable
351 mathServer <- readMVar serverAddr
353 runProcess localNode $ do
355 send mathServer (Add pid 1 2)
356 3 <- expect :: Process Double
357 send mathServer (Divide pid 8 2)
358 4 <- expect :: Process Double
359 send mathServer (Divide pid 8 0)
361 liftIO $ putMVar clientDone ()
365-- | Send first message (i.e. connect) to an already terminated process
366-- (without monitoring); then send another message to a second process on
367-- the same remote node (we're checking that the remote node did not die)
368testSendToTerminated :: TestTransport -> Assertion
369testSendToTerminated TestTransport{..} = do
370 serverAddr1 <- newEmptyMVar
371 serverAddr2 <- newEmptyMVar
372 clientDone <- newEmptyMVar
375 terminated <- newEmptyMVar
376 localNode <- newLocalNode testTransport initRemoteTable
377 addr1 <- forkProcess localNode $ liftIO $ putMVar terminated ()
378 addr2 <- forkProcess localNode $ ping
380 putMVar serverAddr1 addr1
381 putMVar serverAddr2 addr2
384 localNode <- newLocalNode testTransport initRemoteTable
385 server1 <- readMVar serverAddr1
386 server2 <- readMVar serverAddr2
387 runProcess localNode $ do
390 send server2 (Pong pid)
392 True <- return $ pid' == server2
393 liftIO $ putMVar clientDone ()
397-- | Test (non-zero) timeout
398testTimeout :: TestTransport -> Assertion
399testTimeout TestTransport{..} = do
400 localNode <- newLocalNode testTransport initRemoteTable
403 runProcess localNode $ do
404 Nothing <- receiveTimeout 1000000 [match (\(Add _ _ _) -> return ())]
405 liftIO $ putMVar done ()
409-- | Test zero timeout
410testTimeout0 :: TestTransport -> Assertion
411testTimeout0 TestTransport{..} = do
412 serverAddr <- newEmptyMVar
413 clientDone <- newEmptyMVar
416 localNode <- newLocalNode testTransport initRemoteTable
417 addr <- forkProcess localNode $ do
418 -- Variation on the venerable ping server which uses a zero timeout
419 partner <- fix $ \loop ->
420 receiveTimeout 0 [match (\(Pong partner) -> return partner)]
421 >>= maybe (liftIO (threadDelay 100000) >> loop) return
423 send partner (Ping self)
424 putMVar serverAddr addr
427 localNode <- newLocalNode testTransport initRemoteTable
428 server <- readMVar serverAddr
429 runProcess localNode $ do
431 -- Send a bunch of messages. A large number of messages that the server
432 -- is not interested in, and then a single message that it wants
433 replicateM_ 10000 $ send server "Irrelevant message"
434 send server (Pong pid)
436 liftIO $ putMVar clientDone ()
440-- | Test typed channels
441testTypedChannels :: TestTransport -> Assertion
442testTypedChannels TestTransport{..} = do
443 serverChannel <- newEmptyMVar :: IO (MVar (SendPort (SendPort Bool, Int)))
444 clientDone <- newEmptyMVar
447 localNode <- newLocalNode testTransport initRemoteTable
448 forkProcess localNode $ do
449 (serverSendPort, rport) <- newChan
450 liftIO $ putMVar serverChannel serverSendPort
451 (clientSendPort, i) <- receiveChan rport
452 sendChan clientSendPort (even i)
456 localNode <- newLocalNode testTransport initRemoteTable
457 serverSendPort <- readMVar serverChannel
458 runProcess localNode $ do
459 (clientSendPort, rport) <- newChan
460 sendChan serverSendPort (clientSendPort, 5)
461 False <- receiveChan rport
462 liftIO $ putMVar clientDone ()
466-- | Test merging receive ports
467testMergeChannels :: TestTransport -> Assertion
468testMergeChannels TestTransport{..} = do
469 localNode <- newLocalNode testTransport initRemoteTable
470 testFlat localNode True "aaabbbccc"
471 testFlat localNode False "abcabcabc"
472 testNested localNode True True "aaabbbcccdddeeefffggghhhiii"
473 testNested localNode True False "adgadgadgbehbehbehcficficfi"
474 testNested localNode False True "abcabcabcdefdefdefghighighi"
475 testNested localNode False False "adgbehcfiadgbehcfiadgbehcfi"
476 testBlocked localNode True
477 testBlocked localNode False
479 -- Single layer of merging
480 testFlat :: LocalNode -> Bool -> String -> IO ()
481 testFlat localNode biased expected = do
483 forkProcess localNode $ do
484 rs <- mapM charChannel "abc"
485 m <- mergePorts biased rs
486 xs <- replicateM 9 $ receiveChan m
487 True <- return $ xs == expected
488 liftIO $ putMVar done ()
491 -- Two layers of merging
492 testNested :: LocalNode -> Bool -> Bool -> String -> IO ()
493 testNested localNode biasedInner biasedOuter expected = do
495 forkProcess localNode $ do
496 rss <- mapM (mapM charChannel) ["abc", "def", "ghi"]
497 ms <- mapM (mergePorts biasedInner) rss
498 m <- mergePorts biasedOuter ms
499 xs <- replicateM (9 * 3) $ receiveChan m
500 True <- return $ xs == expected
501 liftIO $ putMVar done ()
504 -- Test that if no messages are (immediately) available, the scheduler makes no difference
505 testBlocked :: LocalNode -> Bool -> IO ()
506 testBlocked localNode biased = do
507 vs <- replicateM 3 newEmptyMVar
510 forkProcess localNode $ do
511 [sa, sb, sc] <- liftIO $ mapM readMVar vs
512 mapM_ ((>> liftIO (threadDelay 10000)) . uncurry sendChan)
539 forkProcess localNode $ do
540 (ss, rs) <- unzip <$> replicateM 3 newChan
541 liftIO $ mapM_ (uncurry putMVar) $ zip vs ss
542 m <- mergePorts biased rs
543 xs <- replicateM (6 * 3) $ receiveChan m
544 True <- return $ xs == "abcacbbacbcacabcba"
545 liftIO $ putMVar done ()
549 mergePorts :: Serializable a => Bool -> [ReceivePort a] -> Process (ReceivePort a)
550 mergePorts True = mergePortsBiased
551 mergePorts False = mergePortsRR
553 charChannel :: Char -> Process (ReceivePort Char)
555 (sport, rport) <- newChan
556 replicateM_ 3 $ sendChan sport c
557 liftIO $ threadDelay 10000 -- Make sure messages have been sent
560testTerminate :: TestTransport -> Assertion
561testTerminate TestTransport{..} = do
562 localNode <- newLocalNode testTransport initRemoteTable
563 runProcess localNode $ do
564 e <- try terminate :: Process (Either ProcessTerminationException ())
565 True <- return $ either show show e == show ProcessTerminationException
568testMonitorNode :: TestTransport -> Assertion
569testMonitorNode TestTransport{..} = do
570 [node1, node2] <- replicateM 2 $ newLocalNode testTransport initRemoteTable
575 runProcess node2 $ do
576 ref <- monitorNode (localNodeId node1)
577 NodeMonitorNotification ref' nid DiedDisconnect <- expect
578 True <- return $ ref == ref' && nid == localNodeId node1
579 liftIO $ putMVar done ()
583testMonitorLiveNode :: TestTransport -> Assertion
584testMonitorLiveNode TestTransport{..} = do
585 [node1, node2] <- replicateM 2 $ newLocalNode testTransport initRemoteTable
586 ready <- newEmptyMVar
587 readyr <- newEmptyMVar
590 p <- forkProcess node1 $ return ()
591 forkProcess node2 $ do
592 ref <- monitorNode (localNodeId node1)
593 liftIO $ putMVar ready ()
594 liftIO $ takeMVar readyr
596 NodeMonitorNotification ref' nid _ <- expect
597 True <- return $ ref == ref' && nid == localNodeId node1
598 liftIO $ putMVar done ()
606testMonitorChannel :: TestTransport -> Assertion
607testMonitorChannel TestTransport{..} = do
608 [node1, node2] <- replicateM 2 $ newLocalNode testTransport initRemoteTable
609 gotNotification <- newEmptyMVar
611 pid <- forkProcess node1 $ do
612 sport <- expect :: Process (SendPort ())
613 ref <- monitorPort sport
614 PortMonitorNotification ref' port' reason <- expect
615 -- reason might be DiedUnknownId if the receive port is GCed before the
616 -- monitor is established (TODO: not sure that this is reasonable)
617 return $ ref' == ref && port' == sendPortId sport && (reason == DiedNormal || reason == DiedUnknownId)
618 liftIO $ putMVar gotNotification ()
620 runProcess node2 $ do
621 (sport, _) <- newChan :: Process (SendPort (), ReceivePort ())
623 liftIO $ threadDelay 100000
625 takeMVar gotNotification
627testRegistry :: TestTransport -> Assertion
628testRegistry TestTransport{..} = do
629 node <- newLocalNode testTransport initRemoteTable
632 pingServer <- forkProcess node ping
633 deadProcess <- forkProcess node (return ())
636 register "ping" pingServer
637 Just pid <- whereis "ping"
638 True <- return $ pingServer == pid
640 nsend "ping" (Pong us)
642 True <- return $ pingServer == pid'
643 Left (ProcessRegistrationException "dead" Nothing) <- try $ register "dead" deadProcess
644 Left (ProcessRegistrationException "ping" (Just x)) <- try $ register "ping" deadProcess
645 True <- return $ x == pingServer
646 Left (ProcessRegistrationException "dead" Nothing) <- try $ unregister "dead"
647 liftIO $ putMVar done ()
651testRegistryRemoteProcess :: TestTransport -> Assertion
652testRegistryRemoteProcess TestTransport{..} = do
653 node1 <- newLocalNode testTransport initRemoteTable
654 node2 <- newLocalNode testTransport initRemoteTable
657 pingServer <- forkProcess node1 ping
659 runProcess node2 $ do
660 register "ping" pingServer
661 Just pid <- whereis "ping"
662 True <- return $ pingServer == pid
664 nsend "ping" (Pong us)
666 True <- return $ pingServer == pid'
667 liftIO $ putMVar done ()
671testRemoteRegistry :: TestTransport -> Assertion
672testRemoteRegistry TestTransport{..} = do
673 node1 <- newLocalNode testTransport initRemoteTable
674 node2 <- newLocalNode testTransport initRemoteTable
677 pingServer <- forkProcess node1 ping
678 deadProcess <- forkProcess node1 (return ())
680 runProcess node2 $ do
681 let nid1 = localNodeId node1
682 registerRemoteAsync nid1 "ping" pingServer
684 matchIf (\(RegisterReply label' _ (Just pid)) ->
685 "ping" == label' && pid == pingServer)
686 (\(RegisterReply _ _ _) -> return ()) ]
688 Just pid <- whereisRemote nid1 "ping"
689 True <- return $ pingServer == pid
691 nsendRemote nid1 "ping" (Pong us)
693 True <- return $ pingServer == pid'
695 -- test that if process was not registered Nothing is returned
697 registerRemoteAsync nid1 "dead" deadProcess
699 matchIf (\(RegisterReply label' False Nothing) -> "dead" == label')
700 (\(RegisterReply _ _ _) -> return ()) ]
701 registerRemoteAsync nid1 "ping" deadProcess
703 matchIf (\(RegisterReply label' False (Just pid)) ->
704 "ping" == label' && pid == pingServer)
705 (\(RegisterReply _ _ _) -> return ()) ]
706 unregisterRemoteAsync nid1 "dead"
708 matchIf (\(RegisterReply label' False Nothing) ->
709 "dead" == label' && pid == pingServer)
710 (\(RegisterReply _ _ _) -> return ()) ]
711 liftIO $ putMVar done ()
715testRemoteRegistryRemoteProcess :: TestTransport -> Assertion
716testRemoteRegistryRemoteProcess TestTransport{..} = do
717 node1 <- newLocalNode testTransport initRemoteTable
718 node2 <- newLocalNode testTransport initRemoteTable
721 pingServer <- forkProcess node2 ping
723 runProcess node2 $ do
724 let nid1 = localNodeId node1
725 registerRemoteAsync nid1 "ping" pingServer
727 matchIf (\(RegisterReply label' _ _) -> "ping" == label')
728 (\(RegisterReply _ _ _) -> return ()) ]
729 Just pid <- whereisRemote nid1 "ping"
730 True <- return $ pingServer == pid
732 nsendRemote nid1 "ping" (Pong us)
734 True <- return $ pingServer == pid'
735 liftIO $ putMVar done ()
739testSpawnLocal :: TestTransport -> Assertion
740testSpawnLocal TestTransport{..} = do
741 node <- newLocalNode testTransport initRemoteTable
747 pid <- spawnLocal $ do
749 sendChan sport (1234 :: Int)
751 sport <- spawnChannelLocal $ \rport -> do
752 (1234 :: Int) <- receiveChan rport
757 liftIO $ putMVar done ()
761testSpawnAsyncStrictness :: TestTransport -> Assertion
762testSpawnAsyncStrictness TestTransport{..} = do
763 node <- newLocalNode testTransport initRemoteTable
769 ev <- try $ spawnAsync here (error "boom")
771 Right _ -> putMVar done (error "Exception didn't fire")
772 Left (_::SomeException) -> putMVar done (return ())
776testReconnect :: TestTransport -> Assertion
777testReconnect TestTransport{..} = do
778 [node1, node2] <- replicateM 2 $ newLocalNode testTransport initRemoteTable
779 let nid1 = localNodeId node1
780 processA <- newEmptyMVar
781 [sendTestOk, registerTestOk] <- replicateM 2 newEmptyMVar
783 forkProcess node1 $ do
785 liftIO $ putMVar processA us
788 True <- return $ msg1 == "message 1" && msg2 == "message 3"
789 liftIO $ putMVar sendTestOk ()
791 forkProcess node2 $ do
793 - Make sure there is no implicit reconnect on normal message sending
796 them <- liftIO $ readMVar processA
797 send them "message 1" >> liftIO (threadDelay 100000)
799 -- Simulate network failure
800 liftIO $ syncBreakConnection testBreakConnection node1 node2
804 send them "message 2"
808 send them "message 3"
810 liftIO $ takeMVar sendTestOk
813 - Test that there *is* implicit reconnect on node controller messages
817 registerRemoteAsync nid1 "a" us -- registerRemote is asynchronous
819 matchIf (\(RegisterReply label' _ _) -> "a" == label')
820 (\(RegisterReply _ _ _) -> return ()) ]
822 Just _ <- whereisRemote nid1 "a"
825 -- Simulate network failure
826 liftIO $ syncBreakConnection testBreakConnection node1 node2
828 -- This will happen due to implicit reconnect
829 registerRemoteAsync nid1 "b" us
831 matchIf (\(RegisterReply label' _ _) -> "b" == label')
832 (\(RegisterReply _ _ _) -> return ()) ]
835 registerRemoteAsync nid1 "c" us
837 matchIf (\(RegisterReply label' _ _) -> "c" == label')
838 (\(RegisterReply _ _ _) -> return ()) ]
841 Nothing <- whereisRemote nid1 "a" -- this will fail because the name is removed when the node is disconnected
842 Just _ <- whereisRemote nid1 "b" -- this will suceed because the value is set after thereconnect
843 Just _ <- whereisRemote nid1 "c"
845 liftIO $ putMVar registerTestOk ()
847 takeMVar registerTestOk
849-- | Tests that unreliable messages arrive sorted even when there are connection
851testUSend :: (ProcessId -> Int -> Process ())
852 -> TestTransport -> Int -> Assertion
853testUSend usendPrim TestTransport{..} numMessages = do
854 [node1, node2] <- replicateM 2 $ newLocalNode testTransport initRemoteTable
855 let nid1 = localNodeId node1
856 nid2 = localNodeId node2
857 processA <- newEmptyMVar
858 usendTestOk <- newEmptyMVar
860 forkProcess node1 $ flip catch (\e -> liftIO $ print (e :: SomeException) ) $ do
862 liftIO $ putMVar processA us
866 let -- Collects messages from 'them' until the sender dies.
867 -- Disconnection notifications are ignored.
868 receiveMessages :: Process [Int]
869 receiveMessages = receiveWait
870 [ match $ \mn -> case mn of
871 ProcessMonitorNotification _ _ DiedDisconnect -> do
875 , match $ \i -> fmap (i :) receiveMessages
877 msgs <- receiveMessages
878 let -- Checks that the input list is sorted.
879 isSorted :: [Int] -> Bool
880 isSorted (x : xs@(y : _)) = x <= y && isSorted xs
882 -- The list can't be null since there are no failures after sending
884 True <- return $ isSorted msgs && not (null msgs)
885 liftIO $ putMVar usendTestOk ()
887 forkProcess node2 $ do
888 them <- liftIO $ readMVar processA
889 getSelfPid >>= send them
891 forM_ [1..numMessages] $ \i -> do
892 liftIO $ testBreakConnection (nodeAddress nid1) (nodeAddress nid2)
894 liftIO (threadDelay 30000)
898-- | Test 'matchAny'. This repeats the 'testMath' but with a proxy server
900testMatchAny :: TestTransport -> Assertion
901testMatchAny TestTransport{..} = do
902 proxyAddr <- newEmptyMVar
903 clientDone <- newEmptyMVar
907 localNode <- newLocalNode testTransport initRemoteTable
908 mathServer <- forkProcess localNode math
909 proxyServer <- forkProcess localNode $ forever $ do
910 msg <- receiveWait [ matchAny return ]
911 forward msg mathServer
912 putMVar proxyAddr proxyServer
916 localNode <- newLocalNode testTransport initRemoteTable
917 mathServer <- readMVar proxyAddr
919 runProcess localNode $ do
921 send mathServer (Add pid 1 2)
922 3 <- expect :: Process Double
923 send mathServer (Divide pid 8 2)
924 4 <- expect :: Process Double
925 send mathServer (Divide pid 8 0)
927 liftIO $ putMVar clientDone ()
931-- | Test 'matchAny'. This repeats the 'testMath' but with a proxy server
932-- in between, however we block 'Divide' requests ....
933testMatchAnyHandle :: TestTransport -> Assertion
934testMatchAnyHandle TestTransport{..} = do
935 proxyAddr <- newEmptyMVar
936 clientDone <- newEmptyMVar
940 localNode <- newLocalNode testTransport initRemoteTable
941 mathServer <- forkProcess localNode math
942 proxyServer <- forkProcess localNode $ forever $ do
944 matchAny (maybeForward mathServer)
946 putMVar proxyAddr proxyServer
950 localNode <- newLocalNode testTransport initRemoteTable
951 mathServer <- readMVar proxyAddr
953 runProcess localNode $ do
955 send mathServer (Add pid 1 2)
956 3 <- expect :: Process Double
957 send mathServer (Divide pid 8 2)
958 Nothing <- (expectTimeout 100000) :: Process (Maybe Double)
959 liftIO $ putMVar clientDone ()
962 where maybeForward :: ProcessId -> Message -> Process (Maybe ())
964 handleMessage msg (\m@(Add _ _ _) -> send s m)
966testMatchAnyNoHandle :: TestTransport -> Assertion
967testMatchAnyNoHandle TestTransport{..} = do
969 clientDone <- newEmptyMVar
970 serverDone <- newEmptyMVar
974 localNode <- newLocalNode testTransport initRemoteTable
975 server <- forkProcess localNode $ forever $ do
978 -- the condition has type `Add -> Bool`
979 (\(Add _ _ _) -> True)
980 -- the match `AbstractMessage -> Process ()` will succeed!
982 -- `String -> Process ()` does *not* match the input types however
983 r <- (handleMessage m (\(_ :: String) -> die "NONSENSE" ))
986 Just _ -> die "NONSENSE")
988 -- we *must* have removed the message from our mailbox though!!!
989 Nothing <- receiveTimeout 100000 [ match (\(Add _ _ _) -> return ()) ]
990 liftIO $ putMVar serverDone ()
995 localNode <- newLocalNode testTransport initRemoteTable
996 server <- readMVar addr
998 runProcess localNode $ do
1000 send server (Add pid 1 2)
1001 -- we only care about the client having sent a message, so we're done
1002 liftIO $ putMVar clientDone ()
1007-- | Test 'matchAnyIf'. We provide an /echo/ server, but it ignores requests
1008-- unless the text body @/= "bar"@ - this case should time out rather than
1009-- removing the message from the process mailbox.
1010testMatchAnyIf :: TestTransport -> Assertion
1011testMatchAnyIf TestTransport{..} = do
1012 echoAddr <- newEmptyMVar
1013 clientDone <- newEmptyMVar
1017 localNode <- newLocalNode testTransport initRemoteTable
1018 echoServer <- forkProcess localNode $ forever $ do
1020 matchAnyIf (\(_ :: ProcessId, (s :: String)) -> s /= "bar")
1023 putMVar echoAddr echoServer
1027 localNode <- newLocalNode testTransport initRemoteTable
1028 server <- readMVar echoAddr
1030 runProcess localNode $ do
1032 send server (pid, "foo")
1034 send server (pid, "baz")
1036 send server (pid, "bar")
1037 Nothing <- (expectTimeout 100000) :: Process (Maybe Double)
1038 liftIO $ putMVar clientDone ()
1041 where tryHandleMessage :: Message -> Process (Maybe ())
1042 tryHandleMessage msg =
1043 handleMessage msg (\(pid :: ProcessId, (m :: String))
1044 -> do { send pid m; return () })
1046testMatchMessageWithUnwrap :: TestTransport -> Assertion
1047testMatchMessageWithUnwrap TestTransport{..} = do
1048 echoAddr <- newEmptyMVar
1049 clientDone <- newEmptyMVar
1053 localNode <- newLocalNode testTransport initRemoteTable
1054 echoServer <- forkProcess localNode $ forever $ do
1055 msg <- receiveWait [
1056 matchMessage (\(m :: Message) -> do
1059 unwrapped <- unwrapMessage msg :: Process (Maybe (ProcessId, Message))
1061 (Just (p, msg')) -> forward msg' p
1062 Nothing -> die "unable to unwrap the message"
1063 putMVar echoAddr echoServer
1067 localNode <- newLocalNode testTransport initRemoteTable
1068 server <- readMVar echoAddr
1070 runProcess localNode $ do
1072 send server (pid, wrapMessage ("foo" :: String))
1074 send server (pid, wrapMessage ("baz" :: String))
1076 liftIO $ putMVar clientDone ()
1080-- Test 'receiveChanTimeout'
1081testReceiveChanTimeout :: TestTransport -> Assertion
1082testReceiveChanTimeout TestTransport{..} = do
1083 done <- newEmptyMVar
1084 sendPort <- newEmptyMVar
1087 localNode <- newLocalNode testTransport initRemoteTable
1088 runProcess localNode $ do
1089 -- Create a typed channel
1090 (sp, rp) <- newChan :: Process (SendPort Bool, ReceivePort Bool)
1091 liftIO $ putMVar sendPort sp
1093 -- Wait for a message with a delay. No message arrives, we should get Nothing after 1 second
1094 Nothing <- receiveChanTimeout 1000000 rp
1096 -- Wait for a message with a delay again. Now a message arrives after 0.5 seconds
1097 Just True <- receiveChanTimeout 1000000 rp
1099 -- Wait for a message with zero timeout: non-blocking check. No message is available, we get Nothing
1100 Nothing <- receiveChanTimeout 0 rp
1102 -- Again, but now there is a message available
1103 liftIO $ threadDelay 1000000
1104 Just False <- receiveChanTimeout 0 rp
1106 liftIO $ putMVar done ()
1109 localNode <- newLocalNode testTransport initRemoteTable
1110 runProcess localNode $ do
1111 sp <- liftIO $ readMVar sendPort
1113 liftIO $ threadDelay 1500000
1116 liftIO $ threadDelay 500000
1121-- | Test Functor, Applicative, Alternative and Monad instances for ReceiveChan
1122testReceiveChanFeatures :: TestTransport -> Assertion
1123testReceiveChanFeatures TestTransport{..} = do
1124 done <- newEmptyMVar
1127 localNode <- newLocalNode testTransport initRemoteTable
1128 runProcess localNode $ do
1129 (spInt, rpInt) <- newChan :: Process (SendPort Int, ReceivePort Int)
1130 (spBool, rpBool) <- newChan :: Process (SendPort Bool, ReceivePort Bool)
1132 -- Test Functor instance
1135 sendChan spBool False
1137 rp1 <- mergePortsBiased [even <$> rpInt, rpBool]
1139 True <- receiveChan rp1
1140 False <- receiveChan rp1
1142 -- Test Applicative instance
1147 let rp2 = pure (+) <*> rpInt <*> rpInt
1149 7 <- receiveChan rp2
1151 -- Test Alternative instance
1154 sendChan spBool True
1156 let rp3 = (even <$> rpInt) <|> rpBool
1158 False <- receiveChan rp3
1159 True <- receiveChan rp3
1161 -- Test Monad instance
1163 sendChan spBool True
1164 sendChan spBool False
1167 let rp4 :: ReceivePort Int
1168 rp4 = do b <- rpBool
1173 5 <- receiveChan rp4
1174 7 <- receiveChan rp4
1176 liftIO $ putMVar done ()
1180testKillLocal :: TestTransport -> Assertion
1181testKillLocal TestTransport{..} = do
1182 localNode <- newLocalNode testTransport initRemoteTable
1183 done <- newEmptyMVar
1185 pid <- forkProcess localNode $ do
1186 liftIO $ threadDelay 1000000
1188 runProcess localNode $ do
1192 ProcessMonitorNotification ref' pid' (DiedException ex) <- expect
1193 True <- return $ ref == ref' && pid == pid' && ex == "killed-by=" ++ show us ++ ",reason=TestKill"
1194 liftIO $ putMVar done ()
1198testKillRemote :: TestTransport -> Assertion
1199testKillRemote TestTransport{..} = do
1200 node1 <- newLocalNode testTransport initRemoteTable
1201 node2 <- newLocalNode testTransport initRemoteTable
1202 done <- newEmptyMVar
1204 pid <- forkProcess node1 $ do
1205 liftIO $ threadDelay 1000000
1207 runProcess node2 $ do
1211 ProcessMonitorNotification ref' pid' (DiedException reason) <- expect
1212 True <- return $ ref == ref' && pid == pid' && reason == "killed-by=" ++ show us ++ ",reason=TestKill"
1213 liftIO $ putMVar done ()
1217testCatchesExit :: TestTransport -> Assertion
1218testCatchesExit TestTransport{..} = do
1219 localNode <- newLocalNode testTransport initRemoteTable
1220 done <- newEmptyMVar
1222 _ <- forkProcess localNode $ do
1223 (die ("foobar", 123 :: Int))
1225 (\_ m -> handleMessage m (\(_ :: String) -> return ()))
1226 , (\_ m -> handleMessage m (\(_ :: Maybe Int) -> return ()))
1227 , (\_ m -> handleMessage m (\(_ :: String, _ :: Int)
1228 -> (liftIO $ putMVar done ()) >> return ()))
1233testHandleMessageIf :: TestTransport -> Assertion
1234testHandleMessageIf TestTransport{..} = do
1235 localNode <- newLocalNode testTransport initRemoteTable
1236 done <- newEmptyMVar
1237 _ <- forkProcess localNode $ do
1239 send self (5 :: Integer, 10 :: Integer)
1240 msg <- receiveWait [ matchMessage return ]
1241 Nothing <- handleMessageIf msg (\() -> True) (\() -> die $ "whoops")
1242 handleMessageIf msg (\(x :: Integer, y :: Integer) -> x == 5 && y == 10)
1243 (\input -> liftIO $ putMVar done input)
1246 result <- takeMVar done
1247 expectThat result $ equalTo (5, 10)
1249testCatches :: TestTransport -> Assertion
1250testCatches TestTransport{..} = do
1251 localNode <- newLocalNode testTransport initRemoteTable
1252 done <- newEmptyMVar
1254 _ <- forkProcess localNode $ do
1256 (liftIO $ throwIO (ProcessLinkException (nullProcessId node) DiedNormal))
1258 Handler (\(ProcessLinkException _ _) -> liftIO $ putMVar done ())
1263testMaskRestoreScope :: TestTransport -> Assertion
1264testMaskRestoreScope TestTransport{..} = do
1265 localNode <- newLocalNode testTransport initRemoteTable
1266 parentPid <- newEmptyMVar :: IO (MVar ProcessId)
1267 spawnedPid <- newEmptyMVar :: IO (MVar ProcessId)
1269 void $ runProcess localNode $ mask $ \unmask -> do
1270 getSelfPid >>= liftIO . putMVar parentPid
1271 void $ spawnLocal $ unmask (getSelfPid >>= liftIO . putMVar spawnedPid)
1273 parent <- liftIO $ takeMVar parentPid
1274 child <- liftIO $ takeMVar spawnedPid
1275 expectThat parent $ isNot $ equalTo child
1277testDie :: TestTransport -> Assertion
1278testDie TestTransport{..} = do
1279 localNode <- newLocalNode testTransport initRemoteTable
1280 done <- newEmptyMVar
1282 _ <- forkProcess localNode $ do
1283 (die ("foobar", 123 :: Int))
1284 `catchExit` \_from reason -> do
1285 -- TODO: should verify that 'from' has the right value
1286 True <- return $ reason == ("foobar", 123 :: Int)
1287 liftIO $ putMVar done ()
1291testPrettyExit :: TestTransport -> Assertion
1292testPrettyExit TestTransport{..} = do
1293 localNode <- newLocalNode testTransport initRemoteTable
1294 done <- newEmptyMVar
1296 _ <- forkProcess localNode $ do
1298 `catch` \ex@(ProcessExitException from _) ->
1299 let expected = "exit-from=" ++ (show from)
1301 True <- return $ (show ex) == expected
1302 liftIO $ putMVar done ()
1306testExitLocal :: TestTransport -> Assertion
1307testExitLocal TestTransport{..} = do
1308 localNode <- newLocalNode testTransport initRemoteTable
1309 supervisedDone <- newEmptyMVar
1310 supervisorDone <- newEmptyMVar
1311 -- XXX: we guarantee that exception handler will be set up
1312 -- regardless if forkProcess preserve masking state or not.
1313 handlerSetUp <- newEmptyMVar
1315 pid <- forkProcess localNode $ do
1316 (liftIO (putMVar handlerSetUp ()) >> expect) `catchExit` \_from reason -> do
1317 -- TODO: should verify that 'from' has the right value
1318 True <- return $ reason == "TestExit"
1319 liftIO $ putMVar supervisedDone ()
1321 runProcess localNode $ do
1322 liftIO $ takeMVar handlerSetUp
1325 -- This time the client catches the exception, so it dies normally
1326 ProcessMonitorNotification ref' pid' DiedNormal <- expect
1327 True <- return $ ref == ref' && pid == pid'
1328 liftIO $ putMVar supervisorDone ()
1330 takeMVar supervisedDone
1331 takeMVar supervisorDone
1333testExitRemote :: TestTransport -> Assertion
1334testExitRemote TestTransport{..} = do
1335 node1 <- newLocalNode testTransport initRemoteTable
1336 node2 <- newLocalNode testTransport initRemoteTable
1337 supervisedDone <- newEmptyMVar
1338 supervisorDone <- newEmptyMVar
1340 pid <- forkProcess node1 $ do
1341 (receiveWait [] :: Process ()) -- block forever
1342 `catchExit` \_from reason -> do
1343 -- TODO: should verify that 'from' has the right value
1344 True <- return $ reason == "TestExit"
1345 liftIO $ putMVar supervisedDone ()
1347 runProcess node2 $ do
1350 ProcessMonitorNotification ref' pid' DiedNormal <- expect
1351 True <- return $ ref == ref' && pid == pid'
1352 liftIO $ putMVar supervisorDone ()
1354 takeMVar supervisedDone
1355 takeMVar supervisorDone
1357testUnsafeSend :: TestTransport -> Assertion
1358testUnsafeSend TestTransport{..} = do
1359 serverAddr <- newEmptyMVar
1360 clientDone <- newEmptyMVar
1362 localNode <- newLocalNode testTransport initRemoteTable
1363 void $ forkProcess localNode $ do
1365 liftIO $ putMVar serverAddr self
1366 clientAddr <- expect
1367 unsafeSend clientAddr ()
1369 void $ forkProcess localNode $ do
1370 serverPid <- liftIO $ takeMVar serverAddr
1371 getSelfPid >>= unsafeSend serverPid
1373 liftIO $ putMVar clientDone ()
1377testUnsafeUSend :: TestTransport -> Assertion
1378testUnsafeUSend TestTransport{..} = do
1379 serverAddr <- newEmptyMVar
1380 clientDone <- newEmptyMVar
1382 localNode <- newLocalNode testTransport initRemoteTable
1383 void $ forkProcess localNode $ do
1385 liftIO $ putMVar serverAddr self
1386 clientAddr <- expect
1387 unsafeUSend clientAddr ()
1389 void $ forkProcess localNode $ do
1390 serverPid <- liftIO $ takeMVar serverAddr
1391 getSelfPid >>= unsafeUSend serverPid
1393 liftIO $ putMVar clientDone ()
1397testUnsafeNSend :: TestTransport -> Assertion
1398testUnsafeNSend TestTransport{..} = do
1399 clientDone <- newEmptyMVar
1401 localNode <- newLocalNode testTransport initRemoteTable
1403 pid <- forkProcess localNode $ do
1405 liftIO $ putMVar clientDone ()
1407 void $ runProcess localNode $ do
1408 register "foobar" pid
1409 unsafeNSend "foobar" ()
1413testUnsafeNSendRemote :: TestTransport -> Assertion
1414testUnsafeNSendRemote TestTransport{..} = do
1415 clientDone <- newEmptyMVar
1417 localNode1 <- newLocalNode testTransport initRemoteTable
1418 localNode2 <- newLocalNode testTransport initRemoteTable
1420 _ <- forkProcess localNode1 $ do
1421 getSelfPid >>= register "foobar"
1422 liftIO $ putMVar clientDone ()
1424 liftIO $ putMVar clientDone ()
1427 void $ runProcess localNode2 $ do
1428 unsafeNSendRemote (localNodeId localNode1) "foobar" ()
1432testUnsafeSendChan :: TestTransport -> Assertion
1433testUnsafeSendChan TestTransport{..} = do
1434 serverAddr <- newEmptyMVar
1435 clientDone <- newEmptyMVar
1437 localNode <- newLocalNode testTransport initRemoteTable
1438 void $ forkProcess localNode $ do
1440 liftIO $ putMVar serverAddr self
1442 unsafeSendChan sp ()
1444 void $ forkProcess localNode $ do
1445 serverPid <- liftIO $ takeMVar serverAddr
1447 unsafeSend serverPid sp
1448 () <- receiveChan rp
1449 liftIO $ putMVar clientDone ()
1453testCallLocal :: TestTransport -> Assertion
1454testCallLocal TestTransport{..} = do
1455 node <- newLocalNode testTransport initRemoteTable
1457 -- Testing that (/=) <$> getSelfPid <*> callLocal getSelfPid.
1458 result <- newEmptyMVar
1459 runProcess node $ do
1460 r <- (/=) <$> getSelfPid <*> callLocal getSelfPid
1461 liftIO $ putMVar result r
1462 True <- takeMVar result
1465 -- Testing that when callLocal is interrupted, the worker is interrupted.
1466 ibox <- newIORef False
1467 runProcess node $ do
1468 keeper <- getSelfPid
1470 caller <- getSelfPid
1474 onException (do send keeper caller
1476 (do liftIO $ writeIORef ibox True))
1482 True <- readIORef ibox
1485 -- Testing that when the worker raises an exception, the exception is propagated to the parent.
1486 ibox2 <- newIORef False
1487 runProcess node $ do
1488 r <- try (callLocal $ error "e" >> return ())
1489 liftIO $ writeIORef ibox2 $ case r of
1490 Left (ErrorCall "e") -> True
1492 True <- readIORef ibox
1495 -- Test that caller waits for the worker in correct situation
1496 ibox3 <- newIORef False
1497 result3 <- newEmptyMVar
1498 runProcess node $ do
1499 keeper <- getSelfPid
1502 (do us <- getSelfPid
1506 `finally` (liftIO $ writeIORef ibox3 True)
1507 liftIO $ putMVar result3 =<< readIORef ibox3
1510 True <- takeMVar result3
1513 -- Test that caller waits for the worker in case when caller gets an exception
1514 ibox4 <- newIORef False
1515 result4 <- newEmptyMVar
1516 runProcess node $ do
1517 keeper <- getSelfPid
1519 caller <- getSelfPid
1521 ((do send keeper caller
1523 `finally` (liftIO $ writeIORef ibox4 True))
1524 `finally` (liftIO $ putMVar result4 =<< readIORef ibox4)
1527 True <- takeMVar result4
1529 -- XXX: Testing that when mask_ $ callLocal p runs p in masked state.
1535tests :: TestTransport -> IO [Test]
1536tests testtrans = return [
1537 testGroup "Basic features" [
1538 testCase "Ping" (testPing testtrans)
1539 , testCase "Math" (testMath testtrans)
1540 , testCase "Timeout" (testTimeout testtrans)
1541 , testCase "Timeout0" (testTimeout0 testtrans)
1542 , testCase "SendToTerminated" (testSendToTerminated testtrans)
1543 , testCase "TypedChannnels" (testTypedChannels testtrans)
1544 , testCase "Terminate" (testTerminate testtrans)
1545 , testCase "RegistryRemoteProcess" (testRegistryRemoteProcess testtrans)
1546 , testCase "RemoteRegistry" (testRemoteRegistry testtrans)
1547 , testCase "RemoteRegistryRemoteProcess" (testRemoteRegistryRemoteProcess testtrans)
1548 , testCase "SpawnLocal" (testSpawnLocal testtrans)
1549 , testCase "SpawnAsyncStrictness" (testSpawnAsyncStrictness testtrans)
1550 , testCase "HandleMessageIf" (testHandleMessageIf testtrans)
1551 , testCase "MatchAny" (testMatchAny testtrans)
1552 , testCase "MatchAnyHandle" (testMatchAnyHandle testtrans)
1553 , testCase "MatchAnyNoHandle" (testMatchAnyNoHandle testtrans)
1554 , testCase "MatchAnyIf" (testMatchAnyIf testtrans)
1555 , testCase "MatchMessageUnwrap" (testMatchMessageWithUnwrap testtrans)
1556 , testCase "ReceiveChanTimeout" (testReceiveChanTimeout testtrans)
1557 , testCase "ReceiveChanFeatures" (testReceiveChanFeatures testtrans)
1558 , testCase "KillLocal" (testKillLocal testtrans)
1559 , testCase "KillRemote" (testKillRemote testtrans)
1560 , testCase "Die" (testDie testtrans)
1561 , testCase "PrettyExit" (testPrettyExit testtrans)
1562 , testCase "CatchesExit" (testCatchesExit testtrans)
1563 , testCase "Catches" (testCatches testtrans)
1564 , testCase "MaskRestoreScope" (testMaskRestoreScope testtrans)
1565 , testCase "ExitLocal" (testExitLocal testtrans)
1566 , testCase "ExitRemote" (testExitRemote testtrans)
1567 , testCase "TextCallLocal" (testCallLocal testtrans)
1568 -- Unsafe Primitives
1569 , testCase "TestUnsafeSend" (testUnsafeSend testtrans)
1570 , testCase "TestUnsafeUSend" (testUnsafeUSend testtrans)
1571 , testCase "TestUnsafeNSend" (testUnsafeNSend testtrans)
1572 , testCase "TestUnsafeNSendRemote" (testUnsafeNSendRemote testtrans)
1573 , testCase "TestUnsafeSendChan" (testUnsafeSendChan testtrans)
1575 , testCase "USend" (testUSend usend testtrans 50)
1576 , testCase "UForward"
1577 (testUSend (\p m -> uforward (createUnencodedMessage m) p)
1581 , testGroup "Monitoring and Linking" [
1582 -- Monitoring processes
1584 -- The "missing" combinations in the list below don't make much sense, as
1585 -- we cannot guarantee that the monitor reply or link exception will not
1586 -- happen before the unmonitor or unlink
1587 testCase "MonitorNormalTermination" (testMonitorNormalTermination testtrans True False)
1588 , testCase "MonitorAbnormalTermination" (testMonitorAbnormalTermination testtrans True False)
1589 , testCase "MonitorLocalDeadProcess" (testMonitorLocalDeadProcess testtrans True False)
1590 , testCase "MonitorRemoteDeadProcess" (testMonitorRemoteDeadProcess testtrans True False)
1591 , testCase "MonitorDisconnect" (testMonitorDisconnect testtrans True False)
1592 , testCase "LinkUnreachable" (testMonitorUnreachable testtrans False False)
1593 , testCase "LinkNormalTermination" (testMonitorNormalTermination testtrans False False)
1594 , testCase "LinkAbnormalTermination" (testMonitorAbnormalTermination testtrans False False)
1595 , testCase "LinkLocalDeadProcess" (testMonitorLocalDeadProcess testtrans False False)
1596 , testCase "LinkRemoteDeadProcess" (testMonitorRemoteDeadProcess testtrans False False)
1597 , testCase "LinkDisconnect" (testMonitorDisconnect testtrans False False)
1598 , testCase "UnmonitorNormalTermination" (testMonitorNormalTermination testtrans True True)
1599 , testCase "UnmonitorAbnormalTermination" (testMonitorAbnormalTermination testtrans True True)
1600 , testCase "UnmonitorDisconnect" (testMonitorDisconnect testtrans True True)
1601 , testCase "UnlinkNormalTermination" (testMonitorNormalTermination testtrans False True)
1602 , testCase "UnlinkAbnormalTermination" (testMonitorAbnormalTermination testtrans False True)
1603 , testCase "UnlinkDisconnect" (testMonitorDisconnect testtrans False True)
1604 -- Monitoring nodes and channels
1605 , testCase "MonitorNode" (testMonitorNode testtrans)
1606 , testCase "MonitorLiveNode" (testMonitorLiveNode testtrans)
1607 , testCase "MonitorChannel" (testMonitorChannel testtrans)
1611 -- Tests that fail occasionally and should be revised
1612 , testGroup "Flaky" [
1613 testCase "Reconnect" (testReconnect testtrans)
1614 , testCase "Registry" (testRegistry testtrans)
1615 , testCase "MergeChannels" (testMergeChannels testtrans)
1616 , testCase "MonitorUnreachable" (testMonitorUnreachable testtrans True False)
1620syncBreakConnection :: (NT.EndPointAddress -> NT.EndPointAddress -> IO ()) -> LocalNode -> LocalNode -> IO ()
1621syncBreakConnection breakConnection nid0 nid1 = do
1623 _ <- forkProcess nid1 $ getSelfPid >>= liftIO . putMVar m
1624 runProcess nid0 $ do
1625 them <- liftIO $ takeMVar m
1626 pinger <- spawnLocal $ forever $ send them ()
1627 _ <- monitorNode (localNodeId nid1)
1628 liftIO $ breakConnection (nodeAddress $ localNodeId nid0)
1629 (nodeAddress $ localNodeId nid1)
1630 NodeMonitorNotification _ _ _ <- expect
1631 kill pinger "finished"
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list