Skip to content

Commit 7b4250d

Browse files
Refactor StateModel to distinguish between model types and runtime types
and implement improved compatibility layer for IOSim in StateModel.
1 parent ee6c97a commit 7b4250d

File tree

14 files changed

+154
-282
lines changed

14 files changed

+154
-282
lines changed

cabal.project

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
packages:
22
quickcheck-dynamic
3-
quickcheck-io-sim-compat
3+
quickcheck-dynamic-iosim
44

55
tests: true
66

File renamed without changes.
File renamed without changes.
File renamed without changes.

quickcheck-io-sim-compat/quickcheck-io-sim-compat.cabal renamed to quickcheck-dynamic-iosim/quickcheck-dynamic-iosim.cabal

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,11 @@ library
5959
io-sim,
6060
lens,
6161
containers,
62-
mtl
62+
mtl,
63+
stm,
64+
quickcheck-dynamic
6365

64-
test-suite quickcheck-io-sim-compat-test
66+
test-suite quickcheck-dynamic-iosim
6567
import: lang
6668
type: exitcode-stdio-1.0
6769
main-is: Spec.hs
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{-# LANGUAGE DataKinds #-}
2+
{-# LANGUAGE DerivingStrategies #-}
3+
{-# LANGUAGE FlexibleContexts #-}
4+
{-# LANGUAGE FlexibleInstances #-}
5+
{-# LANGUAGE GADTs #-}
6+
{-# LANGUAGE KindSignatures #-}
7+
{-# LANGUAGE MultiParamTypeClasses #-}
8+
{-# LANGUAGE PolyKinds #-}
9+
{-# LANGUAGE QuantifiedConstraints #-}
10+
{-# LANGUAGE TemplateHaskell #-}
11+
{-# LANGUAGE TupleSections #-}
12+
{-# LANGUAGE TypeFamilies #-}
13+
{-# LANGUAGE UndecidableInstances #-}
14+
{-# OPTIONS_GHC -Wno-name-shadowing #-}
15+
16+
module Test.QuickCheck.StateModel.IOSim where
17+
18+
import Control.Concurrent
19+
import Control.Concurrent.STM
20+
import Control.Monad.IOSim
21+
import Control.Monad.Class.MonadFork qualified as IOClass
22+
import Control.Monad.Class.MonadSTM qualified as IOClass
23+
-- TODO: when we've updated the dependency on io-sim
24+
-- import Control.Monad.Class.MonadMVar qualified as IOClass
25+
26+
import Test.QuickCheck.StateModel
27+
28+
type family RealizeIOSim s a where
29+
RealizeIOSim s ThreadId = IOClass.ThreadId (IOSim s)
30+
RealizeIOSim s (TVar a) = IOClass.TVar (IOSim s) a
31+
-- TODO: when we've updated the dependency on io-sim
32+
-- RealizeIOSim s (MVar a) = IOClass.MVar (IOSim s) a
33+
-- TODO: unfortunately no poly-kinded recursion for type families
34+
-- so we can't do something like :'(
35+
-- RealizeIOSim s (f a) = (RealizeIOSim f) (RealizeIOSim s a)
36+
RealizeIOSim s (f a b) = f (RealizeIOSim s a) (RealizeIOSim s b)
37+
RealizeIOSim s (f a) = f (RealizeIOSim s a)
38+
RealizeIOSim s a = a
39+
40+
type instance Realized (IOSim s) a = RealizeIOSim s a
File renamed without changes.
File renamed without changes.

quickcheck-io-sim-compat/test/Spec/DynamicLogic/RegistryModel.hs renamed to quickcheck-dynamic-iosim/test/Spec/DynamicLogic/RegistryModel.hs

Lines changed: 51 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
{-# LANGUAGE StandaloneDeriving #-}
1111
{-# LANGUAGE TypeFamilies #-}
1212
{-# LANGUAGE UndecidableInstances #-}
13+
{-# LANGUAGE TypeApplications #-}
1314

1415
module Spec.DynamicLogic.RegistryModel where
1516

16-
--import Control.Concurrent
17+
import Control.Concurrent (ThreadId)
1718
import Control.Exception (SomeException (..))
18-
import Control.Monad.Class.MonadFork
19+
import Control.Monad.Class.MonadFork hiding (ThreadId)
20+
import Control.Monad.Class.MonadFork qualified as IOClass
1921

2022
import Control.Monad.Class.MonadThrow (try)
2123
import Control.Monad.Class.MonadTimer (threadDelay)
@@ -33,12 +35,12 @@ import Test.Tasty.QuickCheck (testProperty)
3335
import Spec.DynamicLogic.Registry
3436
import Test.QuickCheck.DynamicLogic.Core
3537
import Test.QuickCheck.StateModel
36-
import Test.QuickCheck.StateModel.IOSim
38+
import Test.QuickCheck.StateModel.IOSim()
3739

3840
data RegState = RegState
39-
{ tids :: [Var ModelThreadId]
40-
, regs :: [(String, Var ModelThreadId)]
41-
, dead :: [Var ModelThreadId]
41+
{ tids :: [Var ThreadId]
42+
, regs :: [(String, Var ThreadId)]
43+
, dead :: [Var ThreadId]
4244
, setup :: Bool
4345
}
4446
deriving (Show)
@@ -48,12 +50,12 @@ deriving instance Eq (Action RegState a)
4850

4951
instance StateModel RegState where
5052
data Action RegState a where
51-
Init :: Action RegState ()
52-
Spawn :: Action RegState ModelThreadId
53-
WhereIs :: String -> Action RegState (Maybe ModelThreadId)
54-
Register :: String -> Var ModelThreadId -> Action RegState (Either SomeException ())
53+
Init :: Action RegState ()
54+
Spawn :: Action RegState ThreadId
55+
WhereIs :: String -> Action RegState (Maybe ThreadId)
56+
Register :: String -> Var ThreadId -> Action RegState (Either SomeException ())
5557
Unregister :: String -> Action RegState (Either SomeException ())
56-
KillThread :: Var ModelThreadId -> Action RegState ()
58+
KillThread :: Var ThreadId -> Action RegState ()
5759
-- not generated
5860
Successful :: Action RegState a -> Action RegState a
5961

@@ -126,15 +128,37 @@ instance StateModel RegState where
126128
precondition s (Successful act) = precondition s act
127129
precondition _ _ = True
128130

129-
postcondition _ Init _ _ = True
131+
type RegM s = StateT (Registry (IOSim s)) (IOSim s)
132+
133+
instance (m ~ RegM s) => RunModel RegState m where
134+
perform _ Init _ = do
135+
reg <- lift setupRegistry
136+
put reg
137+
perform _ Spawn _ = do
138+
lift $ forkIO (threadDelay 10000000)
139+
perform _ (Register name tid) env = do
140+
reg <- get
141+
lift $ try $ register reg name (env tid)
142+
perform _ (Unregister name) _ = do
143+
reg <- get
144+
lift $ try $ unregister reg name
145+
perform _ (WhereIs name) _ = do
146+
reg <- get
147+
lift $ whereis reg name
148+
perform _ (KillThread tid) env = do
149+
lift $ killThread (env tid)
150+
perform s (Successful act) env = do
151+
perform s act env
152+
153+
postcondition _ Init _ _ = pure True
130154
postcondition s (WhereIs name) env mtid =
131-
(env <$> lookup name (regs s)) == mtid
132-
postcondition _s Spawn _ _ = True
155+
pure $ (env <$> lookup name (regs s)) == mtid
156+
postcondition _s Spawn _ _ = pure True
133157
postcondition s (Register name step) _ res =
134-
positive s (Register name step) == isRight res
135-
postcondition _s (Unregister _name) _ _ = True
136-
postcondition _s (KillThread _) _ _ = True
137-
postcondition _s (Successful (Register _ _)) _ res = isRight res
158+
pure $ positive s (Register name step) == isRight res
159+
postcondition _s (Unregister _name) _ _ = pure True
160+
postcondition _s (KillThread _) _ _ = pure True
161+
postcondition _s (Successful (Register _ _)) _ res = pure $ isRight res
138162
postcondition s (Successful act) env res = postcondition s act env res
139163

140164
monitoring (_s, s') act _ res =
@@ -152,35 +176,6 @@ instance StateModel RegState where
152176
WhereIs _ -> tabu "WhereIs" [case res of Nothing -> "fails"; Just _ -> "succeeds"]
153177
_ -> id
154178

155-
runModelIOSim :: forall s. RunModel RegState (IOSimModel (Registry (IOSim s)) s)
156-
runModelIOSim = RunModel performRegistry
157-
where
158-
performRegistry :: forall a. RegState -> Action RegState a -> LookUp -> IOSimModel (Registry (IOSim s)) s a
159-
performRegistry _ Init _ = do
160-
reg <- liftIOSim setupRegistry
161-
put reg
162-
performRegistry _ Spawn _ =
163-
encapsulateM $ forkIO (threadDelay 10000000)
164-
performRegistry _ (Register name tid) env =
165-
do
166-
reg <- get
167-
tid' <- instantiateM (env tid)
168-
liftIOSim $ try $ register reg name tid'
169-
performRegistry _ (Unregister name) _ =
170-
do
171-
reg <- get
172-
liftIOSim $ try $ unregister reg name
173-
performRegistry _ (WhereIs name) _ =
174-
do
175-
reg <- get
176-
encapsulateM $ whereis reg name
177-
performRegistry _ (KillThread tid) env =
178-
do
179-
tid' <- instantiateM (env tid)
180-
liftIOSim $ killThread tid'
181-
performRegistry s (Successful act) env =
182-
performRegistry s act env
183-
184179
positive :: RegState -> Action RegState a -> Bool
185180
positive s (Register name tid) =
186181
name `notElem` map fst (regs s)
@@ -217,7 +212,7 @@ shrinkName name = [n | n <- allNames, n < name]
217212
allNames :: [String]
218213
allNames = ["a", "b", "c", "d", "e"]
219214

220-
shrinkTid :: RegState -> Var ModelThreadId -> [Var ModelThreadId]
215+
shrinkTid :: RegState -> Var ThreadId -> [Var ThreadId]
221216
shrinkTid s tid = [tid' | tid' <- tids s, tid' < tid]
222217

223218
tabu :: String -> [String] -> Property -> Property
@@ -242,15 +237,15 @@ prop_Registry :: Actions RegState -> Property
242237
prop_Registry s =
243238
property $
244239
runIOSimProp $ do
245-
-- _ <- run cleanUp
246240
monitor $ counterexample "\nExecution\n"
247-
_res <- runPropertyIOSim (runActions runModelIOSim s) (error "don't look at uninitialized state")
241+
_res <- runPropertyStateT (runActions s) (error "don't look at uninitialized state")
248242
assert True
249243

250-
-- cleanUp :: IO [Either ErrorCall ()]
251-
-- cleanUp = sequence
252-
-- [try (unregister name) :: IO (Either ErrorCall ())
253-
-- | name <- allNames++["x"]]
244+
-- TODO: put this in some extras module
245+
runPropertyStateT :: Monad m => PropertyM (StateT s m) a -> s -> PropertyM m (a, s)
246+
runPropertyStateT p s0 = MkPropertyM $ \k -> do
247+
m <- unPropertyM (do a <- p; s <- run get; return (a, s)) $ fmap lift . k
248+
return $ evalStateT m s0
254249

255250
propTest :: DynFormula RegState -> Property
256251
propTest d = forAllScripts d prop_Registry
@@ -318,7 +313,7 @@ canRegisterName' name s = forAllQ (elementsQ availableTids) $ \tid ->
318313
where
319314
availableTids = (tids s \\ map snd (regs s)) \\ dead s
320315

321-
canReregister' :: Show (ThreadId (IOSim s)) => RegState -> DynFormula RegState
316+
canReregister' :: Show (IOClass.ThreadId (IOSim s)) => RegState -> DynFormula RegState
322317
canReregister' s
323318
| null (regs s) =
324319
toStop $
@@ -335,4 +330,4 @@ tests =
335330
testGroup
336331
"registry model example"
337332
-- TODO: fix property
338-
[testProperty "prop_Registry" (property True)]
333+
[testProperty "prop_Registry" prop_Registry]

quickcheck-dynamic/quickcheck-dynamic.cabal

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ license: Apache-2.0
55
license-files:
66
LICENSE
77
NOTICE
8-
8+
99
maintainer: [email protected]
1010
author: Ulf Norell
1111
homepage:
@@ -57,4 +57,5 @@ library
5757
build-depends:
5858
QuickCheck -any,
5959
base >=4.7 && <5,
60-
random -any
60+
random -any,
61+
mtl -any

0 commit comments

Comments
 (0)