module Test.StateMachine
(
Program
, programLength
, forAllProgram
, monadicSequential
, runProgram
, prettyProgram
, actionNames
, checkActionNames
, ParallelProgram
, forAllParallelProgram
, History
, monadicParallel
, runParallelProgram
, runParallelProgram'
, prettyParallelProgram
, forAllProgramC
, monadicSequentialC
, forAllParallelProgramC
, monadicParallelC
, module Test.StateMachine.Types
, Test.QuickCheck.quickCheck
) where
import Control.Monad.IO.Class
(MonadIO)
import Control.Monad.State
(evalStateT, replicateM)
import Control.Monad.Trans.Control
(MonadBaseControl)
import Data.Map
(Map)
import qualified Data.Map as M
import Test.QuickCheck
(Property, collect, cover, ioProperty, property)
import qualified Test.QuickCheck
import Test.QuickCheck.Counterexamples
((:&:)(..), PropertyOf, forAllShrink)
import qualified Test.QuickCheck.Counterexamples as CE
import Test.QuickCheck.Monadic
(PropertyM, monadic, run)
import Test.StateMachine.Internal.Parallel
import Test.StateMachine.Internal.Sequential
import Test.StateMachine.Internal.Types
import Test.StateMachine.Internal.Utils
(whenFailM)
import Test.StateMachine.Types
import Test.StateMachine.Types.History
forAllProgram
:: Show (Untyped act)
=> HFoldable act
=> Generator model act
-> Shrinker act
-> Precondition model act
-> Transition model act
-> InitialModel model
-> (Program act -> Property)
-> Property
forAllProgram generator shrinker precondition transition model =
property
. forAllProgramC generator shrinker precondition transition model
. \prop p -> CE.property (prop p)
forAllProgramC
:: Show (Untyped act)
=> HFoldable act
=> Generator model act
-> Shrinker act
-> Precondition model act
-> Transition model act
-> InitialModel model
-> (Program act -> PropertyOf a)
-> PropertyOf (Program act :&: a)
forAllProgramC generator shrinker precondition transition model =
forAllShrink
(evalStateT (generateProgram generator precondition transition 0) model)
(shrinkProgram shrinker precondition transition model)
monadicSequential
:: Monad m
=> Show (Untyped act)
=> HFoldable act
=> StateMachine' model act err m
-> (Program act -> PropertyM m a)
-> Property
monadicSequential sm = property . monadicSequentialC sm
monadicSequentialC
:: Monad m
=> Show (Untyped act)
=> HFoldable act
=> StateMachine' model act err m
-> (Program act -> PropertyM m a)
-> PropertyOf (Program act)
monadicSequentialC StateMachine {..} predicate
= fmap (\(prog :&: ()) -> prog)
. forAllProgramC generator' shrinker' precondition' transition' model'
$ CE.property
. monadic (ioProperty . runner')
. predicate
runProgram
:: forall m act err model
. Monad m
=> Show (Untyped act)
=> HTraversable act
=> StateMachine' model act err m
-> Program act
-> PropertyM m (History act err, model Concrete, Property)
runProgram sm = run . executeProgram sm
prettyProgram
:: MonadIO m
=> Program act
-> History act err
-> model Concrete
-> Property
-> PropertyM m ()
prettyProgram _ hist _ prop = putStrLn (ppHistory hist) `whenFailM` prop
checkActionNames :: Constructors act => Program act -> Property -> Property
checkActionNames prog
= collect names
. cover (length names == numOfConstructors) 1 "coverage"
where
names = actionNames prog
numOfConstructors = nConstructors prog
actionNames :: forall act. Constructors act => Program act -> [(Constructor, Int)]
actionNames = M.toList . foldl go M.empty . unProgram
where
go :: Map Constructor Int -> Internal act -> Map Constructor Int
go ih (Internal act _) = M.insertWith (+) (constructor act) 1 ih
forAllParallelProgram
:: Show (Untyped act)
=> HFoldable act
=> Generator model act
-> Shrinker act
-> Precondition model act
-> Transition model act
-> InitialModel model
-> (ParallelProgram act -> Property)
-> Property
forAllParallelProgram generator shrinker precondition transition model =
property
. forAllParallelProgramC generator shrinker precondition transition model
. \prop p -> CE.property (prop p)
forAllParallelProgramC
:: Show (Untyped act)
=> HFoldable act
=> Generator model act
-> Shrinker act
-> Precondition model act
-> Transition model act
-> InitialModel model
-> (ParallelProgram act -> PropertyOf a)
-> PropertyOf (ParallelProgram act :&: a)
forAllParallelProgramC generator shrinker precondition transition model =
forAllShrink
(generateParallelProgram generator precondition transition model)
(shrinkParallelProgram shrinker precondition transition model)
monadicParallel
:: MonadBaseControl IO m
=> Show (Untyped act)
=> HFoldable act
=> StateMachine' model act err m
-> (ParallelProgram act -> PropertyM m ())
-> Property
monadicParallel sm = property . monadicParallelC sm
monadicParallelC
:: MonadBaseControl IO m
=> Show (Untyped act)
=> HFoldable act
=> StateMachine' model act err m
-> (ParallelProgram act -> PropertyM m ())
-> PropertyOf (ParallelProgram act)
monadicParallelC StateMachine {..} predicate
= fmap (\(prog :&: ()) -> prog)
. forAllParallelProgramC generator' shrinker' precondition' transition' model'
$ CE.property
. monadic (ioProperty . runner')
. predicate
runParallelProgram
:: MonadBaseControl IO m
=> Show (Untyped act)
=> HTraversable act
=> StateMachine' model act err m
-> ParallelProgram act
-> PropertyM m [(History act err, Property)]
runParallelProgram = runParallelProgram' 10
runParallelProgram'
:: MonadBaseControl IO m
=> Show (Untyped act)
=> HTraversable act
=> Int
-> StateMachine' model act err m
-> ParallelProgram act
-> PropertyM m [(History act err, Property)]
runParallelProgram' n StateMachine {..} prog =
replicateM n $ do
hist <- run (executeParallelProgram semantics' prog)
return (hist, linearise transition' postcondition' model' hist)
prettyParallelProgram
:: MonadIO m
=> HFoldable act
=> ParallelProgram act
-> [(History act err, Property)]
-> PropertyM m ()
prettyParallelProgram prog
= mapM_ (\(hist, prop) ->
print (toBoxDrawings prog hist) `whenFailM` prop)