18
18
19
19
-- | Tests for the Genesis State Machine using the [quickcheck-dynamic](https://hackage.haskell.org/package/quickcheck-dynamic) library.
20
20
--
21
- -- The instance 'QD.StateModel ( Model) ' describes the actions that the model supports,
21
+ -- The instance 'QD.StateModel Model' describes the actions that the model supports,
22
22
-- and their semantics in terms for the model and the system-under-test.
23
23
--
24
24
-- We use the [reflection](https://hackage.haskell.org/package/reflection) library to solve the problem of
@@ -30,7 +30,7 @@ module Test.Consensus.GSM (tests) where
30
30
import Cardano.Network.Types (LedgerStateJudgement (.. ))
31
31
import Control.Concurrent.Class.MonadSTM.Strict.TVar.Checked
32
32
import Control.Exception (SomeException (.. ))
33
- import Control.Monad ( replicateM_ )
33
+ import qualified Control.Monad as Monad
34
34
import Control.Monad.Class.MonadAsync (async , poll ,
35
35
uninterruptibleCancel )
36
36
import Control.Monad.Class.MonadFork (MonadFork , yield )
@@ -110,9 +110,9 @@ prop_sequential_iosim pUpstreamPeerBound pInitialJudgement (QC.Fun _ pIsHaaSatis
110
110
, pInitialJudgement
111
111
, pIsHaaSatisfied
112
112
})
113
- -- NOTE: the actions have to be generated with an explicit call, as
114
- -- 'generator' relies on reflection for access to the parameters
115
- $ QC. forAll QC. arbitrary $ \ actions ->
113
+ -- NOTE: the actions have to be generated within the application of 'Reflection.give',
114
+ -- as 'generator' relies on reflection for access to the parameters.
115
+ $ QC. property $ \ actions ->
116
116
runIOSimProp $ prop_sequential_iosim1 actions
117
117
118
118
setupGsm :: (Set. Set UpstreamPeer -> Bool ) -> SystemStateVars (IOSim. IOSim s ) -> GSM. GsmEntryPoints (IOSim. IOSim s )
@@ -151,7 +151,7 @@ setupGsm isHaaSatisfied vars = do
151
151
prop_sequential_iosim1 ::
152
152
forall s .
153
153
Reflection. Given StaticParams =>
154
- QD. Actions ( Model ) ->
154
+ QD. Actions Model ->
155
155
QC. PropertyM (RunMonad (IOSim. IOSim s )) QC. Property
156
156
prop_sequential_iosim1 actions = do
157
157
vars@ SystemStateVars {varEvents} <- lift ask
@@ -169,7 +169,7 @@ prop_sequential_iosim1 actions = do
169
169
170
170
lift . lift $ yieldSeveralTimes
171
171
172
- -- TODO: figure out how to do withAsync here
172
+ -- start the GSM
173
173
hGSM <- lift . lift $ async gsmEntryPoint
174
174
175
175
(metadata, mbExn) <- do
@@ -248,8 +248,8 @@ data Model = Model {
248
248
deriving anyclass (TD.ToExpr )
249
249
250
250
-- | Initialise the 'Model' state from 'StaticParams'
251
- initModel :: StaticParams - > Model
252
- initModel StaticParams {pIsHaaSatisfied, pInitialJudgement} = Model {
251
+ initModel :: Reflection. Given StaticParams = > Model
252
+ initModel = Model {
253
253
mCandidates = Map. empty
254
254
,
255
255
mClock = SI. Time 0
@@ -271,32 +271,34 @@ initModel StaticParams{pIsHaaSatisfied, pInitialJudgement} = Model {
271
271
where
272
272
idlers = Set. empty
273
273
274
+ StaticParams {pIsHaaSatisfied, pInitialJudgement} = Reflection. given
275
+
274
276
-- | The 'StaticParams' are supplied through reflection because this seems to be
275
277
-- the cleanest way to pass static configuration needed by the 'initState' and
276
278
-- 'arbitraryAction' methods
277
- instance Reflection. Given StaticParams => QD. StateModel ( Model ) where
278
- data Action ( Model ) a where
279
- Disconnect :: UpstreamPeer -> QD. Action ( Model ) ()
279
+ instance Reflection. Given StaticParams => QD. StateModel Model where
280
+ data Action Model a where
281
+ Disconnect :: UpstreamPeer -> QD. Action Model ()
280
282
-- ^ INVARIANT must be an existing peer
281
283
--
282
284
-- Mocks the necessary ChainSync client behavior.
283
- ExtendSelection :: S -> QD. Action ( Model ) ()
285
+ ExtendSelection :: S -> QD. Action Model ()
284
286
-- ^ INVARIANT 'selectionIsBehind'
285
287
--
286
288
-- NOTE Harmless to assume it only advances by @'B' 1@ at a time.
287
- ModifyCandidate :: UpstreamPeer -> B -> QD. Action ( Model ) ()
289
+ ModifyCandidate :: UpstreamPeer -> B -> QD. Action Model ()
288
290
-- ^ INVARIANT existing peer
289
291
--
290
292
-- Mocks the necessary ChainSync client behavior.
291
- NewCandidate :: UpstreamPeer -> B -> QD. Action ( Model ) ()
293
+ NewCandidate :: UpstreamPeer -> B -> QD. Action Model ()
292
294
-- ^ INVARIANT new peer
293
295
--
294
296
-- Mocks the necessary ChainSync client behavior.z
295
- ReadGsmState :: QD. Action ( Model ) GSM. GsmState
296
- ReadMarker :: QD. Action ( Model ) MarkerState
297
- StartIdling :: UpstreamPeer -> QD. Action ( Model ) ()
297
+ ReadGsmState :: QD. Action Model GSM. GsmState
298
+ ReadMarker :: QD. Action Model MarkerState
299
+ StartIdling :: UpstreamPeer -> QD. Action Model ()
298
300
-- ^ INVARIANT existing peer, not idling
299
- TimePasses :: Int -> QD. Action ( Model ) ()
301
+ TimePasses :: Int -> QD. Action Model ()
300
302
-- ^ tenths of a second
301
303
--
302
304
-- INVARIANT positive
@@ -313,8 +315,7 @@ instance Reflection.Given StaticParams => QD.StateModel (Model) where
313
315
314
316
shrinkAction _ctx model = shrinker model
315
317
316
- initialState = initModel params
317
- where params = Reflection. given
318
+ initialState = initModel
318
319
319
320
nextState model action _ = transition model action
320
321
@@ -408,7 +409,7 @@ newtype RunMonad m a = RunMonad {runMonad :: ReaderT (SystemStateVars m) m a}
408
409
instance MonadTrans RunMonad where
409
410
lift = RunMonad . lift
410
411
411
- instance (IOLike m , Reflection. Given StaticParams ) => QD. RunModel ( Model ) (RunMonad m ) where
412
+ instance (IOLike m , Reflection. Given StaticParams ) => QD. RunModel Model (RunMonad m ) where
412
413
perform _ action _ = do
413
414
SystemStateVars {varSelection, varStates, varGsmState, varMarker, varEvents} <- ask
414
415
let
@@ -492,7 +493,7 @@ runIOSimProp p =
492
493
-- the modified model and a property that checks that the action brought
493
494
-- the model and the SUT into the same state.
494
495
takeActionInBoth :: (IOLike m , Reflection. Given StaticParams )
495
- => String -> Model -> QD. Action ( Model ) GSM. GsmState
496
+ => String -> Model -> QD. Action Model GSM. GsmState
496
497
-> QC. PropertyM (RunMonad m ) (QC. Property , Model )
497
498
takeActionInBoth conterexampleMessage model action = do
498
499
-- run the action in the model
@@ -530,7 +531,7 @@ boringDur model = boringDurImpl clk sel st
530
531
mState = st
531
532
} = model
532
533
533
- addNotableWhen :: Notable -> Bool -> ( Model ) -> ( Model )
534
+ addNotableWhen :: Notable -> Bool -> Model -> Model
534
535
addNotableWhen n b model =
535
536
if not b then model else
536
537
model { mNotables = n `Set.insert` mNotables model }
@@ -569,7 +570,7 @@ dummyVar = QD.mkVar 0
569
570
--- Definitions used in the 'ModelState' instance
570
571
571
572
precondition :: forall a . Reflection. Given StaticParams
572
- => Model -> QD. Action ( Model ) a -> Bool
573
+ => Model -> QD. Action Model a -> Bool
573
574
precondition model = \ case
574
575
cmd@ ExtendSelection {} ->
575
576
let model' = QD. nextState model cmd dummyVar
@@ -604,8 +605,8 @@ precondition model = \case
604
605
mIdlers = idlers
605
606
} = model
606
607
607
- generator :: forall . Reflection. Given StaticParams
608
- => Model -> QC. Gen (QD. Any (QD. Action ( Model ) ))
608
+ generator :: Reflection. Given StaticParams
609
+ => Model -> QC. Gen (QD. Any (QD. Action Model ))
609
610
generator model = QC. frequency $
610
611
[ (,) 5 $ QD. Some . Disconnect <$> QC. elements old | notNull old ]
611
612
<>
@@ -692,7 +693,7 @@ generator model = QC.frequency $
692
693
]
693
694
694
695
695
- shrinker :: Model -> QD. Action ( Model ) a -> [QD. Any (QD. Action ( Model ) )]
696
+ shrinker :: Model -> QD. Action Model a -> [QD. Any (QD. Action Model )]
696
697
shrinker _model = \ case
697
698
Disconnect {} ->
698
699
[]
@@ -715,7 +716,7 @@ shrinker _model = \case
715
716
shrinkS (S x) = [ S x' | x' <- QC. shrink x ]
716
717
717
718
transition :: forall a . Reflection. Given StaticParams
718
- => Model -> QD. Action ( Model ) a -> Model
719
+ => Model -> QD. Action Model a -> Model
719
720
transition model cmd =
720
721
fixupModelState cmd $
721
722
case cmd of
@@ -766,9 +767,8 @@ transition model cmd =
766
767
767
768
-- | Update the 'mState', assuming that's the only stale field in the given
768
769
-- 'Model'
769
- --
770
770
fixupModelState :: forall a . Reflection. Given StaticParams
771
- => QD. Action ( Model ) a -> Model -> Model
771
+ => QD. Action Model a -> Model -> Model
772
772
fixupModelState cmd model =
773
773
case st of
774
774
ModelPreSyncing
@@ -973,7 +973,7 @@ isIdling (PeerState {psIdling = Idling i}) = i
973
973
-- Despite the crudeness, this seems much more compositional than invasive
974
974
-- explicit synchronization.
975
975
yieldSeveralTimes :: MonadFork m => m ()
976
- yieldSeveralTimes = replicateM_ 10 yield
976
+ yieldSeveralTimes = Monad. replicateM_ 10 yield
977
977
978
978
{-
979
979
0 commit comments