Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 116 additions & 94 deletions accounts/abi/bind/precompilebind/precompile_contract_test_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,106 +23,109 @@ import (
"github.com/stretchr/testify/require"
)

// TestRun tests the Run function of the precompile contract.
// These tests are run against the precompile contract directly with
// the given input and expected output. They're just a guide to
// help you write your own tests. These tests are for general cases like
// allowlist, readOnly behaviour, and gas cost. You should write your own
// tests for specific cases.
func TestRun(t *testing.T) {
tests := map[string]testutils.PrecompileTest{
{{- $contract := .Contract}}
{{- $structs := .Structs}}
{{- range .Contract.Funcs}}
{{- $func := .}}
{{- if $contract.AllowList}}
{{- $roles := mkList "NoRole" "Enabled" "Admin"}}
{{- range $role := $roles}}
{{- $fail := and (not $func.Original.IsConstant) (eq $role "NoRole")}}
"calling {{decapitalise $func.Normalized.Name}} from {{$role}} should {{- if $fail}} fail {{- else}} succeed{{- end}}": {
Caller: allowlist.Test{{$role}}Addr,
BeforeHook: allowlist.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
{{- if len $func.Normalized.Inputs | lt 1}}
// These tests are run against the precompile contract directly with
// the given input and expected output. They're just a guide to
// help you write your own tests. These tests are for general cases like
// allowlist, readOnly behaviour, and gas cost. You should write your own
// tests for specific cases.
var(
tests = map[string]testutils.PrecompileTest{
{{- $contract := .Contract}}
{{- $structs := .Structs}}
{{- range .Contract.Funcs}}
{{- $func := .}}
{{- if $contract.AllowList}}
{{- $roles := mkList "NoRole" "Enabled" "Admin"}}
{{- range $role := $roles}}
{{- $fail := and (not $func.Original.IsConstant) (eq $role "NoRole")}}
"calling {{decapitalise $func.Normalized.Name}} from {{$role}} should {{- if $fail}} fail {{- else}} succeed{{- end}}": {
Caller: allowlist.Test{{$role}}Addr,
BeforeHook: allowlist.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
{{- if len $func.Normalized.Inputs | lt 1}}
// CUSTOM CODE STARTS HERE
// populate test input here
testInput := {{capitalise $func.Normalized.Name}}Input{}
input, err := Pack{{$func.Normalized.Name}}(testInput)
{{- else if len $func.Normalized.Inputs | eq 1 }}
{{- $input := index $func.Normalized.Inputs 0}}
// CUSTOM CODE STARTS HERE
// set test input to a value here
var testInput {{bindtype $input.Type $structs}}
input, err := Pack{{$func.Normalized.Name}}(testInput)
{{- else}}
input, err := Pack{{$func.Normalized.Name}}()
{{- end}}
require.NoError(t, err)
return input
},
{{- if not $fail}}
// This test is for a successful call. You can set the expected output here.
// CUSTOM CODE STARTS HERE
// populate test input here
testInput := {{capitalise $func.Normalized.Name}}Input{}
input, err := Pack{{$func.Normalized.Name}}(testInput)
{{- else if len $func.Normalized.Inputs | eq 1 }}
{{- $input := index $func.Normalized.Inputs 0}}
// CUSTOM CODE STARTS HERE
// set test input to a value here
var testInput {{bindtype $input.Type $structs}}
input, err := Pack{{$func.Normalized.Name}}(testInput)
{{- else}}
input, err := Pack{{$func.Normalized.Name}}()
ExpectedRes: []byte{},
{{- end}}
require.NoError(t, err)
return input
SuppliedGas: {{$func.Normalized.Name}}GasCost,
ReadOnly: false,
ExpectedErr: {{if $fail}} ErrCannot{{$func.Normalized.Name}}.Error() {{- else}} "" {{- end}},
},
{{- if not $fail}}
// This test is for a successful call. You can set the expected output here.
// CUSTOM CODE STARTS HERE
ExpectedRes: []byte{},
{{- end}}
SuppliedGas: {{$func.Normalized.Name}}GasCost,
ReadOnly: false,
ExpectedErr: {{if $fail}} ErrCannot{{$func.Normalized.Name}}.Error() {{- else}} "" {{- end}},
},
{{- end}}
{{- end}}
{{- if not $func.Original.IsConstant}}
"readOnly {{decapitalise $func.Normalized.Name}} should fail": {
Caller: common.Address{1},
InputFn: func(t testing.TB) []byte {
{{- if len $func.Normalized.Inputs | lt 1}}
// CUSTOM CODE STARTS HERE
// populate test input here
testInput := {{capitalise $func.Normalized.Name}}Input{}
input, err := Pack{{$func.Normalized.Name}}(testInput)
{{- else if len $func.Normalized.Inputs | eq 1 }}
{{- $input := index $func.Normalized.Inputs 0}}
// CUSTOM CODE STARTS HERE
// set test input to a value here
var testInput {{bindtype $input.Type $structs}}
input, err := Pack{{$func.Normalized.Name}}(testInput)
{{- else}}
input, err := Pack{{$func.Normalized.Name}}()
{{- end}}
require.NoError(t, err)
return input
{{- end}}
{{- if not $func.Original.IsConstant}}
"readOnly {{decapitalise $func.Normalized.Name}} should fail": {
Caller: common.Address{1},
InputFn: func(t testing.TB) []byte {
{{- if len $func.Normalized.Inputs | lt 1}}
// CUSTOM CODE STARTS HERE
// populate test input here
testInput := {{capitalise $func.Normalized.Name}}Input{}
input, err := Pack{{$func.Normalized.Name}}(testInput)
{{- else if len $func.Normalized.Inputs | eq 1 }}
{{- $input := index $func.Normalized.Inputs 0}}
// CUSTOM CODE STARTS HERE
// set test input to a value here
var testInput {{bindtype $input.Type $structs}}
input, err := Pack{{$func.Normalized.Name}}(testInput)
{{- else}}
input, err := Pack{{$func.Normalized.Name}}()
{{- end}}
require.NoError(t, err)
return input
},
SuppliedGas: {{$func.Normalized.Name}}GasCost,
ReadOnly: true,
ExpectedErr: vmerrs.ErrWriteProtection.Error(),
},
SuppliedGas: {{$func.Normalized.Name}}GasCost,
ReadOnly: true,
ExpectedErr: vmerrs.ErrWriteProtection.Error(),
},
{{- end}}
"insufficient gas for {{decapitalise $func.Normalized.Name}} should fail": {
Caller: common.Address{1},
InputFn: func(t testing.TB) []byte {
{{- if len $func.Normalized.Inputs | lt 1}}
// CUSTOM CODE STARTS HERE
// populate test input here
testInput := {{capitalise $func.Normalized.Name}}Input{}
input, err := Pack{{$func.Normalized.Name}}(testInput)
{{- else if len $func.Normalized.Inputs | eq 1 }}
{{- $input := index $func.Normalized.Inputs 0}}
// CUSTOM CODE STARTS HERE
// set test input to a value here
var testInput {{bindtype $input.Type $structs}}
input, err := Pack{{$func.Normalized.Name}}(testInput)
{{- else}}
input, err := Pack{{$func.Normalized.Name}}()
{{- end}}
require.NoError(t, err)
return input
{{- end}}
"insufficient gas for {{decapitalise $func.Normalized.Name}} should fail": {
Caller: common.Address{1},
InputFn: func(t testing.TB) []byte {
{{- if len $func.Normalized.Inputs | lt 1}}
// CUSTOM CODE STARTS HERE
// populate test input here
testInput := {{capitalise $func.Normalized.Name}}Input{}
input, err := Pack{{$func.Normalized.Name}}(testInput)
{{- else if len $func.Normalized.Inputs | eq 1 }}
{{- $input := index $func.Normalized.Inputs 0}}
// CUSTOM CODE STARTS HERE
// set test input to a value here
var testInput {{bindtype $input.Type $structs}}
input, err := Pack{{$func.Normalized.Name}}(testInput)
{{- else}}
input, err := Pack{{$func.Normalized.Name}}()
{{- end}}
require.NoError(t, err)
return input
},
SuppliedGas: {{$func.Normalized.Name}}GasCost - 1,
ReadOnly: false,
ExpectedErr: vmerrs.ErrOutOfGas.Error(),
},
SuppliedGas: {{$func.Normalized.Name}}GasCost - 1,
ReadOnly: false,
ExpectedErr: vmerrs.ErrOutOfGas.Error(),
},
{{- end}}
}
{{- end}}
}
)

// Test{{.Contract.Type}}Run tests the Run function of the precompile contract.
func Test{{.Contract.Type}}Run(t *testing.T) {
{{- if .Contract.AllowList}}
// Run tests with allowlist tests.
// This adds allowlist run tests to your custom tests
Expand All @@ -139,4 +142,23 @@ func TestRun(t *testing.T) {
}
{{- end}}
}

func Benchmark{{.Contract.Type}}(b *testing.B) {
{{- if .Contract.AllowList}}
// Benchmark tests with allowlist tests.
// This adds allowlist run tests to your custom tests
// and benchmarks them all together.
// Even if you don't add any custom tests, keep this. This will still
// run the default allowlist tests.
allowlist.BenchPrecompileWithAllowList(b, Module, state.NewTestStateDB, tests)
{{- else}}
// Benchmark tests.
for name, test := range tests {
b.Run(name, func(b *testing.B) {
test.Bench(b, module, newStateDB(b))
})
}
{{- end}}
}

`
11 changes: 6 additions & 5 deletions precompile/contracts/helloworld/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ Modifying code outside of these areas should be done with caution and with a dee
6- Add your config unit tests under generated package config_test.go
7- Add your contract unit tests under generated package contract_test.go
8- Additionally you can add a full-fledged VM test for your precompile under plugin/vm/vm_test.go. See existing precompile tests for examples.
9- Add your solidity interface and test contract to contract-examples/contracts
10- Write solidity tests for your precompile in contract-examples/test
11- Create your genesis with your precompile enabled in tests/precompile/genesis/
12- Create e2e test for your solidity test in tests/precompile/solidity/suites.go
13- Run your e2e precompile Solidity tests with './scripts/run_ginkgo.sh`
9- Add your solidity interface and test contract to contracts/contracts
10- Write solidity contract tests for your precompile in contracts/contracts/test
11- Write TypeScript DS-Test counterparts for your solidity tests in contracts/test
12- Create your genesis with your precompile enabled in tests/precompile/genesis/
13- Create e2e test for your solidity test in tests/precompile/solidity/suites.go
14- Run your e2e precompile Solidity tests with './scripts/run_ginkgo.sh`
4 changes: 3 additions & 1 deletion precompile/contracts/helloworld/config_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// (c) 2022 Ava Labs, Inc. All rights reserved.
// (c) 2019-2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// Code generated
Expand All @@ -14,6 +14,7 @@ import (
"github.com/ava-labs/subnet-evm/precompile/allowlist"
"github.com/ava-labs/subnet-evm/precompile/precompileconfig"
"github.com/ava-labs/subnet-evm/precompile/testutils"

"github.com/ethereum/go-ethereum/common"
)

Expand Down Expand Up @@ -45,6 +46,7 @@ func TestVerify(t *testing.T) {
allowlist.VerifyPrecompileWithAllowListTests(t, Module, tests)
}

// TestEqual tests the equality of Config with other precompile configs.
func TestEqual(t *testing.T) {
admins := []common.Address{allowlist.TestAdminAddr}
enableds := []common.Address{allowlist.TestEnabledAddr}
Expand Down
42 changes: 32 additions & 10 deletions precompile/contracts/helloworld/contract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ import (
"github.com/stretchr/testify/require"
)

// TestRun tests the Run function of the precompile contract.
// These tests are run against the precompile contract directly with
// the given input and expected output. They're just a guide to
// help you write your own tests. These tests are for general cases like
// allowlist, readOnly behaviour, and gas cost. You should write your own
// tests for specific cases.
func TestRun(t *testing.T) {
testGreeting := "test"
tests := map[string]testutils.PrecompileTest{
const testGreeting = "test"

var (
tests = map[string]testutils.PrecompileTest{
"calling sayHello from NoRole should succeed": {
Caller: allowlist.TestNoRoleAddr,
BeforeHook: allowlist.SetDefaultRoles(Module.Address),
Expand All @@ -43,7 +43,9 @@ func TestRun(t *testing.T) {
// the module since Config is empty.
// This means we don't apply default greeting to the state.
res, err := PackSayHelloOutput("")
require.NoError(t, err)
if err != nil {
panic(err)
}
return res
}(),
SuppliedGas: SayHelloGasCost,
Expand All @@ -65,7 +67,9 @@ func TestRun(t *testing.T) {
// the module since Config is empty.
// This means we don't apply default greeting to the state.
res, err := PackSayHelloOutput("")
require.NoError(t, err)
if err != nil {
panic(err)
}
return res
}(),
SuppliedGas: SayHelloGasCost,
Expand All @@ -87,7 +91,9 @@ func TestRun(t *testing.T) {
// the module since Config is empty.
// This means we don't apply default greeting to the state.
res, err := PackSayHelloOutput("")
require.NoError(t, err)
if err != nil {
panic(err)
}
return res
}(),
SuppliedGas: SayHelloGasCost,
Expand All @@ -107,7 +113,9 @@ func TestRun(t *testing.T) {
// CUSTOM CODE STARTS HERE
ExpectedRes: func() []byte {
res, err := PackSayHelloOutput(defaultGreeting)
require.NoError(t, err)
if err != nil {
panic(err)
}
return res
}(),
SuppliedGas: SayHelloGasCost,
Expand Down Expand Up @@ -219,14 +227,15 @@ func TestRun(t *testing.T) {
InputFn: func(t testing.TB) []byte {
input, err := PackSayHello()
require.NoError(t, err)

return input
},
SuppliedGas: SayHelloGasCost,
ReadOnly: true,
ExpectedRes: func() []byte {
res, err := PackSayHelloOutput(testGreeting)
require.NoError(t, err)
if err != nil {
panic(err)
}
return res
}(),
},
Expand All @@ -245,10 +254,23 @@ func TestRun(t *testing.T) {
ExpectedErr: ErrInputExceedsLimit.Error(),
},
}
)

// TestHelloWorldRun tests the Run function of the precompile contract.
func TestHelloWorldRun(t *testing.T) {
// Run tests with allowlist tests.
// This adds allowlist run tests to your custom tests
// and runs them all together.
// Even if you don't add any custom tests, keep this. This will still
// run the default allowlist tests.
allowlist.RunPrecompileWithAllowListTests(t, Module, state.NewTestStateDB, tests)
}

func BenchmarkHelloWorld(b *testing.B) {
// Benchmark tests with allowlist tests.
// This adds allowlist run tests to your custom tests
// and benchmarks them all together.
// Even if you don't add any custom tests, keep this. This will still
// run the default allowlist tests.
allowlist.BenchPrecompileWithAllowList(b, Module, state.NewTestStateDB, tests)
}