interprocess-0.2.0.0: test/StoredMVar.hs
module Main (main) where
import Control.Concurrent.Process.StoredMVar
import qualified Control.Concurrent.MVar as Vanilla
import Control.Concurrent (forkIO)
import Control.Monad (void, forM)
-- import Control.Exception (SomeException, catch,
-- displayException)
import Foreign.SharedObjectName
-- import GHC.Environment (getFullArgs)
import System.Environment
import System.Exit
import Data.Monoid
import Text.Read (readMaybe)
import System.Process.Typed
import System.IO
import Control.Exception
import System.IO.Unsafe
import System.Mem
-- | A number of processes trying to do something concurrently.
-- For example, read from the same StoredMVar.
-- Actual number of processes may vary depending on a test case.
-- Minimum allowed number is 1.
concProcBaseN :: Int
concProcBaseN = 20
data BasicRole = Master | Slave
deriving (Eq, Ord, Show, Read)
data ThreeWayRole = Reader | Taker | Putter
deriving (Eq, Ord, Show, Read)
data TestSpec
= SimpleTakePut (SOName (StoredMVar Double)) BasicRole
| ReadersTakers (SOName (StoredMVar Double)) ThreeWayRole
deriving (Eq, Ord, Show, Read)
run :: TestSpec -> IO TestResult
run (SimpleTakePut ref Master) = do
mVar <- lookupMVar ref
putMVar mVar 42
putMVar mVar 17
return Success
run (SimpleTakePut ref Slave) = do
mVar <- lookupMVar ref
a <- takeMVar mVar
b <- takeMVar mVar
return $ if (a + b) == (42 + 17)
then Success
else Failure $ show (a + b) ++ " /= 42 + 17"
run (ReadersTakers ref Putter) = do
mVar <- lookupMVar ref
putMVar mVar 177
putMVar mVar 178
putMVar mVar 179
putMVar mVar 777
return Success
run (ReadersTakers ref Taker) = do
mVar <- lookupMVar ref
a <- takeMVar mVar
b <- takeMVar mVar
c <- takeMVar mVar
putStrLn $ "Taking: " ++ show (a,b,c)
return $ if a < b && b < c
then Success
else Failure "Three taken numbers must go ordered!"
run (ReadersTakers ref Reader) = do
mVar <- lookupMVar ref
a <- readMVar mVar
b <- readMVar mVar
c <- readMVar mVar
putStrLn $ "Reading: " ++ show (a,b,c)
return Success
tests :: [IO ([TestSpec], IO ())]
tests =
[ do
mVar <- newEmptyMVar
return
( [ SimpleTakePut (mVarName mVar) Slave
, SimpleTakePut (mVarName mVar) Master
]
, return (mVar `seq` ())
)
, do
mVar <- newEmptyMVar
return
( replicate concProcBaseN (ReadersTakers (mVarName mVar) Reader)
++
[ ReadersTakers (mVarName mVar) Taker
, ReadersTakers (mVarName mVar) Putter
]
, return (mVar `seq` ())
)
]
data TestResult
= Success
| Failure String
deriving (Eq, Ord, Show, Read)
instance Monoid TestResult where
mempty = Success
mappend Success a = a
mappend (Failure s) Success = Failure s
mappend (Failure s) (Failure t) = Failure $ unlines [s,t]
displayResult :: TestResult -> String
displayResult Success = "OK."
displayResult (Failure s) = unlines $ ("Failure":) . map (" " <>) . filter (not . null) $ lines s
finish :: TestResult -> IO a
finish Success = exitSuccess
finish (Failure s) = die s
main :: IO ()
main = do
execFile <- getExecutablePath
args <- getArgs
case getFirst $ foldMap (First . readMaybe) args of
-- run particular test routine
Just spec -> run spec >>= finish
-- run all tests
Nothing -> do
results <- forM tests $ \iox -> do
(ts, fin) <- iox
r <-runSpecs execFile ts
fin
return r
case foldMap id results of
Success -> exitSuccess
Failure _ -> exitFailure
runSpecs :: FilePath -> [TestSpec] -> IO TestResult
runSpecs f specs = do
putStrLn ""
r <- go (zip [1 :: Int ..] specs) >>= evaluate
performGC
putStrLn $ "Result: " ++ displayResult r
return r
where
conf ts = setStdout createPipe
$ setStderr createPipe
$ proc f [show ts]
go [] = return Success
go ((i,x):xs) = do
mr <- Vanilla.newEmptyMVar
void . forkIO $ withProcess (conf x) $ \p -> do
hGetContents (getStdout p) >>= mapM_ (putStrLn . withI) . lines
errs <- hGetContents (getStderr p)
ecode <- waitExitCode p
evaluate $ foldr seq () errs
Vanilla.putMVar mr $! case ecode of
ExitSuccess -> Success
ExitFailure _ -> Failure (unlines . map withI $ lines errs)
rx <- unsafeInterleaveIO $ Vanilla.takeMVar mr
rxs <- go xs
return $ rx <> rxs
where
withI s = "[" ++ show i ++ "] " ++ s