Skip to content

Commit 221cfa8

Browse files
Move Error state to RunModel
1 parent b50a995 commit 221cfa8

File tree

2 files changed

+35
-31
lines changed

2 files changed

+35
-31
lines changed

quickcheck-dynamic/src/Test/QuickCheck/StateModel.hs

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import Control.Monad
4343
import Control.Monad.Reader
4444
import Control.Monad.Writer (WriterT, runWriterT, tell)
4545
import Data.Data
46+
import Data.Kind
4647
import Data.List
4748
import Data.Monoid (Endo (..))
4849
import Data.Set qualified as Set
@@ -102,13 +103,6 @@ class
102103
-- anything.
103104
data Action state a
104105

105-
-- | The type of errors that actions can throw. If this is defined as anything
106-
-- other than `Void` `perform` is required to return `Either (Error state) a`
107-
-- instead of `a`.
108-
type Error state
109-
110-
type Error state = Void
111-
112106
-- | Display name for `Action`.
113107
-- This is useful to provide sensible statistics about the distribution of `Action`s run
114108
-- when checking a property.
@@ -158,22 +152,6 @@ class
158152

159153
deriving instance (forall a. Show (Action state a)) => Show (Any (Action state))
160154

161-
-- | The result required of `perform` depending on the `Error` type
162-
-- of a state model. If there are no errors, `Error state = Void`, and
163-
-- so we don't need to specify if the action failed or not.
164-
type family PerformResult e a where
165-
PerformResult Void a = a
166-
PerformResult e a = Either e a
167-
168-
class IsPerformResult e a where
169-
performResultToEither :: PerformResult e a -> Either e a
170-
171-
instance {-# OVERLAPPING #-} IsPerformResult Void a where
172-
performResultToEither = Right
173-
174-
instance {-# OVERLAPPABLE #-} (PerformResult e a ~ Either e a) => IsPerformResult e a where
175-
performResultToEither = id
176-
177155
newtype PostconditionM m a = PostconditionM {runPost :: WriterT (Endo Property, Endo Property) m a}
178156
deriving (Functor, Applicative, Monad)
179157

@@ -197,7 +175,33 @@ monitorPost m = PostconditionM $ tell (Endo m, mempty)
197175
counterexamplePost :: Monad m => String -> PostconditionM m ()
198176
counterexamplePost c = PostconditionM $ tell (mempty, Endo $ counterexample c)
199177

178+
-- | The result required of `perform` depending on the `Error` type.
179+
-- If there are no errors, `Error state = Void`, and
180+
-- so we don't need to specify if the action failed or not.
181+
type family PerformResult state (m :: Type -> Type) a where
182+
PerformResult state m a = EitherIsh (Error state m) a
183+
184+
type family EitherIsh e a where
185+
EitherIsh Void a = a
186+
EitherIsh e a = Either e a
187+
188+
class IsPerformResult e a where
189+
performResultToEither :: EitherIsh e a -> Either e a
190+
191+
instance {-# OVERLAPPING #-} IsPerformResult Void a where
192+
performResultToEither = Right
193+
194+
instance {-# OVERLAPPABLE #-} (EitherIsh e a ~ Either e a) => IsPerformResult e a where
195+
performResultToEither = id
196+
200197
class (forall a. Show (Action state a), Monad m) => RunModel state m where
198+
-- | The type of errors that actions can throw. If this is defined as anything
199+
-- other than `Void` `perform` is required to return `Either (Error state) a`
200+
-- instead of `a`.
201+
type Error state m
202+
203+
type Error state m = Void
204+
201205
-- | Perform an `Action` in some `state` in the `Monad` `m`. This
202206
-- is the function that's used to exercise the actual stateful
203207
-- implementation, usually through various side-effects as permitted
@@ -208,7 +212,7 @@ class (forall a. Show (Action state a), Monad m) => RunModel state m where
208212
--
209213
-- The `Lookup` parameter provides an /environment/ to lookup `Var
210214
-- a` instances from previous steps.
211-
perform :: Typeable a => state -> Action state a -> LookUp -> m (PerformResult (Error state) a)
215+
perform :: Typeable a => state -> Action state a -> LookUp -> m (PerformResult state m a)
212216

213217
-- | Postcondition on the `a` value produced at some step.
214218
-- The result is `assert`ed and will make the property fail should it be `False`. This is useful
@@ -220,18 +224,18 @@ class (forall a. Show (Action state a), Monad m) => RunModel state m where
220224
-- The result is `assert`ed and will make the property fail should it be `False`. This is useful
221225
-- to check the implementation produces e.g. the expected errors or to check that the SUT hasn't
222226
-- been updated during the execution of the negative action.
223-
postconditionOnFailure :: (state, state) -> Action state a -> LookUp -> Either (Error state) a -> PostconditionM m Bool
227+
postconditionOnFailure :: (state, state) -> Action state a -> LookUp -> Either (Error state m) a -> PostconditionM m Bool
224228
postconditionOnFailure _ _ _ _ = pure True
225229

226230
-- | Allows the user to attach additional information to the `Property` at each step of the process.
227231
-- This function is given the full transition that's been executed, including the start and ending
228232
-- `state`, the `Action`, the current environment to `Lookup` and the value produced by `perform`
229233
-- while executing this step.
230-
monitoring :: (state, state) -> Action state a -> LookUp -> Either (Error state) a -> Property -> Property
234+
monitoring :: (state, state) -> Action state a -> LookUp -> Either (Error state m) a -> Property -> Property
231235
monitoring _ _ _ _ prop = prop
232236

233237
-- | Allows the user to attach additional information to the `Property` if a positive action fails.
234-
monitoringFailure :: state -> Action state a -> LookUp -> Error state -> Property -> Property
238+
monitoringFailure :: state -> Action state a -> LookUp -> Error state m -> Property -> Property
235239
monitoringFailure _ _ _ _ prop = prop
236240

237241
type LookUp = forall a. Typeable a => Var a -> a
@@ -488,7 +492,7 @@ runActions
488492
:: forall state m e
489493
. ( StateModel state
490494
, RunModel state m
491-
, e ~ Error state
495+
, e ~ Error state m
492496
, forall a. IsPerformResult e a
493497
)
494498
=> Actions state
@@ -506,7 +510,7 @@ runSteps
506510
:: forall state m e
507511
. ( StateModel state
508512
, RunModel state m
509-
, e ~ Error state
513+
, e ~ Error state m
510514
, forall a. IsPerformResult e a
511515
)
512516
=> Annotated state

quickcheck-dynamic/test/Spec/DynamicLogic/RegistryModel.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ instance StateModel RegState where
4444
Unregister :: String -> Action RegState ()
4545
KillThread :: Var ThreadId -> Action RegState ()
4646

47-
type Error RegState = SomeException
48-
4947
precondition s (Register name tid) =
5048
name `Map.notMember` regs s
5149
&& tid `notElem` Map.elems (regs s)
@@ -110,6 +108,8 @@ instance StateModel RegState where
110108
type RegM = ReaderT Registry IO
111109

112110
instance RunModel RegState RegM where
111+
type Error RegState RegM = SomeException
112+
113113
perform _ Spawn _ = do
114114
tid <- lift $ forkIO (threadDelay 10000000)
115115
pure $ Right tid

0 commit comments

Comments
 (0)