dejafu/dejafu-tests/Cases/Async.hs

188 lines
5.2 KiB
Haskell
Raw Normal View History

2015-10-26 01:53:54 +03:00
{-# LANGUAGE DeriveDataTypeable #-}
2017-09-20 22:06:22 +03:00
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}
2015-10-26 01:53:54 +03:00
module Cases.Async where
2015-10-26 01:53:54 +03:00
import Control.Concurrent.Classy.Async
2017-09-20 22:06:22 +03:00
import Control.Concurrent.Classy.CRef
import Control.Exception (AsyncException(..), Exception, SomeException, fromException)
import Control.Monad (forever, when)
import Control.Monad.Catch (finally, try)
import Control.Monad.Conc.Class hiding (threadDelay)
2017-10-11 12:22:47 +03:00
import qualified Control.Monad.Conc.Class as C
2017-09-20 22:06:22 +03:00
import Data.List (sort)
import Data.Maybe (isJust, isNothing)
import Data.Typeable (Typeable)
import Test.DejaFu (alwaysTrue)
import Test.DejaFu.Conc (ConcST)
import Common
{-
Tests from https://github.com/simonmar/async/blob/master/test/test-async.hs
The following are omitted:
* withasync_waitCatch_blocked: because dejafu does not do
BlockedIndefinitelyOnMVar
* concurrently+success, concurrently+failure, race+success,
race+failure, cancel, withAsync: because they rely on timing
-}
2015-10-26 01:53:54 +03:00
tests :: [Test]
tests =
2017-09-20 22:06:22 +03:00
[ testGroup "async"
[ testCase "async_wait" async_wait
, testCase "async_waitCatch" async_waitCatch
, testCase "async_exwait" async_exwait
, testCase "async_exwaitCatch" async_exwaitCatch
, testCase "async_cancel" async_cancel
, testCase "async_poll" async_poll
, testCase "async_poll2" async_poll2
2015-10-26 01:53:54 +03:00
]
2017-09-20 22:06:22 +03:00
, testGroup "withAsync"
[ testCase "withasync_waitCatch" withasync_waitCatch
, testCase "withasync_wait2" withasync_wait2
]
2015-10-26 01:53:54 +03:00
2017-09-20 22:06:22 +03:00
, testGroup "concurrently"
[ testCase "concurrently_" case_concurrently_
, testCase "replicateConcurrently_" case_replicateConcurrently
, testCase "replicateConcurrently" case_replicateConcurrently_
2015-10-26 01:53:54 +03:00
]
]
value :: Int
value = 42
data TestException = TestException deriving (Eq,Show,Typeable)
instance Exception TestException
2017-09-20 22:06:22 +03:00
async_waitCatch :: MonadConc m => m ()
2015-10-26 01:53:54 +03:00
async_waitCatch = do
2017-09-20 22:06:22 +03:00
a <- async (return value)
r <- waitCatch a
case r of
Left _ -> assertFailure ""
Right e -> e @?= value
2015-10-26 01:53:54 +03:00
2017-09-20 22:06:22 +03:00
async_wait :: MonadConc m => m ()
async_wait = do
a <- async (return value)
r <- wait a
assertEqual "async_wait" r value
2015-10-26 01:53:54 +03:00
2017-09-20 22:06:22 +03:00
async_exwaitCatch :: MonadConc m => m ()
2015-10-26 01:53:54 +03:00
async_exwaitCatch = do
2017-09-20 22:06:22 +03:00
a <- async (throwIO TestException)
r <- waitCatch a
case r of
Left e -> fromException e @?= Just TestException
Right _ -> assertFailure ""
async_exwait :: MonadConc m => m ()
async_exwait = do
a <- async (throwIO TestException)
(wait a >> assertFailure "") `catch` \e -> e @?= TestException
withasync_waitCatch :: MonadConc m => m ()
withasync_waitCatch = do
withAsync (return value) $ \a -> do
r <- waitCatch a
case r of
Left _ -> assertFailure ""
Right e -> e @?= value
withasync_wait2 :: MonadConc m => m ()
withasync_wait2 = do
a <- withAsync (threadDelay 1000000) $ return
2015-10-26 01:53:54 +03:00
r <- waitCatch a
2017-09-20 22:06:22 +03:00
case r of
Left e -> fromException e @?= Just ThreadKilled
Right _ -> assertFailure ""
2015-10-26 01:53:54 +03:00
2017-09-20 22:06:22 +03:00
async_cancel :: MonadConc m => m ()
2015-10-26 01:53:54 +03:00
async_cancel = do
2017-09-20 22:06:22 +03:00
a <- async (return value)
2015-10-26 01:53:54 +03:00
cancelWith a TestException
r <- waitCatch a
2017-09-20 22:06:22 +03:00
case r of
Left e -> fromException e @?= Just TestException
Right r -> r @?= value
2015-10-26 01:53:54 +03:00
2017-09-20 22:06:22 +03:00
async_poll :: MonadConc m => m ()
2015-10-26 01:53:54 +03:00
async_poll = do
2017-09-20 22:06:22 +03:00
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 ""
2015-10-26 01:53:54 +03:00
2017-09-20 22:06:22 +03:00
async_poll2 :: MonadConc m => m ()
2015-10-26 01:53:54 +03:00
async_poll2 = do
2017-09-20 22:06:22 +03:00
a <- async (return value)
2015-10-26 01:53:54 +03:00
wait a
2017-09-20 22:06:22 +03:00
r <- poll a
when (isNothing r) $ assertFailure ""
r <- poll a -- poll twice, just to check we don't deadlock
when (isNothing r) $ assertFailure ""
case_concurrently_ :: MonadConc m => m ()
case_concurrently_ = do
ref <- newCRef 0
() <- concurrently_
(atomicModifyCRef ref (\x -> (x + 1, True)))
(atomicModifyCRef ref (\x -> (x + 2, 'x')))
res <- readCRef ref
res @?= 3
case_replicateConcurrently :: MonadConc m => m ()
case_replicateConcurrently = do
ref <- newCRef 0
let action = atomicModifyCRef ref (\x -> (x + 1, x + 1))
resList <- replicateConcurrently 4 action
resVal <- readCRef ref
resVal @?= 4
sort resList @?= [1..4]
case_replicateConcurrently_ :: MonadConc m => m ()
case_replicateConcurrently_ = do
ref <- newCRef 0
let action = atomicModifyCRef ref (\x -> (x + 1, x + 1))
() <- replicateConcurrently_ 4 action
resVal <- readCRef ref
resVal @?= 4
-------------------------------------------------------------------------------
data TestFailed = TestFailed String deriving (Eq,Show,Typeable)
instance Exception TestFailed
assertFailure :: MonadConc m => String -> m b
assertFailure = throw . TestFailed
throwIO :: (Exception e, MonadConc m) => e -> m a
throwIO = throw
-- the tests use 'threadDelay' with a big delay to represent a blocked thread
threadDelay :: MonadConc m => Int -> m ()
2017-10-11 12:22:47 +03:00
threadDelay 0 = yield
threadDelay n = C.threadDelay 1 >> threadDelay (n-1)
2017-09-20 22:06:22 +03:00
(@?=) :: (Eq a, MonadConc m) => a -> a -> m ()
(@?=) = assertEqual "not equal"
assertEqual :: (Eq a, MonadConc m) => String -> a -> a -> m ()
assertEqual err a1 a2
| a1 == a2 = pure ()
| otherwise = assertFailure err
testCase :: String -> (forall t. ConcST t ()) -> Test
testCase name c = djfu name (alwaysTrue p) (try c) where
p (Right (Left (e::SomeException))) = False
p (Right _) = True
p (Left _) = False