mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-22 19:19:09 +00:00
d0c45477f5
Test coverage is automatically enabled when go test is running with coverage enabled. It can be disabled for any Executor by using relevant methods. Coverage is gathered by capturing VM OPs during test contract execution and mapping them to the contract source code using the DebugInfo information. Signed-off-by: Slava0135 <super.novalskiy_0135@inbox.ru>
150 lines
5 KiB
Go
150 lines
5 KiB
Go
package neotest
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
|
"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/nspcc-dev/neo-go/pkg/vm/vmstate"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// ContractInvoker is a client for a specific contract.
|
|
type ContractInvoker struct {
|
|
*Executor
|
|
Hash util.Uint160
|
|
Signers []Signer
|
|
}
|
|
|
|
// NewInvoker creates a new ContractInvoker for the contract with hash h and the specified signers.
|
|
func (e *Executor) NewInvoker(h util.Uint160, signers ...Signer) *ContractInvoker {
|
|
return &ContractInvoker{
|
|
Executor: e,
|
|
Hash: h,
|
|
Signers: signers,
|
|
}
|
|
}
|
|
|
|
// CommitteeInvoker creates a new ContractInvoker for the contract with hash h and a committee multisignature signer.
|
|
func (e *Executor) CommitteeInvoker(h util.Uint160) *ContractInvoker {
|
|
return &ContractInvoker{
|
|
Executor: e,
|
|
Hash: h,
|
|
Signers: []Signer{e.Committee},
|
|
}
|
|
}
|
|
|
|
// ValidatorInvoker creates a new ContractInvoker for the contract with hash h and a validators multisignature signer.
|
|
func (e *Executor) ValidatorInvoker(h util.Uint160) *ContractInvoker {
|
|
return &ContractInvoker{
|
|
Executor: e,
|
|
Hash: h,
|
|
Signers: []Signer{e.Validator},
|
|
}
|
|
}
|
|
|
|
// TestInvokeScript creates test VM and invokes the script with the args and signers.
|
|
func (c *ContractInvoker) TestInvokeScript(t testing.TB, script []byte, signers []Signer, validUntilBlock ...uint32) (*vm.Stack, error) {
|
|
tx := c.PrepareInvocationNoSign(t, script, validUntilBlock...)
|
|
for _, acc := range signers {
|
|
tx.Signers = append(tx.Signers, transaction.Signer{
|
|
Account: acc.ScriptHash(),
|
|
Scopes: transaction.Global,
|
|
})
|
|
}
|
|
b := c.NewUnsignedBlock(t, tx)
|
|
ic, err := c.Chain.GetTestVM(trigger.Application, tx, b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
t.Cleanup(ic.Finalize)
|
|
|
|
if c.collectCoverage {
|
|
ic.VM.SetOnExecHook(coverageHook)
|
|
}
|
|
|
|
ic.VM.LoadWithFlags(tx.Script, callflag.All)
|
|
err = ic.VM.Run()
|
|
return ic.VM.Estack(), err
|
|
}
|
|
|
|
// TestInvoke creates test VM and invokes the method with the args.
|
|
func (c *ContractInvoker) TestInvoke(t testing.TB, method string, args ...any) (*vm.Stack, error) {
|
|
tx := c.PrepareInvokeNoSign(t, method, args...)
|
|
b := c.NewUnsignedBlock(t, tx)
|
|
ic, err := c.Chain.GetTestVM(trigger.Application, tx, b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
t.Cleanup(ic.Finalize)
|
|
|
|
if c.collectCoverage {
|
|
ic.VM.SetOnExecHook(coverageHook)
|
|
}
|
|
|
|
ic.VM.LoadWithFlags(tx.Script, callflag.All)
|
|
err = ic.VM.Run()
|
|
return ic.VM.Estack(), err
|
|
}
|
|
|
|
// WithSigners creates a new client with the provided signer.
|
|
func (c *ContractInvoker) WithSigners(signers ...Signer) *ContractInvoker {
|
|
newC := *c
|
|
newC.Signers = signers
|
|
return &newC
|
|
}
|
|
|
|
// PrepareInvoke creates a new invocation transaction.
|
|
func (c *ContractInvoker) PrepareInvoke(t testing.TB, method string, args ...any) *transaction.Transaction {
|
|
return c.Executor.NewTx(t, c.Signers, c.Hash, method, args...)
|
|
}
|
|
|
|
// PrepareInvokeNoSign creates a new unsigned invocation transaction.
|
|
func (c *ContractInvoker) PrepareInvokeNoSign(t testing.TB, method string, args ...any) *transaction.Transaction {
|
|
return c.Executor.NewUnsignedTx(t, c.Hash, method, args...)
|
|
}
|
|
|
|
// 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 any, method string, args ...any) util.Uint256 {
|
|
tx := c.PrepareInvoke(t, method, args...)
|
|
c.AddNewBlock(t, tx)
|
|
c.CheckHalt(t, tx.Hash(), stackitem.Make(result))
|
|
return tx.Hash()
|
|
}
|
|
|
|
// InvokeAndCheck invokes the method with the args, persists the transaction and checks the result
|
|
// using the provided function. It returns the transaction hash.
|
|
func (c *ContractInvoker) InvokeAndCheck(t testing.TB, checkResult func(t testing.TB, stack []stackitem.Item), method string, args ...any) util.Uint256 {
|
|
tx := c.PrepareInvoke(t, method, args...)
|
|
c.AddNewBlock(t, tx)
|
|
aer, err := c.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
|
require.NoError(t, err)
|
|
require.Equal(t, vmstate.Halt, aer[0].VMState, aer[0].FaultException)
|
|
if checkResult != nil {
|
|
checkResult(t, aer[0].Stack)
|
|
}
|
|
return tx.Hash()
|
|
}
|
|
|
|
// InvokeWithFeeFail is like InvokeFail but sets the custom system fee for the transaction.
|
|
func (c *ContractInvoker) InvokeWithFeeFail(t testing.TB, message string, sysFee int64, method string, args ...any) util.Uint256 {
|
|
tx := c.PrepareInvokeNoSign(t, method, args...)
|
|
c.Executor.SignTx(t, tx, sysFee, c.Signers...)
|
|
c.AddNewBlock(t, tx)
|
|
c.CheckFault(t, tx.Hash(), message)
|
|
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 ...any) util.Uint256 {
|
|
tx := c.PrepareInvoke(t, method, args...)
|
|
c.AddNewBlock(t, tx)
|
|
c.CheckFault(t, tx.Hash(), message)
|
|
return tx.Hash()
|
|
}
|