Skip to content

Commit 4e14e7d

Browse files
store interleavings in a tree
1 parent e340c05 commit 4e14e7d

File tree

2 files changed

+32
-26
lines changed

2 files changed

+32
-26
lines changed

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

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import Test.QuickCheck.StateModel.Variables
6969
import Control.Concurrent
7070
import Control.Monad.Identity
7171
import Control.Arrow (first)
72+
import Data.Tree
7273

7374
-- | The typeclass users implement to define a model against which to validate some implementation.
7475
--
@@ -576,13 +577,6 @@ runActions
576577
=> Actions state
577578
-> PropertyM m (Annotated state, Env)
578579
runActions (Actions_ rejected (Smart _ actions)) = do
579-
let bucket = \n -> let (a, b) = go n in show a ++ " - " ++ show b
580-
where
581-
go n
582-
| n < 100 = (d * 10, d * 10 + 9)
583-
| otherwise = let (a, b) = go d in (a * 10, b * 10 + 9)
584-
where
585-
d = div n 10
586580
monitor $ tabulate "# of actions" [show $ bucket $ length actions]
587581
(finalState, env, names, polars) <- runSteps initialAnnotatedState [] actions
588582
monitor $ tabulate "Actions" names
@@ -592,6 +586,14 @@ runActions (Actions_ rejected (Smart _ actions)) = do
592586
tabulate "Actions rejected by precondition" rejected
593587
return (finalState, env)
594588

589+
bucket = \n -> let (a, b) = go n in show a ++ " - " ++ show b
590+
where
591+
go n
592+
| n < 100 = (d * 10, d * 10 + 9)
593+
| otherwise = let (a, b) = go d in (a * 10, b * 10 + 9)
594+
where
595+
d = div n 10
596+
595597
-- | Core function to execute a sequence of `Step` given some initial `Env`ironment and `Annotated`
596598
-- state. Return the list of action names and polarities to work around
597599
-- https://github.com/nick8325/quickcheck/issues/416 causing repeated calls to tabulate being slow.
@@ -727,7 +729,7 @@ genParActions = do
727729
-- The ~ works around a bug in ghc (https://gitlab.haskell.org/ghc/ghc/-/issues/22004) (which is not in all ghc versions ?!)
728730
as@(~(Actions steps)) <- QC.arbitrary
729731
split <- QC.choose (0, length steps - 1)
730-
let (common, post) = splitAt split steps
732+
let (common, take 10 -> post) = splitAt split steps
731733
commonCtx = allVariables common
732734
(threadA, threadB) <- go post commonCtx commonCtx [] []
733735
return $ ParallelActions as threadA threadB
@@ -756,6 +758,7 @@ data TraceStep state m where
756758
-> TraceStep state m
757759

758760
type Trace state m = [TraceStep state m]
761+
type TraceTree state m = Tree (TraceStep state m)
759762

760763
runTracing :: ( RunModelPar state m
761764
, e ~ Error state m
@@ -800,8 +803,9 @@ runParActions pas = do
800803
monitorTrace env trA
801804
monitor $ counterexample "-- Monitoring thread B"
802805
monitorTrace env trB
803-
let ilvs = map (trC ++) $ interleavings trA trB
804-
assert $ any (checkTrace initialAnnotatedState mempty) ilvs
806+
let ilvs = interleavings trC trA trB
807+
monitor $ tabulate "TraceTree size" (map (bucket . length) ilvs)
808+
assert $ null ilvs || any (checkTrace initialAnnotatedState mempty) ilvs
805809
-- TODO: stats collection and cleanup
806810

807811
monitorTrace :: forall state m. (StateModel state, RunModelPar state m)
@@ -814,9 +818,8 @@ monitorTrace env (TraceStep r v step : tr) = do
814818
env' | Right val <- r = (v :== val) : env
815819
| otherwise = env
816820

817-
checkTrace :: forall state m. (StateModel state, RunModelPar state m) => Annotated state -> Env -> Trace state m -> Bool
818-
checkTrace _ _ [] = True
819-
checkTrace s env (TraceStep r v (ActionWithPolarity a _) : tr) =
821+
checkTrace :: forall state m. (StateModel state, RunModelPar state m) => Annotated state -> Env -> TraceTree state m -> Bool
822+
checkTrace s env (Node (TraceStep r v (ActionWithPolarity a _)) trs) =
820823
-- NOTE: we need to re-compute the polarity of `a` here because it may be that the failure can be explained,
821824
-- but only by the action failing when it was previous successful
822825
let act = actionWithPolarity s a
@@ -834,14 +837,17 @@ checkTrace s env (TraceStep r v (ActionWithPolarity a _) : tr) =
834837
(polarAction act)
835838
(lookUpVar env')
836839
r
837-
in computePrecondition s act && checkPost && checkTrace s' env' tr
838-
839-
interleavings :: [a] -> [a] -> [[a]]
840-
interleavings [] bs = [bs]
841-
interleavings as [] = [as]
842-
interleavings (a:as) (b:bs) =
843-
map (a:) (interleavings as (b:bs)) ++
844-
map (b:) (interleavings (a:as) bs)
840+
in computePrecondition s act && checkPost && (null trs || any (checkTrace s' env') trs)
841+
842+
interleavings :: [a] -> [a] -> [a] -> [Tree a]
843+
interleavings (p:ps) as bs = [Node p (interleavings ps as bs)]
844+
interleavings _ [] bs = spine bs
845+
interleavings _ as [] = spine as
846+
interleavings _ (a:as) (b:bs) = [Node a (interleavings [] as (b:bs)), Node b (interleavings [] (a:as) bs)]
847+
848+
spine :: [a] -> [Tree a]
849+
spine [] = []
850+
spine (x:xs) = [Node x (spine xs)]
845851

846852
-- TODO:
847853
-- - Decide how much of the old QCD interface we can afford to break

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,11 @@ instance RunModel Counter (ReaderT (IORef Int) IO) where
107107
instance RunModelPar Counter (ReaderT (IORef Int) IO) where
108108
performPar Inc _ = do
109109
ref <- ask
110-
lift $ atomicModifyIORef ref (\ c -> (c+1, ()))
111-
-- lift $ do
112-
-- n <- readIORef ref
113-
-- threadDelay 50000
114-
-- writeIORef ref (n + 1)
110+
-- lift $ atomicModifyIORef ref (\ c -> (c+1, ()))
111+
lift $ do
112+
n <- readIORef ref
113+
threadDelay 5000
114+
writeIORef ref (n + 1)
115115
performPar Reset _ = do
116116
ref <- ask
117117
lift $ atomicModifyIORef ref (\ c -> (0, c))

0 commit comments

Comments
 (0)