morley-0.1.0.0: test/Test/Interpreter/CallSelf.hs
-- | Tests for the contract that calls self several times.
module Test.Interpreter.CallSelf
( selfCallerSpec
) where
import Test.Hspec (Expectation, Spec, it, parallel, shouldBe)
import Test.Hspec.QuickCheck (modifyMaxSuccess, prop)
import Test.QuickCheck (Gen, choose, forAll)
import Michelson.Interpret (ContractEnv(..), InterpreterState(..), RemainingSteps(..))
import Michelson.Typed
import Michelson.Untyped (OriginationOperation(..), mkContractAddress)
import qualified Michelson.Untyped as Untyped
import Morley.Aliases (UntypedContract)
import Morley.Runtime (InterpreterOp(..), TxData(..))
import Morley.Runtime.GState
import Morley.Test (ContractPropValidator, contractProp, specWithContract)
import Morley.Test.Dummy
import Morley.Test.Integrational
selfCallerSpec :: Spec
selfCallerSpec =
parallel $
specWithContract "contracts/call_self_several_times.tz" $ \selfCaller ->
specImpl selfCaller
data Fixture = Fixture
{ fMaxSteps :: RemainingSteps
, fParameter :: !Word64
} deriving Show
gasForOneExecution :: Num a => a
gasForOneExecution = 19
gasForLastExecution :: Num a => a
gasForLastExecution = 20
fExpectSuccess :: Fixture -> Bool
fExpectSuccess Fixture {..} =
-- note: fParameter must be ≥ 1
fMaxSteps >= fromIntegral ((fParameter - 1) * gasForOneExecution + gasForLastExecution)
genFixture :: Gen Fixture
genFixture =
Fixture <$> (RemainingSteps <$> choose (minGas, maxGas)) <*> choose (minCalls, maxCalls)
where
minCalls = 1
maxCalls = 10
minGas = 0
maxGas = gasForOneExecution * maxCalls
type Parameter = 'Tc 'CInt
type Storage = 'Tc 'CNat
specImpl ::
(UntypedContract, Contract Parameter Storage)
-> Spec
specImpl (uSelfCaller, selfCaller) = modifyMaxSuccess (min 10) $ do
it ("With parameter 1 single execution consumes " <>
show @_ @Int gasForLastExecution <> " gas") $
contractProp selfCaller (unitValidator gasForLastExecution) unitContractEnv
(1 :: Integer) (0 :: Natural)
it ("With parameter 2 single execution consumes " <>
show @_ @Int gasForOneExecution <> " gas") $
contractProp selfCaller (unitValidator gasForOneExecution) unitContractEnv
(2 :: Integer) (0 :: Natural)
prop propertyDescription $
forAll genFixture $ \fixture ->
integrationalTestProperty dummyNow (fMaxSteps fixture)
(operations fixture) (integValidator fixture)
where
-- Environment for unit test
unitContractEnv = dummyContractEnv
-- Validator for unit test
unitValidator ::
RemainingSteps -> ContractPropValidator Storage Expectation
unitValidator gasDiff (_, isRemainingSteps -> remSteps) =
remSteps `shouldBe` ceMaxSteps unitContractEnv - gasDiff
propertyDescription =
"calls itself as many times as you pass to it as a parameter, " <>
"it fails due to gas limit if the number is large, otherwise the " <>
"storage is updated to the number of calls"
operations :: Fixture -> [InterpreterOp]
operations fixture = [originateOp, transferOp fixture]
origination :: OriginationOperation
origination = dummyOrigination (Untyped.ValueInt 0) uSelfCaller
address = mkContractAddress origination
originateOp = OriginateOp origination
txData :: Fixture -> TxData
txData fixture = TxData
{ tdSenderAddress = genesisAddress
, tdParameter = Untyped.ValueInt (fromIntegral $ fParameter fixture)
, tdAmount = minBound
}
transferOp fixture = TransferOp address (txData fixture)
integValidator :: Fixture -> IntegrationalValidator
integValidator fixture
| fExpectSuccess fixture =
let expectedStorage =
Untyped.ValueInt (fromIntegral $ fParameter fixture)
in Right $ expectStorageConstant address expectedStorage
| otherwise = Left expectGasExhaustion