From 19eda90b9a02a818662928b5b1ff3a87c9557da2 Mon Sep 17 00:00:00 2001 From: Georgy Lukyanov Date: Fri, 28 Feb 2025 10:58:01 +0000 Subject: [PATCH 1/2] Add runActionsFrom Allow running the action sequence from a user supplied initial state --- .../src/Test/QuickCheck/StateModel.hs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/quickcheck-dynamic/src/Test/QuickCheck/StateModel.hs b/quickcheck-dynamic/src/Test/QuickCheck/StateModel.hs index a67507d..1dc867d 100644 --- a/quickcheck-dynamic/src/Test/QuickCheck/StateModel.hs +++ b/quickcheck-dynamic/src/Test/QuickCheck/StateModel.hs @@ -31,6 +31,7 @@ module Test.QuickCheck.StateModel ( monitorPost, counterexamplePost, stateAfter, + runActionsFrom, runActions, lookUpVar, lookUpVarMaybe, @@ -548,7 +549,19 @@ runActions ) => Actions state -> PropertyM m (Annotated state, Env) -runActions (Actions_ rejected (Smart _ actions)) = do +runActions = runActionsFrom initialAnnotatedState + +runActionsFrom + :: forall state m e + . ( StateModel state + , RunModel state m + , e ~ Error state m + , forall a. IsPerformResult e a + ) + => Annotated state + -> Actions state + -> PropertyM m (Annotated state, Env) +runActionsFrom annotatedState (Actions_ rejected (Smart _ actions)) = do let bucket = \n -> let (a, b) = go n in show a ++ " - " ++ show b where go n @@ -557,7 +570,7 @@ runActions (Actions_ rejected (Smart _ actions)) = do where d = div n 10 monitor $ tabulate "# of actions" [show $ bucket $ length actions] - (finalState, env, names, polars) <- runSteps initialAnnotatedState [] actions + (finalState, env, names, polars) <- runSteps annotatedState [] actions monitor $ tabulate "Actions" names monitor $ tabulate "Action polarity" $ map show polars unless (null rejected) $ From dbe1f31e634643f95a090f5a8e874f3fd8119615 Mon Sep 17 00:00:00 2001 From: Georgy Lukyanov Date: Tue, 4 Mar 2025 12:48:29 +0000 Subject: [PATCH 2/2] Allow using a custom initial state when generating actions --- .../src/Test/QuickCheck/StateModel.hs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/quickcheck-dynamic/src/Test/QuickCheck/StateModel.hs b/quickcheck-dynamic/src/Test/QuickCheck/StateModel.hs index 1dc867d..20e7467 100644 --- a/quickcheck-dynamic/src/Test/QuickCheck/StateModel.hs +++ b/quickcheck-dynamic/src/Test/QuickCheck/StateModel.hs @@ -387,7 +387,7 @@ class QCDProp state p | p -> state where instance QCDProp state (QCDProperty state) where qcdProperty = id -instance Testable p => QCDProp state (Actions state -> p) where +instance (StateModel state, Testable p) => QCDProp state (Actions state -> p) where qcdProperty p = QCDProperty (property . p) defaultOptions modifyOptions :: QCDProperty state -> (Options state -> Options state) -> QCDProperty state @@ -397,25 +397,23 @@ modifyOptions p f = moreActions :: QCDProp state p => Rational -> p -> QCDProperty state moreActions r p = - modifyOptions (qcdProperty p) $ \opts -> opts{actionLengthMultiplier = actionLengthMultiplier opts * r} + modifyOptions (qcdProperty p) $ \opts -> opts{oActionLengthMultiplier = oActionLengthMultiplier opts * r} --- NOTE: indexed on state for forwards compatibility, e.g. when we --- want to give an explicit initial state -data Options state = Options {actionLengthMultiplier :: Rational} +data Options state = Options {oActionLengthMultiplier :: Rational, oInitialAnnotatedState :: Annotated state} -defaultOptions :: Options state -defaultOptions = Options{actionLengthMultiplier = 1} +defaultOptions :: StateModel state => Options state +defaultOptions = Options{oActionLengthMultiplier = 1, oInitialAnnotatedState = initialAnnotatedState} -- | Generate arbitrary actions with the `GenActionsOptions`. More flexible than using the type-based -- modifiers. generateActionsWithOptions :: forall state. StateModel state => Options state -> Gen (Actions state) generateActionsWithOptions Options{..} = do - (as, rejected) <- arbActions [] [] initialAnnotatedState 1 + (as, rejected) <- arbActions [] [] oInitialAnnotatedState 1 return $ Actions_ rejected (Smart 0 as) where arbActions :: [Step state] -> [String] -> Annotated state -> Int -> Gen ([Step state], [String]) arbActions steps rejected s step = sized $ \n -> do - let w = round (actionLengthMultiplier * fromIntegral n) `div` 2 + 1 + let w = round (oActionLengthMultiplier * fromIntegral n) `div` 2 + 1 continue <- frequency [(1, pure False), (w, pure True)] if continue then do