cleveland-0.1.1: morley-test/Test/OpSize.hs
-- SPDX-FileCopyrightText: 2021 Oxhead Alpha
-- SPDX-License-Identifier: LicenseRef-MIT-OA
-- | Tests for operation size evaluation.
module Test.OpSize
( test_Nesting
, test_Numbers
, test_Values
, test_Types
, test_Instructions
) where
import Prelude hiding (EQ)
import Data.Text qualified as T
import Fmt (pretty)
import Hedgehog (Gen)
import Test.HUnit (Assertion, (@?=))
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (testCase)
import Hedgehog.Gen.Tezos.Address (genAddress)
import Hedgehog.Gen.Tezos.Crypto (genKeyHash)
import Hedgehog.Gen.Tezos.Crypto.Ed25519 qualified as Ed25519
import Hedgehog.Gen.Tezos.Crypto.Secp256k1 qualified as Secp256k1
import Morley.Michelson.Macro
import Morley.Michelson.Parser qualified as Parser
import Morley.Michelson.Typed
import Morley.Michelson.Untyped (buildEpName)
import Morley.Michelson.Untyped.OpSize
import Morley.Tezos.Core
import Morley.Tezos.Crypto
import Morley.Util.Text (dquotes)
import Test.Cleveland.Util (runGen)
import Test.Util.Parser
(?==) :: HasCallStack => Text -> Word -> Assertion
codeText ?== expectedCost = do
parsed <- Parser.codeEntry `shouldParse` ("{" <> codeText <> "}")
let code = expandList parsed
expandedInstrsOpSize code @?= OpSize expectedCost
{- All particular numbers below are got using dump-op-size.py script -}
test_Nesting :: [TestTree]
test_Nesting =
[ testCase "empty string" $ "" ?== 0
, testCase "{}" $ "{}" ?== 5
, testCase "{}; {}" $ "{}; {}" ?== 10
, testCase "100 * {}" $ mconcat (replicate 100 "{}; ") ?== 500
, testCase "{{}}" $ "{{}}" ?== 10
]
test_Numbers :: [TestTree]
test_Numbers =
[ numTestCase 0 8
, numTestCase 1 8
, numTestCase 2 8
, numTestCase 63 8
, numTestCase 64 9
, numTestCase 65 9
, numTestCase 2048 9
, numTestCase 20480 10
, numTestCase (-1) 8
, numTestCase (-63) 8
, numTestCase (-64) 9
]
where
numTestCase (n :: Int) gas =
testCase (show n) $ ("PUSH int " <> show n <> "; DROP") ?== gas
test_Values :: [TestTree]
test_Values =
[ testGroup "int"
[ valueTestCaseExt "small" "int" "5" 8
, valueTestCaseExt "bigger" "int" "63" 8
, valueTestCaseExt "big" "int" "64" 9
]
, valueTestCase "nat" "5" 8
, testGroup "string"
[ stringTestCase 0 11
, stringTestCase 1 12
, stringTestCase 2 13
, stringTestCase 7 18
, stringTestCase 8 19
]
, testGroup "bytes"
[ bytesTestCase 0 11
, bytesTestCase 1 12
, bytesTestCase 2 13
, bytesTestCase 7 18
, bytesTestCase 8 19
]
, valueTestCase "mutez" "5" 8
, valueTestCase "bool" "True" 8
, valueTestCase "key_hash" (dquotes $ pretty (gen genKeyHash)) 47
, testGroup "timestamp"
[ valueTestCaseExt "small" "timestamp" "5" 8
, valueTestCaseExt "bigger" "timestamp" "123" 9
, valueTestCaseExt "textual" "timestamp" (dquotes "2018-08-08 00:00:00Z") 31
]
, testGroup "address"
[ valueTestCaseExt "plain tz address" "address" (dquotes $ pretty (gen genAddress)) 47
, valueTestCaseExt "plain KT address" "address" (dquotes $ pretty (gen genAddress)) 47
, let addr = gen genAddress
in valueTestCaseExt "empty entrypoint" "address" (dquotes $ pretty addr <> "%") 48
, let epAddr = EpAddress (gen genAddress) . unsafe $ buildEpName "a"
in valueTestCaseExt "short entrypoint" "address" (dquotes $ pretty epAddr) 49
, let epAddr = EpAddress (gen genAddress) . unsafe . buildEpName . mconcat $ replicate 8 "a"
in valueTestCaseExt "long entrypoint" "address" (dquotes $ pretty epAddr) 56
]
-- ed25519 and secp256k1 keys have different size
, valueTestCase "key" (dquotes $ pretty (PublicKeyEd25519 (gen Ed25519.genPublicKey))) 65
, valueTestCase "key" (dquotes $ pretty (PublicKeySecp256k1 (gen Secp256k1.genPublicKey))) 66
, valueTestCase "unit" "Unit" 8
, valueTestCase "signature" (dquotes $ pretty (SignatureEd25519 (gen Ed25519.genSignature))) 110
, testGroup "chain_id"
[ valueTestCase "chain_id" (dquotes $ pretty dummyChainId) 26
, valueTestCase "chain_id" "0x00000000" 15
]
, testGroup "option"
[ valueTestCaseExt "none" "(option int)" "None" 10
, valueTestCaseExt "some" "(option int)" "(Some 5)" 12
]
, testGroup "list"
[ valueTestCaseExt "Empty" "(list int)" "{}" 13
, valueTestCaseExt "Length 1" "(list int)" "{1}" 15
, valueTestCaseExt "Length 8" "(list int)" "{1;1;1;1;1;1;1;1}" 29
]
, testGroup "set"
[ valueTestCaseExt "Empty" "(set int)" "{}" 13
, valueTestCaseExt "Length 1" "(set int)" "{1}" 15
, valueTestCaseExt "Length 1" "(set int)" "{1;2;3;4;5;6;7;8}" 29
]
, valueTestCaseExt "pair" "(pair int int)" "(Pair 1 2)" 16
, valueTestCaseExt "or" "(or int int)" "(Left 1)" 14
, testGroup "lambda"
[ valueTestCaseExt "empty lambda" "(lambda int int)" "{}" 15
, valueTestCaseExt "simple lambda" "(lambda int int)" "{DUP; DROP}" 19
]
, testGroup "map"
[ valueTestCaseExt "Empty" "(map int int)" "{}" 15
, valueTestCaseExt "Length 1" "(map int int)" "{Elt 1 2}" 21
, valueTestCaseExt "Length 1 (big key)" "(map int int)" "{Elt 100 2}" 22
, let val = "{Elt 1 1; Elt 2 2; Elt 3 3; Elt 4 4; \
\ Elt 5 5; Elt 6 6; Elt 7 7; Elt 8 8 }"
in valueTestCaseExt "Length 8" "(map int int)" val 63
]
]
where
valueTestCaseExt name ty val gas =
testCase name $
("PUSH " <> ty <> " " <> val <> "; DROP") ?== gas
valueTestCase ty val gas =
valueTestCaseExt (toString ty) ty val gas
stringTestCase l gas =
let name = "Length " <> show l
val = dquotes $ T.replicate l "a"
in valueTestCaseExt name "string" val gas
bytesTestCase l gas =
let name = "Length " <> show l
val = "0x" <> T.replicate l "12"
in valueTestCaseExt name "bytes" val gas
gen :: Gen a -> a
gen genA = runGen 20 12 genA
test_Types :: [TestTree]
test_Types =
[ typeTestCase "int" 8
, typeTestCase "string" 8
, typeTestCase "mutez" 8
, typeTestCase "key_hash" 8
, typeTestCase "address" 8
, typeTestCase "signature" 8
, typeTestCase "option int" 10
, typeTestCase "list int" 10
, typeTestCase "set int" 10
, typeTestCase "contract int" 10
, typeTestCase "pair int int" 12
, typeTestCase "or int int" 12
, typeTestCase "or (pair nat nat) (pair int nat)" 20
, typeTestCase "lambda int unit" 12
, typeTestCase "map int int" 12
, typeTestCase "big_map int int" 12
, typeTestCase "lambda operation int" 12
, typeTestCase "pair int int" 12
, typeTestCase "pair (int : %) int" 12
, typeTestCase "pair (int :a) int" 18
, typeTestCase "pair (int %a) int" 18
, typeTestCase "pair (int %a :a) int" 21
, typeTestCase "pair (int %a) (int %a)" 24
, typeTestCase "pair :a (int %a) (int %a)" 30
, typeTestCase "pair :a int (int %a)" 24
, typeTestCase "pair : (int %a :) (int %a)" 24
, typeTestCase "pair :a (int %a :a) (int %a)" 33
]
where
typeTestCase t gas =
testCase (toString t) $ ("DUP; CONTRACT " <> t <> "; DROP") ?== gas
test_Instructions :: [TestTree]
test_Instructions =
[ instrTestCase "FAILWITH" 2
, instrTestCase "FAIL" 9
, instrTestCaseFailWith "DUP" 4
, instrTestCaseFailWith "DUP @" 4
, instrTestCaseFailWith "DUP @a" 10
, instrTestCaseFailWith "DUP; DROP" 6
, instrTestCaseFailWith "DUP; SWAP" 6
, instrTestCaseFailWith "DUP; SWAP; SWAP" 8
, instrTestCaseFailWith "PUSH int 0" 8
, instrTestCaseFailWith "PUSH @a int 0" 14
, instrTestCaseFailWith "PUSH @a (int :a) 0" 20
, instrTestCaseFailWith "SOME" 4
, instrTestCaseFailWith "SOME @" 4
, instrTestCaseFailWith "SOME @a" 10
, instrTestCaseFailWith "NONE int" 6
, instrTestCaseFailWith "NONE (option int)" 8
, instrTestCaseFailWith "NONE :a @a (option :a int)" 23
, instrTestCaseFailWith "UNIT" 4
, instrTestCaseFailWith "DUP; SOME; IF_NONE {}{DROP}" 20
, instrTestCaseFailWith "DUP; DUP; PAIR" 8
, instrTestCaseFailWith "DUP; DUP; PAIR % % : @" 8
, instrTestCaseFailWith "DUP; DUP; PAIR %a" 14
, instrTestCaseFailWith "DUP; DUP; PAIR %a %a" 17
, instrTestCaseFailWith "DUP; DUP; PAIR %a %a :a" 20
, instrTestCaseFailWith "DUP; DUP; PAIR %a % :a @a" 20
, instrTestCaseFailWith "DUP; DUP; PAIR % %a :a @a" 23
, instrTestCaseFailWith "DUP; DUP; PAIR %a %a :a @a" 23
-- Further skipping some instructions since everything is trivial
, instrTestCaseFailWith "EMPTY_BIG_MAP :a @a (int :a) (unit :a)" 29
, instrTestCaseFailWith "PUSH (list int) {}; MAP {}" 20
, instrTestCaseFailWith "DIP {}" 9
, instrTestCaseFailWith "DIP 0 {}" 11
, instrTestCaseFailWith "DIP 1 {}" 11
, instrTestCaseFailWith "DROP 0" 6
, instrTestCaseFailWith "PACK" 4
, instrTestCaseFailWith "PUSH int 1; PUSH int 2; ADD" 16
, instrTestCaseFailWith "DUP; CONTRACT int" 8
, namedInstrTestCaseFailWith "CREATE_CONTRACT"
"UNIT; PUSH mutez 5; NONE key_hash; \
\CREATE_CONTRACT \
\{ parameter unit; \
\ storage unit; \
\ code { CDR; NIL operation; PAIR } }" 44
, namedInstrTestCaseFailWith "CREATE_CONTRACT + PUSH or"
"PUSH (or int nat) (Left 5); PUSH mutez 5; NONE key_hash; \
\CREATE_CONTRACT \
\{ parameter unit; \
\ storage (or int nat); \
\ code { DROP; PUSH (or int nat) (Right 2); NIL operation; PAIR } }" 70
, namedInstrTestCaseFailWith "CREATE_CONTRACT + PUSH or + Annotation"
"PUSH (or %root int nat) (Left 5); PUSH mutez 5; NONE key_hash; \
\CREATE_CONTRACT \
\{ parameter unit; \
\ storage (or int nat); \
\ code { DROP; PUSH (or int nat) (Right 2); NIL operation; PAIR } }" 70
]
where
namedInstrTestCase testName instr gas =
testCase testName $ instr ?== gas
namedInstrTestCaseFailWith testName instr gas =
namedInstrTestCase testName (instr <> "; FAILWITH") gas
instrTestCase instr gas =
namedInstrTestCase (toString instr) instr gas
instrTestCaseFailWith instr gas =
instrTestCase (instr <> "; FAILWITH") gas