async-2.1.1: test/test-async.hs
{-# LANGUAGE ScopedTypeVariables,DeriveDataTypeable #-}
module Main where
import Test.Framework (defaultMain, testGroup)
import Test.Framework.Providers.HUnit
import Test.HUnit
import Control.Concurrent.Async
import Control.Exception
import Data.IORef
import Data.Typeable
import Control.Concurrent
import Control.Monad
import Data.List (sort)
import Data.Maybe
import Prelude hiding (catch)
main = defaultMain tests
tests = [
testCase "async_wait" async_wait
, testCase "async_waitCatch" async_waitCatch
, testCase "async_exwait" async_exwait
, testCase "async_exwaitCatch" async_exwaitCatch
, testCase "withasync_waitCatch" withasync_waitCatch
, testCase "withasync_wait2" withasync_wait2
, testGroup "async_cancel_rep" $
replicate 1000 $
testCase "async_cancel" async_cancel
, testCase "async_poll" async_poll
, testCase "async_poll2" async_poll2
, testCase "withasync_waitCatch_blocked" withasync_waitCatch_blocked
, testGroup "children surviving too long"
[ testCase "concurrently+success" concurrently_success
, testCase "concurrently+failure" concurrently_failure
, testCase "race+success" race_success
, testCase "race+failure" race_failure
, testCase "cancel" cancel_survive
, testCase "withAsync" withasync_survive
]
, testCase "concurrently_" case_concurrently_
, testCase "replicateConcurrently_" case_replicateConcurrently
, testCase "replicateConcurrently" case_replicateConcurrently_
]
value = 42 :: Int
data TestException = TestException deriving (Eq,Show,Typeable)
instance Exception TestException
async_waitCatch :: Assertion
async_waitCatch = do
a <- async (return value)
r <- waitCatch a
case r of
Left _ -> assertFailure ""
Right e -> e @?= value
async_wait :: Assertion
async_wait = do
a <- async (return value)
r <- wait a
assertEqual "async_wait" r value
async_exwaitCatch :: Assertion
async_exwaitCatch = do
a <- async (throwIO TestException)
r <- waitCatch a
case r of
Left e -> fromException e @?= Just TestException
Right _ -> assertFailure ""
async_exwait :: Assertion
async_exwait = do
a <- async (throwIO TestException)
(wait a >> assertFailure "") `catch` \e -> e @?= TestException
withasync_waitCatch :: Assertion
withasync_waitCatch = do
withAsync (return value) $ \a -> do
r <- waitCatch a
case r of
Left _ -> assertFailure ""
Right e -> e @?= value
withasync_wait2 :: Assertion
withasync_wait2 = do
a <- withAsync (threadDelay 1000000) $ return
r <- waitCatch a
case r of
Left e -> fromException e @?= Just ThreadKilled
Right _ -> assertFailure ""
async_cancel :: Assertion
async_cancel = do
a <- async (return value)
cancelWith a TestException
r <- waitCatch a
case r of
Left e -> fromException e @?= Just TestException
Right r -> r @?= value
async_poll :: Assertion
async_poll = do
a <- async (threadDelay 1000000)
r <- poll a
when (isJust r) $ assertFailure ""
r <- poll a -- poll twice, just to check we don't deadlock
when (isJust r) $ assertFailure ""
async_poll2 :: Assertion
async_poll2 = do
a <- async (return value)
wait a
r <- poll a
when (isNothing r) $ assertFailure ""
r <- poll a -- poll twice, just to check we don't deadlock
when (isNothing r) $ assertFailure ""
withasync_waitCatch_blocked :: Assertion
withasync_waitCatch_blocked = do
r <- withAsync (newEmptyMVar >>= takeMVar) waitCatch
case r of
Left e ->
case fromException e of
Just BlockedIndefinitelyOnMVar -> return ()
Nothing -> assertFailure $ show e
Right () -> assertFailure ""
concurrently_success :: Assertion
concurrently_success = do
finalRes <- newIORef "never filled"
baton <- newEmptyMVar
let quick = return ()
slow = threadDelay 10000 `finally` do
threadDelay 10000
writeIORef finalRes "slow"
putMVar baton ()
_ <- concurrently quick slow
writeIORef finalRes "parent"
takeMVar baton
res <- readIORef finalRes
res @?= "parent"
concurrently_failure :: Assertion
concurrently_failure = do
finalRes <- newIORef "never filled"
let quick = error "a quick death"
slow = threadDelay 10000 `finally` do
threadDelay 10000
writeIORef finalRes "slow"
_ :: Either SomeException ((), ()) <- try (concurrently quick slow)
writeIORef finalRes "parent"
threadDelay 1000000 -- not using the baton, can lead to deadlock detection
res <- readIORef finalRes
res @?= "parent"
race_success :: Assertion
race_success = do
finalRes <- newIORef "never filled"
let quick = return ()
slow = threadDelay 10000 `finally` do
threadDelay 10000
writeIORef finalRes "slow"
race_ quick slow
writeIORef finalRes "parent"
threadDelay 1000000 -- not using the baton, can lead to deadlock detection
res <- readIORef finalRes
res @?= "parent"
race_failure :: Assertion
race_failure = do
finalRes <- newIORef "never filled"
baton <- newEmptyMVar
let quick = error "a quick death"
slow restore = restore (threadDelay 10000) `finally` do
threadDelay 10000
writeIORef finalRes "slow"
putMVar baton ()
_ :: Either SomeException () <-
try $ mask $ \restore ->
race_ quick (slow restore)
writeIORef finalRes "parent"
takeMVar baton
res <- readIORef finalRes
res @?= "parent"
cancel_survive :: Assertion
cancel_survive = do
finalRes <- newIORef "never filled"
a <- async $ threadDelay 10000 `finally` do
threadDelay 10000
writeIORef finalRes "child"
cancel a
writeIORef finalRes "parent"
threadDelay 1000000 -- not using the baton, can lead to deadlock detection
res <- readIORef finalRes
res @?= "parent"
withasync_survive :: Assertion
withasync_survive = do
finalRes <- newIORef "never filled"
let child = threadDelay 10000 `finally` do
threadDelay 10000
writeIORef finalRes "child"
withAsync child (\_ -> return ())
writeIORef finalRes "parent"
threadDelay 1000000 -- not using the baton, can lead to deadlock detection
res <- readIORef finalRes
res @?= "parent"
case_concurrently_ :: Assertion
case_concurrently_ = do
ref <- newIORef 0
() <- concurrently_
(atomicModifyIORef ref (\x -> (x + 1, True)))
(atomicModifyIORef ref (\x -> (x + 2, 'x')))
res <- readIORef ref
res @?= 3
case_replicateConcurrently :: Assertion
case_replicateConcurrently = do
ref <- newIORef 0
let action = atomicModifyIORef ref (\x -> (x + 1, x + 1))
resList <- replicateConcurrently 100 action
resVal <- readIORef ref
resVal @?= 100
sort resList @?= [1..100]
case_replicateConcurrently_ :: Assertion
case_replicateConcurrently_ = do
ref <- newIORef 0
let action = atomicModifyIORef ref (\x -> (x + 1, x + 1))
() <- replicateConcurrently_ 100 action
resVal <- readIORef ref
resVal @?= 100