summaryrefslogtreecommitdiff
path: root/testsuite/tests/lib/Concurrent
diff options
context:
space:
mode:
Diffstat (limited to 'testsuite/tests/lib/Concurrent')
-rw-r--r--testsuite/tests/lib/Concurrent/4876.hs19
-rw-r--r--testsuite/tests/lib/Concurrent/4876.stdout1
-rw-r--r--testsuite/tests/lib/Concurrent/Chan001.hs109
-rw-r--r--testsuite/tests/lib/Concurrent/Chan001.stdout3
-rw-r--r--testsuite/tests/lib/Concurrent/MVar001.hs148
-rw-r--r--testsuite/tests/lib/Concurrent/MVar001.stdout6
-rw-r--r--testsuite/tests/lib/Concurrent/Makefile3
-rw-r--r--testsuite/tests/lib/Concurrent/QSem001.hs93
-rw-r--r--testsuite/tests/lib/Concurrent/QSem001.stdout2
-rw-r--r--testsuite/tests/lib/Concurrent/QSemN001.hs96
-rw-r--r--testsuite/tests/lib/Concurrent/QSemN001.stdout2
-rw-r--r--testsuite/tests/lib/Concurrent/SampleVar001.hs132
-rw-r--r--testsuite/tests/lib/Concurrent/SampleVar001.stdout6
-rw-r--r--testsuite/tests/lib/Concurrent/ThreadDelay001.hs26
-rw-r--r--testsuite/tests/lib/Concurrent/all.T10
15 files changed, 656 insertions, 0 deletions
diff --git a/testsuite/tests/lib/Concurrent/4876.hs b/testsuite/tests/lib/Concurrent/4876.hs
new file mode 100644
index 0000000000..68c2a871b8
--- /dev/null
+++ b/testsuite/tests/lib/Concurrent/4876.hs
@@ -0,0 +1,19 @@
+import System.Random
+import Control.Concurrent.SampleVar
+import Control.Concurrent
+import Control.Monad
+
+produce, consume :: SampleVar Int -> IO ()
+produce svar = do
+ b <- isEmptySampleVar svar
+ if b then writeSampleVar svar 3 else return ()
+
+consume svar = readSampleVar svar >>= print
+
+main = do
+ svar <- newEmptySampleVar
+ m <- newEmptyMVar
+ forkIO $ consume svar >> putMVar m ()
+ threadDelay 100000 -- 100 ms
+ produce svar
+ takeMVar m -- deadlocked before the fix in #4876
diff --git a/testsuite/tests/lib/Concurrent/4876.stdout b/testsuite/tests/lib/Concurrent/4876.stdout
new file mode 100644
index 0000000000..00750edc07
--- /dev/null
+++ b/testsuite/tests/lib/Concurrent/4876.stdout
@@ -0,0 +1 @@
+3
diff --git a/testsuite/tests/lib/Concurrent/Chan001.hs b/testsuite/tests/lib/Concurrent/Chan001.hs
new file mode 100644
index 0000000000..e4b668ac48
--- /dev/null
+++ b/testsuite/tests/lib/Concurrent/Chan001.hs
@@ -0,0 +1,109 @@
+import Debug.QuickCheck
+import System.IO.Unsafe
+import Control.Concurrent.Chan
+import Control.Concurrent
+import Control.Monad
+
+data Action = NewChan | ReadChan | WriteChan Int | IsEmptyChan | ReturnInt Int
+ | ReturnBool Bool
+ deriving (Eq,Show)
+
+
+main = do
+ t <- myThreadId
+ forkIO (threadDelay 1000000 >> killThread t)
+ -- just in case we deadlock
+ testChan
+
+testChan :: IO ()
+testChan = do
+ quickCheck prop_NewIs_NewRet
+ quickCheck prop_NewWriteIs_NewRet
+ quickCheck prop_NewWriteRead_NewRet
+
+
+prop_NewIs_NewRet =
+ [NewChan,IsEmptyChan] =^ [NewChan,ReturnBool True]
+
+prop_NewWriteIs_NewRet n =
+ [NewChan,WriteChan n,IsEmptyChan] =^ [NewChan,WriteChan n,ReturnBool False]
+
+prop_NewWriteRead_NewRet n =
+ [NewChan,WriteChan n,ReadChan] =^ [NewChan,ReturnInt n]
+
+
+perform :: [Action] -> IO ([Bool],[Int])
+perform [] = return ([],[])
+
+perform (a:as) =
+ case a of
+ ReturnInt v -> liftM (\(b,l) -> (b,v:l)) (perform as)
+ ReturnBool v -> liftM (\(b,l) -> (v:b,l)) (perform as)
+ NewChan -> newChan >>= \chan -> perform' chan as
+ _ -> error $ "Please use NewChan as first action"
+
+
+perform' :: Chan Int -> [Action] -> IO ([Bool],[Int])
+perform' _ [] = return ([],[])
+
+perform' chan (a:as) =
+ case a of
+ ReturnInt v -> liftM (\(b,l) -> (b,v:l)) (perform' chan as)
+ ReturnBool v -> liftM (\(b,l) -> (v:b,l)) (perform' chan as)
+ ReadChan -> liftM2 (\v (b,l) -> (b,v:l)) (readChan chan)
+ (perform' chan as)
+ WriteChan n -> writeChan chan n >> perform' chan as
+ IsEmptyChan -> liftM2 (\v (b,l) -> (v:b,l)) (isEmptyChan chan)
+ (perform' chan as)
+ _ -> error $ "If you want to use " ++ show a
+ ++ " please use the =^ operator"
+
+
+actions :: Gen [Action]
+actions =
+ liftM (NewChan:) (actions' 0)
+
+
+actions' :: Int -> Gen [Action]
+actions' contents =
+ oneof ([return [],
+ liftM (IsEmptyChan:) (actions' contents),
+ liftM2 (:) (liftM WriteChan arbitrary) (actions' (contents+1))]
+ ++
+ if contents==0
+ then []
+ else [liftM (ReadChan:) (actions' (contents-1))])
+
+
+(=^) :: [Action] -> [Action] -> Property
+c =^ c' =
+ forAll (actions' (delta 0 c))
+ (\suff -> observe c suff == observe c' suff)
+ where observe x suff = unsafePerformIO (perform (x++suff))
+
+
+(^=^) :: [Action] -> [Action] -> Property
+c ^=^ c' =
+ forAll actions
+ (\pref -> forAll (actions' (delta 0 (pref++c)))
+ (\suff -> observe c pref suff ==
+ observe c' pref suff))
+ where observe x pref suff = unsafePerformIO (perform (pref++x++suff))
+
+
+delta :: Int -> [Action] -> Int
+delta i [] = i
+
+delta i (ReturnInt _:as) = delta i as
+
+delta i (ReturnBool _:as) = delta i as
+
+delta _ (NewChan:as) = delta 0 as
+
+delta i (WriteChan _:as) = delta (i+1) as
+
+delta i (ReadChan:as) = delta (if i==0
+ then error "read on empty Chan"
+ else i-1) as
+
+delta i (IsEmptyChan:as) = delta i as
diff --git a/testsuite/tests/lib/Concurrent/Chan001.stdout b/testsuite/tests/lib/Concurrent/Chan001.stdout
new file mode 100644
index 0000000000..53bfa8a381
--- /dev/null
+++ b/testsuite/tests/lib/Concurrent/Chan001.stdout
@@ -0,0 +1,3 @@
+0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899OK, passed 100 tests.
+0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899OK, passed 100 tests.
+0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899OK, passed 100 tests.
diff --git a/testsuite/tests/lib/Concurrent/MVar001.hs b/testsuite/tests/lib/Concurrent/MVar001.hs
new file mode 100644
index 0000000000..f787470c51
--- /dev/null
+++ b/testsuite/tests/lib/Concurrent/MVar001.hs
@@ -0,0 +1,148 @@
+import Debug.QuickCheck
+import System.IO.Unsafe
+import Control.Concurrent.MVar
+import Control.Concurrent
+import Control.Monad
+
+
+data Action = NewEmptyMVar | NewMVar Int | TakeMVar | ReadMVar | PutMVar Int
+ | SwapMVar Int | IsEmptyMVar | ReturnInt Int | ReturnBool Bool
+ deriving (Eq,Show)
+
+main = do
+ t <- myThreadId
+ forkIO (threadDelay 1000000 >> killThread t)
+ -- just in case we deadlock
+ testMVar
+
+testMVar :: IO ()
+testMVar = do
+ quickCheck prop_NewEIs_NewERet
+ quickCheck prop_NewIs_NewRet
+ quickCheck prop_NewTake_NewRet
+ quickCheck prop_NewEPutTake_NewERet
+ quickCheck prop_NewRead_NewRet
+ quickCheck prop_NewSwap_New
+
+
+prop_NewEIs_NewERet =
+ [NewEmptyMVar,IsEmptyMVar] =^ [NewEmptyMVar,ReturnBool True]
+
+prop_NewIs_NewRet n =
+ [NewMVar n,IsEmptyMVar] =^ [NewMVar n,ReturnBool False]
+
+prop_NewTake_NewRet n =
+ [NewMVar n,TakeMVar] =^ [NewEmptyMVar,ReturnInt n]
+
+prop_NewEPutTake_NewERet n =
+ [NewEmptyMVar,PutMVar n,TakeMVar] =^
+ [NewEmptyMVar,ReturnInt n]
+
+prop_NewRead_NewRet n =
+ [NewMVar n,ReadMVar] =^ [NewMVar n,ReturnInt n]
+
+prop_NewSwap_New m n =
+ [NewMVar m,SwapMVar n] =^ [NewMVar n]
+
+
+perform :: [Action] -> IO ([Bool],[Int])
+perform [] = return ([],[])
+
+perform (a:as) =
+ case a of
+ ReturnInt v -> liftM (\(b,l) -> (b,v:l)) (perform as)
+ ReturnBool v -> liftM (\(b,l) -> (v:b,l)) (perform as)
+ NewEmptyMVar -> newEmptyMVar >>= \mv -> perform' mv as
+ NewMVar n -> newMVar n >>= \mv -> perform' mv as
+ _ -> error $ "Please use NewMVar or NewEmptyMVar as first "
+ ++ "action"
+
+
+perform' :: MVar Int -> [Action] -> IO ([Bool],[Int])
+perform' _ [] = return ([],[])
+
+perform' mv (a:as) =
+ case a of
+ ReturnInt v -> liftM (\(b,l) -> (b,v:l)) (perform' mv as)
+ ReturnBool v -> liftM (\(b,l) -> (v:b,l)) (perform' mv as)
+ TakeMVar -> liftM2 (\v (b,l) -> (b,v:l)) (takeMVar mv)
+ (perform' mv as)
+ ReadMVar -> liftM2 (\v (b,l) -> (b,v:l)) (readMVar mv)
+ (perform' mv as)
+ PutMVar n -> putMVar mv n >> perform' mv as
+ SwapMVar n -> swapMVar mv n >> perform' mv as
+ IsEmptyMVar -> liftM2 (\v (b,l) -> (v:b,l)) (isEmptyMVar mv)
+ (perform' mv as)
+ _ -> error $ "If you want to use " ++ show a
+ ++ " please use the =^ operator"
+
+
+actions :: Gen [Action]
+actions = do
+ oneof [liftM (NewEmptyMVar:) (actions' True),
+ liftM2 (:) (liftM NewMVar arbitrary) (actions' False)]
+
+
+actions' :: Bool -> Gen [Action]
+actions' empty =
+ oneof ([return [],
+ liftM (IsEmptyMVar:) (actions' empty)] ++
+ if empty
+ then [liftM2 (:) (liftM PutMVar arbitrary) (actions' False)]
+ else []
+ ++
+ if empty
+ then []
+ else [liftM (TakeMVar:) (actions' True)]
+ ++
+ if empty
+ then []
+ else [liftM (ReadMVar:) (actions' False)]
+ ++
+ if empty
+ then []
+ else [liftM2 (:) (liftM SwapMVar arbitrary) (actions' False)] )
+
+
+(=^) :: [Action] -> [Action] -> Property
+c =^ c' =
+ forAll (actions' (delta True c))
+ (\suff -> observe c suff == observe c' suff)
+ where observe x suff = unsafePerformIO (perform (x++suff))
+
+
+(^=^) :: [Action] -> [Action] -> Property
+c ^=^ c' =
+ forAll actions
+ (\pref -> forAll (actions' (delta True (pref++c)))
+ (\suff -> observe c pref suff ==
+ observe c' pref suff))
+ where observe x pref suff = unsafePerformIO (perform (pref++x++suff))
+
+
+delta :: Bool -> [Action] -> Bool
+delta b [] = b
+
+delta b (ReturnInt _:as) = delta b as
+
+delta b (ReturnBool _:as) = delta b as
+
+delta _ (NewEmptyMVar:as) = delta True as
+
+delta _ (NewMVar _:as) = delta False as
+
+delta b (TakeMVar:as) = delta (if b
+ then error "take on empty MVar"
+ else True) as
+
+delta b (ReadMVar:as) = delta (if b
+ then error "read on empty MVar"
+ else False) as
+
+delta _ (PutMVar _:as) = delta False as
+
+delta b (SwapMVar _:as) = delta (if b
+ then error "swap on empty MVar"
+ else False) as
+
+delta b (IsEmptyMVar:as) = delta b as
diff --git a/testsuite/tests/lib/Concurrent/MVar001.stdout b/testsuite/tests/lib/Concurrent/MVar001.stdout
new file mode 100644
index 0000000000..65be56c733
--- /dev/null
+++ b/testsuite/tests/lib/Concurrent/MVar001.stdout
@@ -0,0 +1,6 @@
+0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899OK, passed 100 tests.
+0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899OK, passed 100 tests.
+0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899OK, passed 100 tests.
+0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899OK, passed 100 tests.
+0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899OK, passed 100 tests.
+0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899OK, passed 100 tests.
diff --git a/testsuite/tests/lib/Concurrent/Makefile b/testsuite/tests/lib/Concurrent/Makefile
new file mode 100644
index 0000000000..9101fbd40a
--- /dev/null
+++ b/testsuite/tests/lib/Concurrent/Makefile
@@ -0,0 +1,3 @@
+TOP=../../..
+include $(TOP)/mk/boilerplate.mk
+include $(TOP)/mk/test.mk
diff --git a/testsuite/tests/lib/Concurrent/QSem001.hs b/testsuite/tests/lib/Concurrent/QSem001.hs
new file mode 100644
index 0000000000..1f255997e7
--- /dev/null
+++ b/testsuite/tests/lib/Concurrent/QSem001.hs
@@ -0,0 +1,93 @@
+import Debug.QuickCheck
+import System.IO.Unsafe
+import Control.Concurrent.QSem
+import Control.Concurrent
+import Control.Monad
+
+
+main = do
+ t <- myThreadId
+ forkIO (threadDelay 1000000 >> killThread t)
+ -- just in case we deadlock
+ testQSem
+
+data Action = NewQSem Int | SignalQSem | WaitQSem
+ deriving (Eq,Show)
+
+
+testQSem :: IO ()
+testQSem = do
+ quietCheck prop_SignalWait
+ quietCheck prop_WaitSignal
+
+quietCheck = check defaultConfig{configEvery = \n args -> ""}
+
+prop_SignalWait n =
+ n>=0 ==> [NewQSem n,SignalQSem,WaitQSem] =^ [NewQSem n]
+
+prop_WaitSignal n =
+ n>=1 ==> [NewQSem n,WaitQSem,SignalQSem] =^ [NewQSem n]
+
+
+perform :: [Action] -> IO ()
+perform [] = return ()
+
+perform (a:as) =
+ case a of
+ NewQSem n -> newQSem n >>= \qs -> perform' qs as
+ _ -> error $ "Please use NewQSem as first action" ++ show a
+
+
+perform' :: QSem -> [Action] -> IO ()
+perform' _ [] = return ()
+
+perform' qs (a:as) =
+ case a of
+ SignalQSem -> signalQSem qs >> perform' qs as
+ WaitQSem -> waitQSem qs >> perform' qs as
+ _ -> error $ "If you want to use " ++ show a
+ ++ " please use the =^ operator"
+
+
+actions :: Gen [Action]
+actions = do
+ i <- arbitrary
+ liftM (NewQSem i:) (actions' i)
+
+
+actions' :: Int -> Gen [Action]
+actions' quantity =
+ oneof ([return [],
+ liftM (SignalQSem:) (actions' (quantity+1))] ++
+ if quantity<=0
+ then []
+ else [liftM (WaitQSem:) (actions' (quantity-1))])
+
+
+(=^) :: [Action] -> [Action] -> Property
+c =^ c' =
+ forAll (actions' (delta 0 c))
+ (\suff -> observe c suff == observe c' suff)
+ where observe x suff = unsafePerformIO (perform (x++suff))
+
+
+(^=^) :: [Action] -> [Action] -> Property
+c ^=^ c' =
+ forAll actions
+ (\pref -> forAll (actions' (delta 0 (pref++c)))
+ (\suff -> observe c pref suff ==
+ observe c' pref suff))
+ where observe x pref suff = unsafePerformIO (perform (pref++x++suff))
+
+
+delta :: Int -> [Action] -> Int
+delta i [] = i
+
+delta _ (NewQSem i:as) = delta i as
+
+delta i (SignalQSem:as) = delta (i+1) as
+
+delta i (WaitQSem:as) = delta (if i<=0
+ then error "wait on 'empty' QSem"
+ else i-1) as
+
diff --git a/testsuite/tests/lib/Concurrent/QSem001.stdout b/testsuite/tests/lib/Concurrent/QSem001.stdout
new file mode 100644
index 0000000000..7288d19270
--- /dev/null
+++ b/testsuite/tests/lib/Concurrent/QSem001.stdout
@@ -0,0 +1,2 @@
+OK, passed 100 tests.
+OK, passed 100 tests.
diff --git a/testsuite/tests/lib/Concurrent/QSemN001.hs b/testsuite/tests/lib/Concurrent/QSemN001.hs
new file mode 100644
index 0000000000..c31d6a6964
--- /dev/null
+++ b/testsuite/tests/lib/Concurrent/QSemN001.hs
@@ -0,0 +1,96 @@
+import Debug.QuickCheck
+import System.IO.Unsafe
+import Control.Concurrent.QSemN
+import Control.Concurrent
+import Control.Monad
+
+
+main = do
+ t <- myThreadId
+ forkIO (threadDelay 1000000 >> killThread t)
+ -- just in case we deadlock
+ testQSemN
+
+data Action = NewQSemN Int | SignalQSemN Int | WaitQSemN Int
+ deriving (Eq,Show)
+
+
+testQSemN :: IO ()
+testQSemN = do
+ quietCheck prop_SignalWait
+ quietCheck prop_WaitSignal
+
+quietCheck = check defaultConfig{configEvery = \n args -> ""}
+
+
+prop_SignalWait l m n = l+m>=n ==>
+ [NewQSemN l,SignalQSemN m,WaitQSemN n] =^ [NewQSemN (l+m-n)]
+
+prop_WaitSignal l m n = l>=m ==>
+ [NewQSemN l,WaitQSemN m,SignalQSemN n] =^ [NewQSemN (l-m+n)]
+
+
+perform :: [Action] -> IO [Int]
+perform [] = return []
+
+perform (a:as) =
+ case a of
+ NewQSemN n -> newQSemN n >>= \qs -> perform' qs as
+ _ -> error $ "Please use NewQSemN as first action" ++ show a
+
+
+perform' :: QSemN -> [Action] -> IO [Int]
+perform' _ [] = return []
+
+perform' qs (a:as) =
+ case a of
+ SignalQSemN n -> signalQSemN qs n >> perform' qs as
+ WaitQSemN n -> waitQSemN qs n >> perform' qs as
+ _ -> error $ "If you want to use " ++ show a
+ ++ " please use the =^ operator"
+
+
+actions :: Gen [Action]
+actions = do
+ i <- arbitrary
+ liftM (NewQSemN i:) (actions' i)
+
+
+actions' :: Int -> Gen [Action]
+actions' quantity =
+ oneof ([return [],
+ do i<- choose (0,maxBound)
+ liftM (SignalQSemN i:) (actions' (quantity+i))] ++
+ if quantity<=0
+ then []
+ else [do i<- choose (0,quantity)
+ liftM (WaitQSemN i:) (actions' (quantity-i))])
+
+
+(=^) :: [Action] -> [Action] -> Property
+c =^ c' =
+ forAll (actions' (delta 0 c))
+ (\suff -> observe c suff == observe c' suff)
+ where observe x suff = unsafePerformIO (perform (x++suff))
+
+
+(^=^) :: [Action] -> [Action] -> Property
+c ^=^ c' =
+ forAll actions
+ (\pref -> forAll (actions' (delta 0 (pref++c)))
+ (\suff -> observe c pref suff ==
+ observe c' pref suff))
+ where observe x pref suff = unsafePerformIO (perform (pref++x++suff))
+
+
+delta :: Int -> [Action] -> Int
+delta i [] = i
+
+delta _ (NewQSemN i:as) = delta i as
+
+delta i (SignalQSemN n:as) = delta (i+n) as
+
+delta i (WaitQSemN n:as) = delta (if i<n
+ then error "wait on 'empty' QSemN"
+ else i-n) as
+
diff --git a/testsuite/tests/lib/Concurrent/QSemN001.stdout b/testsuite/tests/lib/Concurrent/QSemN001.stdout
new file mode 100644
index 0000000000..7288d19270
--- /dev/null
+++ b/testsuite/tests/lib/Concurrent/QSemN001.stdout
@@ -0,0 +1,2 @@
+OK, passed 100 tests.
+OK, passed 100 tests.
diff --git a/testsuite/tests/lib/Concurrent/SampleVar001.hs b/testsuite/tests/lib/Concurrent/SampleVar001.hs
new file mode 100644
index 0000000000..def86c5d54
--- /dev/null
+++ b/testsuite/tests/lib/Concurrent/SampleVar001.hs
@@ -0,0 +1,132 @@
+-------------------------------------------------------------------------------
+-- Module : SampleVarTest
+-------------------------------------------------------------------------------
+
+import Debug.QuickCheck
+import System.IO.Unsafe
+import Control.Concurrent
+import Control.Concurrent.SampleVar
+import Control.Monad
+
+
+data Action = NewEmptySampleVar | NewSampleVar Int | EmptySampleVar
+ | ReadSampleVar | WriteSampleVar Int | IsEmptySampleVar
+ | ReturnInt Int | ReturnBool Bool
+ deriving (Eq,Show)
+
+
+main = do
+ t <- myThreadId
+ forkIO (threadDelay 1000000 >> killThread t)
+ -- just in case we deadlock
+ testSampleVar
+
+testSampleVar :: IO ()
+testSampleVar = do
+ quickCheck prop_NewEIs_NewERet
+ quickCheck prop_NewIs_NewRet
+ quickCheck prop_NewRead_NewRet
+ quickCheck prop_NewEWriteRead_NewERet
+ quickCheck prop_WriteEmpty_Empty
+ quickCheck prop_WriteRead_Ret
+
+
+
+perform :: [Action] -> IO ([Bool],[Int])
+perform [] = return ([],[])
+
+perform (a:as) =
+ case a of
+ ReturnInt v -> liftM (\(b,l) -> (b,v:l)) (perform as)
+ ReturnBool v -> liftM (\(b,l) -> (v:b,l)) (perform as)
+ NewEmptySampleVar -> newEmptySampleVar >>= \sv -> perform' sv as
+ NewSampleVar n -> newSampleVar n >>= \sv -> perform' sv as
+
+
+perform' :: SampleVar Int -> [Action] -> IO ([Bool],[Int])
+perform' _ [] = return ([],[])
+
+perform' sv (a:as) =
+ case a of
+ ReturnInt v -> liftM (\(b,l) -> (b,v:l)) (perform' sv as)
+ ReturnBool v -> liftM (\(b,l) -> (v:b,l)) (perform' sv as)
+ EmptySampleVar -> emptySampleVar sv >> perform' sv as
+ ReadSampleVar -> liftM2 (\v (b,l) -> (b,v:l)) (readSampleVar sv)
+ (perform' sv as)
+ WriteSampleVar n -> writeSampleVar sv n >> perform' sv as
+ IsEmptySampleVar -> liftM2 (\v (b,l) -> (v:b,l)) (isEmptySampleVar sv)
+ (perform' sv as)
+
+
+actions :: Gen [Action]
+actions = do
+ oneof [liftM (NewEmptySampleVar:) (actions' True),
+ liftM2 (:) (liftM NewSampleVar arbitrary) (actions' False)]
+
+
+actions' :: Bool -> Gen [Action]
+actions' empty =
+ oneof ([return [],
+ liftM (IsEmptySampleVar:) (actions' empty),
+ liftM (EmptySampleVar:) (actions' True),
+ liftM2 (:) (liftM WriteSampleVar arbitrary) (actions' False)] ++
+ if empty
+ then []
+ else [liftM (ReadSampleVar:) (actions' True)])
+
+
+(=^) :: [Action] -> [Action] -> Property
+c =^ c' =
+ forAll (actions' (delta True c))
+ (\suff -> observe c suff == observe c' suff)
+ where observe x suff = unsafePerformIO (perform (x++suff))
+
+
+(^=^) :: [Action] -> [Action] -> Property
+c ^=^ c' =
+ forAll actions
+ (\pref -> forAll (actions' (delta True (pref++c)))
+ (\suff -> observe c pref suff ==
+ observe c' pref suff))
+ where observe x pref suff = unsafePerformIO (perform (pref++x++suff))
+
+
+delta :: Bool -> [Action] -> Bool
+delta b [] = b
+
+delta b (ReturnInt _:as) = delta b as
+
+delta b (ReturnBool _:as) = delta b as
+
+delta _ (NewEmptySampleVar:as) = delta True as
+
+delta _ (NewSampleVar _:as) = delta False as
+
+delta _ (EmptySampleVar:as) = delta True as
+
+delta b (ReadSampleVar:as) = delta (if b
+ then error "read on empty SampleVar"
+ else True) as
+delta _ (WriteSampleVar _:as) = delta False as
+
+delta b (IsEmptySampleVar:as) = delta b as
+
+
+prop_NewEIs_NewERet =
+ [NewEmptySampleVar,IsEmptySampleVar] =^ [NewEmptySampleVar,ReturnBool True]
+
+prop_NewIs_NewRet n =
+ [NewSampleVar n,IsEmptySampleVar] =^ [NewSampleVar n,ReturnBool False]
+
+prop_NewRead_NewRet n =
+ [NewSampleVar n,ReadSampleVar] =^ [NewEmptySampleVar,ReturnInt n]
+
+prop_NewEWriteRead_NewERet n =
+ [NewEmptySampleVar,WriteSampleVar n,ReadSampleVar] =^
+ [NewEmptySampleVar,ReturnInt n]
+
+prop_WriteEmpty_Empty n =
+ [WriteSampleVar n,EmptySampleVar] ^=^ [EmptySampleVar]
+
+prop_WriteRead_Ret n =
+ [WriteSampleVar n,ReadSampleVar] ^=^ [EmptySampleVar,ReturnInt n]
diff --git a/testsuite/tests/lib/Concurrent/SampleVar001.stdout b/testsuite/tests/lib/Concurrent/SampleVar001.stdout
new file mode 100644
index 0000000000..65be56c733
--- /dev/null
+++ b/testsuite/tests/lib/Concurrent/SampleVar001.stdout
@@ -0,0 +1,6 @@
+0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899OK, passed 100 tests.
+0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899OK, passed 100 tests.
+0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899OK, passed 100 tests.
+0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899OK, passed 100 tests.
+0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899OK, passed 100 tests.
+0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899OK, passed 100 tests.
diff --git a/testsuite/tests/lib/Concurrent/ThreadDelay001.hs b/testsuite/tests/lib/Concurrent/ThreadDelay001.hs
new file mode 100644
index 0000000000..c60f997039
--- /dev/null
+++ b/testsuite/tests/lib/Concurrent/ThreadDelay001.hs
@@ -0,0 +1,26 @@
+
+-- Test that threadDelay actually sleeps for (at least) as long as we
+-- ask it
+
+module Main (main) where
+
+import Control.Concurrent
+import Control.Monad
+import System.Time
+
+main = mapM_ delay (0 : take 11 (iterate (*5) 1))
+
+delay n = do
+ tS <- getClockTime
+ threadDelay n
+ tE <- getClockTime
+
+ let req = fromIntegral n * 10 ^ (6 :: Int)
+ obs = case normalizeTimeDiff (diffClockTimes tE tS) of
+ TimeDiff 0 0 0 0 0 s ps -> 10^12 * fromIntegral s + ps
+ diff = obs - req
+ diff' :: Double
+ diff' = fromIntegral diff / 10^(12 :: Int)
+
+ when (obs < req) $ print (tS, tE, req, obs, diff, diff')
+
diff --git a/testsuite/tests/lib/Concurrent/all.T b/testsuite/tests/lib/Concurrent/all.T
new file mode 100644
index 0000000000..004c6a1226
--- /dev/null
+++ b/testsuite/tests/lib/Concurrent/all.T
@@ -0,0 +1,10 @@
+setTestOpts(only_compiler_types(['ghc']))
+
+test('SampleVar001', reqlib('QuickCheck'), compile_and_run, ['-package QuickCheck'])
+test('4876', reqlib('random'), compile_and_run, ['']) # another SampleVar test
+
+test('Chan001', reqlib('QuickCheck'), compile_and_run, ['-package QuickCheck'])
+test('MVar001', reqlib('QuickCheck'), compile_and_run, ['-package QuickCheck'])
+test('QSemN001', reqlib('QuickCheck'), compile_and_run, ['-package QuickCheck'])
+test('QSem001', reqlib('QuickCheck'), compile_and_run, ['-package QuickCheck'])
+test('ThreadDelay001', normal, compile_and_run, [''])