diff --git a/.gitignore b/.gitignore index b5ef81d..eeee6ba 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ config.json /vendor/ .idea /bin/ +*.out # debhelpers **/.debhelper diff --git a/covertest/client.go b/covertest/client.go new file mode 100644 index 0000000..c7eb848 --- /dev/null +++ b/covertest/client.go @@ -0,0 +1,133 @@ +package covertest + +import ( + "testing" + + "github.com/nspcc-dev/neo-go/pkg/core" + "github.com/nspcc-dev/neo-go/pkg/core/block" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/neotest" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/stretchr/testify/require" +) + +// ContractInvoker is a client for a specific contract. +type ContractInvoker struct { + *neotest.ContractInvoker + Methods []Method +} + +type Method struct { + Name string + Instructions []InstrHash +} + +// CommitteeInvoker creates a new ContractInvoker for the contract with hash h and a committee multisignature signer. +func CommitteeInvoker(e *neotest.Executor, h util.Uint160) *ContractInvoker { + return &ContractInvoker{ + ContractInvoker: &neotest.ContractInvoker{ + Executor: e, + Hash: h, + Signers: []neotest.Signer{e.Committee}, + }, + Methods: nil, + } +} + +// Invoke invokes the method with the args, persists the transaction and checks the result. +// Returns transaction hash. +func (c *ContractInvoker) Invoke(t testing.TB, result interface{}, method string, args ...interface{}) util.Uint256 { + c.Methods = append(c.Methods, Method{ + Name: method, + Instructions: nil, + }) + tx := c.PrepareInvoke(t, method, args...) + c.AddNewBlock(t, tx) + c.CheckHalt(t, tx.Hash(), stackitem.Make(result)) + return tx.Hash() +} + +// InvokeFail invokes the method with the args, persists the transaction and checks the error message. +// It returns the transaction hash. +func (c *ContractInvoker) InvokeFail(t testing.TB, message string, method string, args ...interface{}) util.Uint256 { + c.Methods = append(c.Methods, Method{ + Name: method, + Instructions: nil, + }) + tx := c.PrepareInvoke(t, method, args...) + c.AddNewBlock(t, tx) + c.CheckFault(t, tx.Hash(), message) + return tx.Hash() +} + +// PrepareInvoke creates a new invocation transaction. +func (c *ContractInvoker) PrepareInvoke(t testing.TB, method string, args ...interface{}) *transaction.Transaction { + return c.NewTx(t, c.Signers, c.Hash, method, args...) +} + +// NewTx creates a new transaction which invokes the contract method. +// The transaction is signed by the signers. +func (c *ContractInvoker) NewTx(t testing.TB, signers []neotest.Signer, + hash util.Uint160, method string, args ...interface{}) *transaction.Transaction { + tx := c.NewUnsignedTx(t, hash, method, args...) + return c.SignTx(t, tx, -1, signers...) +} + +// SignTx signs a transaction using the provided signers. +func (c *ContractInvoker) SignTx(t testing.TB, tx *transaction.Transaction, sysFee int64, signers ...neotest.Signer) *transaction.Transaction { + for _, acc := range signers { + tx.Signers = append(tx.Signers, transaction.Signer{ + Account: acc.ScriptHash(), + Scopes: transaction.Global, + }) + } + neotest.AddNetworkFee(c.Chain, tx, signers...) + c.AddSystemFee(c.Chain, tx, sysFee) + + for _, acc := range signers { + require.NoError(t, acc.SignTx(c.Chain.GetConfig().Magic, tx)) + } + return tx +} + +// AddSystemFee adds system fee to the transaction. If negative value specified, +// then system fee is defined by test invocation. +func (c *ContractInvoker) AddSystemFee(bc *core.Blockchain, tx *transaction.Transaction, sysFee int64) { + if sysFee >= 0 { + tx.SystemFee = sysFee + return + } + ops, v, _ := TestInvoke(bc, tx) // ignore error to support failing transactions + tx.SystemFee = v.GasConsumed() + c.Methods[len(c.Methods)-1].Instructions = make([]InstrHash, len(ops)) + copy(c.Methods[len(c.Methods)-1].Instructions, ops) +} + +// TestInvoke creates a test VM with a dummy block and executes a transaction in it. +func TestInvoke(bc *core.Blockchain, tx *transaction.Transaction) ([]InstrHash, *vm.VM, error) { + lastBlock, err := bc.GetBlock(bc.GetHeaderHash(bc.BlockHeight())) + if err != nil { + return nil, nil, err + } + b := &block.Block{ + Header: block.Header{ + Index: bc.BlockHeight() + 1, + Timestamp: lastBlock.Timestamp + 1, + }, + } + + // `GetTestVM` as well as `Run` can use a transaction hash which will set a cached value. + // This is unwanted behavior, so we explicitly copy the transaction to perform execution. + ttx := *tx + ic, _ := bc.GetTestVM(trigger.Application, &ttx, b) + + defer ic.Finalize() + + ic.VM.LoadWithFlags(tx.Script, callflag.All) + ops, err := Run(ic.VM) + return ops, ic.VM, err +} diff --git a/covertest/compile.go b/covertest/compile.go new file mode 100644 index 0000000..0b9c3b7 --- /dev/null +++ b/covertest/compile.go @@ -0,0 +1,56 @@ +package covertest + +import ( + "testing" + + "github.com/nspcc-dev/neo-go/cli/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/compiler" + "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/neotest" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/stretchr/testify/require" +) + +// ContractWithDebugInfo contains contract info for deployment and debug information for coverage. +type ContractWithDebugInfo struct { + Contract *neotest.Contract + DebugInfo *compiler.DebugInfo +} + +// CompileFile compiles a contract from the file and returns its NEF, manifest, hash and debug information. +func CompileFile(t testing.TB, sender util.Uint160, srcPath string, configPath string) *ContractWithDebugInfo { + // nef.NewFile() cares about version a lot. + config.Version = "neotest" + + ne, di, err := compiler.CompileWithOptions(srcPath, nil, nil) + require.NoError(t, err) + + conf, err := smartcontract.ParseContractConfig(configPath) + require.NoError(t, err) + + o := &compiler.Options{} + o.Name = conf.Name + o.ContractEvents = conf.Events + o.ContractSupportedStandards = conf.SupportedStandards + o.Permissions = make([]manifest.Permission, len(conf.Permissions)) + for i := range conf.Permissions { + o.Permissions[i] = manifest.Permission(conf.Permissions[i]) + } + o.SafeMethods = conf.SafeMethods + o.Overloads = conf.Overloads + o.SourceURL = conf.SourceURL + m, err := compiler.CreateManifest(di, o) + require.NoError(t, err) + + c := &neotest.Contract{ + Hash: state.CreateContractHash(sender, ne.Checksum, m.Name), + NEF: ne, + Manifest: m, + } + return &ContractWithDebugInfo{ + Contract: c, + DebugInfo: di, + } +} diff --git a/covertest/cover.go b/covertest/cover.go new file mode 100644 index 0000000..9d7a2e3 --- /dev/null +++ b/covertest/cover.go @@ -0,0 +1,124 @@ +package covertest + +import ( + "fmt" + "os" + "strings" + "sync" + "testing" + + "github.com/nspcc-dev/neo-go/pkg/compiler" + "github.com/stretchr/testify/require" +) + +var mu sync.Mutex + +type coverline struct { + Doc string + Opcode int + StartLine int + StartCol int + EndLine int + EndCol int + WTFnumber int + IsCovered bool +} + +// MakeCoverage generates an output file with coverage info in correct format +func (c *ContractInvoker) MakeCoverage(t testing.TB, ctrdi *ContractWithDebugInfo, ctrPath string, fileName string) { + docs := getDocuments(t, ctrdi.DebugInfo.Documents, ctrPath) + cov := getSeqPoints(t, ctrdi.DebugInfo, docs) + for _, iMethod := range c.Methods { + countInstructions(cov, iMethod.Instructions) + } + printToFile(t, cov, fileName) +} + +func getDocuments(t testing.TB, docs []string, substr string) []int { + res := make([]int, 0, len(docs)) + + for i, cDoc := range docs { + if strings.Contains(cDoc, substr) { + res = append(res, i) + } + } + if len(res) == 0 { + t.Log("Cannot get document\n") + t.FailNow() + } + return res +} + +func getSeqPoints(t testing.TB, di *compiler.DebugInfo, docs []int) []coverline { + res := make([]coverline, 0, 10) + + for _, method := range di.Methods { + maxLine := method.Range.End + for _, seqPoint := range method.SeqPoints { + if isValidDocument(seqPoint.Document, docs) && seqPoint.Opcode < int(maxLine) { + res = append(res, coverline{ + Doc: di.Documents[seqPoint.Document], + Opcode: seqPoint.Opcode, + StartLine: seqPoint.StartLine, + StartCol: seqPoint.StartCol, + EndLine: seqPoint.EndLine, + EndCol: seqPoint.EndCol, + WTFnumber: 1, + IsCovered: false, + }) + } + } + } + return res +} + +func isValidDocument(iDocToCheck int, docs []int) bool { + for _, iDoc := range docs { + if iDoc == iDocToCheck { + return true + } + } + return false +} + +func countInstructions(cov []coverline, codes []InstrHash) { + for i := 0; i < len(cov); i++ { + for _, code := range codes { + if code.Offset == cov[i].Opcode { + cov[i].IsCovered = true + //cov[i].WTFnumber++ + //break + } + } + } +} + +func printToFile(t testing.TB, cov []coverline, name string) { + mu.Lock() + defer mu.Unlock() + + f, err := os.OpenFile(name, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) + require.NoError(t, err) + + defer f.Close() + + fi, err := os.Stat(name) + require.NoError(t, err) + firstToWrite := "" + if fi.Size() == 0 { + firstToWrite = "mode: set\n" + } + + _, err = f.WriteString(firstToWrite) + require.NoError(t, err) + + for _, info := range cov { + covered := 0 + if info.IsCovered { + covered++ + } + line := fmt.Sprintf("%s:%d.%d,%d.%d %d %d\n", info.Doc, info.StartLine, info.StartCol, info.EndLine, info.EndCol, info.WTFnumber, covered) + _, err = f.WriteString(line) + require.NoError(t, err) + } +} diff --git a/covertest/run.go b/covertest/run.go new file mode 100644 index 0000000..98dae12 --- /dev/null +++ b/covertest/run.go @@ -0,0 +1,58 @@ +package covertest + +import ( + "errors" + + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/nspcc-dev/neo-go/pkg/vm/opcode" + "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" +) + +// InstrHash maps instruction with scripthash of a contract it belongs to. +type InstrHash struct { + Offset int + Instruction opcode.Opcode + ScriptHash util.Uint160 +} + +// Run starts execution of the loaded program and accumulates all seen opcodes +// together with the scripthash of a contract they belong to. +// Original vm.Run(): https://github.com/nspcc-dev/neo-go/blob/v0.101.3/pkg/vm/vm.go#L418 +func Run(v *vm.VM) ([]InstrHash, error) { + if !v.Ready() { + return nil, errors.New("no program loaded") + } + + if v.HasFailed() { + // VM already ran something and failed, in general its state is + // undefined in this case so we can't run anything. + return nil, errors.New("VM has failed") + } + + // vmstate.Halt (the default) or vmstate.Break are safe to continue. + var ops []InstrHash + for { + switch { + case v.HasFailed(): + // Should be caught and reported already by the v.Step(), + // but we're checking here anyway just in case. + return ops, errors.New("VM has failed") + case v.HasHalted(), v.AtBreakpoint(): + // Normal exit from this loop. + return ops, nil + case v.State() == vmstate.None: + nStr, curInstr := v.Context().NextInstr() + ops = append(ops, InstrHash{ + Offset: nStr, + Instruction: curInstr, + ScriptHash: v.Context().ScriptHash(), + }) + if err := v.Step(); err != nil { + return ops, err + } + default: + return ops, errors.New("unknown state") + } + } +} diff --git a/go.mod b/go.mod index 86314a0..3f34538 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.14 require ( github.com/mr-tron/base58 v1.2.0 - github.com/nspcc-dev/neo-go v0.101.5-0.20230808195420-5fc61be5f6c5 - github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230808195420-5fc61be5f6c5 + github.com/nspcc-dev/neo-go v0.101.5-0.20230811211719-9fb154a376fc + github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230810094130-11bb733f1a2c github.com/stretchr/testify v1.8.1 ) diff --git a/go.sum b/go.sum index 0419387..8e61593 100644 --- a/go.sum +++ b/go.sum @@ -698,14 +698,12 @@ github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1: github.com/nspcc-dev/neo-go v0.98.0/go.mod h1:E3cc1x6RXSXrJb2nDWXTXjnXk3rIqVN8YdFyWv+FrqM= github.com/nspcc-dev/neo-go v0.99.2/go.mod h1:9P0yWqhZX7i/ChJ+zjtiStO1uPTolPFUM+L5oNznU8E= github.com/nspcc-dev/neo-go v0.100.1/go.mod h1:Nnp7F4e9IBccsgtCeLtUWV+0T6gk1PtP5HRtA13hUfc= -github.com/nspcc-dev/neo-go v0.101.5-0.20230808195420-5fc61be5f6c5 h1:AXI2upTPeTqX+n4xrBEzPATgEviOM/Prg6UQ6KDm+DU= -github.com/nspcc-dev/neo-go v0.101.5-0.20230808195420-5fc61be5f6c5/go.mod h1:Z0kpjwnTJj/ik/X6z18xjCL0X2+RNbqlnhKrl+MYgP8= +github.com/nspcc-dev/neo-go v0.101.5-0.20230811211719-9fb154a376fc h1:U4x1uOG3JXy9TWgcH2B/ZOWfxZmerkymkZibBrxSA1Q= +github.com/nspcc-dev/neo-go v0.101.5-0.20230811211719-9fb154a376fc/go.mod h1:QXxpZxJT2KedwM0Nlj8UO0/fZN2WIe4h/i03uBHKbnc= github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220809123759-3094d3e0c14b/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a h1:63sh46kfKF/g2IE1z/EV8CBEKCVmGJXSSH0ZHqTDGCY= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230420112658-c50ab951645a/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230808195420-5fc61be5f6c5 h1:/d7mY5hYlNhmEXexKcyqSR0b1Hdl5hf/c5o8Vi/1vt4= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230808195420-5fc61be5f6c5/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230810094130-11bb733f1a2c h1:upj+XyoI0dS7CYnZOTAeyuznhvDIJurQXL4FvHNKyl4= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230810094130-11bb733f1a2c/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211201134523-3604d96f3fe1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs= github.com/nspcc-dev/neofs-api-go/v2 v2.11.1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs= github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 h1:jhuN8Ldqz7WApvUJRFY0bjRXE1R3iCkboMX5QVZhHVk= diff --git a/tests/balance_test.go b/tests/balance_test.go index 03b076e..1795b53 100644 --- a/tests/balance_test.go +++ b/tests/balance_test.go @@ -4,6 +4,7 @@ import ( "path" "testing" + "git.frostfs.info/TrueCloudLab/frostfs-contract/covertest" "github.com/nspcc-dev/neo-go/pkg/neotest" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -12,17 +13,17 @@ import ( const balancePath = "../balance" func deployBalanceContract(t *testing.T, e *neotest.Executor, addrNetmap, addrContainer util.Uint160) util.Uint160 { - c := neotest.CompileFile(t, e.CommitteeHash, balancePath, path.Join(balancePath, "config.yml")) + c := covertest.CompileFile(t, e.CommitteeHash, balancePath, path.Join(balancePath, "config.yml")) args := make([]interface{}, 3) args[0] = false args[1] = addrNetmap args[2] = addrContainer - e.DeployContract(t, c, args) - return c.Hash + e.DeployContract(t, c.Contract, args) + return c.Contract.Hash } -func balanceMint(t *testing.T, c *neotest.ContractInvoker, acc neotest.Signer, amount int64, details []byte) { +func balanceMint(t *testing.T, c *covertest.ContractInvoker, acc neotest.Signer, amount int64, details []byte) { c.Invoke(t, stackitem.Null{}, "mint", acc.ScriptHash(), amount, details) } diff --git a/tests/container_test.go b/tests/container_test.go index aa98f13..c4df0ae 100644 --- a/tests/container_test.go +++ b/tests/container_test.go @@ -9,6 +9,7 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-contract/common" "git.frostfs.info/TrueCloudLab/frostfs-contract/container" + "git.frostfs.info/TrueCloudLab/frostfs-contract/covertest" "git.frostfs.info/TrueCloudLab/frostfs-contract/nns" "github.com/mr-tron/base58" "github.com/nspcc-dev/neo-go/pkg/core/interop/storage" @@ -35,26 +36,26 @@ func deployContainerContract(t *testing.T, e *neotest.Executor, addrNetmap, addr args[4] = addrNNS args[5] = "frostfs" - c := neotest.CompileFile(t, e.CommitteeHash, containerPath, path.Join(containerPath, "config.yml")) - e.DeployContract(t, c, args) - return c.Hash + c := covertest.CompileFile(t, e.CommitteeHash, containerPath, path.Join(containerPath, "config.yml")) + e.DeployContract(t, c.Contract, args) + return c.Contract.Hash } -func newContainerInvoker(t *testing.T) (*neotest.ContractInvoker, *neotest.ContractInvoker, *neotest.ContractInvoker) { +func newContainerInvoker(t *testing.T) (*covertest.ContractWithDebugInfo, *covertest.ContractInvoker, *covertest.ContractInvoker, *covertest.ContractInvoker) { e := newExecutor(t) - ctrNNS := neotest.CompileFile(t, e.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml")) - ctrNetmap := neotest.CompileFile(t, e.CommitteeHash, netmapPath, path.Join(netmapPath, "config.yml")) - ctrBalance := neotest.CompileFile(t, e.CommitteeHash, balancePath, path.Join(balancePath, "config.yml")) - ctrContainer := neotest.CompileFile(t, e.CommitteeHash, containerPath, path.Join(containerPath, "config.yml")) + ctrNNS := covertest.CompileFile(t, e.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml")) + ctrNetmap := covertest.CompileFile(t, e.CommitteeHash, netmapPath, path.Join(netmapPath, "config.yml")) + ctrBalance := covertest.CompileFile(t, e.CommitteeHash, balancePath, path.Join(balancePath, "config.yml")) + ctrContainer := covertest.CompileFile(t, e.CommitteeHash, containerPath, path.Join(containerPath, "config.yml")) - e.DeployContract(t, ctrNNS, nil) - deployNetmapContract(t, e, ctrBalance.Hash, ctrContainer.Hash, + e.DeployContract(t, ctrNNS.Contract, nil) + deployNetmapContract(t, e, ctrBalance.Contract.Hash, ctrContainer.Contract.Hash, container.RegistrationFeeKey, int64(containerFee), container.AliasFeeKey, int64(containerAliasFee)) - deployBalanceContract(t, e, ctrNetmap.Hash, ctrContainer.Hash) - deployContainerContract(t, e, ctrNetmap.Hash, ctrBalance.Hash, ctrNNS.Hash) - return e.CommitteeInvoker(ctrContainer.Hash), e.CommitteeInvoker(ctrBalance.Hash), e.CommitteeInvoker(ctrNetmap.Hash) + deployBalanceContract(t, e, ctrNetmap.Contract.Hash, ctrContainer.Contract.Hash) + deployContainerContract(t, e, ctrNetmap.Contract.Hash, ctrBalance.Contract.Hash, ctrNNS.Contract.Hash) + return ctrContainer, covertest.CommitteeInvoker(e, ctrContainer.Contract.Hash), covertest.CommitteeInvoker(e, ctrBalance.Contract.Hash), covertest.CommitteeInvoker(e, ctrNetmap.Contract.Hash) } func setContainerOwner(c []byte, acc neotest.Signer) { @@ -86,7 +87,7 @@ func dummyContainer(owner neotest.Signer) testContainer { } func TestContainerCount(t *testing.T) { - c, cBal, _ := newContainerInvoker(t) + ctrdi, c, cBal, _ := newContainerInvoker(t) checkCount := func(t *testing.T, expected int64) { s, err := c.TestInvoke(t, "count") @@ -120,9 +121,10 @@ func TestContainerCount(t *testing.T) { c.Invoke(t, stackitem.Null{}, "delete", cnt3.id[:], cnt3.sig, cnt3.pub, cnt3.token) checkCount(t, 0) checkContainerList(t, c, [][]byte{}) + c.MakeCoverage(t, ctrdi, "container", "c.out") } -func checkContainerList(t *testing.T, c *neotest.ContractInvoker, expected [][]byte) { +func checkContainerList(t *testing.T, c *covertest.ContractInvoker, expected [][]byte) { t.Run("check with `list`", func(t *testing.T) { s, err := c.TestInvoke(t, "list", nil) require.NoError(t, err) @@ -166,7 +168,7 @@ func checkContainerList(t *testing.T, c *neotest.ContractInvoker, expected [][]b } func TestContainerPut(t *testing.T) { - c, cBal, _ := newContainerInvoker(t) + ctrdi, c, cBal, _ := newContainerInvoker(t) acc := c.NewAccount(t) cnt := dummyContainer(acc) @@ -182,8 +184,8 @@ func TestContainerPut(t *testing.T) { c.Invoke(t, stackitem.Null{}, "put", putArgs...) t.Run("with nice names", func(t *testing.T) { - ctrNNS := neotest.CompileFile(t, c.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml")) - nnsHash := ctrNNS.Hash + ctrNNS := covertest.CompileFile(t, c.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml")) + nnsHash := ctrNNS.Contract.Hash balanceMint(t, cBal, acc, containerFee*1, []byte{}) @@ -231,9 +233,10 @@ func TestContainerPut(t *testing.T) { cNNS.Invoke(t, expected, "resolve", "domain.cdn", int64(nns.TXT)) }) }) + c.MakeCoverage(t, ctrdi, "container", "c.out") } -func addContainer(t *testing.T, c, cBal *neotest.ContractInvoker) (neotest.Signer, testContainer) { +func addContainer(t *testing.T, c, cBal *covertest.ContractInvoker) (neotest.Signer, testContainer) { acc := c.NewAccount(t) cnt := dummyContainer(acc) @@ -243,7 +246,7 @@ func addContainer(t *testing.T, c, cBal *neotest.ContractInvoker) (neotest.Signe } func TestContainerDelete(t *testing.T) { - c, cBal, cNm := newContainerInvoker(t) + ctrdi, c, cBal, cNm := newContainerInvoker(t) acc, cnt := addContainer(t, c, cBal) cAcc := c.WithSigners(acc) @@ -283,10 +286,11 @@ func TestContainerDelete(t *testing.T) { }) c.InvokeFail(t, container.NotFoundError, "get", cnt.id[:]) + c.MakeCoverage(t, ctrdi, "container", "c.out") } func TestContainerOwner(t *testing.T) { - c, cBal, _ := newContainerInvoker(t) + ctrdi, c, cBal, _ := newContainerInvoker(t) acc, cnt := addContainer(t, c, cBal) @@ -298,10 +302,11 @@ func TestContainerOwner(t *testing.T) { owner := signerToOwner(acc) c.Invoke(t, stackitem.NewBuffer(owner), "owner", cnt.id[:]) + c.MakeCoverage(t, ctrdi, "container", "c.out") } func TestContainerGet(t *testing.T) { - c, cBal, _ := newContainerInvoker(t) + ctrdi, c, cBal, _ := newContainerInvoker(t) _, cnt := addContainer(t, c, cBal) @@ -318,6 +323,7 @@ func TestContainerGet(t *testing.T) { stackitem.NewByteArray(cnt.token), }) c.Invoke(t, expected, "get", cnt.id[:]) + c.MakeCoverage(t, ctrdi, "container", "c.out") } type eacl struct { @@ -339,7 +345,7 @@ func dummyEACL(containerID [32]byte) eacl { } func TestContainerSetEACL(t *testing.T) { - c, cBal, _ := newContainerInvoker(t) + ctrdi, c, cBal, _ := newContainerInvoker(t) acc, cnt := addContainer(t, c, cBal) @@ -364,10 +370,11 @@ func TestContainerSetEACL(t *testing.T) { stackitem.NewByteArray(e.token), }) c.Invoke(t, expected, "eACL", cnt.id[:]) + c.MakeCoverage(t, ctrdi, "container", "c.out") } func TestContainerSizeEstimation(t *testing.T) { - c, cBal, cNm := newContainerInvoker(t) + ctrdi, c, cBal, cNm := newContainerInvoker(t) _, cnt := addContainer(t, c, cBal) nodes := []testNodeInfo{ @@ -435,6 +442,7 @@ func TestContainerSizeEstimation(t *testing.T) { } checkEstimations(t, c, 2, cnt) checkEstimations(t, c, epoch, cnt, estimation{nodes[1].pub, int64(999)}) + c.MakeCoverage(t, ctrdi, "container", "c.out") } type estimation struct { @@ -442,7 +450,7 @@ type estimation struct { size int64 } -func checkEstimations(t *testing.T, c *neotest.ContractInvoker, epoch int64, cnt testContainer, estimations ...estimation) { +func checkEstimations(t *testing.T, c *covertest.ContractInvoker, epoch int64, cnt testContainer, estimations ...estimation) { // Check that listed estimations match expected listEstimations := getListEstimations(t, c, epoch, cnt) requireEstimationsMatch(t, estimations, listEstimations) @@ -452,7 +460,7 @@ func checkEstimations(t *testing.T, c *neotest.ContractInvoker, epoch int64, cnt requireEstimationsMatch(t, estimations, iterEstimations) } -func getListEstimations(t *testing.T, c *neotest.ContractInvoker, epoch int64, cnt testContainer) []estimation { +func getListEstimations(t *testing.T, c *covertest.ContractInvoker, epoch int64, cnt testContainer) []estimation { s, err := c.TestInvoke(t, "listContainerSizes", epoch) require.NoError(t, err) @@ -481,7 +489,7 @@ func getListEstimations(t *testing.T, c *neotest.ContractInvoker, epoch int64, c return convertStackToEstimations(sizes[1].Value().([]stackitem.Item)) } -func getIterEstimations(t *testing.T, c *neotest.ContractInvoker, epoch int64) []estimation { +func getIterEstimations(t *testing.T, c *covertest.ContractInvoker, epoch int64) []estimation { iterStack, err := c.TestInvoke(t, "iterateContainerSizes", epoch) require.NoError(t, err) iter := iterStack.Pop().Value().(*storage.Iterator) diff --git a/tests/frostfsid_test.go b/tests/frostfsid_test.go index 2e25e58..dd46537 100644 --- a/tests/frostfsid_test.go +++ b/tests/frostfsid_test.go @@ -7,6 +7,7 @@ import ( "testing" "git.frostfs.info/TrueCloudLab/frostfs-contract/container" + "git.frostfs.info/TrueCloudLab/frostfs-contract/covertest" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/neotest" "github.com/nspcc-dev/neo-go/pkg/util" @@ -16,37 +17,37 @@ import ( const frostfsidPath = "../frostfsid" -func deployFrostFSIDContract(t *testing.T, e *neotest.Executor, addrNetmap, addrContainer util.Uint160) util.Uint160 { +func deployFrostFSIDContract(t *testing.T, e *neotest.Executor, addrNetmap, addrContainer util.Uint160) (util.Uint160, *covertest.ContractWithDebugInfo) { args := make([]interface{}, 5) args[0] = false args[1] = addrNetmap args[2] = addrContainer - c := neotest.CompileFile(t, e.CommitteeHash, frostfsidPath, path.Join(frostfsidPath, "config.yml")) - e.DeployContract(t, c, args) - return c.Hash + c := covertest.CompileFile(t, e.CommitteeHash, frostfsidPath, path.Join(frostfsidPath, "config.yml")) + e.DeployContract(t, c.Contract, args) + return c.Contract.Hash, c } -func newFrostFSIDInvoker(t *testing.T) *neotest.ContractInvoker { +func newFrostFSIDInvoker(t *testing.T) (*covertest.ContractInvoker, *covertest.ContractWithDebugInfo) { e := newExecutor(t) - ctrNNS := neotest.CompileFile(t, e.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml")) - ctrNetmap := neotest.CompileFile(t, e.CommitteeHash, netmapPath, path.Join(netmapPath, "config.yml")) - ctrBalance := neotest.CompileFile(t, e.CommitteeHash, balancePath, path.Join(balancePath, "config.yml")) - ctrContainer := neotest.CompileFile(t, e.CommitteeHash, containerPath, path.Join(containerPath, "config.yml")) + ctrNNS := covertest.CompileFile(t, e.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml")) + ctrNetmap := covertest.CompileFile(t, e.CommitteeHash, netmapPath, path.Join(netmapPath, "config.yml")) + ctrBalance := covertest.CompileFile(t, e.CommitteeHash, balancePath, path.Join(balancePath, "config.yml")) + ctrContainer := covertest.CompileFile(t, e.CommitteeHash, containerPath, path.Join(containerPath, "config.yml")) - e.DeployContract(t, ctrNNS, nil) - deployNetmapContract(t, e, ctrBalance.Hash, ctrContainer.Hash, + e.DeployContract(t, ctrNNS.Contract, nil) + deployNetmapContract(t, e, ctrBalance.Contract.Hash, ctrContainer.Contract.Hash, container.RegistrationFeeKey, int64(containerFee), container.AliasFeeKey, int64(containerAliasFee)) - deployBalanceContract(t, e, ctrNetmap.Hash, ctrContainer.Hash) - deployContainerContract(t, e, ctrNetmap.Hash, ctrBalance.Hash, ctrNNS.Hash) - h := deployFrostFSIDContract(t, e, ctrNetmap.Hash, ctrContainer.Hash) - return e.CommitteeInvoker(h) + deployBalanceContract(t, e, ctrNetmap.Contract.Hash, ctrContainer.Contract.Hash) + deployContainerContract(t, e, ctrNetmap.Contract.Hash, ctrBalance.Contract.Hash, ctrNNS.Contract.Hash) + h, c := deployFrostFSIDContract(t, e, ctrNetmap.Contract.Hash, ctrContainer.Contract.Hash) + return covertest.CommitteeInvoker(e, h), c } func TestFrostFSID_AddKey(t *testing.T) { - e := newFrostFSIDInvoker(t) + e, c := newFrostFSIDInvoker(t) pubs := make([][]byte, 6) for i := range pubs { @@ -108,4 +109,5 @@ func TestFrostFSID_AddKey(t *testing.T) { arr = []stackitem.Item{stackitem.NewBuffer(pubs[3])} e.Invoke(t, stackitem.NewArray(arr), "key", owner) }) + e.MakeCoverage(t, c, "frostfsid", "c.out") } diff --git a/tests/netmap_test.go b/tests/netmap_test.go index 4c08c0f..ec6dba4 100644 --- a/tests/netmap_test.go +++ b/tests/netmap_test.go @@ -9,6 +9,7 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-contract/common" "git.frostfs.info/TrueCloudLab/frostfs-contract/container" + "git.frostfs.info/TrueCloudLab/frostfs-contract/covertest" "git.frostfs.info/TrueCloudLab/frostfs-contract/netmap" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/neotest" @@ -36,19 +37,19 @@ func deployNetmapContract(t *testing.T, e *neotest.Executor, addrBalance, addrCo return c.Hash } -func newNetmapInvoker(t *testing.T, config ...interface{}) *neotest.ContractInvoker { +func newNetmapInvoker(t *testing.T, config ...interface{}) *covertest.ContractInvoker { e := newExecutor(t) - ctrNNS := neotest.CompileFile(t, e.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml")) - ctrNetmap := neotest.CompileFile(t, e.CommitteeHash, netmapPath, path.Join(netmapPath, "config.yml")) - ctrBalance := neotest.CompileFile(t, e.CommitteeHash, balancePath, path.Join(balancePath, "config.yml")) - ctrContainer := neotest.CompileFile(t, e.CommitteeHash, containerPath, path.Join(containerPath, "config.yml")) + ctrNNS := covertest.CompileFile(t, e.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml")) + ctrNetmap := covertest.CompileFile(t, e.CommitteeHash, netmapPath, path.Join(netmapPath, "config.yml")) + ctrBalance := covertest.CompileFile(t, e.CommitteeHash, balancePath, path.Join(balancePath, "config.yml")) + ctrContainer := covertest.CompileFile(t, e.CommitteeHash, containerPath, path.Join(containerPath, "config.yml")) - e.DeployContract(t, ctrNNS, nil) - deployContainerContract(t, e, ctrNetmap.Hash, ctrBalance.Hash, ctrNNS.Hash) - deployBalanceContract(t, e, ctrNetmap.Hash, ctrContainer.Hash) - deployNetmapContract(t, e, ctrBalance.Hash, ctrContainer.Hash, config...) - return e.CommitteeInvoker(ctrNetmap.Hash) + e.DeployContract(t, ctrNNS.Contract, nil) + deployContainerContract(t, e, ctrNetmap.Contract.Hash, ctrBalance.Contract.Hash, ctrNNS.Contract.Hash) + deployBalanceContract(t, e, ctrNetmap.Contract.Hash, ctrContainer.Contract.Hash) + deployNetmapContract(t, e, ctrBalance.Contract.Hash, ctrContainer.Contract.Hash, config...) + return covertest.CommitteeInvoker(e, ctrNetmap.Contract.Hash) } func TestDeploySetConfig(t *testing.T) { @@ -80,7 +81,7 @@ func dummyNodeInfo(acc neotest.Signer) testNodeInfo { } } -func newStorageNode(t *testing.T, c *neotest.ContractInvoker) testNodeInfo { +func newStorageNode(t *testing.T, c *covertest.ContractInvoker) testNodeInfo { return dummyNodeInfo(c.NewAccount(t)) } @@ -173,7 +174,7 @@ func TestUpdateSnapshotCount(t *testing.T) { require.True(t, netmap.DefaultSnapshotCount > 5) // sanity check, adjust tests if false. - prepare := func(t *testing.T, cNm *neotest.ContractInvoker, epochCount int) [][]testNodeInfo { + prepare := func(t *testing.T, cNm *covertest.ContractInvoker, epochCount int) [][]testNodeInfo { nodes := make([][]testNodeInfo, epochCount) nodes[0] = []testNodeInfo{newStorageNode(t, cNm)} cNm.Invoke(t, stackitem.Null{}, "addPeerIR", nodes[0][0].raw) @@ -279,7 +280,7 @@ func TestUpdateSnapshotCount(t *testing.T) { }) } -func checkSnapshotAt(t *testing.T, epoch int, cNm *neotest.ContractInvoker, nodes []testNodeInfo) { +func checkSnapshotAt(t *testing.T, epoch int, cNm *covertest.ContractInvoker, nodes []testNodeInfo) { s, err := cNm.TestInvoke(t, "snapshot", int64(epoch)) require.NoError(t, err) require.Equal(t, 1, s.Len()) @@ -402,7 +403,7 @@ func TestUpdateState(t *testing.T) { checkNetmapCandidates(t, cNm, 0) } -func checkNetmapCandidates(t *testing.T, c *neotest.ContractInvoker, size int) []stackitem.Item { +func checkNetmapCandidates(t *testing.T, c *covertest.ContractInvoker, size int) []stackitem.Item { s, err := c.TestInvoke(t, "netmapCandidates") require.NoError(t, err) require.Equal(t, 1, s.Len())