@@ -4,12 +4,13 @@ module TestTransport where
4import Prelude hiding (catch, (>>=), (>>), return, fail)
4import Prelude hiding (catch, (>>=), (>>), return, fail)
5import TestAuxiliary (forkTry, runTests, trySome, randomThreadDelay)
5import TestAuxiliary (forkTry, runTests, trySome, randomThreadDelay)
6import Control.Concurrent (forkIO, killThread, yield)
6import Control.Concurrent (forkIO, killThread, yield)
7−import Control.Concurrent.MVar (newEmptyMVar, takeMVar, putMVar, readMVar, tryTakeMVar)
7+import Control.Concurrent.MVar (newEmptyMVar, takeMVar, putMVar, readMVar, tryTakeMVar, modifyMVar_, newMVar)
8−import Control.Exception (evaluate, throw, throwIO)
8+import Control.Exception (evaluate, throw, throwIO, bracket)
9−import Control.Monad (replicateM, replicateM_, when, guard, forM, forM_, unless)
9+import Control.Monad (replicateM, replicateM_, when, guard, forM_, unless)
10import Control.Monad.Error ()
10import Control.Monad.Error ()
11+import Control.Applicative ((<$>))
11import Network.Transport
12import Network.Transport
12−import Network.Transport.Internal (tlog)
13+import Network.Transport.Internal (tlog, tryIO, timeoutMaybe)
13import Network.Transport.Util (spawn)
14import Network.Transport.Util (spawn)
14import System.Random (randomIO)
15import System.Random (randomIO)
15import Data.ByteString (ByteString)
16import Data.ByteString (ByteString)
@@ -271,30 +272,37 @@ testCloseOneDirection transport numPings = do
272 mapM_ takeMVar [doneA, doneB]
273 mapM_ takeMVar [doneA, doneB]
274−-- | Collect a given number of events and order them by connection ID
275+-- | Collect events and order them by connection ID
275−collect :: EndPoint -> Int -> IO [(ConnectionId, [[ByteString]])]
276+collect :: EndPoint -> Maybe Int -> Maybe Int -> IO [(ConnectionId, [[ByteString]])]
276−collect endPoint numEvents = go numEvents Map.empty Map.empty
277+collect endPoint maxEvents timeout = go maxEvents Map.empty Map.empty
278 -- TODO: for more serious use of this function we'd need to make these arguments strict
279 -- TODO: for more serious use of this function we'd need to make these arguments strict
279− go 0 open closed = if Map.null open
280+ go (Just 0) open closed = finish open closed
280− then return . Map.toList . Map.map reverse $ closed
281− else fail "Open connections"
282 go n open closed = do
281 go n open closed = do
283− event <- receive endPoint
282+ mEvent <- tryIO . timeoutMaybe timeout (userError "timeout") $ receive endPoint
284− case event of
283+ case mEvent of
284+ Left _ -> finish open closed
286+ let n' = (\x -> x - 1) <$> n
285− ConnectionOpened cid _ _ ->
288+ ConnectionOpened cid _ _ ->
286− go (n - 1) (Map.insert cid [] open) closed
289+ go n' (Map.insert cid [] open) closed
287− ConnectionClosed cid ->
290+ ConnectionClosed cid ->
288− let list = Map.findWithDefault (error "Invalid ConnectionClosed") cid open in
291+ let list = Map.findWithDefault (error "Invalid ConnectionClosed") cid open in
289− go (n - 1) (Map.delete cid open) (Map.insert cid list closed)
292+ go n' (Map.delete cid open) (Map.insert cid list closed)
290− Received cid msg ->
293+ Received cid msg ->
291− go (n - 1) (Map.adjust (msg :) cid open) closed
294+ go n' (Map.adjust (msg :) cid open) closed
292− ReceivedMulticast _ _ ->
295+ ReceivedMulticast _ _ ->
293− fail "Unexpected multicast"
296+ fail "Unexpected multicast"
294− ErrorEvent _ ->
297+ ErrorEvent _ ->
295− fail "Unexpected error"
298+ fail "Unexpected error"
296− EndPointClosed ->
299+ EndPointClosed ->
297− fail "Unexpected endpoint closure"
300+ fail "Unexpected endpoint closure"
302+ finish open closed =
304+ then return . Map.toList . Map.map reverse $ closed
305+ else fail "Open connections"
299-- | Open connection, close it, then reopen it
307-- | Open connection, close it, then reopen it
300-- (In the TCP transport this means the socket will be closed, then reopened)
308-- (In the TCP transport this means the socket will be closed, then reopened)
@@ -342,7 +350,7 @@ testCloseReopen transport numPings = do
342 Right endpoint <- newEndPoint transport
350 Right endpoint <- newEndPoint transport
343 putMVar addrB (address endpoint)
351 putMVar addrB (address endpoint)
345− eventss <- collect endpoint (numRepeats * (numPings + 2))
353+ eventss <- collect endpoint (Just (numRepeats * (numPings + 2))) Nothing
347 forM_ (zip [1 .. numRepeats] eventss) $ \(i, (_, events)) -> do
355 forM_ (zip [1 .. numRepeats] eventss) $ \(i, (_, events)) -> do
348 forM_ (zip [1 .. numPings] events) $ \(j, event) -> do
356 forM_ (zip [1 .. numPings] events) $ \(j, event) -> do
@@ -368,7 +376,7 @@ testParallelConnects transport numPings = do
368 close conn
376 close conn
370 forkTry $ do
378 forkTry $ do
371− eventss <- collect endpoint (numPings * 4)
379+ eventss <- collect endpoint (Just (numPings * 4)) Nothing
372 -- Check that no pings got sent to the wrong connection
380 -- Check that no pings got sent to the wrong connection
373 forM_ eventss $ \(_, [[ping1], [ping2]]) ->
381 forM_ eventss $ \(_, [[ping1], [ping2]]) ->
374 guard (ping1 == ping2)
382 guard (ping1 == ping2)
@@ -519,7 +527,7 @@ testConnectToSelfTwice transport numPings = do
519 forkTry $ do
527 forkTry $ do
520 tlog $ "reading"
528 tlog $ "reading"
522− [(_, events1), (_, events2)] <- collect endpoint (2 * (numPings + 2))
530+ [(_, events1), (_, events2)] <- collect endpoint (Just (2 * (numPings + 2))) Nothing
523 True <- return $ events1 == replicate numPings ["pingA"]
531 True <- return $ events1 == replicate numPings ["pingA"]
524 True <- return $ events2 == replicate numPings ["pingB"]
532 True <- return $ events2 == replicate numPings ["pingB"]
@@ -826,31 +834,34 @@ testKill newTransport numThreads = do
826 Right endpoint1 <- newEndPoint transport1
834 Right endpoint1 <- newEndPoint transport1
827 Right endpoint2 <- newEndPoint transport2
835 Right endpoint2 <- newEndPoint transport2
829− Right conn <- connect endpoint1 (address endpoint2) ReliableOrdered defaultConnectHints
830− ConnectionOpened _ _ _ <- receive endpoint2
832 threads <- replicateM numThreads . forkIO $ do
837 threads <- replicateM numThreads . forkIO $ do
833− randomThreadDelay 10
838+ randomThreadDelay 100
839+ bracket (connect endpoint1 (address endpoint2) ReliableOrdered defaultConnectHints)
840+ (\(Right conn) -> randomThreadDelay 100 >> close conn)
834− Right () <- send conn ["ping"]
841+ (\(Right conn) -> randomThreadDelay 100 >> do Right () <- send conn ["ping"] ; return ())
836−
843+ numAlive <- newMVar (0 :: Int)
837− -- Kill half of those threads, and wait on the rest
845+ -- Kill half of those threads
838− killerDone <- newEmptyMVar
840− killed <- forM threads $ \tid -> do
846+ forkIO . forM_ threads $ \tid -> do
841− shouldKill <- randomIO
847+ shouldKill <- randomIO
842− when shouldKill $ randomThreadDelay 10 >> killThread tid
849+ then randomThreadDelay 600 >> killThread tid
843− return shouldKill
850+ else modifyMVar_ numAlive (return . (+ 1))
845− -- We should receive at least the pings from the threads that we didn't
852+ -- Since it is impossible to predict when the kill exactly happens, we don't
846− -- kill (we might get more, depending on when exactly the kill happens)
853+ -- know how many connects were opened and how many pings were sent. But we
847− forM_ killed $ \wasKilled -> unless wasKilled $ do
854+ -- should not have any open connections (if we do, collect will throw an
848− Received _ ["ping"] <- receive endpoint2
855+ -- error) and we should have at least the number of pings equal to the number
849− return ()
856+ -- of threads we did *not* kill
850−
857+ eventss <- collect endpoint2 Nothing (Just 1000000)
851− putMVar killerDone ()
858+ let actualPings = sum . map (length . snd) $ eventss
859+ expectedPings <- takeMVar numAlive
860+ unless (actualPings >= expectedPings) $
861+ throwIO (userError "Missing pings")
863+-- print (actualPings, expectedPings)
855-- | Set up conditions with a high likelyhood of "crossing" (for transports
866-- | Set up conditions with a high likelyhood of "crossing" (for transports
856-- that multiplex lightweight connections across heavyweight connections)
867-- that multiplex lightweight connections across heavyweight connections)