{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Test.Control.State.Transition.Generator (
HasTrace,
SignalGenerator,
BaseEnv,
interpretSTS,
envGen,
sigGen,
traceSigGen,
genTrace,
trace,
traceWithProfile,
traceOfLength,
traceOfLengthWithInitState,
traceSuchThat,
ofLengthAtLeast,
suchThatLastState,
nonTrivialTrace,
HasSizeInfo,
isTrivial,
sampleMaxTraceSize,
randomTrace,
randomTraceOfSize,
TraceLength (Maximum, Desired),
TraceProfile (TraceProfile, proportionOfValidSignals, failures),
proportionOfInvalidSignals,
invalidTrace,
classifyTraceLength,
classifySize,
mkIntervals,
ratio,
traceLengthsAreClassified,
onlyValidSignalsAreGenerated,
onlyValidSignalsAreGeneratedForTrace,
invalidSignalsAreGenerated,
coverFailures,
)
where
import Control.Arrow (second)
import Control.Monad (forM, void)
import Control.Monad.Trans.Maybe (MaybeT)
import Control.State.Transition.Extended (
BaseM,
Environment,
IRC (IRC),
PredicateFailure,
STS,
Signal,
State,
TRC (TRC),
)
import Data.Data (Constr, Data, toConstr)
import Data.Either (isLeft)
import Data.Foldable (traverse_)
import Data.Functor.Identity (Identity (..))
import Data.Kind (Type)
import Data.List.NonEmpty (NonEmpty)
import Data.Maybe (fromMaybe)
import Data.String (fromString)
import Data.Word (Word64)
import GHC.Stack (HasCallStack)
import Hedgehog (
Gen,
MonadTest,
Property,
PropertyT,
classify,
cover,
evalEither,
footnoteShow,
forAll,
property,
success,
)
import qualified Hedgehog.Gen as Gen
import Hedgehog.Internal.Gen (integral_, runDiscardEffectT)
import Hedgehog.Internal.Property (CoverPercentage)
import Hedgehog.Internal.Tree (NodeT (NodeT), TreeT, nodeChildren, treeValue)
import Hedgehog.Range (Size (Size))
import qualified Hedgehog.Range as Range
import qualified Test.Control.State.Transition.Invalid.Trace as Invalid
import Test.Control.State.Transition.Trace (
Trace,
TraceOrder (OldestFirst),
applySTSTest,
closure,
extractValues,
lastState,
mkTrace,
traceLength,
traceSignals,
_traceEnv,
)
import Test.Hedgehog.Extra.Manual (Manual)
import qualified Test.Hedgehog.Extra.Manual as Manual
class STS s => HasTrace s where
type BaseEnv s :: Type
type BaseEnv s = ()
interpretSTS :: forall a. (BaseEnv s -> BaseM s a -> a)
default interpretSTS :: BaseM s ~ Identity => forall a. BaseEnv s -> BaseM s a -> a
interpretSTS BaseEnv s
_ (Identity a
x) = a
x
envGen ::
Word64 ->
Gen (Environment s)
sigGen ::
SignalGenerator s
trace ::
BaseEnv s ->
Word64 ->
Gen (Trace s)
trace BaseEnv s
baseEnv Word64
n = forall s.
HasTrace s =>
BaseEnv s -> Word64 -> TraceProfile s -> Gen (Trace s)
traceWithProfile @s BaseEnv s
baseEnv Word64
n forall s. TraceProfile s
allValid
traceWithProfile ::
BaseEnv s ->
Word64 ->
TraceProfile s ->
Gen (Trace s)
traceWithProfile BaseEnv s
baseEnv Word64
n TraceProfile s
p = forall s.
HasTrace s =>
BaseEnv s
-> TraceLength
-> TraceProfile s
-> SignalGenerator s
-> Gen (Trace s)
traceSigGenWithProfile BaseEnv s
baseEnv (Word64 -> TraceLength
Maximum Word64
n) TraceProfile s
p (forall s. HasTrace s => SignalGenerator s
sigGen @s)
traceOfLength ::
BaseEnv s ->
Word64 ->
Gen (Trace s)
traceOfLength BaseEnv s
baseEnv Word64
n = forall s.
HasTrace s =>
BaseEnv s
-> TraceLength
-> TraceProfile s
-> SignalGenerator s
-> Gen (Trace s)
traceSigGenWithProfile BaseEnv s
baseEnv (Word64 -> TraceLength
Desired Word64
n) forall s. TraceProfile s
allValid (forall s. HasTrace s => SignalGenerator s
sigGen @s)
traceOfLengthWithInitState ::
BaseEnv s ->
Word64 ->
(Environment s -> Gen (State s)) ->
Gen (Trace s)
traceOfLengthWithInitState BaseEnv s
baseEnv Word64
n Environment s -> Gen (State s)
mkSt0 =
forall s.
HasTrace s =>
BaseEnv s
-> TraceLength
-> TraceProfile s
-> SignalGenerator s
-> (Environment s -> Gen (State s))
-> Gen (Trace s)
traceSigGenWithProfileAndInitState BaseEnv s
baseEnv (Word64 -> TraceLength
Desired Word64
n) forall s. TraceProfile s
allValid (forall s. HasTrace s => SignalGenerator s
sigGen @s) Environment s -> Gen (State s)
mkSt0
type SignalGenerator s = Environment s -> State s -> Gen (Signal s)
data TraceLength = Maximum Word64 | Desired Word64
data TraceProfile s = TraceProfile
{ forall s. TraceProfile s -> Int
proportionOfValidSignals :: !Int
, forall s. TraceProfile s -> [(Int, SignalGenerator s)]
failures :: ![(Int, SignalGenerator s)]
}
proportionOfInvalidSignals :: TraceProfile s -> Int
proportionOfInvalidSignals :: forall s. TraceProfile s -> Int
proportionOfInvalidSignals = forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a b. (a, b) -> a
fst forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s. TraceProfile s -> [(Int, SignalGenerator s)]
failures
allValid :: TraceProfile s
allValid :: forall s. TraceProfile s
allValid =
TraceProfile
{ proportionOfValidSignals :: Int
proportionOfValidSignals = Int
1
, failures :: [(Int, SignalGenerator s)]
failures = []
}
generateSignalWithFailureProportions ::
[(Int, SignalGenerator s)] ->
SignalGenerator s
generateSignalWithFailureProportions :: forall s. [(Int, SignalGenerator s)] -> SignalGenerator s
generateSignalWithFailureProportions [(Int, SignalGenerator s)]
proportions Environment s
env State s
st =
forall (m :: * -> *) a. MonadGen m => [(Int, m a)] -> m a
Gen.frequency forall a b. (a -> b) -> a -> b
$ forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (d, b) (d, c)
second SignalGenerator s -> GenT Identity (Signal s)
aSigGenWithFailure forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(Int, SignalGenerator s)]
proportions
where
aSigGenWithFailure :: SignalGenerator s -> GenT Identity (Signal s)
aSigGenWithFailure SignalGenerator s
invalidSigGen = SignalGenerator s
invalidSigGen Environment s
env State s
st
traceLengthValue :: TraceLength -> Word64
traceLengthValue :: TraceLength -> Word64
traceLengthValue (Maximum Word64
n) = Word64
n
traceLengthValue (Desired Word64
n) = Word64
n
traceSigGen ::
forall s.
HasTrace s =>
BaseEnv s ->
TraceLength ->
SignalGenerator s ->
Gen (Trace s)
traceSigGen :: forall s.
HasTrace s =>
BaseEnv s -> TraceLength -> SignalGenerator s -> Gen (Trace s)
traceSigGen BaseEnv s
baseEnv TraceLength
aTraceLength = forall s.
HasTrace s =>
BaseEnv s
-> TraceLength
-> TraceProfile s
-> SignalGenerator s
-> Gen (Trace s)
traceSigGenWithProfile BaseEnv s
baseEnv TraceLength
aTraceLength forall s. TraceProfile s
allValid
traceSigGenWithProfile ::
forall s.
HasTrace s =>
BaseEnv s ->
TraceLength ->
TraceProfile s ->
SignalGenerator s ->
Gen (Trace s)
traceSigGenWithProfile :: forall s.
HasTrace s =>
BaseEnv s
-> TraceLength
-> TraceProfile s
-> SignalGenerator s
-> Gen (Trace s)
traceSigGenWithProfile BaseEnv s
baseEnv TraceLength
aTraceLength TraceProfile s
profile SignalGenerator s
gen = do
Environment s
env <- forall s. HasTrace s => Word64 -> Gen (Environment s)
envGen @s (TraceLength -> Word64
traceLengthValue TraceLength
aTraceLength)
case forall s a. HasTrace s => BaseEnv s -> BaseM s a -> a
interpretSTS @s BaseEnv s
baseEnv forall a b. (a -> b) -> a -> b
$ forall s (m :: * -> *) (rtype :: RuleType).
(STS s, RuleTypeRep rtype, m ~ BaseM s) =>
RuleContext rtype s
-> m (Either (NonEmpty (PredicateFailure s)) (State s))
applySTSTest @s (forall sts. Environment sts -> IRC sts
IRC Environment s
env) of
Left NonEmpty (PredicateFailure s)
_pf -> forall s.
HasTrace s =>
BaseEnv s -> TraceLength -> SignalGenerator s -> Gen (Trace s)
traceSigGen BaseEnv s
baseEnv TraceLength
aTraceLength SignalGenerator s
gen
Right State s
st -> forall s.
HasTrace s =>
BaseEnv s
-> TraceLength
-> TraceProfile s
-> Environment s
-> State s
-> SignalGenerator s
-> Gen (Trace s)
genTraceOfMaxOrDesiredLength BaseEnv s
baseEnv TraceLength
aTraceLength TraceProfile s
profile Environment s
env State s
st SignalGenerator s
gen
traceSigGenWithProfileAndInitState ::
forall s.
HasTrace s =>
BaseEnv s ->
TraceLength ->
TraceProfile s ->
SignalGenerator s ->
(Environment s -> Gen (State s)) ->
Gen (Trace s)
traceSigGenWithProfileAndInitState :: forall s.
HasTrace s =>
BaseEnv s
-> TraceLength
-> TraceProfile s
-> SignalGenerator s
-> (Environment s -> Gen (State s))
-> Gen (Trace s)
traceSigGenWithProfileAndInitState BaseEnv s
baseEnv TraceLength
aTraceLength TraceProfile s
profile SignalGenerator s
gen Environment s -> Gen (State s)
mkSt0 = do
Environment s
env <- forall s. HasTrace s => Word64 -> Gen (Environment s)
envGen @s (TraceLength -> Word64
traceLengthValue TraceLength
aTraceLength)
State s
st0 <- Environment s -> Gen (State s)
mkSt0 Environment s
env
forall s.
HasTrace s =>
BaseEnv s
-> TraceLength
-> TraceProfile s
-> Environment s
-> State s
-> SignalGenerator s
-> Gen (Trace s)
genTraceOfMaxOrDesiredLength BaseEnv s
baseEnv TraceLength
aTraceLength TraceProfile s
profile Environment s
env State s
st0 SignalGenerator s
gen
genTraceOfMaxOrDesiredLength ::
forall s.
HasTrace s =>
BaseEnv s ->
TraceLength ->
TraceProfile s ->
Environment s ->
State s ->
SignalGenerator s ->
Gen (Trace s)
genTraceOfMaxOrDesiredLength :: forall s.
HasTrace s =>
BaseEnv s
-> TraceLength
-> TraceProfile s
-> Environment s
-> State s
-> SignalGenerator s
-> Gen (Trace s)
genTraceOfMaxOrDesiredLength BaseEnv s
baseEnv TraceLength
aTraceLength TraceProfile s
profile Environment s
env State s
st0 SignalGenerator s
gen =
case TraceLength
aTraceLength of
Maximum Word64
n -> forall s.
HasTrace s =>
BaseEnv s
-> Word64
-> TraceProfile s
-> Environment s
-> State s
-> SignalGenerator s
-> Gen (Trace s)
genTraceWithProfile BaseEnv s
baseEnv Word64
n TraceProfile s
profile Environment s
env State s
st0 SignalGenerator s
gen
Desired Word64
n -> forall s.
HasTrace s =>
BaseEnv s
-> Word64
-> TraceProfile s
-> Environment s
-> State s
-> SignalGenerator s
-> Gen (Trace s)
genTraceOfLength BaseEnv s
baseEnv Word64
n TraceProfile s
profile Environment s
env State s
st0 SignalGenerator s
gen
genTrace ::
forall s.
HasTrace s =>
BaseEnv s ->
Word64 ->
Environment s ->
State s ->
SignalGenerator s ->
Gen (Trace s)
genTrace :: forall s.
HasTrace s =>
BaseEnv s
-> Word64
-> Environment s
-> State s
-> SignalGenerator s
-> Gen (Trace s)
genTrace BaseEnv s
baseEnv Word64
ub = forall s.
HasTrace s =>
BaseEnv s
-> Word64
-> TraceProfile s
-> Environment s
-> State s
-> SignalGenerator s
-> Gen (Trace s)
genTraceWithProfile BaseEnv s
baseEnv Word64
ub forall s. TraceProfile s
allValid
genTraceWithProfile ::
forall s.
HasTrace s =>
BaseEnv s ->
Word64 ->
TraceProfile s ->
Environment s ->
State s ->
SignalGenerator s ->
Gen (Trace s)
genTraceWithProfile :: forall s.
HasTrace s =>
BaseEnv s
-> Word64
-> TraceProfile s
-> Environment s
-> State s
-> SignalGenerator s
-> Gen (Trace s)
genTraceWithProfile BaseEnv s
baseEnv Word64
ub TraceProfile s
profile Environment s
env State s
st0 SignalGenerator s
aSigGen =
do
Word64
n <-
forall (m :: * -> *) a. MonadGen m => [(Int, m a)] -> m a
Gen.frequency
[ (Int
5, forall (f :: * -> *) a. Applicative f => a -> f a
pure Word64
0)
, (Int
85, forall (m :: * -> *) a. (MonadGen m, Integral a) => Range a -> m a
integral_ forall a b. (a -> b) -> a -> b
$ forall a. Integral a => a -> a -> Range a
Range.linear Word64
1 Word64
ub)
, (Int
5, forall (f :: * -> *) a. Applicative f => a -> f a
pure Word64
ub)
]
forall s.
HasTrace s =>
BaseEnv s
-> Word64
-> TraceProfile s
-> Environment s
-> State s
-> SignalGenerator s
-> Gen (Trace s)
genTraceOfLength BaseEnv s
baseEnv Word64
n TraceProfile s
profile Environment s
env State s
st0 SignalGenerator s
aSigGen
genTraceOfLength ::
forall s.
HasTrace s =>
BaseEnv s ->
Word64 ->
TraceProfile s ->
Environment s ->
State s ->
SignalGenerator s ->
Gen (Trace s)
genTraceOfLength :: forall s.
HasTrace s =>
BaseEnv s
-> Word64
-> TraceProfile s
-> Environment s
-> State s
-> SignalGenerator s
-> Gen (Trace s)
genTraceOfLength BaseEnv s
baseEnv Word64
aTraceLength TraceProfile s
profile Environment s
env State s
st0 SignalGenerator s
aSigGen =
forall a. Manual (TreeT (MaybeT Identity) a) -> Gen a
Manual.fromManual forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [(State s, TreeT (MaybeT Identity) (Signal s))]
-> TreeT (MaybeT Identity) (Trace s)
interleaveSigs forall a b. (a -> b) -> a -> b
$ Word64
-> State s
-> [(State s, TreeT (MaybeT Identity) (Signal s))]
-> Manual [(State s, TreeT (MaybeT Identity) (Signal s))]
loop Word64
aTraceLength State s
st0 []
where
loop ::
Word64 ->
State s ->
[(State s, TreeT (MaybeT Identity) (Signal s))] ->
Manual [(State s, TreeT (MaybeT Identity) (Signal s))]
loop :: Word64
-> State s
-> [(State s, TreeT (MaybeT Identity) (Signal s))]
-> Manual [(State s, TreeT (MaybeT Identity) (Signal s))]
loop Word64
0 State s
_ [(State s, TreeT (MaybeT Identity) (Signal s))]
acc = forall (f :: * -> *) a. Applicative f => a -> f a
pure [(State s, TreeT (MaybeT Identity) (Signal s))]
acc
loop !Word64
d State s
sti [(State s, TreeT (MaybeT Identity) (Signal s))]
acc = do
TreeT (MaybeT Identity) (Signal s)
sigTree :: TreeT (MaybeT Identity) (Signal s) <-
forall a. Gen a -> Manual (TreeT (MaybeT Identity) a)
Manual.toManual forall a b. (a -> b) -> a -> b
$
forall (m :: * -> *) a. MonadGen m => [(Int, m a)] -> m a
Gen.frequency
[
( forall s. TraceProfile s -> Int
proportionOfValidSignals TraceProfile s
profile
, SignalGenerator s
aSigGen Environment s
env State s
sti
)
,
( forall s. TraceProfile s -> Int
proportionOfInvalidSignals TraceProfile s
profile
, forall s. [(Int, SignalGenerator s)] -> SignalGenerator s
generateSignalWithFailureProportions @s (forall s. TraceProfile s -> [(Int, SignalGenerator s)]
failures TraceProfile s
profile) Environment s
env State s
sti
)
]
let
mSig :: Maybe (Signal s)
mSig = forall a. Tree a -> a
treeValue forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a.
Monad m =>
TreeT (MaybeT m) a -> TreeT m (Maybe a)
runDiscardEffectT TreeT (MaybeT Identity) (Signal s)
sigTree
case Maybe (Signal s)
mSig of
Maybe (Signal s)
Nothing ->
Word64
-> State s
-> [(State s, TreeT (MaybeT Identity) (Signal s))]
-> Manual [(State s, TreeT (MaybeT Identity) (Signal s))]
loop (Word64
d forall a. Num a => a -> a -> a
- Word64
1) State s
sti [(State s, TreeT (MaybeT Identity) (Signal s))]
acc
Just Signal s
sig ->
case forall s a. HasTrace s => BaseEnv s -> BaseM s a -> a
interpretSTS @s BaseEnv s
baseEnv forall a b. (a -> b) -> a -> b
$ forall s (m :: * -> *) (rtype :: RuleType).
(STS s, RuleTypeRep rtype, m ~ BaseM s) =>
RuleContext rtype s
-> m (Either (NonEmpty (PredicateFailure s)) (State s))
applySTSTest @s (forall sts. (Environment sts, State sts, Signal sts) -> TRC sts
TRC (Environment s
env, State s
sti, Signal s
sig)) of
Left NonEmpty (PredicateFailure s)
_err -> Word64
-> State s
-> [(State s, TreeT (MaybeT Identity) (Signal s))]
-> Manual [(State s, TreeT (MaybeT Identity) (Signal s))]
loop (Word64
d forall a. Num a => a -> a -> a
- Word64
1) State s
sti [(State s, TreeT (MaybeT Identity) (Signal s))]
acc
Right State s
sti' -> Word64
-> State s
-> [(State s, TreeT (MaybeT Identity) (Signal s))]
-> Manual [(State s, TreeT (MaybeT Identity) (Signal s))]
loop (Word64
d forall a. Num a => a -> a -> a
- Word64
1) State s
sti' ((State s
sti', TreeT (MaybeT Identity) (Signal s)
sigTree) forall a. a -> [a] -> [a]
: [(State s, TreeT (MaybeT Identity) (Signal s))]
acc)
interleaveSigs ::
[(State s, TreeT (MaybeT Identity) (Signal s))] ->
TreeT (MaybeT Identity) (Trace s)
interleaveSigs :: [(State s, TreeT (MaybeT Identity) (Signal s))]
-> TreeT (MaybeT Identity) (Trace s)
interleaveSigs [(State s, TreeT (MaybeT Identity) (Signal s))]
stateSignalTrees =
forall a.
Maybe (NodeT (MaybeT Identity) a) -> TreeT (MaybeT Identity) a
Manual.wrapTreeT forall a b. (a -> b) -> a -> b
$
forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$
forall (m :: * -> *) a. a -> [TreeT m a] -> NodeT m a
NodeT
Trace s
rootTrace
(forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (forall s a. HasTrace s => BaseEnv s -> BaseM s a -> a
interpretSTS @s BaseEnv s
baseEnv forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s (m :: * -> *).
(STS s, m ~ BaseM s) =>
Environment s -> State s -> [Signal s] -> m (Trace s)
closure @s Environment s
env State s
st0)) [TreeT (MaybeT Identity) [Signal s]]
signalShrinksChilren)
where
rootStates :: [State s]
signalTrees :: [TreeT (MaybeT Identity) (Signal s)]
([State s]
rootStates, [TreeT (MaybeT Identity) (Signal s)]
signalTrees) = forall a b. [(a, b)] -> ([a], [b])
unzip [(State s, TreeT (MaybeT Identity) (Signal s))]
stateSignalTrees
rootSignals :: [Signal s]
rootSignals :: [Signal s]
rootSignals = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (forall a. a -> Maybe a -> a
fromMaybe forall {a}. a
err forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Tree a -> a
treeValue forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) a.
Monad m =>
TreeT (MaybeT m) a -> TreeT m (Maybe a)
runDiscardEffectT) [TreeT (MaybeT Identity) (Signal s)]
signalTrees
err :: a
err = forall a. HasCallStack => [Char] -> a
error [Char]
"genTraceOfLength: the tree nodes must always contain a signal"
rootTrace :: Trace s
rootTrace :: Trace s
rootTrace = forall s.
Environment s -> State s -> [(State s, Signal s)] -> Trace s
mkTrace Environment s
env State s
st0 (forall a b. [a] -> [b] -> [(a, b)]
zip [State s]
rootStates [Signal s]
rootSignals)
signalShrinks :: TreeT (MaybeT Identity) [Signal s]
signalShrinks :: TreeT (MaybeT Identity) [Signal s]
signalShrinks = forall a.
[TreeT (MaybeT Identity) a] -> TreeT (MaybeT Identity) [a]
Manual.interleave [TreeT (MaybeT Identity) (Signal s)]
signalTrees
signalShrinksChilren :: [TreeT (MaybeT Identity) [Signal s]]
signalShrinksChilren :: [TreeT (MaybeT Identity) [Signal s]]
signalShrinksChilren = forall (m :: * -> *) a. NodeT m a -> [TreeT m a]
nodeChildren forall a b. (a -> b) -> a -> b
$ forall a. a -> Maybe a -> a
fromMaybe forall {a}. a
err forall a b. (a -> b) -> a -> b
$ forall a.
TreeT (MaybeT Identity) a -> Maybe (NodeT (MaybeT Identity) a)
Manual.unwrapTreeT TreeT (MaybeT Identity) [Signal s]
signalShrinks
invalidTrace ::
forall s.
HasTrace s =>
BaseEnv s ->
Word64 ->
[(Int, SignalGenerator s)] ->
Gen (Invalid.Trace s)
invalidTrace :: forall s.
HasTrace s =>
BaseEnv s -> Word64 -> [(Int, SignalGenerator s)] -> Gen (Trace s)
invalidTrace BaseEnv s
baseEnv Word64
maxTraceLength [(Int, SignalGenerator s)]
failureProfile = do
Trace s
tr <- forall s. HasTrace s => BaseEnv s -> Word64 -> Gen (Trace s)
trace @s BaseEnv s
baseEnv Word64
maxTraceLength
let env :: Environment s
env = forall s. Trace s -> Environment s
_traceEnv Trace s
tr
st :: State s
st = forall s. Trace s -> State s
lastState Trace s
tr
Signal s
iSig <- forall s. [(Int, SignalGenerator s)] -> SignalGenerator s
generateSignalWithFailureProportions @s [(Int, SignalGenerator s)]
failureProfile Environment s
env State s
st
let est' :: Either (NonEmpty (PredicateFailure s)) (State s)
est' = forall s a. HasTrace s => BaseEnv s -> BaseM s a -> a
interpretSTS @s BaseEnv s
baseEnv forall a b. (a -> b) -> a -> b
$ forall s (m :: * -> *) (rtype :: RuleType).
(STS s, RuleTypeRep rtype, m ~ BaseM s) =>
RuleContext rtype s
-> m (Either (NonEmpty (PredicateFailure s)) (State s))
applySTSTest @s forall a b. (a -> b) -> a -> b
$ forall sts. (Environment sts, State sts, Signal sts) -> TRC sts
TRC (Environment s
env, State s
st, Signal s
iSig)
forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$!
Invalid.Trace
{ validPrefix :: Trace s
Invalid.validPrefix = Trace s
tr
, signal :: Signal s
Invalid.signal = Signal s
iSig
, errorOrLastState :: Either (NonEmpty (PredicateFailure s)) (State s)
Invalid.errorOrLastState = Either (NonEmpty (PredicateFailure s)) (State s)
est'
}
traceSuchThat ::
forall s.
HasTrace s =>
BaseEnv s ->
Word64 ->
(Trace s -> Bool) ->
Gen (Trace s)
traceSuchThat :: forall s.
HasTrace s =>
BaseEnv s -> Word64 -> (Trace s -> Bool) -> Gen (Trace s)
traceSuchThat BaseEnv s
baseEnv Word64
n Trace s -> Bool
cond = forall (m :: * -> *) a.
(MonadGen m, GenBase m ~ Identity) =>
(a -> Bool) -> m a -> m a
Gen.filter Trace s -> Bool
cond (forall s. HasTrace s => BaseEnv s -> Word64 -> Gen (Trace s)
trace @s BaseEnv s
baseEnv Word64
n)
ofLengthAtLeast :: Gen (Trace s) -> Int -> Gen (Trace s)
ofLengthAtLeast :: forall s. Gen (Trace s) -> Int -> Gen (Trace s)
ofLengthAtLeast Gen (Trace s)
traceGen Int
minLength =
forall (m :: * -> *) a.
(MonadGen m, GenBase m ~ Identity) =>
(a -> Bool) -> m a -> m a
Gen.filter ((Int
minLength forall a. Ord a => a -> a -> Bool
<=) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s. Trace s -> Int
traceLength) Gen (Trace s)
traceGen
suchThatLastState ::
forall s.
Gen (Trace s) ->
(State s -> Bool) ->
Gen (Trace s)
suchThatLastState :: forall s. Gen (Trace s) -> (State s -> Bool) -> Gen (Trace s)
suchThatLastState Gen (Trace s)
traceGen State s -> Bool
cond = forall (m :: * -> *) a.
(MonadGen m, GenBase m ~ Identity) =>
(a -> Bool) -> m a -> m a
Gen.filter (State s -> Bool
cond forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s. Trace s -> State s
lastState) Gen (Trace s)
traceGen
nonTrivialTrace ::
forall s.
(HasTrace s, HasSizeInfo (Signal s)) =>
BaseEnv s ->
Word64 ->
Gen (Trace s)
nonTrivialTrace :: forall s.
(HasTrace s, HasSizeInfo (Signal s)) =>
BaseEnv s -> Word64 -> Gen (Trace s)
nonTrivialTrace BaseEnv s
baseEnv Word64
ub =
forall (m :: * -> *) a.
(MonadGen m, GenBase m ~ Identity) =>
(a -> Bool) -> m a -> m a
Gen.filter (forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall sig. HasSizeInfo sig => sig -> Bool
isTrivial) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s. TraceOrder -> Trace s -> [Signal s]
traceSignals TraceOrder
OldestFirst) (forall s. HasTrace s => BaseEnv s -> Word64 -> Gen (Trace s)
trace BaseEnv s
baseEnv Word64
ub)
class HasSizeInfo sig where
isTrivial :: sig -> Bool
instance HasSizeInfo [a] where
isTrivial :: [a] -> Bool
isTrivial = forall (t :: * -> *) a. Foldable t => t a -> Bool
null
sampleMaxTraceSize ::
forall s.
HasTrace s =>
BaseEnv s ->
Word64 ->
Int ->
Word64 ->
IO Int
sampleMaxTraceSize :: forall s.
HasTrace s =>
BaseEnv s -> Word64 -> Int -> Word64 -> IO Int
sampleMaxTraceSize BaseEnv s
baseEnv Word64
ub Int
d Word64
n =
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [Word64
0 .. Word64
n] (forall a b. a -> b -> a
const forall a b. (a -> b) -> a -> b
$ forall s. Trace s -> Int
traceLength forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a. MonadIO m => Gen a -> m a
Gen.sample (forall (m :: * -> *) a. MonadGen m => Size -> m a -> m a
Gen.resize (Int -> Size
Size Int
d) (forall s. HasTrace s => BaseEnv s -> Word64 -> Gen (Trace s)
trace @s BaseEnv s
baseEnv Word64
ub)))
randomTrace ::
forall s.
HasTrace s =>
BaseEnv s ->
Word64 ->
IO (Trace s)
randomTrace :: forall s. HasTrace s => BaseEnv s -> Word64 -> IO (Trace s)
randomTrace BaseEnv s
baseEnv Word64
ub = forall (m :: * -> *) a. MonadIO m => Gen a -> m a
Gen.sample (forall s. HasTrace s => BaseEnv s -> Word64 -> Gen (Trace s)
trace BaseEnv s
baseEnv Word64
ub)
randomTraceOfSize ::
forall s.
HasTrace s =>
BaseEnv s ->
Word64 ->
IO (Trace s)
randomTraceOfSize :: forall s. HasTrace s => BaseEnv s -> Word64 -> IO (Trace s)
randomTraceOfSize BaseEnv s
baseEnv Word64
desiredTraceLength = forall (m :: * -> *) a. MonadIO m => Gen a -> m a
Gen.sample (forall s. HasTrace s => BaseEnv s -> Word64 -> Gen (Trace s)
traceOfLength BaseEnv s
baseEnv Word64
desiredTraceLength)
classifyTraceLength ::
Trace s ->
Word64 ->
Word64 ->
PropertyT IO ()
classifyTraceLength :: forall s. Trace s -> Word64 -> Word64 -> PropertyT IO ()
classifyTraceLength Trace s
tr = forall n a.
(Show n, Integral n) =>
[Char] -> a -> (a -> n) -> n -> n -> PropertyT IO ()
classifySize [Char]
"trace length:" Trace s
tr (forall a b. (Integral a, Num b) => a -> b
fromIntegral forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s. Trace s -> Int
traceLength)
classifySize ::
(Show n, Integral n) =>
String ->
a ->
(a -> n) ->
n ->
n ->
PropertyT IO ()
classifySize :: forall n a.
(Show n, Integral n) =>
[Char] -> a -> (a -> n) -> n -> n -> PropertyT IO ()
classifySize [Char]
prefixLabel a
value a -> n
sizeF n
upBound n
step = do
forall (m :: * -> *).
(MonadTest m, HasCallStack) =>
LabelName -> Bool -> m ()
classify ([Char] -> LabelName
mkLabel [Char]
"empty") forall a b. (a -> b) -> a -> b
$ a -> n
sizeF a
value forall a. Eq a => a -> a -> Bool
== n
0
forall (m :: * -> *).
(MonadTest m, HasCallStack) =>
LabelName -> Bool -> m ()
classify ([Char] -> LabelName
mkLabel [Char]
"singleton") forall a b. (a -> b) -> a -> b
$ a -> n
sizeF a
value forall a. Eq a => a -> a -> Bool
== n
1
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ (n, n) -> PropertyT IO ()
classifySizeInterval forall a b. (a -> b) -> a -> b
$ forall n. Integral n => n -> n -> n -> [(n, n)]
mkIntervals n
2 (n
upBound forall a. Num a => a -> a -> a
- n
1) n
step
forall (m :: * -> *).
(MonadTest m, HasCallStack) =>
LabelName -> Bool -> m ()
classify LabelName
upBoundLabel forall a b. (a -> b) -> a -> b
$ a -> n
sizeF a
value forall a. Eq a => a -> a -> Bool
== n
upBound
where
upBoundLabel :: LabelName
upBoundLabel = [Char] -> LabelName
mkLabel forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> [Char]
show n
upBound
mkLabel :: [Char] -> LabelName
mkLabel = forall a. IsString a => [Char] -> a
fromString forall b c a. (b -> c) -> (a -> b) -> a -> c
. (([Char]
prefixLabel forall a. [a] -> [a] -> [a]
++ [Char]
" ") forall a. [a] -> [a] -> [a]
++)
classifySizeInterval :: (n, n) -> PropertyT IO ()
classifySizeInterval (n
low, n
high) =
forall (m :: * -> *).
(MonadTest m, HasCallStack) =>
LabelName -> Bool -> m ()
classify LabelName
desc forall a b. (a -> b) -> a -> b
$! n
low forall a. Ord a => a -> a -> Bool
<= a -> n
sizeF a
value Bool -> Bool -> Bool
&& a -> n
sizeF a
value forall a. Ord a => a -> a -> Bool
< n
high
where
desc :: LabelName
desc = [Char] -> LabelName
mkLabel forall a b. (a -> b) -> a -> b
$ [Char]
"[" forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> [Char]
show n
low forall a. Semigroup a => a -> a -> a
<> [Char]
", " forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> [Char]
show n
high forall a. Semigroup a => a -> a -> a
<> [Char]
")"
mkIntervals ::
Integral n =>
n ->
n ->
n ->
[(n, n)]
mkIntervals :: forall n. Integral n => n -> n -> n -> [(n, n)]
mkIntervals n
low n
high n
step
| n
0 forall a. Ord a => a -> a -> Bool
<= n
low Bool -> Bool -> Bool
&& n
low forall a. Ord a => a -> a -> Bool
<= n
high Bool -> Bool -> Bool
&& n
0 forall a. Ord a => a -> a -> Bool
< n
step =
[(n
low forall a. Num a => a -> a -> a
+ n
i forall a. Num a => a -> a -> a
* n
step, n
high forall a. Ord a => a -> a -> a
`min` (n
low forall a. Num a => a -> a -> a
+ (n
i forall a. Num a => a -> a -> a
+ n
1) forall a. Num a => a -> a -> a
* n
step)) | n
i <- [n
0 .. n
n forall a. Num a => a -> a -> a
- n
1]]
| Bool
otherwise = []
where
highNorm :: n
highNorm = n
high forall a. Num a => a -> a -> a
- n
low
n :: n
n = n
highNorm forall a. Integral a => a -> a -> a
`div` n
step forall a. Num a => a -> a -> a
+ n
1 forall a. Ord a => a -> a -> a
`min` (n
highNorm forall a. Integral a => a -> a -> a
`mod` n
step)
ratio ::
Integral a =>
(Trace s -> a) ->
Trace s ->
Double
ratio :: forall a s. Integral a => (Trace s -> a) -> Trace s -> Double
ratio Trace s -> a
f Trace s
tr = forall a b. (Integral a, Num b) => a -> b
fromIntegral (Trace s -> a
f Trace s
tr) forall a. Fractional a => a -> a -> a
/ forall a b. (Integral a, Num b) => a -> b
fromIntegral (forall s. Trace s -> Int
traceLength Trace s
tr)
traceLengthsAreClassified ::
forall s.
(HasTrace s, Show (Environment s), Show (State s), Show (Signal s)) =>
BaseEnv s ->
Word64 ->
Word64 ->
Property
traceLengthsAreClassified :: forall s.
(HasTrace s, Show (Environment s), Show (State s),
Show (Signal s)) =>
BaseEnv s -> Word64 -> Word64 -> Property
traceLengthsAreClassified BaseEnv s
baseEnv Word64
maximumTraceLength Word64
intervalSize =
HasCallStack => PropertyT IO () -> Property
property forall a b. (a -> b) -> a -> b
$ do
Trace s
traceSample <- forall (m :: * -> *) a.
(Monad m, Show a, HasCallStack) =>
Gen a -> PropertyT m a
forAll (forall s. HasTrace s => BaseEnv s -> Word64 -> Gen (Trace s)
trace @s BaseEnv s
baseEnv Word64
maximumTraceLength)
forall s. Trace s -> Word64 -> Word64 -> PropertyT IO ()
classifyTraceLength Trace s
traceSample Word64
maximumTraceLength Word64
intervalSize
forall (m :: * -> *). MonadTest m => m ()
success
onlyValidSignalsAreGenerated ::
forall s.
(HasTrace s, Show (Environment s), Show (State s), Show (Signal s), HasCallStack) =>
BaseEnv s ->
Word64 ->
Property
onlyValidSignalsAreGenerated :: forall s.
(HasTrace s, Show (Environment s), Show (State s), Show (Signal s),
HasCallStack) =>
BaseEnv s -> Word64 -> Property
onlyValidSignalsAreGenerated BaseEnv s
baseEnv Word64
maximumTraceLength =
forall s.
(HasTrace s, Show (Environment s), Show (State s), Show (Signal s),
HasCallStack) =>
BaseEnv s -> Gen (Trace s) -> Property
onlyValidSignalsAreGeneratedForTrace BaseEnv s
baseEnv (forall s. HasTrace s => BaseEnv s -> Word64 -> Gen (Trace s)
trace @s BaseEnv s
baseEnv Word64
maximumTraceLength)
onlyValidSignalsAreGeneratedForTrace ::
forall s.
(HasTrace s, Show (Environment s), Show (State s), Show (Signal s), HasCallStack) =>
BaseEnv s ->
Gen (Trace s) ->
Property
onlyValidSignalsAreGeneratedForTrace :: forall s.
(HasTrace s, Show (Environment s), Show (State s), Show (Signal s),
HasCallStack) =>
BaseEnv s -> Gen (Trace s) -> Property
onlyValidSignalsAreGeneratedForTrace BaseEnv s
baseEnv Gen (Trace s)
traceGen = HasCallStack => PropertyT IO () -> Property
property forall a b. (a -> b) -> a -> b
$ do
Trace s
tr <- forall (m :: * -> *) a.
(Monad m, Show a, HasCallStack) =>
Gen a -> PropertyT m a
forAll Gen (Trace s)
traceGen
let env :: Environment s
env :: Environment s
env = forall s. Trace s -> Environment s
_traceEnv Trace s
tr
st' :: State s
st' :: State s
st' = forall s. Trace s -> State s
lastState Trace s
tr
Signal s
sig <- forall (m :: * -> *) a.
(Monad m, Show a, HasCallStack) =>
Gen a -> PropertyT m a
forAll (forall s. HasTrace s => SignalGenerator s
sigGen @s Environment s
env State s
st')
let result :: Either (NonEmpty (PredicateFailure s)) (State s)
result = forall s a. HasTrace s => BaseEnv s -> BaseM s a -> a
interpretSTS @s BaseEnv s
baseEnv forall a b. (a -> b) -> a -> b
$ forall s (m :: * -> *) (rtype :: RuleType).
(STS s, RuleTypeRep rtype, m ~ BaseM s) =>
RuleContext rtype s
-> m (Either (NonEmpty (PredicateFailure s)) (State s))
applySTSTest @s (forall sts. (Environment sts, State sts, Signal sts) -> TRC sts
TRC (Environment s
env, State s
st', Signal s
sig))
forall (m :: * -> *) a. (MonadTest m, Show a) => a -> m ()
footnoteShow State s
st'
forall (m :: * -> *) a. (MonadTest m, Show a) => a -> m ()
footnoteShow Signal s
sig
forall (m :: * -> *) a. (MonadTest m, Show a) => a -> m ()
footnoteShow Either (NonEmpty (PredicateFailure s)) (State s)
result
forall (f :: * -> *) a. Functor f => f a -> f ()
void forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) x a.
(MonadTest m, Show x, HasCallStack) =>
Either x a -> m a
evalEither Either (NonEmpty (PredicateFailure s)) (State s)
result
coverFailures ::
forall m s a.
( MonadTest m
, HasCallStack
, Data (PredicateFailure s)
, Data a
) =>
CoverPercentage ->
[PredicateFailure s] ->
a ->
m ()
coverFailures :: forall (m :: * -> *) s a.
(MonadTest m, HasCallStack, Data (PredicateFailure s), Data a) =>
CoverPercentage -> [PredicateFailure s] -> a -> m ()
coverFailures CoverPercentage
coverPercentage [PredicateFailure s]
targetFailures a
failureStructure = do
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ (Constr -> m ()
coverFailure forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Data a => a -> Constr
toConstr) [PredicateFailure s]
targetFailures
where
coverFailure :: Constr -> m ()
coverFailure Constr
predicateFailureConstructor =
forall (m :: * -> *).
(MonadTest m, HasCallStack) =>
CoverPercentage -> LabelName -> Bool -> m ()
cover
CoverPercentage
coverPercentage
(forall a. IsString a => [Char] -> a
fromString forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> [Char]
show Constr
predicateFailureConstructor)
(Constr
predicateFailureConstructor forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Constr]
failuresConstructors)
where
subFailures :: [PredicateFailure s]
subFailures :: [PredicateFailure s]
subFailures = forall d a. (Data d, Typeable a) => d -> [a]
extractValues a
failureStructure
failuresConstructors :: [Constr]
failuresConstructors :: [Constr]
failuresConstructors = forall a. Data a => a -> Constr
toConstr forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [PredicateFailure s]
subFailures
invalidSignalsAreGenerated ::
forall s.
(HasTrace s, Show (Environment s), Show (State s), Show (Signal s), HasCallStack) =>
BaseEnv s ->
[(Int, SignalGenerator s)] ->
Word64 ->
(NonEmpty (PredicateFailure s) -> PropertyT IO ()) ->
Property
invalidSignalsAreGenerated :: forall s.
(HasTrace s, Show (Environment s), Show (State s), Show (Signal s),
HasCallStack) =>
BaseEnv s
-> [(Int, SignalGenerator s)]
-> Word64
-> (NonEmpty (PredicateFailure s) -> PropertyT IO ())
-> Property
invalidSignalsAreGenerated BaseEnv s
baseEnv [(Int, SignalGenerator s)]
failureProfile Word64
maximumTraceLength NonEmpty (PredicateFailure s) -> PropertyT IO ()
checkFailures = HasCallStack => PropertyT IO () -> Property
property forall a b. (a -> b) -> a -> b
$ do
Trace s
tr <- forall (m :: * -> *) a.
(Monad m, Show a, HasCallStack) =>
Gen a -> PropertyT m a
forAll (forall s.
HasTrace s =>
BaseEnv s -> Word64 -> [(Int, SignalGenerator s)] -> Gen (Trace s)
invalidTrace @s BaseEnv s
baseEnv Word64
maximumTraceLength [(Int, SignalGenerator s)]
failureProfile)
forall (m :: * -> *).
(MonadTest m, HasCallStack) =>
CoverPercentage -> LabelName -> Bool -> m ()
cover
CoverPercentage
80
LabelName
"Invalid signals are generated when requested"
(forall a b. Either a b -> Bool
isLeft forall a b. (a -> b) -> a -> b
$ forall s.
Trace s -> Either (NonEmpty (PredicateFailure s)) (State s)
Invalid.errorOrLastState Trace s
tr)
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either NonEmpty (PredicateFailure s) -> PropertyT IO ()
checkFailures (forall a b. a -> b -> a
const forall (m :: * -> *). MonadTest m => m ()
success) (forall s.
Trace s -> Either (NonEmpty (PredicateFailure s)) (State s)
Invalid.errorOrLastState Trace s
tr)