neo-go/pkg/vm/stack_item_test.go
Evgenii Stratonikov c3f7832f3b vm: implement Buffer stack item
Buffer is a stack item type introduced in NEO3
which represents mutable byte-array.
2020-05-20 17:45:56 +03:00

485 lines
11 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(big.NewInt(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",
},
{
input: NewPointerItem(0, nil),
result: "Pointer",
},
}
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(big.NewInt(1)),
result: false,
},
{
item1: NewStructItem(nil),
item2: NewStructItem([]StackItem{NewBigIntegerItem(big.NewInt(1))}),
result: false,
},
{
item1: NewStructItem([]StackItem{NewBigIntegerItem(big.NewInt(1))}),
item2: NewStructItem([]StackItem{NewBigIntegerItem(big.NewInt(2))}),
result: false,
},
{
item1: NewStructItem([]StackItem{NewBigIntegerItem(big.NewInt(1))}),
item2: NewStructItem([]StackItem{NewBigIntegerItem(big.NewInt(1))}),
result: true,
},
},
"bigint": {
{
item1: NewBigIntegerItem(big.NewInt(2)),
item2: nil,
result: false,
},
{
item1: NewBigIntegerItem(big.NewInt(2)),
item2: NewBigIntegerItem(big.NewInt(2)),
result: true,
},
{
item1: NewBigIntegerItem(big.NewInt(2)),
item2: NewBoolItem(false),
result: false,
},
{
item1: NewBigIntegerItem(big.NewInt(0)),
item2: NewBoolItem(false),
result: false,
},
{
item1: NewBigIntegerItem(big.NewInt(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(big.NewInt(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(big.NewInt(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(big.NewInt(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(big.NewInt(1))}, {NewBoolItem(true), NewByteArrayItem([]byte{2})}}},
item2: &MapItem{value: []MapElement{{NewByteArrayItem([]byte("first")), NewBigIntegerItem(big.NewInt(1))}, {NewBoolItem(true), NewByteArrayItem([]byte{2})}}},
result: false,
},
{
item1: &MapItem{value: []MapElement{{NewByteArrayItem([]byte("first")), NewBigIntegerItem(big.NewInt(1))}, {NewBoolItem(true), NewByteArrayItem([]byte{2})}}},
item2: &MapItem{value: []MapElement{{NewByteArrayItem([]byte("first")), NewBigIntegerItem(big.NewInt(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,
},
},
"pointer": {
{
item1: NewPointerItem(0, []byte{}),
result: false,
},
{
item1: NewPointerItem(1, []byte{1}),
item2: NewPointerItem(1, []byte{1}),
result: true,
},
{
item1: NewPointerItem(1, []byte{1}),
item2: NewPointerItem(2, []byte{1}),
result: false,
},
{
item1: NewPointerItem(1, []byte{1}),
item2: NewPointerItem(1, []byte{2}),
result: false,
},
{
item1: NewPointerItem(0, []byte{}),
item2: NewBigIntegerItem(big.NewInt(0)),
result: false,
},
},
}
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(big.NewInt(2)),
result: []byte(`2`),
},
{
input: NewBoolItem(true),
result: []byte(`true`),
},
{
input: NewByteArrayItem([]byte{1, 2, 3}),
result: []byte(`"010203"`),
},
{
input: NewBufferItem([]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(big.NewInt(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: NewBufferItem([]byte{0x01, 0x02, 0x03}),
result: smartcontract.Parameter{Type: smartcontract.ByteArrayType, Value: []byte{0x01, 0x02, 0x03}},
},
{
input: NewArrayItem([]StackItem{NewBigIntegerItem(big.NewInt(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(big.NewInt(1)), NewBoolItem(true)},
{NewByteArrayItem([]byte("qwerty")), NewBigIntegerItem(big.NewInt(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)
}
}