neo-go/pkg/vm/stack_item_test.go
Roman Khimov 2d0ad30fcf vm: rework Map with internal slice representation
Which makes iterating over map stable which is important for serialization and
and even fixes occasional test failures. We use the same ordering here as
NEO 3.0 uses, but it should also be fine for NEO 2.0 because it has no
defined order.
2020-04-01 19:33:53 +03:00

447 lines
10 KiB
Go

package vm
import (
"math/big"
"testing"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/stretchr/testify/assert"
)
var makeStackItemTestCases = []struct {
input interface{}
result StackItem
}{
{
input: int64(3),
result: &BigIntegerItem{value: big.NewInt(3)},
},
{
input: int16(3),
result: &BigIntegerItem{value: big.NewInt(3)},
},
{
input: 3,
result: &BigIntegerItem{value: big.NewInt(3)},
},
{
input: uint8(3),
result: &BigIntegerItem{value: big.NewInt(3)},
},
{
input: uint16(3),
result: &BigIntegerItem{value: big.NewInt(3)},
},
{
input: uint32(3),
result: &BigIntegerItem{value: big.NewInt(3)},
},
{
input: uint64(3),
result: &BigIntegerItem{value: big.NewInt(3)},
},
{
input: big.NewInt(3),
result: &BigIntegerItem{value: big.NewInt(3)},
},
{
input: []byte{1, 2, 3, 4},
result: &ByteArrayItem{value: []byte{1, 2, 3, 4}},
},
{
input: []byte{},
result: &ByteArrayItem{value: []byte{}},
},
{
input: "bla",
result: &ByteArrayItem{value: []byte("bla")},
},
{
input: "",
result: &ByteArrayItem{value: []byte{}},
},
{
input: true,
result: &BoolItem{value: true},
},
{
input: false,
result: &BoolItem{value: false},
},
{
input: []StackItem{&BigIntegerItem{value: big.NewInt(3)}, &ByteArrayItem{value: []byte{1, 2, 3}}},
result: &ArrayItem{value: []StackItem{&BigIntegerItem{value: big.NewInt(3)}, &ByteArrayItem{value: []byte{1, 2, 3}}}},
},
{
input: []int{1, 2, 3},
result: &ArrayItem{value: []StackItem{&BigIntegerItem{value: big.NewInt(1)}, &BigIntegerItem{value: big.NewInt(2)}, &BigIntegerItem{value: big.NewInt(3)}}},
},
}
var makeStackItemErrorCases = []struct {
input interface{}
}{
{
input: nil,
},
}
func TestMakeStackItem(t *testing.T) {
for _, testCase := range makeStackItemTestCases {
assert.Equal(t, testCase.result, makeStackItem(testCase.input))
}
for _, errorCase := range makeStackItemErrorCases {
assert.Panics(t, func() { makeStackItem(errorCase.input) })
}
}
var stringerTestCases = []struct {
input StackItem
result string
}{
{
input: NewStructItem([]StackItem{}),
result: "Struct",
},
{
input: NewBigIntegerItem(3),
result: "BigInteger",
},
{
input: NewBoolItem(true),
result: "Boolean",
},
{
input: NewByteArrayItem([]byte{}),
result: "ByteArray",
},
{
input: NewArrayItem([]StackItem{}),
result: "Array",
},
{
input: NewMapItem(),
result: "Map",
},
{
input: NewInteropItem(nil),
result: "InteropItem",
},
}
func TestStringer(t *testing.T) {
for _, testCase := range stringerTestCases {
assert.Equal(t, testCase.result, testCase.input.String())
}
}
var equalsTestCases = map[string][]struct {
item1 StackItem
item2 StackItem
result bool
}{
"struct": {
{
item1: NewStructItem(nil),
item2: nil,
result: false,
},
{
item1: NewStructItem(nil),
item2: NewBigIntegerItem(1),
result: false,
},
{
item1: NewStructItem(nil),
item2: NewStructItem([]StackItem{NewBigIntegerItem(1)}),
result: false,
},
{
item1: NewStructItem([]StackItem{NewBigIntegerItem(1)}),
item2: NewStructItem([]StackItem{NewBigIntegerItem(2)}),
result: false,
},
{
item1: NewStructItem([]StackItem{NewBigIntegerItem(1)}),
item2: NewStructItem([]StackItem{NewBigIntegerItem(1)}),
result: true,
},
},
"bigint": {
{
item1: NewBigIntegerItem(2),
item2: nil,
result: false,
},
{
item1: NewBigIntegerItem(2),
item2: NewBigIntegerItem(2),
result: true,
},
{
item1: NewBigIntegerItem(2),
item2: NewBoolItem(false),
result: false,
},
{
item1: NewBigIntegerItem(0),
item2: NewBoolItem(false),
result: false,
},
{
item1: NewBigIntegerItem(2),
item2: makeStackItem(int32(2)),
result: true,
},
},
"bool": {
{
item1: NewBoolItem(true),
item2: nil,
result: false,
},
{
item1: NewBoolItem(true),
item2: NewBoolItem(true),
result: true,
},
{
item1: NewBoolItem(true),
item2: NewBigIntegerItem(1),
result: true,
},
{
item1: NewBoolItem(true),
item2: NewBoolItem(false),
result: false,
},
{
item1: NewBoolItem(true),
item2: makeStackItem(true),
result: true,
},
},
"bytearray": {
{
item1: NewByteArrayItem(nil),
item2: nil,
result: false,
},
{
item1: NewByteArrayItem([]byte{1, 2, 3}),
item2: NewByteArrayItem([]byte{1, 2, 3}),
result: true,
},
{
item1: NewByteArrayItem([]byte{1}),
item2: NewBigIntegerItem(1),
result: true,
},
{
item1: NewByteArrayItem([]byte{1, 2, 3}),
item2: NewByteArrayItem([]byte{1, 2, 4}),
result: false,
},
{
item1: NewByteArrayItem([]byte{1, 2, 3}),
item2: makeStackItem([]byte{1, 2, 3}),
result: true,
},
},
"array": {
{
item1: NewArrayItem(nil),
item2: nil,
result: false,
},
{
item1: NewArrayItem([]StackItem{&BigIntegerItem{big.NewInt(1)}, &BigIntegerItem{big.NewInt(2)}, &BigIntegerItem{big.NewInt(3)}}),
item2: NewArrayItem([]StackItem{&BigIntegerItem{big.NewInt(1)}, &BigIntegerItem{big.NewInt(2)}, &BigIntegerItem{big.NewInt(3)}}),
result: false,
},
{
item1: NewArrayItem([]StackItem{&BigIntegerItem{big.NewInt(1)}}),
item2: NewBigIntegerItem(1),
result: false,
},
{
item1: NewArrayItem([]StackItem{&BigIntegerItem{big.NewInt(1)}, &BigIntegerItem{big.NewInt(2)}, &BigIntegerItem{big.NewInt(3)}}),
item2: NewArrayItem([]StackItem{&BigIntegerItem{big.NewInt(1)}, &BigIntegerItem{big.NewInt(2)}, &BigIntegerItem{big.NewInt(4)}}),
result: false,
},
},
"map": {
{
item1: NewMapItem(),
item2: nil,
result: false,
},
{
item1: NewMapItem(),
item2: NewMapItem(),
result: false,
},
{
item1: &MapItem{value: []MapElement{{NewByteArrayItem([]byte("first")), NewBigIntegerItem(1)}, {NewBoolItem(true), NewByteArrayItem([]byte{2})}}},
item2: &MapItem{value: []MapElement{{NewByteArrayItem([]byte("first")), NewBigIntegerItem(1)}, {NewBoolItem(true), NewByteArrayItem([]byte{2})}}},
result: false,
},
{
item1: &MapItem{value: []MapElement{{NewByteArrayItem([]byte("first")), NewBigIntegerItem(1)}, {NewBoolItem(true), NewByteArrayItem([]byte{2})}}},
item2: &MapItem{value: []MapElement{{NewByteArrayItem([]byte("first")), NewBigIntegerItem(1)}, {NewBoolItem(true), NewByteArrayItem([]byte{3})}}},
result: false,
},
},
"interop": {
{
item1: NewInteropItem(nil),
item2: nil,
result: false,
},
{
item1: NewInteropItem(nil),
item2: NewInteropItem(nil),
result: true,
},
{
item1: NewInteropItem(2),
item2: NewInteropItem(3),
result: false,
},
{
item1: NewInteropItem(3),
item2: NewInteropItem(3),
result: true,
},
},
}
func TestEquals(t *testing.T) {
for name, testBatch := range equalsTestCases {
for _, testCase := range testBatch {
t.Run(name, func(t *testing.T) {
assert.Equal(t, testCase.result, testCase.item1.Equals(testCase.item2))
// Reference equals
assert.Equal(t, true, testCase.item1.Equals(testCase.item1))
})
}
}
}
var marshalJSONTestCases = []struct {
input StackItem
result []byte
}{
{
input: NewBigIntegerItem(2),
result: []byte(`2`),
},
{
input: NewBoolItem(true),
result: []byte(`true`),
},
{
input: NewByteArrayItem([]byte{1, 2, 3}),
result: []byte(`"010203"`),
},
{
input: &ArrayItem{value: []StackItem{&BigIntegerItem{value: big.NewInt(3)}, &ByteArrayItem{value: []byte{1, 2, 3}}}},
result: []byte(`[3,"010203"]`),
},
{
input: &InteropItem{value: 3},
result: []byte(`3`),
},
}
func TestMarshalJSON(t *testing.T) {
var (
actual []byte
err error
)
for _, testCase := range marshalJSONTestCases {
switch testCase.input.(type) {
case *BigIntegerItem:
actual, err = testCase.input.(*BigIntegerItem).MarshalJSON()
case *BoolItem:
actual, err = testCase.input.(*BoolItem).MarshalJSON()
case *ByteArrayItem:
actual, err = testCase.input.(*ByteArrayItem).MarshalJSON()
case *ArrayItem:
actual, err = testCase.input.(*ArrayItem).MarshalJSON()
case *InteropItem:
actual, err = testCase.input.(*InteropItem).MarshalJSON()
default:
continue
}
assert.NoError(t, err)
assert.Equal(t, testCase.result, actual)
}
}
var toContractParameterTestCases = []struct {
input StackItem
result smartcontract.Parameter
}{
{
input: NewStructItem([]StackItem{
NewBigIntegerItem(1),
NewBoolItem(true),
}),
result: smartcontract.Parameter{Type: smartcontract.ArrayType, Value: []smartcontract.Parameter{
{Type: smartcontract.IntegerType, Value: int64(1)},
{Type: smartcontract.BoolType, Value: true},
}},
},
{
input: NewBoolItem(false),
result: smartcontract.Parameter{Type: smartcontract.BoolType, Value: false},
},
{
input: NewByteArrayItem([]byte{0x01, 0x02, 0x03}),
result: smartcontract.Parameter{Type: smartcontract.ByteArrayType, Value: []byte{0x01, 0x02, 0x03}},
},
{
input: NewArrayItem([]StackItem{NewBigIntegerItem(2), NewBoolItem(true)}),
result: smartcontract.Parameter{Type: smartcontract.ArrayType, Value: []smartcontract.Parameter{
{Type: smartcontract.IntegerType, Value: int64(2)},
{Type: smartcontract.BoolType, Value: true},
}},
},
{
input: NewInteropItem(nil),
result: smartcontract.Parameter{Type: smartcontract.InteropInterfaceType, Value: nil},
},
{
input: &MapItem{value: []MapElement{
{NewBigIntegerItem(1), NewBoolItem(true)},
{NewByteArrayItem([]byte("qwerty")), NewBigIntegerItem(3)},
{NewBoolItem(true), NewBoolItem(false)},
}},
result: smartcontract.Parameter{
Type: smartcontract.MapType,
Value: []smartcontract.ParameterPair{
{
Key: smartcontract.Parameter{Type: smartcontract.IntegerType, Value: int64(1)},
Value: smartcontract.Parameter{Type: smartcontract.BoolType, Value: true},
}, {
Key: smartcontract.Parameter{Type: smartcontract.ByteArrayType, Value: []byte("qwerty")},
Value: smartcontract.Parameter{Type: smartcontract.IntegerType, Value: int64(3)},
}, {
Key: smartcontract.Parameter{Type: smartcontract.BoolType, Value: true},
Value: smartcontract.Parameter{Type: smartcontract.BoolType, Value: false},
},
},
},
},
}
func TestToContractParameter(t *testing.T) {
for _, tc := range toContractParameterTestCases {
seen := make(map[StackItem]bool)
res := tc.input.ToContractParameter(seen)
assert.Equal(t, res, tc.result)
}
}