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_)
17import Control.Concurrent (forkIO, threadDelay, myThreadId, throwTo, ThreadId, yield)
18import Control.Concurrent.MVar
25import Control.Monad (replicateM_, replicateM, forever, void, unless, join)
26import Control.Exception (SomeException, throwIO, ErrorCall(..))
27import qualified Control.Exception as Ex (catch)
28import Control.Applicative ((<$>), (<*>), pure, (<|>))
29import qualified Network.Transport as NT (closeEndPoint, EndPointAddress)
30import Control.Distributed.Process
31import Control.Distributed.Process.Internal.Types
33 , LocalNode(localEndPoint)
34 , ProcessExitException(..)
36 , createUnencodedMessage
38import Control.Distributed.Process.Node
39import Control.Distributed.Process.Serializable (Serializable)
41import Test.HUnit (Assertion, assertFailure)
42import Test.Framework (Test, testGroup)
43import Test.Framework.Providers.HUnit (testCase)
44import Control.Rematch hiding (match)
45import Control.Rematch.Run (Match(..))
47newtype Ping = Ping ProcessId
48 deriving (Typeable, Binary, Show)
50newtype Pong = Pong ProcessId
51 deriving (Typeable, Binary, Show)
53--------------------------------------------------------------------------------
54-- Supporting definitions --
55--------------------------------------------------------------------------------
57expectThat :: a -> Matcher a -> Assertion
58expectThat a matcher = case res of
59 MatchSuccess -> return ()
60 (MatchFailure msg) -> assertFailure msg
61 where res = runMatch matcher a
63-- | Like fork, but throw exceptions in the child thread to the parent
64forkTry :: IO () -> IO ThreadId
67 forkIO $ Ex.catch p (\e -> throwTo tid (e :: SomeException))
69-- | The ping server from the paper
72 Pong partner <- expect
74 send partner (Ping self)
77-- | Quick and dirty synchronous version of whereisRemoteAsync
78whereisRemote :: NodeId -> String -> Process (Maybe ProcessId)
79whereisRemote nid string = do
80 whereisRemoteAsync nid string
81 WhereIsReply _ mPid <- expect
84data Add = Add ProcessId Double Double deriving (Typeable)
85data Divide = Divide ProcessId Double Double deriving (Typeable)
86data DivByZero = DivByZero deriving (Typeable)
88instance Binary Add where
89 put (Add pid x y) = put pid >> put x >> put y
90 get = Add <$> get <*> get <*> get
92instance Binary Divide where
93 put (Divide pid x y) = put pid >> put x >> put y
94 get = Divide <$> get <*> get <*> get
96instance Binary DivByZero where
97 put DivByZero = return ()
98 get = return DivByZero
100-- The math server from the paper
104 [ match (\(Add pid x y) -> send pid (x + y))
105 , matchIf (\(Divide _ _ y) -> y /= 0)
106 (\(Divide pid x y) -> send pid (x / y))
107 , match (\(Divide pid _ _) -> send pid DivByZero)
111-- | Monitor or link to a remote node
112monitorOrLink :: Bool -- ^ 'True' for monitor, 'False' for link
113 -> ProcessId -- ^ Process to monitor/link to
114 -> Maybe (MVar ()) -- ^ MVar to signal on once the monitor has been set up
115 -> Process (Maybe MonitorRef)
116monitorOrLink mOrL pid mSignal = do
117 result <- if mOrL then Just <$> monitor pid
118 else link pid >> return Nothing
119 -- Monitor is asynchronous, which usually does not matter but if we want a
120 -- *specific* signal then it does. Therefore we wait until the MonitorRef is
121 -- listed in the ProcessInfo and hope that this means the monitor has been set
123 forM_ mSignal $ \signal -> do
127 liftIO $ threadDelay 100000
128 mpinfo <- getProcessInfo pid
130 Nothing -> waitForMOrL
133 unless (result == lookup self (infoMonitors pinfo)) waitForMOrL
135 unless (elem self $ infoLinks pinfo) waitForMOrL
137 liftIO $ putMVar signal ()
140monitorTestProcess :: ProcessId -- Process to monitor/link to
141 -> Bool -- 'True' for monitor, 'False' for link
142 -> Bool -- Should we unmonitor?
143 -> DiedReason -- Expected cause of death
144 -> Maybe (MVar ()) -- Signal for 'monitor set up'
145 -> MVar () -- Signal for successful termination
147monitorTestProcess theirAddr mOrL un reason monitorSetup done =
148 catch (do mRef <- monitorOrLink mOrL theirAddr monitorSetup
150 (True, Nothing) -> do
152 liftIO $ putMVar done ()
153 (True, Just ref) -> do
155 liftIO $ putMVar done ()
157 ProcessMonitorNotification ref' pid reason' <- expect
158 True <- return $ Just ref' == ref && pid == theirAddr && mOrL && reason == reason'
159 liftIO $ putMVar done ()
161 (\(ProcessLinkException pid reason') -> do
162 True <- return $ pid == theirAddr && not mOrL && not un && reason == reason'
163 liftIO $ putMVar done ()
166--------------------------------------------------------------------------------
167-- The tests proper --
168--------------------------------------------------------------------------------
171testPing :: TestTransport -> Assertion
172testPing TestTransport{..} = do
173 serverAddr <- newEmptyMVar
174 clientDone <- newEmptyMVar
178 localNode <- newLocalNode testTransport initRemoteTable
179 addr <- forkProcess localNode ping
180 putMVar serverAddr addr
184 localNode <- newLocalNode testTransport initRemoteTable
185 pingServer <- readMVar serverAddr
189 runProcess localNode $ do
191 replicateM_ numPings $ do
192 send pingServer (Pong pid)
196 putMVar clientDone ()
200-- | Monitor a process on an unreachable node
201testMonitorUnreachable :: TestTransport -> Bool -> Bool -> Assertion
202testMonitorUnreachable TestTransport{..} mOrL un = do
203 deadProcess <- newEmptyMVar
207 localNode <- newLocalNode testTransport initRemoteTable
208 addr <- forkProcess localNode expect
209 closeLocalNode localNode
210 putMVar deadProcess addr
213 localNode <- newLocalNode testTransport initRemoteTable
214 theirAddr <- readMVar deadProcess
215 runProcess localNode $
216 monitorTestProcess theirAddr mOrL un DiedDisconnect Nothing done
220-- | Monitor a process which terminates normally
221testMonitorNormalTermination :: TestTransport -> Bool -> Bool -> Assertion
222testMonitorNormalTermination TestTransport{..} mOrL un = do
223 monitorSetup <- newEmptyMVar
224 monitoredProcess <- newEmptyMVar
228 localNode <- newLocalNode testTransport initRemoteTable
229 addr <- forkProcess localNode $
230 liftIO $ readMVar monitorSetup
231 putMVar monitoredProcess addr
234 localNode <- newLocalNode testTransport initRemoteTable
235 theirAddr <- readMVar monitoredProcess
236 runProcess localNode $
237 monitorTestProcess theirAddr mOrL un DiedNormal (Just monitorSetup) done
241-- | Monitor a process which terminates abnormally
242testMonitorAbnormalTermination :: TestTransport -> Bool -> Bool -> Assertion
243testMonitorAbnormalTermination TestTransport{..} mOrL un = do
244 monitorSetup <- newEmptyMVar
245 monitoredProcess <- newEmptyMVar
248 let err = userError "Abnormal termination"
251 localNode <- newLocalNode testTransport initRemoteTable
252 addr <- forkProcess localNode . liftIO $ do
253 readMVar monitorSetup
255 putMVar monitoredProcess addr
258 localNode <- newLocalNode testTransport initRemoteTable
259 theirAddr <- readMVar monitoredProcess
260 runProcess localNode $
261 monitorTestProcess theirAddr mOrL un (DiedException (show err)) (Just monitorSetup) done
265-- | Monitor a local process that is already dead
266testMonitorLocalDeadProcess :: TestTransport -> Bool -> Bool -> Assertion
267testMonitorLocalDeadProcess TestTransport{..} mOrL un = do
268 processAddr <- newEmptyMVar
269 localNode <- newLocalNode testTransport initRemoteTable
273 addr <- forkProcess localNode $ return ()
274 putMVar processAddr addr
277 theirAddr <- readMVar processAddr
278 runProcess localNode $ do
280 -- wait for the process to die
281 ProcessMonitorNotification _ _ _ <- expect
282 monitorTestProcess theirAddr mOrL un DiedUnknownId Nothing done
286-- | Monitor a remote process that is already dead
287testMonitorRemoteDeadProcess :: TestTransport -> Bool -> Bool -> Assertion
288testMonitorRemoteDeadProcess TestTransport{..} mOrL un = do
289 processDead <- newEmptyMVar
290 processAddr <- newEmptyMVar
294 localNode <- newLocalNode testTransport initRemoteTable
295 addr <- forkProcess localNode . liftIO $ putMVar processDead ()
296 putMVar processAddr addr
299 localNode <- newLocalNode testTransport initRemoteTable
300 theirAddr <- readMVar processAddr
302 runProcess localNode $ do
303 monitorTestProcess theirAddr mOrL un DiedUnknownId Nothing done
307-- | Monitor a process that becomes disconnected
308testMonitorDisconnect :: TestTransport -> Bool -> Bool -> Assertion
309testMonitorDisconnect TestTransport{..} mOrL un = do
310 processAddr <- newEmptyMVar
311 processAddr2 <- newEmptyMVar
312 monitorSetup <- newEmptyMVar
316 localNode <- newLocalNode testTransport initRemoteTable
317 addr <- forkProcess localNode $ expect
318 addr2 <- forkProcess localNode $ return ()
319 putMVar processAddr addr
320 readMVar monitorSetup
321 NT.closeEndPoint (localEndPoint localNode)
322 putMVar processAddr2 addr2
325 localNode <- newLocalNode testTransport initRemoteTable
326 theirAddr <- readMVar processAddr
327 forkProcess localNode $ do
328 lc <- liftIO $ readMVar processAddr2
330 runProcess localNode $ do
331 monitorTestProcess theirAddr mOrL un DiedDisconnect (Just monitorSetup) done
335-- | Test the math server (i.e., receiveWait)
336testMath :: TestTransport -> Assertion
337testMath TestTransport{..} = do
338 serverAddr <- newEmptyMVar
339 clientDone <- newEmptyMVar
343 localNode <- newLocalNode testTransport initRemoteTable
344 addr <- forkProcess localNode math
345 putMVar serverAddr addr
349 localNode <- newLocalNode testTransport initRemoteTable
350 mathServer <- readMVar serverAddr
352 runProcess localNode $ do
354 send mathServer (Add pid 1 2)
355 3 <- expect :: Process Double
356 send mathServer (Divide pid 8 2)
357 4 <- expect :: Process Double
358 send mathServer (Divide pid 8 0)
360 liftIO $ putMVar clientDone ()
364-- | Send first message (i.e. connect) to an already terminated process
365-- (without monitoring); then send another message to a second process on
366-- the same remote node (we're checking that the remote node did not die)
367testSendToTerminated :: TestTransport -> Assertion
368testSendToTerminated TestTransport{..} = do
369 serverAddr1 <- newEmptyMVar
370 serverAddr2 <- newEmptyMVar
371 clientDone <- newEmptyMVar
374 terminated <- newEmptyMVar
375 localNode <- newLocalNode testTransport initRemoteTable
376 addr1 <- forkProcess localNode $ liftIO $ putMVar terminated ()
377 addr2 <- forkProcess localNode $ ping
379 putMVar serverAddr1 addr1
380 putMVar serverAddr2 addr2
383 localNode <- newLocalNode testTransport initRemoteTable
384 server1 <- readMVar serverAddr1
385 server2 <- readMVar serverAddr2
386 runProcess localNode $ do
389 send server2 (Pong pid)
391 True <- return $ pid' == server2
392 liftIO $ putMVar clientDone ()
396-- | Test (non-zero) timeout
397testTimeout :: TestTransport -> Assertion
398testTimeout TestTransport{..} = do
399 localNode <- newLocalNode testTransport initRemoteTable
402 runProcess localNode $ do
403 Nothing <- receiveTimeout 1000000 [match (\(Add _ _ _) -> return ())]
404 liftIO $ putMVar done ()
408-- | Test zero timeout
409testTimeout0 :: TestTransport -> Assertion
410testTimeout0 TestTransport{..} = do
411 serverAddr <- newEmptyMVar
412 clientDone <- newEmptyMVar
413 messagesSent <- newEmptyMVar
416 localNode <- newLocalNode testTransport initRemoteTable
417 addr <- forkProcess localNode $ do
418 liftIO $ readMVar messagesSent >> threadDelay 1000000
419 -- Variation on the venerable ping server which uses a zero timeout
420 -- Since we wait for all messages to be sent before doing this receive,
421 -- we should nevertheless find the right message immediately
422 Just partner <- receiveTimeout 0 [match (\(Pong partner) -> return partner)]
424 send partner (Ping self)
425 putMVar serverAddr addr
428 localNode <- newLocalNode testTransport initRemoteTable
429 server <- readMVar serverAddr
430 runProcess localNode $ do
432 -- Send a bunch of messages. A large number of messages that the server
433 -- is not interested in, and then a single message that it wants
434 replicateM_ 10000 $ send server "Irrelevant message"
435 send server (Pong pid)
436 liftIO $ putMVar messagesSent ()
438 liftIO $ putMVar clientDone ()
442-- | Test typed channels
443testTypedChannels :: TestTransport -> Assertion
444testTypedChannels TestTransport{..} = do
445 serverChannel <- newEmptyMVar :: IO (MVar (SendPort (SendPort Bool, Int)))
446 clientDone <- newEmptyMVar
449 localNode <- newLocalNode testTransport initRemoteTable
450 forkProcess localNode $ do
451 (serverSendPort, rport) <- newChan
452 liftIO $ putMVar serverChannel serverSendPort
453 (clientSendPort, i) <- receiveChan rport
454 sendChan clientSendPort (even i)
458 localNode <- newLocalNode testTransport initRemoteTable
459 serverSendPort <- readMVar serverChannel
460 runProcess localNode $ do
461 (clientSendPort, rport) <- newChan
462 sendChan serverSendPort (clientSendPort, 5)
463 False <- receiveChan rport
464 liftIO $ putMVar clientDone ()
468-- | Test merging receive ports
469testMergeChannels :: TestTransport -> Assertion
470testMergeChannels TestTransport{..} = do
471 localNode <- newLocalNode testTransport initRemoteTable
472 testFlat localNode True "aaabbbccc"
473 testFlat localNode False "abcabcabc"
474 testNested localNode True True "aaabbbcccdddeeefffggghhhiii"
475 testNested localNode True False "adgadgadgbehbehbehcficficfi"
476 testNested localNode False True "abcabcabcdefdefdefghighighi"
477 testNested localNode False False "adgbehcfiadgbehcfiadgbehcfi"
478 testBlocked localNode True
479 testBlocked localNode False
481 -- Single layer of merging
482 testFlat :: LocalNode -> Bool -> String -> IO ()
483 testFlat localNode biased expected = do
485 forkProcess localNode $ do
486 rs <- mapM charChannel "abc"
487 m <- mergePorts biased rs
488 xs <- replicateM 9 $ receiveChan m
489 True <- return $ xs == expected
490 liftIO $ putMVar done ()
493 -- Two layers of merging
494 testNested :: LocalNode -> Bool -> Bool -> String -> IO ()
495 testNested localNode biasedInner biasedOuter expected = do
497 forkProcess localNode $ do
498 rss <- mapM (mapM charChannel) ["abc", "def", "ghi"]
499 ms <- mapM (mergePorts biasedInner) rss
500 m <- mergePorts biasedOuter ms
501 xs <- replicateM (9 * 3) $ receiveChan m
502 True <- return $ xs == expected
503 liftIO $ putMVar done ()
506 -- Test that if no messages are (immediately) available, the scheduler makes no difference
507 testBlocked :: LocalNode -> Bool -> IO ()
508 testBlocked localNode biased = do
509 vs <- replicateM 3 newEmptyMVar
512 forkProcess localNode $ do
513 [sa, sb, sc] <- liftIO $ mapM readMVar vs
514 mapM_ ((>> liftIO (threadDelay 10000)) . uncurry sendChan)
541 forkProcess localNode $ do
542 (ss, rs) <- unzip <$> replicateM 3 newChan
543 liftIO $ mapM_ (uncurry putMVar) $ zip vs ss
544 m <- mergePorts biased rs
545 xs <- replicateM (6 * 3) $ receiveChan m
546 True <- return $ xs == "abcacbbacbcacabcba"
547 liftIO $ putMVar done ()
551 mergePorts :: Serializable a => Bool -> [ReceivePort a] -> Process (ReceivePort a)
552 mergePorts True = mergePortsBiased
553 mergePorts False = mergePortsRR
555 charChannel :: Char -> Process (ReceivePort Char)
557 (sport, rport) <- newChan
558 replicateM_ 3 $ sendChan sport c
559 liftIO $ threadDelay 10000 -- Make sure messages have been sent
562testTerminate :: TestTransport -> Assertion
563testTerminate TestTransport{..} = do
564 localNode <- newLocalNode testTransport initRemoteTable
567 pid <- forkProcess localNode $ do
568 liftIO $ threadDelay 100000
571 runProcess localNode $ do
573 ProcessMonitorNotification ref' pid' (DiedException ex) <- expect
574 True <- return $ ref == ref' && pid == pid' && ex == show ProcessTerminationException
575 liftIO $ putMVar done ()
579testMonitorNode :: TestTransport -> Assertion
580testMonitorNode TestTransport{..} = do
581 [node1, node2] <- replicateM 2 $ newLocalNode testTransport initRemoteTable
586 runProcess node2 $ do
587 ref <- monitorNode (localNodeId node1)
588 NodeMonitorNotification ref' nid DiedDisconnect <- expect
589 True <- return $ ref == ref' && nid == localNodeId node1
590 liftIO $ putMVar done ()
594testMonitorLiveNode :: TestTransport -> Assertion
595testMonitorLiveNode TestTransport{..} = do
596 [node1, node2] <- replicateM 2 $ newLocalNode testTransport initRemoteTable
597 ready <- newEmptyMVar
598 readyr <- newEmptyMVar
601 p <- forkProcess node1 $ return ()
602 forkProcess node2 $ do
603 ref <- monitorNode (localNodeId node1)
604 liftIO $ putMVar ready ()
605 liftIO $ takeMVar readyr
607 NodeMonitorNotification ref' nid _ <- expect
608 True <- return $ ref == ref' && nid == localNodeId node1
609 liftIO $ putMVar done ()
617testMonitorChannel :: TestTransport -> Assertion
618testMonitorChannel TestTransport{..} = do
619 [node1, node2] <- replicateM 2 $ newLocalNode testTransport initRemoteTable
620 gotNotification <- newEmptyMVar
622 pid <- forkProcess node1 $ do
623 sport <- expect :: Process (SendPort ())
624 ref <- monitorPort sport
625 PortMonitorNotification ref' port' reason <- expect
626 -- reason might be DiedUnknownId if the receive port is GCed before the
627 -- monitor is established (TODO: not sure that this is reasonable)
628 return $ ref' == ref && port' == sendPortId sport && (reason == DiedNormal || reason == DiedUnknownId)
629 liftIO $ putMVar gotNotification ()
631 runProcess node2 $ do
632 (sport, _) <- newChan :: Process (SendPort (), ReceivePort ())
634 liftIO $ threadDelay 100000
636 takeMVar gotNotification
638testRegistry :: TestTransport -> Assertion
639testRegistry TestTransport{..} = do
640 node <- newLocalNode testTransport initRemoteTable
643 pingServer <- forkProcess node ping
644 deadProcess <- forkProcess node (return ())
647 register "ping" pingServer
648 Just pid <- whereis "ping"
649 True <- return $ pingServer == pid
651 nsend "ping" (Pong us)
653 True <- return $ pingServer == pid'
654 Left (ProcessRegistrationException "dead" Nothing) <- try $ register "dead" deadProcess
655 Left (ProcessRegistrationException "ping" (Just x)) <- try $ register "ping" deadProcess
656 True <- return $ x == pingServer
657 Left (ProcessRegistrationException "dead" Nothing) <- try $ unregister "dead"
658 liftIO $ putMVar done ()
662testRegistryRemoteProcess :: TestTransport -> Assertion
663testRegistryRemoteProcess TestTransport{..} = do
664 node1 <- newLocalNode testTransport initRemoteTable
665 node2 <- newLocalNode testTransport initRemoteTable
668 pingServer <- forkProcess node1 ping
670 runProcess node2 $ do
671 register "ping" pingServer
672 Just pid <- whereis "ping"
673 True <- return $ pingServer == pid
675 nsend "ping" (Pong us)
677 True <- return $ pingServer == pid'
678 liftIO $ putMVar done ()
682testRemoteRegistry :: TestTransport -> Assertion
683testRemoteRegistry TestTransport{..} = do
684 node1 <- newLocalNode testTransport initRemoteTable
685 node2 <- newLocalNode testTransport initRemoteTable
688 pingServer <- forkProcess node1 ping
689 deadProcess <- forkProcess node1 (return ())
691 runProcess node2 $ do
692 let nid1 = localNodeId node1
693 registerRemoteAsync nid1 "ping" pingServer
695 matchIf (\(RegisterReply label' _ (Just pid)) ->
696 "ping" == label' && pid == pingServer)
697 (\(RegisterReply _ _ _) -> return ()) ]
699 Just pid <- whereisRemote nid1 "ping"
700 True <- return $ pingServer == pid
702 nsendRemote nid1 "ping" (Pong us)
704 True <- return $ pingServer == pid'
706 -- test that if process was not registered Nothing is returned
708 registerRemoteAsync nid1 "dead" deadProcess
710 matchIf (\(RegisterReply label' False Nothing) -> "dead" == label')
711 (\(RegisterReply _ _ _) -> return ()) ]
712 registerRemoteAsync nid1 "ping" deadProcess
714 matchIf (\(RegisterReply label' False (Just pid)) ->
715 "ping" == label' && pid == pingServer)
716 (\(RegisterReply _ _ _) -> return ()) ]
717 unregisterRemoteAsync nid1 "dead"
719 matchIf (\(RegisterReply label' False Nothing) ->
720 "dead" == label' && pid == pingServer)
721 (\(RegisterReply _ _ _) -> return ()) ]
722 liftIO $ putMVar done ()
726testRemoteRegistryRemoteProcess :: TestTransport -> Assertion
727testRemoteRegistryRemoteProcess TestTransport{..} = do
728 node1 <- newLocalNode testTransport initRemoteTable
729 node2 <- newLocalNode testTransport initRemoteTable
732 pingServer <- forkProcess node2 ping
734 runProcess node2 $ do
735 let nid1 = localNodeId node1
736 registerRemoteAsync nid1 "ping" pingServer
738 matchIf (\(RegisterReply label' _ _) -> "ping" == label')
739 (\(RegisterReply _ _ _) -> return ()) ]
740 Just pid <- whereisRemote nid1 "ping"
741 True <- return $ pingServer == pid
743 nsendRemote nid1 "ping" (Pong us)
745 True <- return $ pingServer == pid'
746 liftIO $ putMVar done ()
750testSpawnLocal :: TestTransport -> Assertion
751testSpawnLocal TestTransport{..} = do
752 node <- newLocalNode testTransport initRemoteTable
758 pid <- spawnLocal $ do
760 sendChan sport (1234 :: Int)
762 sport <- spawnChannelLocal $ \rport -> do
763 (1234 :: Int) <- receiveChan rport
768 liftIO $ putMVar done ()
772testSpawnAsyncStrictness :: TestTransport -> Assertion
773testSpawnAsyncStrictness TestTransport{..} = do
774 node <- newLocalNode testTransport initRemoteTable
780 ev <- try $ spawnAsync here (error "boom")
782 Right _ -> putMVar done (error "Exception didn't fire")
783 Left (_::SomeException) -> putMVar done (return ())
787testReconnect :: TestTransport -> Assertion
788testReconnect TestTransport{..} = do
789 [node1, node2] <- replicateM 2 $ newLocalNode testTransport initRemoteTable
790 let nid1 = localNodeId node1
791 processA <- newEmptyMVar
792 [sendTestOk, registerTestOk] <- replicateM 2 newEmptyMVar
794 forkProcess node1 $ do
796 liftIO $ putMVar processA us
799 True <- return $ msg1 == "message 1" && msg2 == "message 3"
800 liftIO $ putMVar sendTestOk ()
802 forkProcess node2 $ do
804 - Make sure there is no implicit reconnect on normal message sending
807 them <- liftIO $ readMVar processA
808 send them "message 1" >> liftIO (threadDelay 100000)
810 -- Simulate network failure
811 liftIO $ syncBreakConnection testBreakConnection node1 node2
815 send them "message 2"
819 send them "message 3"
821 liftIO $ takeMVar sendTestOk
824 - Test that there *is* implicit reconnect on node controller messages
828 registerRemoteAsync nid1 "a" us -- registerRemote is asynchronous
830 matchIf (\(RegisterReply label' _ _) -> "a" == label')
831 (\(RegisterReply _ _ _) -> return ()) ]
833 Just _ <- whereisRemote nid1 "a"
836 -- Simulate network failure
837 liftIO $ syncBreakConnection testBreakConnection node1 node2
839 -- This will happen due to implicit reconnect
840 registerRemoteAsync nid1 "b" us
842 matchIf (\(RegisterReply label' _ _) -> "b" == label')
843 (\(RegisterReply _ _ _) -> return ()) ]
846 registerRemoteAsync nid1 "c" us
848 matchIf (\(RegisterReply label' _ _) -> "c" == label')
849 (\(RegisterReply _ _ _) -> return ()) ]
852 Nothing <- whereisRemote nid1 "a" -- this will fail because the name is removed when the node is disconnected
853 Just _ <- whereisRemote nid1 "b" -- this will suceed because the value is set after thereconnect
854 Just _ <- whereisRemote nid1 "c"
856 liftIO $ putMVar registerTestOk ()
858 takeMVar registerTestOk
860-- | Tests that unreliable messages arrive sorted even when there are connection
862testUSend :: (ProcessId -> Int -> Process ())
863 -> TestTransport -> Int -> Assertion
864testUSend usendPrim TestTransport{..} numMessages = do
865 [node1, node2] <- replicateM 2 $ newLocalNode testTransport initRemoteTable
866 let nid1 = localNodeId node1
867 nid2 = localNodeId node2
868 processA <- newEmptyMVar
869 usendTestOk <- newEmptyMVar
871 forkProcess node1 $ flip catch (\e -> liftIO $ print (e :: SomeException) ) $ do
873 liftIO $ putMVar processA us
877 let -- Collects messages from 'them' until the sender dies.
878 -- Disconnection notifications are ignored.
879 receiveMessages :: Process [Int]
880 receiveMessages = receiveWait
881 [ match $ \mn -> case mn of
882 ProcessMonitorNotification _ _ DiedDisconnect -> do
886 , match $ \i -> fmap (i :) receiveMessages
888 msgs <- receiveMessages
889 let -- Checks that the input list is sorted.
890 isSorted :: [Int] -> Bool
891 isSorted (x : xs@(y : _)) = x <= y && isSorted xs
893 -- The list can't be null since there are no failures after sending
895 True <- return $ isSorted msgs && not (null msgs)
896 liftIO $ putMVar usendTestOk ()
898 forkProcess node2 $ do
899 them <- liftIO $ readMVar processA
900 getSelfPid >>= send them
902 forM_ [1..numMessages] $ \i -> do
903 liftIO $ testBreakConnection (nodeAddress nid1) (nodeAddress nid2)
905 liftIO (threadDelay 30000)
909-- | Test 'matchAny'. This repeats the 'testMath' but with a proxy server
911testMatchAny :: TestTransport -> Assertion
912testMatchAny TestTransport{..} = do
913 proxyAddr <- newEmptyMVar
914 clientDone <- newEmptyMVar
918 localNode <- newLocalNode testTransport initRemoteTable
919 mathServer <- forkProcess localNode math
920 proxyServer <- forkProcess localNode $ forever $ do
921 msg <- receiveWait [ matchAny return ]
922 forward msg mathServer
923 putMVar proxyAddr proxyServer
927 localNode <- newLocalNode testTransport initRemoteTable
928 mathServer <- readMVar proxyAddr
930 runProcess localNode $ do
932 send mathServer (Add pid 1 2)
933 3 <- expect :: Process Double
934 send mathServer (Divide pid 8 2)
935 4 <- expect :: Process Double
936 send mathServer (Divide pid 8 0)
938 liftIO $ putMVar clientDone ()
942-- | Test 'matchAny'. This repeats the 'testMath' but with a proxy server
943-- in between, however we block 'Divide' requests ....
944testMatchAnyHandle :: TestTransport -> Assertion
945testMatchAnyHandle TestTransport{..} = do
946 proxyAddr <- newEmptyMVar
947 clientDone <- newEmptyMVar
951 localNode <- newLocalNode testTransport initRemoteTable
952 mathServer <- forkProcess localNode math
953 proxyServer <- forkProcess localNode $ forever $ do
955 matchAny (maybeForward mathServer)
957 putMVar proxyAddr proxyServer
961 localNode <- newLocalNode testTransport initRemoteTable
962 mathServer <- readMVar proxyAddr
964 runProcess localNode $ do
966 send mathServer (Add pid 1 2)
967 3 <- expect :: Process Double
968 send mathServer (Divide pid 8 2)
969 Nothing <- (expectTimeout 100000) :: Process (Maybe Double)
970 liftIO $ putMVar clientDone ()
973 where maybeForward :: ProcessId -> Message -> Process (Maybe ())
975 handleMessage msg (\m@(Add _ _ _) -> send s m)
977testMatchAnyNoHandle :: TestTransport -> Assertion
978testMatchAnyNoHandle TestTransport{..} = do
980 clientDone <- newEmptyMVar
981 serverDone <- newEmptyMVar
985 localNode <- newLocalNode testTransport initRemoteTable
986 server <- forkProcess localNode $ forever $ do
989 -- the condition has type `Add -> Bool`
990 (\(Add _ _ _) -> True)
991 -- the match `AbstractMessage -> Process ()` will succeed!
993 -- `String -> Process ()` does *not* match the input types however
994 r <- (handleMessage m (\(_ :: String) -> die "NONSENSE" ))
997 Just _ -> die "NONSENSE")
999 -- we *must* have removed the message from our mailbox though!!!
1000 Nothing <- receiveTimeout 100000 [ match (\(Add _ _ _) -> return ()) ]
1001 liftIO $ putMVar serverDone ()
1006 localNode <- newLocalNode testTransport initRemoteTable
1007 server <- readMVar addr
1009 runProcess localNode $ do
1011 send server (Add pid 1 2)
1012 -- we only care about the client having sent a message, so we're done
1013 liftIO $ putMVar clientDone ()
1018-- | Test 'matchAnyIf'. We provide an /echo/ server, but it ignores requests
1019-- unless the text body @/= "bar"@ - this case should time out rather than
1020-- removing the message from the process mailbox.
1021testMatchAnyIf :: TestTransport -> Assertion
1022testMatchAnyIf TestTransport{..} = do
1023 echoAddr <- newEmptyMVar
1024 clientDone <- newEmptyMVar
1028 localNode <- newLocalNode testTransport initRemoteTable
1029 echoServer <- forkProcess localNode $ forever $ do
1031 matchAnyIf (\(_ :: ProcessId, (s :: String)) -> s /= "bar")
1034 putMVar echoAddr echoServer
1038 localNode <- newLocalNode testTransport initRemoteTable
1039 server <- readMVar echoAddr
1041 runProcess localNode $ do
1043 send server (pid, "foo")
1045 send server (pid, "baz")
1047 send server (pid, "bar")
1048 Nothing <- (expectTimeout 100000) :: Process (Maybe Double)
1049 liftIO $ putMVar clientDone ()
1052 where tryHandleMessage :: Message -> Process (Maybe ())
1053 tryHandleMessage msg =
1054 handleMessage msg (\(pid :: ProcessId, (m :: String))
1055 -> do { send pid m; return () })
1057testMatchMessageWithUnwrap :: TestTransport -> Assertion
1058testMatchMessageWithUnwrap TestTransport{..} = do
1059 echoAddr <- newEmptyMVar
1060 clientDone <- newEmptyMVar
1064 localNode <- newLocalNode testTransport initRemoteTable
1065 echoServer <- forkProcess localNode $ forever $ do
1066 msg <- receiveWait [
1067 matchMessage (\(m :: Message) -> do
1070 unwrapped <- unwrapMessage msg :: Process (Maybe (ProcessId, Message))
1072 (Just (p, msg')) -> forward msg' p
1073 Nothing -> die "unable to unwrap the message"
1074 putMVar echoAddr echoServer
1078 localNode <- newLocalNode testTransport initRemoteTable
1079 server <- readMVar echoAddr
1081 runProcess localNode $ do
1083 send server (pid, wrapMessage ("foo" :: String))
1085 send server (pid, wrapMessage ("baz" :: String))
1087 liftIO $ putMVar clientDone ()
1091-- Test 'receiveChanTimeout'
1092testReceiveChanTimeout :: TestTransport -> Assertion
1093testReceiveChanTimeout TestTransport{..} = do
1094 done <- newEmptyMVar
1095 sendPort <- newEmptyMVar
1098 localNode <- newLocalNode testTransport initRemoteTable
1099 runProcess localNode $ do
1100 -- Create a typed channel
1101 (sp, rp) <- newChan :: Process (SendPort Bool, ReceivePort Bool)
1102 liftIO $ putMVar sendPort sp
1104 -- Wait for a message with a delay. No message arrives, we should get Nothing after 1 second
1105 Nothing <- receiveChanTimeout 1000000 rp
1107 -- Wait for a message with a delay again. Now a message arrives after 0.5 seconds
1108 Just True <- receiveChanTimeout 1000000 rp
1110 -- Wait for a message with zero timeout: non-blocking check. No message is available, we get Nothing
1111 Nothing <- receiveChanTimeout 0 rp
1113 -- Again, but now there is a message available
1114 liftIO $ threadDelay 1000000
1115 Just False <- receiveChanTimeout 0 rp
1117 liftIO $ putMVar done ()
1120 localNode <- newLocalNode testTransport initRemoteTable
1121 runProcess localNode $ do
1122 sp <- liftIO $ readMVar sendPort
1124 liftIO $ threadDelay 1500000
1127 liftIO $ threadDelay 500000
1132-- | Test Functor, Applicative, Alternative and Monad instances for ReceiveChan
1133testReceiveChanFeatures :: TestTransport -> Assertion
1134testReceiveChanFeatures TestTransport{..} = do
1135 done <- newEmptyMVar
1138 localNode <- newLocalNode testTransport initRemoteTable
1139 runProcess localNode $ do
1140 (spInt, rpInt) <- newChan :: Process (SendPort Int, ReceivePort Int)
1141 (spBool, rpBool) <- newChan :: Process (SendPort Bool, ReceivePort Bool)
1143 -- Test Functor instance
1146 sendChan spBool False
1148 rp1 <- mergePortsBiased [even <$> rpInt, rpBool]
1150 True <- receiveChan rp1
1151 False <- receiveChan rp1
1153 -- Test Applicative instance
1158 let rp2 = pure (+) <*> rpInt <*> rpInt
1160 7 <- receiveChan rp2
1162 -- Test Alternative instance
1165 sendChan spBool True
1167 let rp3 = (even <$> rpInt) <|> rpBool
1169 False <- receiveChan rp3
1170 True <- receiveChan rp3
1172 -- Test Monad instance
1174 sendChan spBool True
1175 sendChan spBool False
1178 let rp4 :: ReceivePort Int
1179 rp4 = do b <- rpBool
1184 5 <- receiveChan rp4
1185 7 <- receiveChan rp4
1187 liftIO $ putMVar done ()
1191testKillLocal :: TestTransport -> Assertion
1192testKillLocal TestTransport{..} = do
1193 localNode <- newLocalNode testTransport initRemoteTable
1194 done <- newEmptyMVar
1196 pid <- forkProcess localNode $ do
1197 liftIO $ threadDelay 1000000
1199 runProcess localNode $ do
1203 ProcessMonitorNotification ref' pid' (DiedException ex) <- expect
1204 True <- return $ ref == ref' && pid == pid' && ex == "killed-by=" ++ show us ++ ",reason=TestKill"
1205 liftIO $ putMVar done ()
1209testKillRemote :: TestTransport -> Assertion
1210testKillRemote TestTransport{..} = do
1211 node1 <- newLocalNode testTransport initRemoteTable
1212 node2 <- newLocalNode testTransport initRemoteTable
1213 done <- newEmptyMVar
1215 pid <- forkProcess node1 $ do
1216 liftIO $ threadDelay 1000000
1218 runProcess node2 $ do
1222 ProcessMonitorNotification ref' pid' (DiedException reason) <- expect
1223 True <- return $ ref == ref' && pid == pid' && reason == "killed-by=" ++ show us ++ ",reason=TestKill"
1224 liftIO $ putMVar done ()
1228testCatchesExit :: TestTransport -> Assertion
1229testCatchesExit TestTransport{..} = do
1230 localNode <- newLocalNode testTransport initRemoteTable
1231 done <- newEmptyMVar
1233 _ <- forkProcess localNode $ do
1234 (die ("foobar", 123 :: Int))
1236 (\_ m -> handleMessage m (\(_ :: String) -> return ()))
1237 , (\_ m -> handleMessage m (\(_ :: Maybe Int) -> return ()))
1238 , (\_ m -> handleMessage m (\(_ :: String, _ :: Int)
1239 -> (liftIO $ putMVar done ()) >> return ()))
1244testHandleMessageIf :: TestTransport -> Assertion
1245testHandleMessageIf TestTransport{..} = do
1246 localNode <- newLocalNode testTransport initRemoteTable
1247 done <- newEmptyMVar
1248 _ <- forkProcess localNode $ do
1250 send self (5 :: Integer, 10 :: Integer)
1251 msg <- receiveWait [ matchMessage return ]
1252 Nothing <- handleMessageIf msg (\() -> True) (\() -> die $ "whoops")
1253 handleMessageIf msg (\(x :: Integer, y :: Integer) -> x == 5 && y == 10)
1254 (\input -> liftIO $ putMVar done input)
1257 result <- takeMVar done
1258 expectThat result $ equalTo (5, 10)
1260testCatches :: TestTransport -> Assertion
1261testCatches TestTransport{..} = do
1262 localNode <- newLocalNode testTransport initRemoteTable
1263 done <- newEmptyMVar
1265 _ <- forkProcess localNode $ do
1267 (liftIO $ throwIO (ProcessLinkException (nullProcessId node) DiedNormal))
1269 Handler (\(ProcessLinkException _ _) -> liftIO $ putMVar done ())
1274testMaskRestoreScope :: TestTransport -> Assertion
1275testMaskRestoreScope TestTransport{..} = do
1276 localNode <- newLocalNode testTransport initRemoteTable
1277 parentPid <- newEmptyMVar :: IO (MVar ProcessId)
1278 spawnedPid <- newEmptyMVar :: IO (MVar ProcessId)
1280 void $ runProcess localNode $ mask $ \unmask -> do
1281 getSelfPid >>= liftIO . putMVar parentPid
1282 void $ spawnLocal $ unmask (getSelfPid >>= liftIO . putMVar spawnedPid)
1284 parent <- liftIO $ takeMVar parentPid
1285 child <- liftIO $ takeMVar spawnedPid
1286 expectThat parent $ isNot $ equalTo child
1288testDie :: TestTransport -> Assertion
1289testDie TestTransport{..} = do
1290 localNode <- newLocalNode testTransport initRemoteTable
1291 done <- newEmptyMVar
1293 _ <- forkProcess localNode $ do
1294 (die ("foobar", 123 :: Int))
1295 `catchExit` \_from reason -> do
1296 -- TODO: should verify that 'from' has the right value
1297 True <- return $ reason == ("foobar", 123 :: Int)
1298 liftIO $ putMVar done ()
1302testPrettyExit :: TestTransport -> Assertion
1303testPrettyExit TestTransport{..} = do
1304 localNode <- newLocalNode testTransport initRemoteTable
1305 done <- newEmptyMVar
1307 _ <- forkProcess localNode $ do
1309 `catch` \ex@(ProcessExitException from _) ->
1310 let expected = "exit-from=" ++ (show from)
1312 True <- return $ (show ex) == expected
1313 liftIO $ putMVar done ()
1317testExitLocal :: TestTransport -> Assertion
1318testExitLocal TestTransport{..} = do
1319 localNode <- newLocalNode testTransport initRemoteTable
1320 supervisedDone <- newEmptyMVar
1321 supervisorDone <- newEmptyMVar
1322 -- XXX: we guarantee that exception handler will be set up
1323 -- regardless if forkProcess preserve masking state or not.
1324 handlerSetUp <- newEmptyMVar
1326 pid <- forkProcess localNode $ do
1327 (liftIO (putMVar handlerSetUp ()) >> expect) `catchExit` \_from reason -> do
1328 -- TODO: should verify that 'from' has the right value
1329 True <- return $ reason == "TestExit"
1330 liftIO $ putMVar supervisedDone ()
1332 runProcess localNode $ do
1333 liftIO $ takeMVar handlerSetUp
1336 -- This time the client catches the exception, so it dies normally
1337 ProcessMonitorNotification ref' pid' DiedNormal <- expect
1338 True <- return $ ref == ref' && pid == pid'
1339 liftIO $ putMVar supervisorDone ()
1341 takeMVar supervisedDone
1342 takeMVar supervisorDone
1344testExitRemote :: TestTransport -> Assertion
1345testExitRemote TestTransport{..} = do
1346 node1 <- newLocalNode testTransport initRemoteTable
1347 node2 <- newLocalNode testTransport initRemoteTable
1348 supervisedDone <- newEmptyMVar
1349 supervisorDone <- newEmptyMVar
1351 pid <- forkProcess node1 $ do
1352 (receiveWait [] :: Process ()) -- block forever
1353 `catchExit` \_from reason -> do
1354 -- TODO: should verify that 'from' has the right value
1355 True <- return $ reason == "TestExit"
1356 liftIO $ putMVar supervisedDone ()
1358 runProcess node2 $ do
1361 ProcessMonitorNotification ref' pid' DiedNormal <- expect
1362 True <- return $ ref == ref' && pid == pid'
1363 liftIO $ putMVar supervisorDone ()
1365 takeMVar supervisedDone
1366 takeMVar supervisorDone
1368testUnsafeSend :: TestTransport -> Assertion
1369testUnsafeSend TestTransport{..} = do
1370 serverAddr <- newEmptyMVar
1371 clientDone <- newEmptyMVar
1373 localNode <- newLocalNode testTransport initRemoteTable
1374 void $ forkProcess localNode $ do
1376 liftIO $ putMVar serverAddr self
1377 clientAddr <- expect
1378 unsafeSend clientAddr ()
1380 void $ forkProcess localNode $ do
1381 serverPid <- liftIO $ takeMVar serverAddr
1382 getSelfPid >>= unsafeSend serverPid
1384 liftIO $ putMVar clientDone ()
1388testUnsafeNSend :: TestTransport -> Assertion
1389testUnsafeNSend TestTransport{..} = do
1390 clientDone <- newEmptyMVar
1392 localNode <- newLocalNode testTransport initRemoteTable
1394 pid <- forkProcess localNode $ do
1396 liftIO $ putMVar clientDone ()
1398 void $ runProcess localNode $ do
1399 register "foobar" pid
1400 unsafeNSend "foobar" ()
1404testUnsafeSendChan :: TestTransport -> Assertion
1405testUnsafeSendChan TestTransport{..} = do
1406 serverAddr <- newEmptyMVar
1407 clientDone <- newEmptyMVar
1409 localNode <- newLocalNode testTransport initRemoteTable
1410 void $ forkProcess localNode $ do
1412 liftIO $ putMVar serverAddr self
1414 unsafeSendChan sp ()
1416 void $ forkProcess localNode $ do
1417 serverPid <- liftIO $ takeMVar serverAddr
1419 unsafeSend serverPid sp
1420 () <- receiveChan rp
1421 liftIO $ putMVar clientDone ()
1425testCallLocal :: TestTransport -> Assertion
1426testCallLocal TestTransport{..} = do
1427 node <- newLocalNode testTransport initRemoteTable
1429 -- Testing that (/=) <$> getSelfPid <*> callLocal getSelfPid.
1430 result <- newEmptyMVar
1431 runProcess node $ do
1432 r <- (/=) <$> getSelfPid <*> callLocal getSelfPid
1433 liftIO $ putMVar result r
1434 True <- takeMVar result
1437 -- Testing that when callLocal is interrupted, the worker is interrupted.
1438 ibox <- newIORef False
1439 runProcess node $ do
1440 keeper <- getSelfPid
1442 caller <- getSelfPid
1446 onException (do send keeper caller
1448 (do liftIO $ writeIORef ibox True))
1454 True <- readIORef ibox
1457 -- Testing that when the worker raises an exception, the exception is propagated to the parent.
1458 ibox2 <- newIORef False
1459 runProcess node $ do
1460 r <- try (callLocal $ error "e" >> return ())
1461 liftIO $ writeIORef ibox2 $ case r of
1462 Left (ErrorCall "e") -> True
1464 True <- readIORef ibox
1467 -- Test that caller waits for the worker in correct situation
1468 ibox3 <- newIORef False
1469 result3 <- newEmptyMVar
1470 runProcess node $ do
1471 keeper <- getSelfPid
1474 (do us <- getSelfPid
1478 `finally` (liftIO $ writeIORef ibox3 True)
1479 liftIO $ putMVar result3 =<< readIORef ibox3
1482 True <- takeMVar result3
1485 -- Test that caller waits for the worker in case when caller gets an exception
1486 ibox4 <- newIORef False
1487 result4 <- newEmptyMVar
1488 runProcess node $ do
1489 keeper <- getSelfPid
1491 caller <- getSelfPid
1493 ((do send keeper caller
1495 `finally` (liftIO $ writeIORef ibox4 True))
1496 `finally` (liftIO $ putMVar result4 =<< readIORef ibox4)
1499 True <- takeMVar result4
1501 -- XXX: Testing that when mask_ $ callLocal p runs p in masked state.
1507tests :: TestTransport -> IO [Test]
1508tests testtrans = return [
1509 testGroup "Basic features" [
1510 testCase "Ping" (testPing testtrans)
1511 , testCase "Math" (testMath testtrans)
1512 , testCase "Timeout" (testTimeout testtrans)
1513 , testCase "Timeout0" (testTimeout0 testtrans)
1514 , testCase "SendToTerminated" (testSendToTerminated testtrans)
1515 , testCase "TypedChannnels" (testTypedChannels testtrans)
1516 , testCase "Terminate" (testTerminate testtrans)
1517 , testCase "RegistryRemoteProcess" (testRegistryRemoteProcess testtrans)
1518 , testCase "RemoteRegistry" (testRemoteRegistry testtrans)
1519 , testCase "RemoteRegistryRemoteProcess" (testRemoteRegistryRemoteProcess testtrans)
1520 , testCase "SpawnLocal" (testSpawnLocal testtrans)
1521 , testCase "SpawnAsyncStrictness" (testSpawnAsyncStrictness testtrans)
1522 , testCase "HandleMessageIf" (testHandleMessageIf testtrans)
1523 , testCase "MatchAny" (testMatchAny testtrans)
1524 , testCase "MatchAnyHandle" (testMatchAnyHandle testtrans)
1525 , testCase "MatchAnyNoHandle" (testMatchAnyNoHandle testtrans)
1526 , testCase "MatchAnyIf" (testMatchAnyIf testtrans)
1527 , testCase "MatchMessageUnwrap" (testMatchMessageWithUnwrap testtrans)
1528 , testCase "ReceiveChanTimeout" (testReceiveChanTimeout testtrans)
1529 , testCase "ReceiveChanFeatures" (testReceiveChanFeatures testtrans)
1530 , testCase "KillLocal" (testKillLocal testtrans)
1531 , testCase "KillRemote" (testKillRemote testtrans)
1532 , testCase "Die" (testDie testtrans)
1533 , testCase "PrettyExit" (testPrettyExit testtrans)
1534 , testCase "CatchesExit" (testCatchesExit testtrans)
1535 , testCase "Catches" (testCatches testtrans)
1536 , testCase "MaskRestoreScope" (testMaskRestoreScope testtrans)
1537 , testCase "ExitLocal" (testExitLocal testtrans)
1538 , testCase "ExitRemote" (testExitRemote testtrans)
1539 , testCase "TextCallLocal" (testCallLocal testtrans)
1540 -- Unsafe Primitives
1541 , testCase "TestUnsafeSend" (testUnsafeSend testtrans)
1542 , testCase "TestUnsafeNSend" (testUnsafeNSend testtrans)
1543 , testCase "TestUnsafeSendChan" (testUnsafeSendChan testtrans)
1545 , testCase "USend" (testUSend usend testtrans 50)
1546 , testCase "UForward"
1547 (testUSend (\p m -> uforward (createUnencodedMessage m) p)
1551 , testGroup "Monitoring and Linking" [
1552 -- Monitoring processes
1554 -- The "missing" combinations in the list below don't make much sense, as
1555 -- we cannot guarantee that the monitor reply or link exception will not
1556 -- happen before the unmonitor or unlink
1557 testCase "MonitorNormalTermination" (testMonitorNormalTermination testtrans True False)
1558 , testCase "MonitorAbnormalTermination" (testMonitorAbnormalTermination testtrans True False)
1559 , testCase "MonitorLocalDeadProcess" (testMonitorLocalDeadProcess testtrans True False)
1560 , testCase "MonitorRemoteDeadProcess" (testMonitorRemoteDeadProcess testtrans True False)
1561 , testCase "MonitorDisconnect" (testMonitorDisconnect testtrans True False)
1562 , testCase "LinkUnreachable" (testMonitorUnreachable testtrans False False)
1563 , testCase "LinkNormalTermination" (testMonitorNormalTermination testtrans False False)
1564 , testCase "LinkAbnormalTermination" (testMonitorAbnormalTermination testtrans False False)
1565 , testCase "LinkLocalDeadProcess" (testMonitorLocalDeadProcess testtrans False False)
1566 , testCase "LinkRemoteDeadProcess" (testMonitorRemoteDeadProcess testtrans False False)
1567 , testCase "LinkDisconnect" (testMonitorDisconnect testtrans False False)
1568 , testCase "UnmonitorNormalTermination" (testMonitorNormalTermination testtrans True True)
1569 , testCase "UnmonitorAbnormalTermination" (testMonitorAbnormalTermination testtrans True True)
1570 , testCase "UnmonitorDisconnect" (testMonitorDisconnect testtrans True True)
1571 , testCase "UnlinkNormalTermination" (testMonitorNormalTermination testtrans False True)
1572 , testCase "UnlinkAbnormalTermination" (testMonitorAbnormalTermination testtrans False True)
1573 , testCase "UnlinkDisconnect" (testMonitorDisconnect testtrans False True)
1574 -- Monitoring nodes and channels
1575 , testCase "MonitorNode" (testMonitorNode testtrans)
1576 , testCase "MonitorLiveNode" (testMonitorLiveNode testtrans)
1577 , testCase "MonitorChannel" (testMonitorChannel testtrans)
1581 -- Tests that fail occasionally and should be revised
1582 , testGroup "Flaky" [
1583 testCase "Reconnect" (testReconnect testtrans)
1584 , testCase "Registry" (testRegistry testtrans)
1585 , testCase "MergeChannels" (testMergeChannels testtrans)
1586 , testCase "MonitorUnreachable" (testMonitorUnreachable testtrans True False)
1590syncBreakConnection :: (NT.EndPointAddress -> NT.EndPointAddress -> IO ()) -> LocalNode -> LocalNode -> IO ()
1591syncBreakConnection breakConnection nid0 nid1 = do
1593 _ <- forkProcess nid1 $ getSelfPid >>= liftIO . putMVar m
1594 runProcess nid0 $ do
1595 them <- liftIO $ takeMVar m
1596 pinger <- spawnLocal $ forever $ send them ()
1597 _ <- monitorNode (localNodeId nid1)
1598 liftIO $ breakConnection (nodeAddress $ localNodeId nid0)
1599 (nodeAddress $ localNodeId nid1)
1600 NodeMonitorNotification _ _ _ <- expect
1601 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