@@ -4,10 +4,11 @@ module Main where
44import Prelude hiding (catch)
55#endif
66
7+
78import Data.Binary (Binary(..))
89import Data.Typeable (Typeable)
910import Data.Foldable (forM_)
10−import Control.Concurrent (forkIO, threadDelay)
11+import Control.Concurrent (forkIO, threadDelay, myThreadId, throwTo, ThreadId)
1112import Control.Concurrent.MVar
1213 ( MVar
1314 , newEmptyMVar
@@ -16,7 +17,8 @@ import Control.Concurrent.MVar
1617 , readMVar
1718 )
1819import Control.Monad (replicateM_, replicateM, forever)
19−import Control.Exception (throwIO)
20+import Control.Exception (SomeException, throwIO)
21+import qualified Control.Exception as Ex (catch)
2022import Control.Applicative ((<$>), (<*>))
2123import qualified Network.Transport as NT (Transport, closeEndPoint)
2224import Network.Socket (sClose)
@@ -32,7 +34,10 @@ import Control.Distributed.Process.Internal.Types
3234 )
3335import Control.Distributed.Process.Node
3436import Control.Distributed.Process.Serializable (Serializable)
35−import TestAuxiliary
37+
38+import Test.HUnit (Assertion)
39+import Test.Framework (Test, defaultMain, testGroup)
40+import Test.Framework.Providers.HUnit (testCase)
3641
3742newtype Ping = Ping ProcessId
3843 deriving (Typeable, Binary, Show)
@@ -40,6 +45,16 @@ newtype Ping = Ping ProcessId
4045newtype Pong = Pong ProcessId
4146 deriving (Typeable, Binary, Show)
4247
48+--------------------------------------------------------------------------------
49+-- Supporting definitions --
50+--------------------------------------------------------------------------------
51+
52+-- | Like fork, but throw exceptions in the child thread to the parent
53+forkTry :: IO () -> IO ThreadId
54+forkTry p = do
55+ tid <- myThreadId
56+ forkIO $ Ex.catch p (\e -> throwTo tid (e :: SomeException))
57+
4358-- | The ping server from the paper
4459ping :: Process ()
4560ping = do
@@ -55,35 +70,32 @@ whereisRemote nid string = do
5570 WhereIsReply _ mPid <- expect
5671 return mPid
5772
58−-- | Basic ping test
59−testPing :: NT.Transport -> IO ()
60−testPing transport = do
61− serverAddr <- newEmptyMVar
62− clientDone <- newEmptyMVar
63−
64− -- Server
65− forkIO $ do
66− localNode <- newLocalNode transport initRemoteTable
67− addr <- forkProcess localNode ping
68− putMVar serverAddr addr
69−
70− -- Client
71− forkIO $ do
72− localNode <- newLocalNode transport initRemoteTable
73− pingServer <- readMVar serverAddr
73+data Add = Add ProcessId Double Double deriving (Typeable)
74+data Divide = Divide ProcessId Double Double deriving (Typeable)
75+data DivByZero = DivByZero deriving (Typeable)
7476
75− let numPings = 10000
77+instance Binary Add where
78+ put (Add pid x y) = put pid >> put x >> put y
79+ get = Add <$> get <*> get <*> get
7680
77− runProcess localNode $ do
78− pid <- getSelfPid
79− replicateM_ numPings $ do
80− send pingServer (Pong pid)
81− Ping _ <- expect
82− return ()
81+instance Binary Divide where
82+ put (Divide pid x y) = put pid >> put x >> put y
83+ get = Divide <$> get <*> get <*> get
8384
84− putMVar clientDone ()
85+instance Binary DivByZero where
86+ put DivByZero = return ()
87+ get = return DivByZero
8588
86− takeMVar clientDone
89+-- The math server from the paper
90+math :: Process ()
91+math = do
92+ receiveWait
93+ [ match (\(Add pid x y) -> send pid (x + y))
94+ , matchIf (\(Divide _ _ y) -> y /= 0)
95+ (\(Divide pid x y) -> send pid (x / y))
96+ , match (\(Divide pid _ _) -> send pid DivByZero)
97+ ]
98+ math
8799
88100-- | Monitor or link to a remote node
89101monitorOrLink :: Bool -- ^ 'True' for monitor, 'False' for link
@@ -125,9 +137,42 @@ monitorTestProcess theirAddr mOrL un reason monitorSetup done =
125137 liftIO $ putMVar done ()
126138 )
127139
140+--------------------------------------------------------------------------------
141+-- The tests proper --
142+--------------------------------------------------------------------------------
143+
144+-- | Basic ping test
145+testPing :: NT.Transport -> Assertion
146+testPing transport = do
147+ serverAddr <- newEmptyMVar
148+ clientDone <- newEmptyMVar
149+
150+ -- Server
151+ forkIO $ do
152+ localNode <- newLocalNode transport initRemoteTable
153+ addr <- forkProcess localNode ping
154+ putMVar serverAddr addr
155+
156+ -- Client
157+ forkIO $ do
158+ localNode <- newLocalNode transport initRemoteTable
159+ pingServer <- readMVar serverAddr
160+
161+ let numPings = 10000
162+
163+ runProcess localNode $ do
164+ pid <- getSelfPid
165+ replicateM_ numPings $ do
166+ send pingServer (Pong pid)
167+ Ping _ <- expect
168+ return ()
169+
170+ putMVar clientDone ()
171+
172+ takeMVar clientDone
128173
129174-- | Monitor a process on an unreachable node
130−testMonitorUnreachable :: NT.Transport -> Bool -> Bool -> IO ()
175+testMonitorUnreachable :: NT.Transport -> Bool -> Bool -> Assertion
131176testMonitorUnreachable transport mOrL un = do
132177 deadProcess <- newEmptyMVar
133178 done <- newEmptyMVar
@@ -147,7 +192,7 @@ testMonitorUnreachable transport mOrL un = do
147192 takeMVar done
148193
149194-- | Monitor a process which terminates normally
150−testMonitorNormalTermination :: NT.Transport -> Bool -> Bool -> IO ()
195+testMonitorNormalTermination :: NT.Transport -> Bool -> Bool -> Assertion
151196testMonitorNormalTermination transport mOrL un = do
152197 monitorSetup <- newEmptyMVar
153198 monitoredProcess <- newEmptyMVar
@@ -168,7 +213,7 @@ testMonitorNormalTermination transport mOrL un = do
168213 takeMVar done
169214
170215-- | Monitor a process which terminates abnormally
171−testMonitorAbnormalTermination :: NT.Transport -> Bool -> Bool -> IO ()
216+testMonitorAbnormalTermination :: NT.Transport -> Bool -> Bool -> Assertion
172217testMonitorAbnormalTermination transport mOrL un = do
173218 monitorSetup <- newEmptyMVar
174219 monitoredProcess <- newEmptyMVar
@@ -192,7 +237,7 @@ testMonitorAbnormalTermination transport mOrL un = do
192237 takeMVar done
193238
194239-- | Monitor a local process that is already dead
195−testMonitorLocalDeadProcess :: NT.Transport -> Bool -> Bool -> IO ()
240+testMonitorLocalDeadProcess :: NT.Transport -> Bool -> Bool -> Assertion
196241testMonitorLocalDeadProcess transport mOrL un = do
197242 processDead <- newEmptyMVar
198243 processAddr <- newEmptyMVar
@@ -212,7 +257,7 @@ testMonitorLocalDeadProcess transport mOrL un = do
212257 takeMVar done
213258
214259-- | Monitor a remote process that is already dead
215−testMonitorRemoteDeadProcess :: NT.Transport -> Bool -> Bool -> IO ()
260+testMonitorRemoteDeadProcess :: NT.Transport -> Bool -> Bool -> Assertion
216261testMonitorRemoteDeadProcess transport mOrL un = do
217262 processDead <- newEmptyMVar
218263 processAddr <- newEmptyMVar
@@ -233,7 +278,7 @@ testMonitorRemoteDeadProcess transport mOrL un = do
233278 takeMVar done
234279
235280-- | Monitor a process that becomes disconnected
236−testMonitorDisconnect :: NT.Transport -> Bool -> Bool -> IO ()
281+testMonitorDisconnect :: NT.Transport -> Bool -> Bool -> Assertion
237282testMonitorDisconnect transport mOrL un = do
238283 processAddr <- newEmptyMVar
239284 monitorSetup <- newEmptyMVar
@@ -254,34 +299,8 @@ testMonitorDisconnect transport mOrL un = do
254299
255300 takeMVar done
256301
257−data Add = Add ProcessId Double Double deriving (Typeable)
258−data Divide = Divide ProcessId Double Double deriving (Typeable)
259−data DivByZero = DivByZero deriving (Typeable)
260−
261−instance Binary Add where
262− put (Add pid x y) = put pid >> put x >> put y
263− get = Add <$> get <*> get <*> get
264−
265−instance Binary Divide where
266− put (Divide pid x y) = put pid >> put x >> put y
267− get = Divide <$> get <*> get <*> get
268−
269−instance Binary DivByZero where
270− put DivByZero = return ()
271− get = return DivByZero
272−
273−math :: Process ()
274−math = do
275− receiveWait
276− [ match (\(Add pid x y) -> send pid (x + y))
277− , matchIf (\(Divide _ _ y) -> y /= 0)
278− (\(Divide pid x y) -> send pid (x / y))
279− , match (\(Divide pid _ _) -> send pid DivByZero)
280− ]
281− math
282−
283302-- | Test the math server (i.e., receiveWait)
284−testMath :: NT.Transport -> IO ()
303+testMath :: NT.Transport -> Assertion
285304testMath transport = do
286305 serverAddr <- newEmptyMVar
287306 clientDone <- newEmptyMVar
@@ -312,7 +331,7 @@ testMath transport = do
312331-- | Send first message (i.e. connect) to an already terminated process
313332-- (without monitoring); then send another message to a second process on
314333-- the same remote node (we're checking that the remote node did not die)
315−testSendToTerminated :: NT.Transport -> IO ()
334+testSendToTerminated :: NT.Transport -> Assertion
316335testSendToTerminated transport = do
317336 serverAddr1 <- newEmptyMVar
318337 serverAddr2 <- newEmptyMVar
@@ -342,7 +361,7 @@ testSendToTerminated transport = do
342361 takeMVar clientDone
343362
344363-- | Test (non-zero) timeout
345−testTimeout :: NT.Transport -> IO ()
364+testTimeout :: NT.Transport -> Assertion
346365testTimeout transport = do
347366 localNode <- newLocalNode transport initRemoteTable
348367 runProcess localNode $ do
@@ -350,7 +369,7 @@ testTimeout transport = do
350369 return ()
351370
352371-- | Test zero timeout
353−testTimeout0 :: NT.Transport -> IO ()
372+testTimeout0 :: NT.Transport -> Assertion
354373testTimeout0 transport = do
355374 serverAddr <- newEmptyMVar
356375 clientDone <- newEmptyMVar
@@ -384,7 +403,7 @@ testTimeout0 transport = do
384403 takeMVar clientDone
385404
386405-- | Test typed channels
387−testTypedChannels :: NT.Transport -> IO ()
406+testTypedChannels :: NT.Transport -> Assertion
388407testTypedChannels transport = do
389408 serverChannel <- newEmptyMVar :: IO (MVar (SendPort (SendPort Bool, Int)))
390409 clientDone <- newEmptyMVar
@@ -410,7 +429,7 @@ testTypedChannels transport = do
410429 takeMVar clientDone
411430
412431-- | Test merging receive ports
413−testMergeChannels :: NT.Transport -> IO ()
432+testMergeChannels :: NT.Transport -> Assertion
414433testMergeChannels transport = do
415434 localNode <- newLocalNode transport initRemoteTable
416435 testFlat localNode True "aaabbbccc"
@@ -496,7 +515,7 @@ testMergeChannels transport = do
496515 liftIO $ threadDelay 10000 -- Make sure messages have been sent
497516 return rport
498517
499−testTerminate :: NT.Transport -> IO ()
518+testTerminate :: NT.Transport -> Assertion
500519testTerminate transport = do
501520 localNode <- newLocalNode transport initRemoteTable
502521
@@ -510,7 +529,7 @@ testTerminate transport = do
510529 True <- return $ ref == ref' && pid == pid' && ex == show ProcessTerminationException
511530 return ()
512531
513−testMonitorNode :: NT.Transport -> IO ()
532+testMonitorNode :: NT.Transport -> Assertion
514533testMonitorNode transport = do
515534 [node1, node2] <- replicateM 2 $ newLocalNode transport initRemoteTable
516535
@@ -522,7 +541,7 @@ testMonitorNode transport = do
522541 True <- return $ ref == ref' && nid == localNodeId node1
523542 return ()
524543
525−testMonitorChannel :: NT.Transport -> IO ()
544+testMonitorChannel :: NT.Transport -> Assertion
526545testMonitorChannel transport = do
527546 [node1, node2] <- replicateM 2 $ newLocalNode transport initRemoteTable
528547 gotNotification <- newEmptyMVar
@@ -543,7 +562,7 @@ testMonitorChannel transport = do
543562
544563 takeMVar gotNotification
545564
546−testRegistry :: NT.Transport -> IO ()
565+testRegistry :: NT.Transport -> Assertion
547566testRegistry transport = do
548567 node <- newLocalNode transport initRemoteTable
549568
@@ -559,7 +578,7 @@ testRegistry transport = do
559578 True <- return $ pingServer == pid'
560579 return ()
561580
562−testRemoteRegistry :: NT.Transport -> IO ()
581+testRemoteRegistry :: NT.Transport -> Assertion
563582testRemoteRegistry transport = do
564583 node1 <- newLocalNode transport initRemoteTable
565584 node2 <- newLocalNode transport initRemoteTable
@@ -577,7 +596,7 @@ testRemoteRegistry transport = do
577596 True <- return $ pingServer == pid'
578597 return ()
579598
580−testSpawnLocal :: NT.Transport -> IO ()
599+testSpawnLocal :: NT.Transport -> Assertion
581600testSpawnLocal transport = do
582601 node <- newLocalNode transport initRemoteTable
583602
@@ -595,7 +614,7 @@ testSpawnLocal transport = do
595614 send pid sport
596615 expect
597616
598−testReconnect :: NT.Transport -> TransportInternals -> IO ()
617+testReconnect :: NT.Transport -> TransportInternals -> Assertion
599618testReconnect transport transportInternals = do
600619 [node1, node2] <- replicateM 2 $ newLocalNode transport initRemoteTable
601620 let nid1 = localNodeId node1
@@ -664,7 +683,7 @@ testReconnect transport transportInternals = do
664683
665684-- | Test 'matchAny'. This repeats the 'testMath' but with a proxy server
666685-- in between
667−testMatchAny :: NT.Transport -> IO ()
686+testMatchAny :: NT.Transport -> Assertion
668687testMatchAny transport = do
669688 proxyAddr <- newEmptyMVar
670689 clientDone <- newEmptyMVar
@@ -696,12 +715,12 @@ testMatchAny transport = do
696715 takeMVar clientDone
697716
698717-- Test 'receiveChanTimeout'
699−testReceiveChanTimeout :: NT.Transport -> IO ()
718+testReceiveChanTimeout :: NT.Transport -> Assertion
700719testReceiveChanTimeout transport = do
701720 done <- newEmptyMVar
702721 sendPort <- newEmptyMVar
703722
704− forkIO $ do
723+ forkTry $ do
705724 localNode <- newLocalNode transport initRemoteTable
706725 runProcess localNode $ do
707726 -- Create a typed channel
@@ -723,7 +742,7 @@ testReceiveChanTimeout transport = do
723742
724743 liftIO $ putMVar done ()
725744
726− forkIO $ do
745+ forkTry $ do
727746 localNode <- newLocalNode transport initRemoteTable
728747 runProcess localNode $ do
729748 sp <- liftIO $ readMVar sendPort
@@ -736,50 +755,56 @@ testReceiveChanTimeout transport = do
736755
737756 takeMVar done
738757
739−main :: IO ()
740−main = do
741− Right (transport, transportInternals) <- createTransportExposeInternals "127.0.0.1" "8080" defaultTCPParameters
742− runTests
743− [ ("Ping", testPing transport)
744− , ("Math", testMath transport)
745− , ("Timeout", testTimeout transport)
746− , ("Timeout0", testTimeout0 transport)
747− , ("SendToTerminated", testSendToTerminated transport)
748− , ("TypedChannnels", testTypedChannels transport)
749− , ("MergeChannels", testMergeChannels transport)
750− , ("Terminate", testTerminate transport)
751− , ("Registry", testRegistry transport)
752− , ("RemoteRegistry", testRemoteRegistry transport)
753− , ("SpawnLocal", testSpawnLocal transport)
754− , ("MatchAny", testMatchAny transport)
758+tests :: (NT.Transport, TransportInternals) -> [Test]
759+tests (transport, transportInternals) = [
760+ testGroup "Basic features" [
761+ testCase "Ping" (testPing transport)
762+ , testCase "Math" (testMath transport)
763+ , testCase "Timeout" (testTimeout transport)
764+ , testCase "Timeout0" (testTimeout0 transport)
765+ , testCase "SendToTerminated" (testSendToTerminated transport)
766+ , testCase "TypedChannnels" (testTypedChannels transport)
767+ , testCase "MergeChannels" (testMergeChannels transport)
768+ , testCase "Terminate" (testTerminate transport)
769+ , testCase "Registry" (testRegistry transport)
770+ , testCase "RemoteRegistry" (testRemoteRegistry transport)
771+ , testCase "SpawnLocal" (testSpawnLocal transport)
772+ , testCase "MatchAny" (testMatchAny transport)
773+ , testCase "ReceiveChanTimeout" (testReceiveChanTimeout transport)
774+ ]
775+ , testGroup "Monitoring and Linking" [
755776 -- Monitoring processes
756777 --
757778 -- The "missing" combinations in the list below don't make much sense, as
758779 -- we cannot guarantee that the monitor reply or link exception will not
759780 -- happen before the unmonitor or unlink
760− , ("MonitorUnreachable", testMonitorUnreachable transport True False)
761− , ("MonitorNormalTermination", testMonitorNormalTermination transport True False)
762− , ("MonitorAbnormalTermination", testMonitorAbnormalTermination transport True False)
763− , ("MonitorLocalDeadProcess", testMonitorLocalDeadProcess transport True False)
764− , ("MonitorRemoteDeadProcess", testMonitorRemoteDeadProcess transport True False)
765− , ("MonitorDisconnect", testMonitorDisconnect transport True False)
766− , ("LinkUnreachable", testMonitorUnreachable transport False False)
767− , ("LinkNormalTermination", testMonitorNormalTermination transport False False)
768− , ("LinkAbnormalTermination", testMonitorAbnormalTermination transport False False)
769− , ("LinkLocalDeadProcess", testMonitorLocalDeadProcess transport False False)
770− , ("LinkRemoteDeadProcess", testMonitorRemoteDeadProcess transport False False)
771− , ("LinkDisconnect", testMonitorDisconnect transport False False)
772− , ("UnmonitorNormalTermination", testMonitorNormalTermination transport True True)
773− , ("UnmonitorAbnormalTermination", testMonitorAbnormalTermination transport True True)
774− , ("UnmonitorDisconnect", testMonitorDisconnect transport True True)
775− , ("UnlinkNormalTermination", testMonitorNormalTermination transport False True)
776− , ("UnlinkAbnormalTermination", testMonitorAbnormalTermination transport False True)
777− , ("UnlinkDisconnect", testMonitorDisconnect transport False True)
781+ testCase "MonitorUnreachable" (testMonitorUnreachable transport True False)
782+ , testCase "MonitorNormalTermination" (testMonitorNormalTermination transport True False)
783+ , testCase "MonitorAbnormalTermination" (testMonitorAbnormalTermination transport True False)
784+ , testCase "MonitorLocalDeadProcess" (testMonitorLocalDeadProcess transport True False)
785+ , testCase "MonitorRemoteDeadProcess" (testMonitorRemoteDeadProcess transport True False)
786+ , testCase "MonitorDisconnect" (testMonitorDisconnect transport True False)
787+ , testCase "LinkUnreachable" (testMonitorUnreachable transport False False)
788+ , testCase "LinkNormalTermination" (testMonitorNormalTermination transport False False)
789+ , testCase "LinkAbnormalTermination" (testMonitorAbnormalTermination transport False False)
790+ , testCase "LinkLocalDeadProcess" (testMonitorLocalDeadProcess transport False False)
791+ , testCase "LinkRemoteDeadProcess" (testMonitorRemoteDeadProcess transport False False)
792+ , testCase "LinkDisconnect" (testMonitorDisconnect transport False False)
793+ , testCase "UnmonitorNormalTermination" (testMonitorNormalTermination transport True True)
794+ , testCase "UnmonitorAbnormalTermination" (testMonitorAbnormalTermination transport True True)
795+ , testCase "UnmonitorDisconnect" (testMonitorDisconnect transport True True)
796+ , testCase "UnlinkNormalTermination" (testMonitorNormalTermination transport False True)
797+ , testCase "UnlinkAbnormalTermination" (testMonitorAbnormalTermination transport False True)
798+ , testCase "UnlinkDisconnect" (testMonitorDisconnect transport False True)
778799 -- Monitoring nodes and channels
779− , ("MonitorNode", testMonitorNode transport)
780− , ("MonitorChannel", testMonitorChannel transport)
800+ , testCase "MonitorNode" (testMonitorNode transport)
801+ , testCase "MonitorChannel" (testMonitorChannel transport)
781802 -- Reconnect
782− , ("Reconnect", testReconnect transport transportInternals)
783− -- ReceiveChanTimeout
784− , ("ReceiveChanTimeout", testReceiveChanTimeout transport)
803+ , testCase "Reconnect" (testReconnect transport transportInternals)
785804 ]
805+ ]
806+
807+main :: IO ()
808+main = do
809+ Right transport <- createTransportExposeInternals "127.0.0.1" "8080" defaultTCPParameters
810+ defaultMain (tests transport)