299 lines
5.7 KiB
Go
Executable File
299 lines
5.7 KiB
Go
Executable File
package compiler_test
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.marketally.com/tutus-one/tutus-chain/pkg/compiler"
|
|
"git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestMaxMin(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
args []stackitem.Item
|
|
expected map[string]*big.Int
|
|
expectedErr string
|
|
srcTemplate string
|
|
}{
|
|
{
|
|
name: "invalid type named",
|
|
expectedErr: "only integer types are supported by %s",
|
|
srcTemplate: `
|
|
package foo
|
|
|
|
type MyInt int
|
|
|
|
func Main() {
|
|
_ = %s(MyInt(1))
|
|
}
|
|
`,
|
|
},
|
|
{
|
|
name: "invalid type string",
|
|
expectedErr: "only integer types are supported by %s, got untyped string at 1",
|
|
srcTemplate: `
|
|
package foo
|
|
|
|
func Main() {
|
|
_ = %s("42")
|
|
}
|
|
`,
|
|
},
|
|
{
|
|
name: "single value as func call",
|
|
expected: map[string]*big.Int{
|
|
"max": big.NewInt(42),
|
|
"min": big.NewInt(42),
|
|
},
|
|
srcTemplate: `
|
|
package foo
|
|
|
|
func f() int {
|
|
return 42
|
|
}
|
|
|
|
func Main() int {
|
|
return %s(f())
|
|
}
|
|
`,
|
|
},
|
|
{
|
|
name: "two values",
|
|
args: []stackitem.Item{stackitem.Make(10), stackitem.Make(7)},
|
|
expected: map[string]*big.Int{
|
|
"max": big.NewInt(10),
|
|
"min": big.NewInt(7),
|
|
},
|
|
srcTemplate: `
|
|
package foo
|
|
|
|
func Main(args []any) int {
|
|
return %s(args[0].(int), args[1].(int))
|
|
}
|
|
`,
|
|
},
|
|
{
|
|
name: "three values",
|
|
args: []stackitem.Item{stackitem.Make(5), stackitem.Make(6), stackitem.Make(3)},
|
|
expected: map[string]*big.Int{
|
|
"max": big.NewInt(6),
|
|
"min": big.NewInt(3),
|
|
},
|
|
srcTemplate: `
|
|
package foo
|
|
|
|
func Main(args []any) int {
|
|
return %s(args[0].(int), args[1].(int), args[2].(int))
|
|
}
|
|
`,
|
|
},
|
|
}
|
|
|
|
for _, op := range []string{"max", "min"} {
|
|
for _, tc := range tests {
|
|
src := fmt.Sprintf(tc.srcTemplate, op)
|
|
if tc.expectedErr != "" {
|
|
_, _, err := compiler.CompileWithOptions("main.go", strings.NewReader(src), nil)
|
|
require.ErrorContains(t, err, fmt.Sprintf(tc.expectedErr, op))
|
|
} else {
|
|
evalWithArgs(t, src, nil, tc.args, tc.expected[op])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestClear(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
src string
|
|
expectedErr string
|
|
expected any
|
|
}{
|
|
{
|
|
name: "invalid type array",
|
|
src: `package foo
|
|
func Main() [42]int {
|
|
var m = [42]int{}
|
|
clear(m)
|
|
return m
|
|
}`,
|
|
expectedErr: "invalid argument: cannot clear m (variable of type [42]int): " +
|
|
"argument must be (or constrained by) map or slice",
|
|
},
|
|
{
|
|
name: "map",
|
|
src: `package foo
|
|
func Main() map[string]int {
|
|
var m = map[string]int{
|
|
"one": 1,
|
|
"two": 2,
|
|
}
|
|
clear(m)
|
|
return m
|
|
}`,
|
|
expected: []stackitem.MapElement{},
|
|
},
|
|
{
|
|
name: "slice of integers",
|
|
src: `package foo
|
|
func Main() []int {
|
|
var s = []int{1, 2, 3}
|
|
clear(s)
|
|
return s
|
|
}`,
|
|
expected: []stackitem.Item{stackitem.Make(0), stackitem.Make(0), stackitem.Make(0)},
|
|
},
|
|
{
|
|
name: "slice of strings",
|
|
src: `package foo
|
|
func Main() []string {
|
|
var s = []string{"one", "two", "three"}
|
|
clear(s)
|
|
return s
|
|
}`,
|
|
expected: []stackitem.Item{stackitem.Make(""), stackitem.Make(""), stackitem.Make("")},
|
|
},
|
|
{
|
|
name: "slice of structs",
|
|
src: `package foo
|
|
type S2 struct {
|
|
A int
|
|
B string
|
|
C bool
|
|
}
|
|
|
|
type S1 struct {
|
|
S S2
|
|
}
|
|
|
|
func Main() []S1 {
|
|
var s = []S1 {
|
|
{
|
|
S: S2 {
|
|
A: 42,
|
|
B: "first",
|
|
C: true,
|
|
},
|
|
},
|
|
{
|
|
S: S2 {
|
|
A: 24,
|
|
B: "second",
|
|
C: true,
|
|
},
|
|
},
|
|
}
|
|
clear(s)
|
|
return s
|
|
}`,
|
|
expected: []stackitem.Item{
|
|
stackitem.NewStruct([]stackitem.Item{
|
|
stackitem.NewStruct([]stackitem.Item{
|
|
stackitem.Make(0),
|
|
stackitem.Make(""),
|
|
stackitem.Make(false),
|
|
}),
|
|
}),
|
|
stackitem.NewStruct([]stackitem.Item{
|
|
stackitem.NewStruct([]stackitem.Item{
|
|
stackitem.Make(0),
|
|
stackitem.Make(""),
|
|
stackitem.Make(false),
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
{
|
|
name: "slice of slices",
|
|
src: `package foo
|
|
func Main() [][]int {
|
|
var s = [][]int{{1, 2, 3}, {4, 5, 6}, {7, 8}}
|
|
clear(s)
|
|
return s
|
|
}`,
|
|
expected: []stackitem.Item{stackitem.Null{}, stackitem.Null{}, stackitem.Null{}},
|
|
},
|
|
{
|
|
name: "slice of maps",
|
|
src: `package foo
|
|
func Main() []map[string]string {
|
|
var s = []map[string]string{
|
|
{
|
|
"key": "val",
|
|
},
|
|
{
|
|
"key": "val",
|
|
},
|
|
}
|
|
clear(s)
|
|
return s
|
|
}`,
|
|
expected: []stackitem.Item{stackitem.Null{}, stackitem.Null{}},
|
|
},
|
|
{
|
|
name: "empty slice",
|
|
src: `package foo
|
|
func Main() []any {
|
|
var s = []any{}
|
|
clear(s)
|
|
return s
|
|
}`,
|
|
expected: []stackitem.Item{},
|
|
},
|
|
{
|
|
name: "empty map",
|
|
src: `package foo
|
|
func Main() map[any]any {
|
|
var m = map[any]any{}
|
|
clear(m)
|
|
return m
|
|
}`,
|
|
expected: []stackitem.MapElement{},
|
|
},
|
|
{
|
|
name: "slice of simple structs",
|
|
src: `package foo
|
|
type S struct {
|
|
A int
|
|
}
|
|
|
|
func Main() []S {
|
|
var s = []S {
|
|
{
|
|
A: 1,
|
|
},
|
|
{
|
|
A: 2,
|
|
},
|
|
}
|
|
clear(s)
|
|
s[0].A = 42
|
|
return s
|
|
}`,
|
|
expected: []stackitem.Item{
|
|
stackitem.NewStruct([]stackitem.Item{
|
|
stackitem.Make(42),
|
|
}),
|
|
stackitem.NewStruct([]stackitem.Item{
|
|
stackitem.Make(0),
|
|
}),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if tc.expectedErr != "" {
|
|
_, _, err := compiler.CompileWithOptions("main.go", strings.NewReader(tc.src), nil)
|
|
require.ErrorContains(t, err, tc.expectedErr)
|
|
} else {
|
|
eval(t, tc.src, tc.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|