morley-1.4.0: src/Util/Test/Gen.hs
-- SPDX-FileCopyrightText: 2020 Tocqueville Group
--
-- SPDX-License-Identifier: LicenseRef-MIT-TQ
module Util.Test.Gen
( genInternalByteString
, genVar
, genTyVar
, genExpandedOp
, genInstrCallStack
, genSrcPos
, genPos
, genLetName
, genExtInstrAbstract
, genPrintComment
, genStackRef
, genTestAssert
, genStackFn
, genStackTypePattern
, genInstrAbstract
, genContract
, genContract'
, genValue
, genValue'
, genElt
, genParameterType
, genType
, genEpName
, genAnnotation
, genT
) where
import Prelude hiding (EQ, GT, LT)
import qualified Data.Text as T
import Hedgehog (MonadGen(GenBase), Range)
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import Michelson.ErrorPos (InstrCallStack(..), LetName(..), Pos(..), SrcPos(..))
import Michelson.Test.Util (genEither)
import Michelson.Text (genMText)
import Michelson.Untyped
(Annotation, Contract, Contract'(..), Elt(..), EpName, ExpandedOp(..), ExtInstrAbstract(..),
InstrAbstract(..), InternalByteString(..), ParameterType(..), PrintComment(..), StackFn(..),
StackRef(..), StackTypePattern(..), T(..), TestAssert(..), TyVar(..), Type(..), Value,
Value'(..), Var(..), epNameFromRefAnn, mkAnnotation, specialFieldAnn, specialVarAnns)
genInternalByteString :: MonadGen m => m InternalByteString
genInternalByteString = InternalByteString <$> Gen.bytes (Range.linear 0 100)
genVar :: MonadGen m => m Var
genVar = Var <$> genSmallText
genTyVar :: (MonadGen m, GenBase m ~ Identity) => m TyVar
genTyVar = Gen.choice [VarID <$> genVar, TyCon <$> genType]
genExpandedOp :: (MonadGen m, GenBase m ~ Identity) => m ExpandedOp
genExpandedOp = Gen.recursive Gen.choice
-- non-recursive constructors
[
-- NB: When used together, genExpandedOp & genInstrAbstract are mutually recursive.
-- So we use a generator of InstrAbstract that does not contain any ops as an escape hatch.
PrimEx <$> Gen.choice instrAbstractWithoutOp
]
-- recursive constructors
[ PrimEx <$> genInstrAbstract genExpandedOp
, SeqEx <$> genSmallList genExpandedOp
, Gen.subtermM genExpandedOp $ \expandedOp -> WithSrcEx <$> genInstrCallStack <*> pure expandedOp
]
genInstrCallStack :: MonadGen m => m InstrCallStack
genInstrCallStack = InstrCallStack <$> genLetCallStack <*> genSrcPos
where
genLetCallStack = Gen.frequency
[ (80, pure [])
, (18, Gen.list (Range.singleton 1) genLetName)
, (2, Gen.list (Range.singleton 2) genLetName)
]
genSrcPos :: MonadGen m => m SrcPos
genSrcPos = SrcPos <$> genPos <*> genPos
genPos :: MonadGen m => m Pos
genPos = Pos <$> Gen.word Range.linearBounded
genLetName :: MonadGen m => m LetName
genLetName = LetName <$> Gen.text (Range.linear 0 3) Gen.unicodeAll
genExtInstrAbstract :: (MonadGen m, GenBase m ~ Identity) => m op -> m (ExtInstrAbstract op)
genExtInstrAbstract genOp = Gen.choice
[ STACKTYPE <$> genStackTypePattern
, FN <$> genSmallText <*> genStackFn <*> genSmallList genOp
, UTEST_ASSERT <$> genTestAssert genOp
, UPRINT <$> genPrintComment
, UCOMMENT <$> genSmallText
]
genPrintComment :: MonadGen m => m PrintComment
genPrintComment = PrintComment <$> Gen.list (Range.linear 0 5) (genEither genSmallText genStackRef)
genStackRef :: MonadGen m => m StackRef
genStackRef = StackRef <$> Gen.integral (Range.linear 0 (fromIntegral $ maxBound @Word64))
genTestAssert :: MonadGen m => m op -> m (TestAssert op)
genTestAssert genOp = TestAssert <$> genSmallText <*> genPrintComment <*> genSmallList genOp
genStackFn :: (MonadGen m, GenBase m ~ Identity) => m StackFn
genStackFn = StackFn
<$> Gen.maybe (Gen.set smallCollectionRange genVar)
<*> genStackTypePattern
<*> genStackTypePattern
genStackTypePattern :: (MonadGen m, GenBase m ~ Identity) => m StackTypePattern
genStackTypePattern = Gen.recursive Gen.choice
[ pure StkEmpty, pure StkRest ]
[ Gen.subtermM genStackTypePattern $ \stp -> StkCons <$> genTyVar <*> pure stp ]
genInstrAbstract :: (MonadGen m, GenBase m ~ Identity) => m op -> m (InstrAbstract op)
genInstrAbstract genOp =
Gen.choice $ instrAbstractWithOp genOp <> instrAbstractWithoutOp
instrAbstractWithOp :: (MonadGen m, GenBase m ~ Identity) => m op -> [m (InstrAbstract op)]
instrAbstractWithOp genOp =
[ EXT <$> genExtInstrAbstract genOp
, PUSH <$> genAnnotation <*> genType <*> genValue' genOp
, IF_NONE <$> genSmallList genOp <*> genSmallList genOp
, IF_LEFT <$> genSmallList genOp <*> genSmallList genOp
, IF_CONS <$> genSmallList genOp <*> genSmallList genOp
, MAP <$> genAnnotation <*> genSmallList genOp
, ITER <$> genSmallList genOp
, IF <$> genSmallList genOp <*> genSmallList genOp
, LOOP <$> genSmallList genOp
, LOOP_LEFT <$> genSmallList genOp
, LAMBDA <$> genAnnotation <*> genType <*> genType <*> genSmallList genOp
, DIP <$> genSmallList genOp
, DIPN <$> Gen.word Range.linearBounded <*> genSmallList genOp
, CREATE_CONTRACT <$> genAnnotation <*> genAnnotation <*> genContract' genOp
]
instrAbstractWithoutOp :: (MonadGen m, GenBase m ~ Identity) => [m (InstrAbstract op)]
instrAbstractWithoutOp =
[ DROPN <$> Gen.word Range.linearBounded
, pure DROP
, DUP <$> genAnnotation
, pure SWAP
, DIG <$> Gen.word Range.linearBounded
, DUG <$> Gen.word Range.linearBounded
, SOME <$> genAnnotation <*> genAnnotation
, NONE <$> genAnnotation <*> genAnnotation <*> genType
, UNIT <$> genAnnotation <*> genAnnotation
, PAIR <$> genAnnotation <*> genAnnotation <*> genAnnotation <*> genAnnotation
, CAR <$> genAnnotation <*> genAnnotation
, CDR <$> genAnnotation <*> genAnnotation
, LEFT <$> genAnnotation <*> genAnnotation <*> genAnnotation <*> genAnnotation <*> genType
, RIGHT <$> genAnnotation <*> genAnnotation <*> genAnnotation <*> genAnnotation <*> genType
, NIL <$> genAnnotation <*> genAnnotation <*> genType
, CONS <$> genAnnotation
, SIZE <$> genAnnotation
, EMPTY_SET <$> genAnnotation <*> genAnnotation <*> genType
, EMPTY_MAP <$> genAnnotation <*> genAnnotation <*> genType <*> genType
, EMPTY_BIG_MAP <$> genAnnotation <*> genAnnotation <*> genType <*> genType
, MEM <$> genAnnotation
, GET <$> genAnnotation
, UPDATE <$> genAnnotation
, EXEC <$> genAnnotation
, APPLY <$> genAnnotation
, pure FAILWITH
, CAST <$> genAnnotation <*> genType
, RENAME <$> genAnnotation
, PACK <$> genAnnotation
, UNPACK <$> genAnnotation <*> genAnnotation <*> genType
, CONCAT <$> genAnnotation
, SLICE <$> genAnnotation
, ISNAT <$> genAnnotation
, ADD <$> genAnnotation
, SUB <$> genAnnotation
, MUL <$> genAnnotation
, EDIV <$> genAnnotation
, ABS <$> genAnnotation
, NEG <$> genAnnotation
, LSL <$> genAnnotation
, LSR <$> genAnnotation
, OR <$> genAnnotation
, AND <$> genAnnotation
, XOR <$> genAnnotation
, NOT <$> genAnnotation
, COMPARE <$> genAnnotation
, EQ <$> genAnnotation
, NEQ <$> genAnnotation
, LT <$> genAnnotation
, GT <$> genAnnotation
, LE <$> genAnnotation
, GE <$> genAnnotation
, INT <$> genAnnotation
, SELF <$> genAnnotation <*> genAnnotation
, CONTRACT <$> genAnnotation <*> genAnnotation <*> genType
, TRANSFER_TOKENS <$> genAnnotation
, SET_DELEGATE <$> genAnnotation
, IMPLICIT_ACCOUNT <$> genAnnotation
, NOW <$> genAnnotation
, AMOUNT <$> genAnnotation
, BALANCE <$> genAnnotation
, CHECK_SIGNATURE <$> genAnnotation
, SHA256 <$> genAnnotation
, SHA512 <$> genAnnotation
, BLAKE2B <$> genAnnotation
, HASH_KEY <$> genAnnotation
, SOURCE <$> genAnnotation
, SENDER <$> genAnnotation
, ADDRESS <$> genAnnotation
, CHAIN_ID <$> genAnnotation
]
genContract :: (MonadGen m, GenBase m ~ Identity) => m Contract
genContract = genContract' genExpandedOp
genContract' :: (MonadGen m, GenBase m ~ Identity) => m op -> m (Contract' op)
genContract' genOp = Contract <$> genParameterType <*> genType <*> genSmallList genOp
genValue :: (MonadGen m, GenBase m ~ Identity) => m Value
genValue = genValue' genExpandedOp
genValue' :: MonadGen m => m op -> m (Value' op)
genValue' genOp = Gen.recursive Gen.choice
-- non-recursive constructors
[ ValueInt <$> Gen.integral (Range.linearFrom 0 (fromIntegral $ minBound @Int64) (fromIntegral $ maxBound @Word64))
, ValueString <$> genMText
, ValueBytes <$> genInternalByteString
, pure ValueUnit
, pure ValueTrue
, pure ValueFalse
, pure ValueNone
, pure ValueNil
]
-- recursive constructors
[ Gen.subterm2 (genValue' genOp) (genValue' genOp) ValuePair
, Gen.subterm (genValue' genOp) ValueLeft
, Gen.subterm (genValue' genOp) ValueRight
, Gen.subterm (genValue' genOp) ValueSome
, ValueSeq <$> genSmallNonEmpty (genValue' genOp)
, ValueMap <$> genSmallNonEmpty (genElt genOp)
, ValueLambda <$> genSmallNonEmpty genOp
]
genElt :: MonadGen m => m op -> m (Elt op)
genElt genOp = Elt <$> genValue' genOp <*> genValue' genOp
genParameterType :: (MonadGen m, GenBase m ~ Identity) => m ParameterType
genParameterType = ParameterType <$> genType <*> genAnnotation
genType :: (MonadGen m, GenBase m ~ Identity) => m Type
genType = Type <$> genT <*> genAnnotation
genEpName :: (MonadGen m, GenBase m ~ Identity) => m EpName
genEpName = Gen.mapMaybe (rightToMaybe . epNameFromRefAnn) genAnnotation
genAnnotation :: forall m a. (MonadGen m, GenBase m ~ Identity) => m (Annotation a)
genAnnotation = Gen.mapMaybe (rightToMaybe . mkAnnotation) genAnnotationText
where
genAnnotationText :: m Text
genAnnotationText = Gen.frequency
[ (1, pure "")
, (17, T.cons <$> annStart <*> Gen.text (Range.linear 0 100) annBodyChar)
, (1, pure specialFieldAnn)
, (1, Gen.element specialVarAnns)
]
annStart :: m Char
annStart = Gen.choice [ pure '_', Gen.alphaNum ]
annBodyChar :: m Char
annBodyChar = Gen.choice [ annStart, Gen.element (".%@" :: String) ]
genT :: (MonadGen m, GenBase m ~ Identity) => m T
genT =
Gen.recursive Gen.choice
-- non-recursive constructors
[ pure TKey
, pure TUnit
, pure TSignature
, pure TChainId
, pure TOperation
, pure TInt
, pure TNat
, pure TString
, pure TBytes
, pure TMutez
, pure TBool
, pure TKeyHash
, pure TTimestamp
, pure TAddress
]
-- recursive constructors
[ TOption <$> genType
, TList <$> genType
, TSet <$> genType
, TContract <$> genType
, TPair <$> genAnnotation <*> genAnnotation <*> genType <*> genType
, TOr <$> genAnnotation <*> genAnnotation <*> genType <*> genType
, TLambda <$> genType <*> genType
, TMap <$> genType <*> genType
, TBigMap <$> genType <*> genType
]
smallCollectionRange :: Range Int
smallCollectionRange = Range.linear 0 3
genSmallList :: MonadGen m => m a -> m [a]
genSmallList = Gen.list smallCollectionRange
genSmallNonEmpty :: MonadGen m => m a -> m (NonEmpty a)
genSmallNonEmpty = Gen.nonEmpty smallCollectionRange
genSmallText :: MonadGen m => m Text
genSmallText = Gen.text (Range.linear 0 10) Gen.unicodeAll