module Test.Framework.Runners.Core (
RunTest(..), RunningTest, SomeImproving(..), FinishedTest, runTests,
TestRunner(..), runTestTree
) where
import Test.Framework.Core
import Test.Framework.Improving
import Test.Framework.Options
import Test.Framework.Runners.Options
import Test.Framework.Runners.TestPattern
import Test.Framework.Runners.ThreadPool
import Test.Framework.Seed
import Test.Framework.Utilities
import Control.Concurrent.MVar ( newEmptyMVar, putMVar, takeMVar )
import Control.Exception (mask, finally, onException)
import Control.Monad ( liftM, forM )
import Data.Maybe ( catMaybes )
import Data.Typeable ( Typeable )
data RunTest a = RunTest TestName TestTypeName a
| RunTestGroup TestName [RunTest a]
deriving (Int -> RunTest a -> ShowS
[RunTest a] -> ShowS
RunTest a -> String
(Int -> RunTest a -> ShowS)
-> (RunTest a -> String)
-> ([RunTest a] -> ShowS)
-> Show (RunTest a)
forall a. Show a => Int -> RunTest a -> ShowS
forall a. Show a => [RunTest a] -> ShowS
forall a. Show a => RunTest a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall a. Show a => Int -> RunTest a -> ShowS
showsPrec :: Int -> RunTest a -> ShowS
$cshow :: forall a. Show a => RunTest a -> String
show :: RunTest a -> String
$cshowList :: forall a. Show a => [RunTest a] -> ShowS
showList :: [RunTest a] -> ShowS
Show)
data SomeImproving = forall i r. TestResultlike i r => SomeImproving (i :~> r)
type RunningTest = RunTest SomeImproving
type FinishedTest = RunTest (String, Bool)
runTests :: CompleteRunnerOptions
-> [Test]
-> IO [RunningTest]
runTests :: CompleteRunnerOptions -> [Test] -> IO [RunningTest]
runTests CompleteRunnerOptions
ropts [Test]
tests = do
let test_patterns :: [TestPattern]
test_patterns = K [TestPattern] -> [TestPattern]
forall a. K a -> a
unK (K [TestPattern] -> [TestPattern])
-> K [TestPattern] -> [TestPattern]
forall a b. (a -> b) -> a -> b
$ CompleteRunnerOptions -> K [TestPattern]
forall (f :: * -> *). RunnerOptions' f -> f [TestPattern]
ropt_test_patterns CompleteRunnerOptions
ropts
test_options :: TestOptions
test_options = K TestOptions -> TestOptions
forall a. K a -> a
unK (K TestOptions -> TestOptions) -> K TestOptions -> TestOptions
forall a b. (a -> b) -> a -> b
$ CompleteRunnerOptions -> K TestOptions
forall (f :: * -> *). RunnerOptions' f -> f TestOptions
ropt_test_options CompleteRunnerOptions
ropts
([RunningTest]
run_tests, [IO ()]
actions) <- [StdRunner] -> IO ([RunningTest], [IO ()])
runTests' ([StdRunner] -> IO ([RunningTest], [IO ()]))
-> [StdRunner] -> IO ([RunningTest], [IO ()])
forall a b. (a -> b) -> a -> b
$ (Test -> StdRunner) -> [Test] -> [StdRunner]
forall a b. (a -> b) -> [a] -> [b]
map (TestOptions -> [TestPattern] -> Test -> StdRunner
forall b. TestRunner b => TestOptions -> [TestPattern] -> Test -> b
runTestTree TestOptions
test_options [TestPattern]
test_patterns) [Test]
tests
[()]
_ <- Int -> [IO ()] -> IO [()]
forall a. Int -> [IO a] -> IO [a]
executeOnPool (K Int -> Int
forall a. K a -> a
unK (K Int -> Int) -> K Int -> Int
forall a b. (a -> b) -> a -> b
$ CompleteRunnerOptions -> K Int
forall (f :: * -> *). RunnerOptions' f -> f Int
ropt_threads CompleteRunnerOptions
ropts) [IO ()]
actions
[RunningTest] -> IO [RunningTest]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return [RunningTest]
run_tests
class TestRunner b where
runSimpleTest :: (Testlike i r t, Typeable t) => TestOptions -> TestName -> t -> b
skipTest :: b
runIOTest :: IO (b, IO ()) -> b
runGroup :: TestName -> [b] -> b
runTestTree
:: TestRunner b
=> TestOptions
-> [TestPattern]
-> Test
-> b
runTestTree :: forall b. TestRunner b => TestOptions -> [TestPattern] -> Test -> b
runTestTree TestOptions
initialOpts [TestPattern]
pats Test
topTest = TestOptions -> [String] -> Test -> b
forall {p}. TestRunner p => TestOptions -> [String] -> Test -> p
go TestOptions
initialOpts [] Test
topTest
where
go :: TestOptions -> [String] -> Test -> p
go TestOptions
opts [String]
path Test
t = case Test
t of
Test String
name t
testlike ->
if [TestPattern] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [TestPattern]
pats Bool -> Bool -> Bool
|| (TestPattern -> Bool) -> [TestPattern] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (TestPattern -> [String] -> Bool
`testPatternMatches` ([String]
path [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
name])) [TestPattern]
pats
then TestOptions -> String -> t -> p
forall b i r t.
(TestRunner b, Testlike i r t, Typeable t) =>
TestOptions -> String -> t -> b
forall i r t.
(Testlike i r t, Typeable t) =>
TestOptions -> String -> t -> p
runSimpleTest TestOptions
opts String
name t
testlike
else p
forall b. TestRunner b => b
skipTest
TestGroup String
name [Test]
tests ->
let path' :: [String]
path' = [String]
path [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
name]
in String -> [p] -> p
forall b. TestRunner b => String -> [b] -> b
runGroup String
name ([p] -> p) -> [p] -> p
forall a b. (a -> b) -> a -> b
$ (Test -> p) -> [Test] -> [p]
forall a b. (a -> b) -> [a] -> [b]
map (TestOptions -> [String] -> Test -> p
go TestOptions
opts [String]
path') [Test]
tests
PlusTestOptions TestOptions
extra_topts Test
test -> TestOptions -> [String] -> Test -> p
go (TestOptions
opts TestOptions -> TestOptions -> TestOptions
forall a. Monoid a => a -> a -> a
`mappend` TestOptions
extra_topts) [String]
path Test
test
BuildTestBracketed IO (Test, IO ())
build ->
IO (p, IO ()) -> p
forall b. TestRunner b => IO (b, IO ()) -> b
runIOTest (IO (p, IO ()) -> p) -> IO (p, IO ()) -> p
forall a b. (a -> b) -> a -> b
$ (Test -> p) -> (Test, IO ()) -> (p, IO ())
forall a c b. (a -> c) -> (a, b) -> (c, b)
onLeft (TestOptions -> [String] -> Test -> p
go TestOptions
opts [String]
path) ((Test, IO ()) -> (p, IO ())) -> IO (Test, IO ()) -> IO (p, IO ())
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` IO (Test, IO ())
build
newtype StdRunner = StdRunner { StdRunner -> IO (Maybe (RunningTest, [IO ()]))
run :: IO (Maybe (RunningTest, [IO ()])) }
instance TestRunner StdRunner where
runSimpleTest :: forall i r t.
(Testlike i r t, Typeable t) =>
TestOptions -> String -> t -> StdRunner
runSimpleTest TestOptions
topts String
name t
testlike = IO (Maybe (RunningTest, [IO ()])) -> StdRunner
StdRunner (IO (Maybe (RunningTest, [IO ()])) -> StdRunner)
-> IO (Maybe (RunningTest, [IO ()])) -> StdRunner
forall a b. (a -> b) -> a -> b
$ do
(i :~> r
result, IO ()
action) <- CompleteTestOptions -> t -> IO (i :~> r, IO ())
forall i r t.
Testlike i r t =>
CompleteTestOptions -> t -> IO (i :~> r, IO ())
runTest (TestOptions -> CompleteTestOptions
completeTestOptions TestOptions
topts) t
testlike
Maybe (RunningTest, [IO ()]) -> IO (Maybe (RunningTest, [IO ()]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ((RunningTest, [IO ()]) -> Maybe (RunningTest, [IO ()])
forall a. a -> Maybe a
Just (String -> String -> SomeImproving -> RunningTest
forall a. String -> String -> a -> RunTest a
RunTest String
name (t -> String
forall i r t. Testlike i r t => t -> String
testTypeName t
testlike) ((i :~> r) -> SomeImproving
forall i r. TestResultlike i r => (i :~> r) -> SomeImproving
SomeImproving i :~> r
result), [IO ()
action]))
skipTest :: StdRunner
skipTest = IO (Maybe (RunningTest, [IO ()])) -> StdRunner
StdRunner (IO (Maybe (RunningTest, [IO ()])) -> StdRunner)
-> IO (Maybe (RunningTest, [IO ()])) -> StdRunner
forall a b. (a -> b) -> a -> b
$ Maybe (RunningTest, [IO ()]) -> IO (Maybe (RunningTest, [IO ()]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe (RunningTest, [IO ()])
forall a. Maybe a
Nothing
runGroup :: String -> [StdRunner] -> StdRunner
runGroup String
name [StdRunner]
tests = IO (Maybe (RunningTest, [IO ()])) -> StdRunner
StdRunner (IO (Maybe (RunningTest, [IO ()])) -> StdRunner)
-> IO (Maybe (RunningTest, [IO ()])) -> StdRunner
forall a b. (a -> b) -> a -> b
$ do
([RunningTest]
results, [IO ()]
actions) <- [StdRunner] -> IO ([RunningTest], [IO ()])
runTests' [StdRunner]
tests
Maybe (RunningTest, [IO ()]) -> IO (Maybe (RunningTest, [IO ()]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (RunningTest, [IO ()]) -> IO (Maybe (RunningTest, [IO ()])))
-> Maybe (RunningTest, [IO ()])
-> IO (Maybe (RunningTest, [IO ()]))
forall a b. (a -> b) -> a -> b
$ if [RunningTest] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [RunningTest]
results then Maybe (RunningTest, [IO ()])
forall a. Maybe a
Nothing else (RunningTest, [IO ()]) -> Maybe (RunningTest, [IO ()])
forall a. a -> Maybe a
Just ((String -> [RunningTest] -> RunningTest
forall a. String -> [RunTest a] -> RunTest a
RunTestGroup String
name [RunningTest]
results), [IO ()]
actions)
runIOTest :: IO (StdRunner, IO ()) -> StdRunner
runIOTest IO (StdRunner, IO ())
ioTest = IO (Maybe (RunningTest, [IO ()])) -> StdRunner
StdRunner (IO (Maybe (RunningTest, [IO ()])) -> StdRunner)
-> IO (Maybe (RunningTest, [IO ()])) -> StdRunner
forall a b. (a -> b) -> a -> b
$ ((forall a. IO a -> IO a) -> IO (Maybe (RunningTest, [IO ()])))
-> IO (Maybe (RunningTest, [IO ()]))
forall b. ((forall a. IO a -> IO a) -> IO b) -> IO b
mask (((forall a. IO a -> IO a) -> IO (Maybe (RunningTest, [IO ()])))
-> IO (Maybe (RunningTest, [IO ()])))
-> ((forall a. IO a -> IO a) -> IO (Maybe (RunningTest, [IO ()])))
-> IO (Maybe (RunningTest, [IO ()]))
forall a b. (a -> b) -> a -> b
$ \forall a. IO a -> IO a
restore -> IO (StdRunner, IO ())
ioTest IO (StdRunner, IO ())
-> ((StdRunner, IO ()) -> IO (Maybe (RunningTest, [IO ()])))
-> IO (Maybe (RunningTest, [IO ()]))
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \(StdRunner IO (Maybe (RunningTest, [IO ()]))
test, IO ()
cleanup) -> do
Maybe (RunningTest, [IO ()])
mb_res <- IO (Maybe (RunningTest, [IO ()]))
-> IO (Maybe (RunningTest, [IO ()]))
forall a. IO a -> IO a
restore IO (Maybe (RunningTest, [IO ()]))
test IO (Maybe (RunningTest, [IO ()]))
-> IO () -> IO (Maybe (RunningTest, [IO ()]))
forall a b. IO a -> IO b -> IO a
`onException` IO ()
cleanup
case Maybe (RunningTest, [IO ()])
mb_res of
Maybe (RunningTest, [IO ()])
Nothing -> IO ()
cleanup IO ()
-> IO (Maybe (RunningTest, [IO ()]))
-> IO (Maybe (RunningTest, [IO ()]))
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Maybe (RunningTest, [IO ()]) -> IO (Maybe (RunningTest, [IO ()]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe (RunningTest, [IO ()])
forall a. Maybe a
Nothing
Just (RunningTest
run_test, [IO ()]
actions) -> do
([MVar ()]
mvars, [IO ()]
actions') <- ([(MVar (), IO ())] -> ([MVar ()], [IO ()]))
-> IO [(MVar (), IO ())] -> IO ([MVar ()], [IO ()])
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM [(MVar (), IO ())] -> ([MVar ()], [IO ()])
forall a b. [(a, b)] -> ([a], [b])
unzip (IO [(MVar (), IO ())] -> IO ([MVar ()], [IO ()]))
-> IO [(MVar (), IO ())] -> IO ([MVar ()], [IO ()])
forall a b. (a -> b) -> a -> b
$ [IO ()] -> (IO () -> IO (MVar (), IO ())) -> IO [(MVar (), IO ())]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [IO ()]
actions ((IO () -> IO (MVar (), IO ())) -> IO [(MVar (), IO ())])
-> (IO () -> IO (MVar (), IO ())) -> IO [(MVar (), IO ())]
forall a b. (a -> b) -> a -> b
$ \IO ()
action -> do
MVar ()
mvar <- IO (MVar ())
forall a. IO (MVar a)
newEmptyMVar
(MVar (), IO ()) -> IO (MVar (), IO ())
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (MVar ()
mvar, IO ()
action IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO a
`finally` MVar () -> () -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar ()
mvar ())
Maybe (RunningTest, [IO ()]) -> IO (Maybe (RunningTest, [IO ()]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (RunningTest, [IO ()]) -> IO (Maybe (RunningTest, [IO ()])))
-> Maybe (RunningTest, [IO ()])
-> IO (Maybe (RunningTest, [IO ()]))
forall a b. (a -> b) -> a -> b
$ (RunningTest, [IO ()]) -> Maybe (RunningTest, [IO ()])
forall a. a -> Maybe a
Just (RunningTest
run_test, [IO ()]
actions' [IO ()] -> [IO ()] -> [IO ()]
forall a. [a] -> [a] -> [a]
++ [(IO ()
cleanup IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> (MVar () -> IO ()) -> [MVar ()] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ MVar () -> IO ()
forall a. MVar a -> IO a
takeMVar [MVar ()]
mvars)])
runTests' :: [StdRunner] -> IO ([RunningTest], [IO ()])
runTests' :: [StdRunner] -> IO ([RunningTest], [IO ()])
runTests' = ([Maybe (RunningTest, [IO ()])] -> ([RunningTest], [IO ()]))
-> IO [Maybe (RunningTest, [IO ()])] -> IO ([RunningTest], [IO ()])
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (([[IO ()]] -> [IO ()])
-> ([RunningTest], [[IO ()]]) -> ([RunningTest], [IO ()])
forall b c a. (b -> c) -> (a, b) -> (a, c)
onRight [[IO ()]] -> [IO ()]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (([RunningTest], [[IO ()]]) -> ([RunningTest], [IO ()]))
-> ([Maybe (RunningTest, [IO ()])] -> ([RunningTest], [[IO ()]]))
-> [Maybe (RunningTest, [IO ()])]
-> ([RunningTest], [IO ()])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(RunningTest, [IO ()])] -> ([RunningTest], [[IO ()]])
forall a b. [(a, b)] -> ([a], [b])
unzip ([(RunningTest, [IO ()])] -> ([RunningTest], [[IO ()]]))
-> ([Maybe (RunningTest, [IO ()])] -> [(RunningTest, [IO ()])])
-> [Maybe (RunningTest, [IO ()])]
-> ([RunningTest], [[IO ()]])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Maybe (RunningTest, [IO ()])] -> [(RunningTest, [IO ()])]
forall a. [Maybe a] -> [a]
catMaybes) (IO [Maybe (RunningTest, [IO ()])] -> IO ([RunningTest], [IO ()]))
-> ([StdRunner] -> IO [Maybe (RunningTest, [IO ()])])
-> [StdRunner]
-> IO ([RunningTest], [IO ()])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (StdRunner -> IO (Maybe (RunningTest, [IO ()])))
-> [StdRunner] -> IO [Maybe (RunningTest, [IO ()])]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM StdRunner -> IO (Maybe (RunningTest, [IO ()]))
run
completeTestOptions :: TestOptions -> CompleteTestOptions
completeTestOptions :: TestOptions -> CompleteTestOptions
completeTestOptions TestOptions
to = TestOptions {
topt_seed :: K Seed
topt_seed = Seed -> K Seed
forall a. a -> K a
K (Seed -> K Seed) -> Seed -> K Seed
forall a b. (a -> b) -> a -> b
$ TestOptions -> Maybe Seed
forall (f :: * -> *). TestOptions' f -> f Seed
topt_seed TestOptions
to Maybe Seed -> Seed -> Seed
forall a. Maybe a -> a -> a
`orElse` Seed
RandomSeed,
topt_maximum_generated_tests :: K Int
topt_maximum_generated_tests = Int -> K Int
forall a. a -> K a
K (Int -> K Int) -> Int -> K Int
forall a b. (a -> b) -> a -> b
$ TestOptions -> Maybe Int
forall (f :: * -> *). TestOptions' f -> f Int
topt_maximum_generated_tests TestOptions
to Maybe Int -> Int -> Int
forall a. Maybe a -> a -> a
`orElse` Int
100,
topt_maximum_unsuitable_generated_tests :: K Int
topt_maximum_unsuitable_generated_tests = Int -> K Int
forall a. a -> K a
K (Int -> K Int) -> Int -> K Int
forall a b. (a -> b) -> a -> b
$ TestOptions -> Maybe Int
forall (f :: * -> *). TestOptions' f -> f Int
topt_maximum_unsuitable_generated_tests TestOptions
to Maybe Int -> Int -> Int
forall a. Maybe a -> a -> a
`orElse` Int
1000,
topt_maximum_test_size :: K Int
topt_maximum_test_size = Int -> K Int
forall a. a -> K a
K (Int -> K Int) -> Int -> K Int
forall a b. (a -> b) -> a -> b
$ TestOptions -> Maybe Int
forall (f :: * -> *). TestOptions' f -> f Int
topt_maximum_test_size TestOptions
to Maybe Int -> Int -> Int
forall a. Maybe a -> a -> a
`orElse` Int
100,
topt_maximum_test_depth :: K Int
topt_maximum_test_depth = Int -> K Int
forall a. a -> K a
K (Int -> K Int) -> Int -> K Int
forall a b. (a -> b) -> a -> b
$ TestOptions -> Maybe Int
forall (f :: * -> *). TestOptions' f -> f Int
topt_maximum_test_depth TestOptions
to Maybe Int -> Int -> Int
forall a. Maybe a -> a -> a
`orElse` Int
5,
topt_timeout :: K (Maybe Int)
topt_timeout = Maybe Int -> K (Maybe Int)
forall a. a -> K a
K (Maybe Int -> K (Maybe Int)) -> Maybe Int -> K (Maybe Int)
forall a b. (a -> b) -> a -> b
$ TestOptions -> Maybe (Maybe Int)
forall (f :: * -> *). TestOptions' f -> f (Maybe Int)
topt_timeout TestOptions
to Maybe (Maybe Int) -> Maybe Int -> Maybe Int
forall a. Maybe a -> a -> a
`orElse` Maybe Int
forall a. Maybe a
Nothing
}