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 mvSender <- newEmptyMVar
1085 sendPort <- newEmptyMVar
1088 localNode <- newLocalNode testTransport initRemoteTable
1089 runProcess localNode $ do
1090 -- Create a typed channel
1091 (sp, rp) <- newChan :: Process (SendPort Bool, ReceivePort Bool)
1092 liftIO $ putMVar sendPort sp
1094 -- Wait for a message with a delay. No message arrives, we should get
1095 -- Nothing after the delay.
1096 Nothing <- receiveChanTimeout 100000 rp
1098 -- Let the sender know that it can send a message.
1099 liftIO $ putMVar mvSender ()
1101 -- Wait for a message with a delay again. Now a message arrives after
1103 Just True <- receiveChanTimeout 20000000 rp
1105 -- Wait for a message with zero timeout: non-blocking check. No message is
1106 -- available, we get Nothing
1107 Nothing <- receiveChanTimeout 0 rp
1109 -- Let the sender know that it can send a message.
1110 liftIO $ putMVar mvSender ()
1112 -- Again, but now there is a message available
1114 liftIO $ threadDelay 100000
1115 mb <- receiveChanTimeout 0 rp
1117 Just b -> do False <- return b; return ()
1120 liftIO $ putMVar done ()
1123 localNode <- newLocalNode testTransport initRemoteTable
1124 runProcess localNode $ do
1125 sp <- liftIO $ readMVar sendPort
1127 liftIO $ takeMVar mvSender
1128 liftIO $ threadDelay 100000
1131 liftIO $ takeMVar mvSender
1136-- | Test Functor, Applicative, Alternative and Monad instances for ReceiveChan
1137testReceiveChanFeatures :: TestTransport -> Assertion
1138testReceiveChanFeatures TestTransport{..} = do
1139 done <- newEmptyMVar
1142 localNode <- newLocalNode testTransport initRemoteTable
1143 runProcess localNode $ do
1144 (spInt, rpInt) <- newChan :: Process (SendPort Int, ReceivePort Int)
1145 (spBool, rpBool) <- newChan :: Process (SendPort Bool, ReceivePort Bool)
1147 -- Test Functor instance
1150 sendChan spBool False
1152 rp1 <- mergePortsBiased [even <$> rpInt, rpBool]
1154 True <- receiveChan rp1
1155 False <- receiveChan rp1
1157 -- Test Applicative instance
1162 let rp2 = pure (+) <*> rpInt <*> rpInt
1164 7 <- receiveChan rp2
1166 -- Test Alternative instance
1169 sendChan spBool True
1171 let rp3 = (even <$> rpInt) <|> rpBool
1173 False <- receiveChan rp3
1174 True <- receiveChan rp3
1176 -- Test Monad instance
1178 sendChan spBool True
1179 sendChan spBool False
1182 let rp4 :: ReceivePort Int
1183 rp4 = do b <- rpBool
1188 5 <- receiveChan rp4
1189 7 <- receiveChan rp4
1191 liftIO $ putMVar done ()
1195testKillLocal :: TestTransport -> Assertion
1196testKillLocal TestTransport{..} = do
1197 localNode <- newLocalNode testTransport initRemoteTable
1198 done <- newEmptyMVar
1200 pid <- forkProcess localNode $ do
1201 liftIO $ threadDelay 1000000
1203 runProcess localNode $ do
1207 ProcessMonitorNotification ref' pid' (DiedException ex) <- expect
1208 True <- return $ ref == ref' && pid == pid' && ex == "killed-by=" ++ show us ++ ",reason=TestKill"
1209 liftIO $ putMVar done ()
1213testKillRemote :: TestTransport -> Assertion
1214testKillRemote TestTransport{..} = do
1215 node1 <- newLocalNode testTransport initRemoteTable
1216 node2 <- newLocalNode testTransport initRemoteTable
1217 done <- newEmptyMVar
1219 pid <- forkProcess node1 $ do
1220 liftIO $ threadDelay 1000000
1222 runProcess node2 $ do
1226 ProcessMonitorNotification ref' pid' (DiedException reason) <- expect
1227 True <- return $ ref == ref' && pid == pid' && reason == "killed-by=" ++ show us ++ ",reason=TestKill"
1228 liftIO $ putMVar done ()
1232testCatchesExit :: TestTransport -> Assertion
1233testCatchesExit TestTransport{..} = do
1234 localNode <- newLocalNode testTransport initRemoteTable
1235 done <- newEmptyMVar
1237 _ <- forkProcess localNode $ do
1238 (die ("foobar", 123 :: Int))
1240 (\_ m -> handleMessage m (\(_ :: String) -> return ()))
1241 , (\_ m -> handleMessage m (\(_ :: Maybe Int) -> return ()))
1242 , (\_ m -> handleMessage m (\(_ :: String, _ :: Int)
1243 -> (liftIO $ putMVar done ()) >> return ()))
1248testHandleMessageIf :: TestTransport -> Assertion
1249testHandleMessageIf TestTransport{..} = do
1250 localNode <- newLocalNode testTransport initRemoteTable
1251 done <- newEmptyMVar
1252 _ <- forkProcess localNode $ do
1254 send self (5 :: Integer, 10 :: Integer)
1255 msg <- receiveWait [ matchMessage return ]
1256 Nothing <- handleMessageIf msg (\() -> True) (\() -> die $ "whoops")
1257 handleMessageIf msg (\(x :: Integer, y :: Integer) -> x == 5 && y == 10)
1258 (\input -> liftIO $ putMVar done input)
1261 result <- takeMVar done
1262 expectThat result $ equalTo (5, 10)
1264testCatches :: TestTransport -> Assertion
1265testCatches TestTransport{..} = do
1266 localNode <- newLocalNode testTransport initRemoteTable
1267 done <- newEmptyMVar
1269 _ <- forkProcess localNode $ do
1271 (liftIO $ throwIO (ProcessLinkException (nullProcessId node) DiedNormal))
1273 Handler (\(ProcessLinkException _ _) -> liftIO $ putMVar done ())
1278testMaskRestoreScope :: TestTransport -> Assertion
1279testMaskRestoreScope TestTransport{..} = do
1280 localNode <- newLocalNode testTransport initRemoteTable
1281 parentPid <- newEmptyMVar :: IO (MVar ProcessId)
1282 spawnedPid <- newEmptyMVar :: IO (MVar ProcessId)
1284 void $ runProcess localNode $ mask $ \unmask -> do
1285 getSelfPid >>= liftIO . putMVar parentPid
1286 void $ spawnLocal $ unmask (getSelfPid >>= liftIO . putMVar spawnedPid)
1288 parent <- liftIO $ takeMVar parentPid
1289 child <- liftIO $ takeMVar spawnedPid
1290 expectThat parent $ isNot $ equalTo child
1292testDie :: TestTransport -> Assertion
1293testDie TestTransport{..} = do
1294 localNode <- newLocalNode testTransport initRemoteTable
1295 done <- newEmptyMVar
1297 _ <- forkProcess localNode $ do
1298 (die ("foobar", 123 :: Int))
1299 `catchExit` \_from reason -> do
1300 -- TODO: should verify that 'from' has the right value
1301 True <- return $ reason == ("foobar", 123 :: Int)
1302 liftIO $ putMVar done ()
1306testPrettyExit :: TestTransport -> Assertion
1307testPrettyExit TestTransport{..} = do
1308 localNode <- newLocalNode testTransport initRemoteTable
1309 done <- newEmptyMVar
1311 _ <- forkProcess localNode $ do
1313 `catch` \ex@(ProcessExitException from _) ->
1314 let expected = "exit-from=" ++ (show from)
1316 True <- return $ (show ex) == expected
1317 liftIO $ putMVar done ()
1321testExitLocal :: TestTransport -> Assertion
1322testExitLocal TestTransport{..} = do
1323 localNode <- newLocalNode testTransport initRemoteTable
1324 supervisedDone <- newEmptyMVar
1325 supervisorDone <- newEmptyMVar
1326 -- XXX: we guarantee that exception handler will be set up
1327 -- regardless if forkProcess preserve masking state or not.
1328 handlerSetUp <- newEmptyMVar
1330 pid <- forkProcess localNode $ do
1331 (liftIO (putMVar handlerSetUp ()) >> expect) `catchExit` \_from reason -> do
1332 -- TODO: should verify that 'from' has the right value
1333 True <- return $ reason == "TestExit"
1334 liftIO $ putMVar supervisedDone ()
1336 runProcess localNode $ do
1337 liftIO $ takeMVar handlerSetUp
1340 -- This time the client catches the exception, so it dies normally
1341 ProcessMonitorNotification ref' pid' DiedNormal <- expect
1342 True <- return $ ref == ref' && pid == pid'
1343 liftIO $ putMVar supervisorDone ()
1345 takeMVar supervisedDone
1346 takeMVar supervisorDone
1348testExitRemote :: TestTransport -> Assertion
1349testExitRemote TestTransport{..} = do
1350 node1 <- newLocalNode testTransport initRemoteTable
1351 node2 <- newLocalNode testTransport initRemoteTable
1352 supervisedDone <- newEmptyMVar
1353 supervisorDone <- newEmptyMVar
1355 pid <- forkProcess node1 $ do
1356 (receiveWait [] :: Process ()) -- block forever
1357 `catchExit` \_from reason -> do
1358 -- TODO: should verify that 'from' has the right value
1359 True <- return $ reason == "TestExit"
1360 liftIO $ putMVar supervisedDone ()
1362 runProcess node2 $ do
1365 ProcessMonitorNotification ref' pid' DiedNormal <- expect
1366 True <- return $ ref == ref' && pid == pid'
1367 liftIO $ putMVar supervisorDone ()
1369 takeMVar supervisedDone
1370 takeMVar supervisorDone
1372testUnsafeSend :: TestTransport -> Assertion
1373testUnsafeSend TestTransport{..} = do
1374 serverAddr <- newEmptyMVar
1375 clientDone <- newEmptyMVar
1377 localNode <- newLocalNode testTransport initRemoteTable
1378 void $ forkProcess localNode $ do
1380 liftIO $ putMVar serverAddr self
1381 clientAddr <- expect
1382 unsafeSend clientAddr ()
1384 void $ forkProcess localNode $ do
1385 serverPid <- liftIO $ takeMVar serverAddr
1386 getSelfPid >>= unsafeSend serverPid
1388 liftIO $ putMVar clientDone ()
1392testUnsafeUSend :: TestTransport -> Assertion
1393testUnsafeUSend TestTransport{..} = do
1394 serverAddr <- newEmptyMVar
1395 clientDone <- newEmptyMVar
1397 localNode <- newLocalNode testTransport initRemoteTable
1398 void $ forkProcess localNode $ do
1400 liftIO $ putMVar serverAddr self
1401 clientAddr <- expect
1402 unsafeUSend clientAddr ()
1404 void $ forkProcess localNode $ do
1405 serverPid <- liftIO $ takeMVar serverAddr
1406 getSelfPid >>= unsafeUSend serverPid
1408 liftIO $ putMVar clientDone ()
1412testUnsafeNSend :: TestTransport -> Assertion
1413testUnsafeNSend TestTransport{..} = do
1414 clientDone <- newEmptyMVar
1416 localNode <- newLocalNode testTransport initRemoteTable
1418 pid <- forkProcess localNode $ do
1420 liftIO $ putMVar clientDone ()
1422 void $ runProcess localNode $ do
1423 register "foobar" pid
1424 unsafeNSend "foobar" ()
1428testUnsafeNSendRemote :: TestTransport -> Assertion
1429testUnsafeNSendRemote TestTransport{..} = do
1430 clientDone <- newEmptyMVar
1432 localNode1 <- newLocalNode testTransport initRemoteTable
1433 localNode2 <- newLocalNode testTransport initRemoteTable
1435 _ <- forkProcess localNode1 $ do
1436 getSelfPid >>= register "foobar"
1437 liftIO $ putMVar clientDone ()
1439 liftIO $ putMVar clientDone ()
1442 void $ runProcess localNode2 $ do
1443 unsafeNSendRemote (localNodeId localNode1) "foobar" ()
1447testUnsafeSendChan :: TestTransport -> Assertion
1448testUnsafeSendChan TestTransport{..} = do
1449 serverAddr <- newEmptyMVar
1450 clientDone <- newEmptyMVar
1452 localNode <- newLocalNode testTransport initRemoteTable
1453 void $ forkProcess localNode $ do
1455 liftIO $ putMVar serverAddr self
1457 unsafeSendChan sp ()
1459 void $ forkProcess localNode $ do
1460 serverPid <- liftIO $ takeMVar serverAddr
1462 unsafeSend serverPid sp
1463 () <- receiveChan rp
1464 liftIO $ putMVar clientDone ()
1468testCallLocal :: TestTransport -> Assertion
1469testCallLocal TestTransport{..} = do
1470 node <- newLocalNode testTransport initRemoteTable
1472 -- Testing that (/=) <$> getSelfPid <*> callLocal getSelfPid.
1473 result <- newEmptyMVar
1474 runProcess node $ do
1475 r <- (/=) <$> getSelfPid <*> callLocal getSelfPid
1476 liftIO $ putMVar result r
1477 True <- takeMVar result
1480 -- Testing that when callLocal is interrupted, the worker is interrupted.
1481 ibox <- newIORef False
1482 runProcess node $ do
1483 keeper <- getSelfPid
1485 caller <- getSelfPid
1489 onException (do send keeper caller
1491 (do liftIO $ writeIORef ibox True))
1497 True <- readIORef ibox
1500 -- Testing that when the worker raises an exception, the exception is propagated to the parent.
1501 ibox2 <- newIORef False
1502 runProcess node $ do
1503 r <- try (callLocal $ error "e" >> return ())
1504 liftIO $ writeIORef ibox2 $ case r of
1505 Left (ErrorCall "e") -> True
1507 True <- readIORef ibox
1510 -- Test that caller waits for the worker in correct situation
1511 ibox3 <- newIORef False
1512 result3 <- newEmptyMVar
1513 runProcess node $ do
1514 keeper <- getSelfPid
1517 (do us <- getSelfPid
1521 `finally` (liftIO $ writeIORef ibox3 True)
1522 liftIO $ putMVar result3 =<< readIORef ibox3
1525 True <- takeMVar result3
1528 -- Test that caller waits for the worker in case when caller gets an exception
1529 ibox4 <- newIORef False
1530 result4 <- newEmptyMVar
1531 runProcess node $ do
1532 keeper <- getSelfPid
1534 caller <- getSelfPid
1536 ((do send keeper caller
1538 `finally` (liftIO $ writeIORef ibox4 True))
1539 `finally` (liftIO $ putMVar result4 =<< readIORef ibox4)
1542 True <- takeMVar result4
1544 -- XXX: Testing that when mask_ $ callLocal p runs p in masked state.
1550tests :: TestTransport -> IO [Test]
1551tests testtrans = return [
1552 testGroup "Basic features" [
1553 testCase "Ping" (testPing testtrans)
1554 , testCase "Math" (testMath testtrans)
1555 , testCase "Timeout" (testTimeout testtrans)
1556 , testCase "Timeout0" (testTimeout0 testtrans)
1557 , testCase "SendToTerminated" (testSendToTerminated testtrans)
1558 , testCase "TypedChannnels" (testTypedChannels testtrans)
1559 , testCase "Terminate" (testTerminate testtrans)
1560 , testCase "RegistryRemoteProcess" (testRegistryRemoteProcess testtrans)
1561 , testCase "RemoteRegistry" (testRemoteRegistry testtrans)
1562 , testCase "RemoteRegistryRemoteProcess" (testRemoteRegistryRemoteProcess testtrans)
1563 , testCase "SpawnLocal" (testSpawnLocal testtrans)
1564 , testCase "SpawnAsyncStrictness" (testSpawnAsyncStrictness testtrans)
1565 , testCase "HandleMessageIf" (testHandleMessageIf testtrans)
1566 , testCase "MatchAny" (testMatchAny testtrans)
1567 , testCase "MatchAnyHandle" (testMatchAnyHandle testtrans)
1568 , testCase "MatchAnyNoHandle" (testMatchAnyNoHandle testtrans)
1569 , testCase "MatchAnyIf" (testMatchAnyIf testtrans)
1570 , testCase "MatchMessageUnwrap" (testMatchMessageWithUnwrap testtrans)
1571 , testCase "ReceiveChanTimeout" (testReceiveChanTimeout testtrans)
1572 , testCase "ReceiveChanFeatures" (testReceiveChanFeatures testtrans)
1573 , testCase "KillLocal" (testKillLocal testtrans)
1574 , testCase "KillRemote" (testKillRemote testtrans)
1575 , testCase "Die" (testDie testtrans)
1576 , testCase "PrettyExit" (testPrettyExit testtrans)
1577 , testCase "CatchesExit" (testCatchesExit testtrans)
1578 , testCase "Catches" (testCatches testtrans)
1579 , testCase "MaskRestoreScope" (testMaskRestoreScope testtrans)
1580 , testCase "ExitLocal" (testExitLocal testtrans)
1581 , testCase "ExitRemote" (testExitRemote testtrans)
1582 , testCase "TextCallLocal" (testCallLocal testtrans)
1583 -- Unsafe Primitives
1584 , testCase "TestUnsafeSend" (testUnsafeSend testtrans)
1585 , testCase "TestUnsafeUSend" (testUnsafeUSend testtrans)
1586 , testCase "TestUnsafeNSend" (testUnsafeNSend testtrans)
1587 , testCase "TestUnsafeNSendRemote" (testUnsafeNSendRemote testtrans)
1588 , testCase "TestUnsafeSendChan" (testUnsafeSendChan testtrans)
1590 , testCase "USend" (testUSend usend testtrans 50)
1591 , testCase "UForward"
1592 (testUSend (\p m -> uforward (createUnencodedMessage m) p)
1596 , testGroup "Monitoring and Linking" [
1597 -- Monitoring processes
1599 -- The "missing" combinations in the list below don't make much sense, as
1600 -- we cannot guarantee that the monitor reply or link exception will not
1601 -- happen before the unmonitor or unlink
1602 testCase "MonitorNormalTermination" (testMonitorNormalTermination testtrans True False)
1603 , testCase "MonitorAbnormalTermination" (testMonitorAbnormalTermination testtrans True False)
1604 , testCase "MonitorLocalDeadProcess" (testMonitorLocalDeadProcess testtrans True False)
1605 , testCase "MonitorRemoteDeadProcess" (testMonitorRemoteDeadProcess testtrans True False)
1606 , testCase "MonitorDisconnect" (testMonitorDisconnect testtrans True False)
1607 , testCase "LinkUnreachable" (testMonitorUnreachable testtrans False False)
1608 , testCase "LinkNormalTermination" (testMonitorNormalTermination testtrans False False)
1609 , testCase "LinkAbnormalTermination" (testMonitorAbnormalTermination testtrans False False)
1610 , testCase "LinkLocalDeadProcess" (testMonitorLocalDeadProcess testtrans False False)
1611 , testCase "LinkRemoteDeadProcess" (testMonitorRemoteDeadProcess testtrans False False)
1612 , testCase "LinkDisconnect" (testMonitorDisconnect testtrans False False)
1613 , testCase "UnmonitorNormalTermination" (testMonitorNormalTermination testtrans True True)
1614 , testCase "UnmonitorAbnormalTermination" (testMonitorAbnormalTermination testtrans True True)
1615 , testCase "UnmonitorDisconnect" (testMonitorDisconnect testtrans True True)
1616 , testCase "UnlinkNormalTermination" (testMonitorNormalTermination testtrans False True)
1617 , testCase "UnlinkAbnormalTermination" (testMonitorAbnormalTermination testtrans False True)
1618 , testCase "UnlinkDisconnect" (testMonitorDisconnect testtrans False True)
1619 -- Monitoring nodes and channels
1620 , testCase "MonitorNode" (testMonitorNode testtrans)
1621 , testCase "MonitorLiveNode" (testMonitorLiveNode testtrans)
1622 , testCase "MonitorChannel" (testMonitorChannel testtrans)
1626 -- Tests that fail occasionally and should be revised
1627 , testGroup "Flaky" [
1628 testCase "Reconnect" (testReconnect testtrans)
1629 , testCase "Registry" (testRegistry testtrans)
1630 , testCase "MergeChannels" (testMergeChannels testtrans)
1631 , testCase "MonitorUnreachable" (testMonitorUnreachable testtrans True False)
1635syncBreakConnection :: (NT.EndPointAddress -> NT.EndPointAddress -> IO ()) -> LocalNode -> LocalNode -> IO ()
1636syncBreakConnection breakConnection nid0 nid1 = do
1638 _ <- forkProcess nid1 $ getSelfPid >>= liftIO . putMVar m
1639 runProcess nid0 $ do
1640 them <- liftIO $ takeMVar m
1641 pinger <- spawnLocal $ forever $ send them ()
1642 _ <- monitorNode (localNodeId nid1)
1643 liftIO $ breakConnection (nodeAddress $ localNodeId nid0)
1644 (nodeAddress $ localNodeId nid1)
1645 NodeMonitorNotification _ _ _ <- expect
1646 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