You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Jun 18, 2021. It is now read-only.
Writing example Commands by hand (when developing the tests, or when wanting to save particular generated tests) is a bit inconvenient. Not only does Commands capture the result of the mock implementation (and so when the model changes, we have to update our examples, if even if the commands are still the same), we also have to manually deal with references. To make this a bit more convenient, I wrote the following helper, which I find rather neat:
{-# LANGUAGE ScopedTypeVariables #-}
moduleTest.Util.QSM (
Example-- opaque
, run
, run'
, example
) whereimportControl.MonadimportControl.Monad.FailimportData.TypeableimportTest.StateMachine.SequentialimportTest.StateMachine.TypesimportqualifiedTest.StateMachine.Types.Rank2asRank2dataExamplecmda=Donea
| Run (cmdSymbolic) ([Var] ->Examplecmda)
| FailStringinstanceFunctor (Examplecmd) wherefmap= liftM
instanceApplicative (Examplecmd) wherepure=Done(<*>)= ap
instanceMonad (Examplecmd) wherereturn=pureDone a >>= f = f a
Run c k >>= f =Run c (k >=> f)
Fail err >>= _ =Fail err
instanceMonadFail (Examplecmd) wherefail=Fail--| Run a command, and capture its referencesrun::Typeablea=>cmdSymbolic->Examplecmd [ReferenceaSymbolic]
run cmd =Run cmd (Done.map (Reference.Symbolic))
--| Run a command, ignoring its referencesrun'::cmdSymbolic->Examplecmd()
run' cmd =Run cmd (\_vars ->Done())
example::forallmodelcmdmresp.Rank2.Foldableresp=>StateMachinemodelcmdmresp->Examplecmd()->Commandscmdresp
example sm =Commands.fst.flip runGenSym newCounter . go (initModel sm)
wherego::modelSymbolic->Examplecmd()->GenSym [Commandcmdresp]
go _ (Done()) =return[]
go _ (Fail err) =error$"example: "++ err
go m (Run cmd k) =do
resp <- mock sm m cmd
let m' ::modelSymbolic
m' = transition sm m cmd resp
vars:: [Var]
vars = getUsedVars resp
cmd'::Commandcmdresp
cmd' =Command cmd resp vars
(cmd' :) <$> go m' (k vars)
For example, I am currently working on some tests to do with threads, killing them, etc. Here are some manually written Commands:
_forkCount::Commands (AtIOCmd) (AtIOSuccess)
_forkCount = example sm' $do
run' $At$Fork
run' $At$CountTopLevel_forkKillCount::Commands (AtIOCmd) (AtIOSuccess)
_forkKillCount = example sm' $do
[tid] <- run $AtFork
run' $At$Kill tid
run' $At$CountTopLevel
Quite nice, I think. Might be worth adding to the library?
Writing example
Commandsby hand (when developing the tests, or when wanting to save particular generated tests) is a bit inconvenient. Not only doesCommandscapture the result of the mock implementation (and so when the model changes, we have to update our examples, if even if the commands are still the same), we also have to manually deal with references. To make this a bit more convenient, I wrote the following helper, which I find rather neat:{-# LANGUAGE ScopedTypeVariables #-} module Test.Util.QSM ( Example -- opaque , run , run' , example ) where import Control.Monad import Control.Monad.Fail import Data.Typeable import Test.StateMachine.Sequential import Test.StateMachine.Types import qualified Test.StateMachine.Types.Rank2 as Rank2 data Example cmd a = Done a | Run (cmd Symbolic) ([Var] -> Example cmd a) | Fail String instance Functor (Example cmd) where fmap = liftM instance Applicative (Example cmd) where pure = Done (<*>) = ap instance Monad (Example cmd) where return = pure Done a >>= f = f a Run c k >>= f = Run c (k >=> f) Fail err >>= _ = Fail err instance MonadFail (Example cmd) where fail = Fail -- | Run a command, and capture its references run :: Typeable a => cmd Symbolic -> Example cmd [Reference a Symbolic] run cmd = Run cmd (Done . map (Reference . Symbolic)) -- | Run a command, ignoring its references run' :: cmd Symbolic -> Example cmd () run' cmd = Run cmd (\_vars -> Done ()) example :: forall model cmd m resp. Rank2.Foldable resp => StateMachine model cmd m resp -> Example cmd () -> Commands cmd resp example sm = Commands . fst . flip runGenSym newCounter . go (initModel sm) where go :: model Symbolic -> Example cmd () -> GenSym [Command cmd resp] go _ (Done ()) = return [] go _ (Fail err) = error $ "example: " ++ err go m (Run cmd k) = do resp <- mock sm m cmd let m' :: model Symbolic m' = transition sm m cmd resp vars :: [Var] vars = getUsedVars resp cmd' :: Command cmd resp cmd' = Command cmd resp vars (cmd' :) <$> go m' (k vars)For example, I am currently working on some tests to do with threads, killing them, etc. Here are some manually written
Commands:Quite nice, I think. Might be worth adding to the library?