Merge pull request #2382 from nspcc-dev/compiler-optimize

compiler: optimize tests
This commit is contained in:
Roman Khimov 2022-03-04 18:55:40 +03:00 committed by GitHub
commit 96cd415384
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 804 additions and 783 deletions

View file

@ -1,16 +1,21 @@
package compiler_test
import (
"bytes"
"fmt"
"math/big"
"strings"
"testing"
"github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/stretchr/testify/require"
)
var assignTestCases = []testCase{
{
"chain define",
`
package foo
func Main() int {
`func F%d() int {
x := 4
y := x
z := y
@ -23,9 +28,7 @@ var assignTestCases = []testCase{
},
{
"simple assign",
`
package foo
func Main() int {
`func F%d() int {
x := 4
x = 8
return x
@ -35,9 +38,7 @@ var assignTestCases = []testCase{
},
{
"add assign",
`
package foo
func Main() int {
`func F%d() int {
x := 4
x += 8
return x
@ -47,9 +48,7 @@ var assignTestCases = []testCase{
},
{
"sub assign",
`
package foo
func Main() int {
`func F%d() int {
x := 4
x -= 2
return x
@ -59,9 +58,7 @@ var assignTestCases = []testCase{
},
{
"mul assign",
`
package foo
func Main() int {
`func F%d() int {
x := 4
x *= 2
return x
@ -71,9 +68,7 @@ var assignTestCases = []testCase{
},
{
"div assign",
`
package foo
func Main() int {
`func F%d() int {
x := 4
x /= 2
return x
@ -83,9 +78,7 @@ var assignTestCases = []testCase{
},
{
"add assign binary expr",
`
package foo
func Main() int {
`func F%d() int {
x := 4
x += 6 + 2
return x
@ -95,9 +88,7 @@ var assignTestCases = []testCase{
},
{
"add assign binary expr ident",
`
package foo
func Main() int {
`func F%d() int {
x := 4
y := 5
x += 6 + y
@ -108,19 +99,17 @@ var assignTestCases = []testCase{
},
{
"add assign for string",
`package foo
func Main() string {
`func F%d() string {
s := "Hello, "
s += "world!"
return s
}`,
}
`,
[]byte("Hello, world!"),
},
{
"decl assign",
`
package foo
func Main() int {
`func F%d() int {
var x int = 4
return x
}
@ -129,32 +118,41 @@ var assignTestCases = []testCase{
},
{
"multi assign",
`
package foo
func Main() int {
`func F%d() int {
x, y := 1, 2
return x + y
}
`,
big.NewInt(3),
},
{
"many assignments",
`func F%d() int {
a := 0
` + strings.Repeat("a += 1\n", 1024) + `
return a
}
`,
big.NewInt(1024),
},
}
func TestAssignments(t *testing.T) {
runTestCases(t, assignTestCases)
}
func TestManyAssignments(t *testing.T) {
src1 := `package foo
func Main() int {
a := 0
`
src2 := `return a
}`
for i := 0; i < 1024; i++ {
src1 += "a += 1\n"
srcBuilder := bytes.NewBuffer([]byte("package testcase\n"))
for i, tc := range assignTestCases {
srcBuilder.WriteString(fmt.Sprintf(tc.src, i))
}
eval(t, src1+src2, big.NewInt(1024))
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
require.NoError(t, err)
for i, tc := range assignTestCases {
v := vm.New()
t.Run(tc.name, func(t *testing.T) {
v.Istack().Clear()
v.Estack().Clear()
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
runAndCheck(t, v, tc.result)
})
}
}

View file

@ -15,9 +15,7 @@ import (
var binaryExprTestCases = []testCase{
{
"simple add",
`
package testcase
func Main() int {
`func F%d() int {
x := 2 + 2
return x
}
@ -26,9 +24,7 @@ var binaryExprTestCases = []testCase{
},
{
"simple sub",
`
package testcase
func Main() int {
`func F%d() int {
x := 2 - 2
return x
}
@ -37,9 +33,7 @@ var binaryExprTestCases = []testCase{
},
{
"simple div",
`
package testcase
func Main() int {
`func F%d() int {
x := 2 / 2
return x
}
@ -48,10 +42,8 @@ var binaryExprTestCases = []testCase{
},
{
"simple mod",
`
package testcase
func Main() int {
x := 3 % 2
`func F%d() int {
x := 3 %% 2
return x
}
`,
@ -59,9 +51,7 @@ var binaryExprTestCases = []testCase{
},
{
"simple mul",
`
package testcase
func Main() int {
`func F%d() int {
x := 4 * 2
return x
}
@ -70,9 +60,7 @@ var binaryExprTestCases = []testCase{
},
{
"simple binary expr in return",
`
package testcase
func Main() int {
`func F%d() int {
x := 2
return 2 + x
}
@ -81,9 +69,7 @@ var binaryExprTestCases = []testCase{
},
{
"complex binary expr",
`
package testcase
func Main() int {
`func F%d() int {
x := 4
y := 8
z := x + 2 + 2 - 8
@ -94,9 +80,7 @@ var binaryExprTestCases = []testCase{
},
{
"compare not equal strings with eql",
`
package testcase
func Main() int {
`func F%d() int {
str := "a string"
if str == "another string" {
return 1
@ -108,9 +92,7 @@ var binaryExprTestCases = []testCase{
},
{
"compare equal strings with eql",
`
package testcase
func Main() int {
`func F%d() int {
str := "a string"
if str == "a string" {
return 1
@ -122,9 +104,7 @@ var binaryExprTestCases = []testCase{
},
{
"compare not equal strings with neq",
`
package testcase
func Main() int {
`func F%d() int {
str := "a string"
if str != "another string" {
return 1
@ -136,9 +116,7 @@ var binaryExprTestCases = []testCase{
},
{
"compare equal strings with neq",
`
package testcase
func Main() int {
`func F%d() int {
str := "a string"
if str != "a string" {
return 1
@ -150,9 +128,7 @@ var binaryExprTestCases = []testCase{
},
{
"compare equal ints with eql",
`
package testcase
func Main() int {
`func F%d() int {
x := 10
if x == 10 {
return 1
@ -164,9 +140,7 @@ var binaryExprTestCases = []testCase{
},
{
"compare equal ints with neq",
`
package testcase
func Main() int {
`func F%d() int {
x := 10
if x != 10 {
return 1
@ -178,9 +152,7 @@ var binaryExprTestCases = []testCase{
},
{
"compare not equal ints with eql",
`
package testcase
func Main() int {
`func F%d() int {
x := 11
if x == 10 {
return 1
@ -192,9 +164,7 @@ var binaryExprTestCases = []testCase{
},
{
"compare not equal ints with neq",
`
package testcase
func Main() int {
`func F%d() int {
x := 11
if x != 10 {
return 1
@ -206,9 +176,7 @@ var binaryExprTestCases = []testCase{
},
{
"simple add and assign",
`
package testcase
func Main() int {
`func F%d() int {
x := 2
x += 1
return x
@ -218,9 +186,7 @@ var binaryExprTestCases = []testCase{
},
{
"simple sub and assign",
`
package testcase
func Main() int {
`func F%d() int {
x := 2
x -= 1
return x
@ -230,9 +196,7 @@ var binaryExprTestCases = []testCase{
},
{
"simple mul and assign",
`
package testcase
func Main() int {
`func F%d() int {
x := 2
x *= 2
return x
@ -242,9 +206,7 @@ var binaryExprTestCases = []testCase{
},
{
"simple div and assign",
`
package testcase
func Main() int {
`func F%d() int {
x := 2
x /= 2
return x
@ -254,11 +216,9 @@ var binaryExprTestCases = []testCase{
},
{
"simple mod and assign",
`
package testcase
func Main() int {
`func F%d() int {
x := 5
x %= 2
x %%= 2
return x
}
`,
@ -267,7 +227,23 @@ var binaryExprTestCases = []testCase{
}
func TestBinaryExprs(t *testing.T) {
runTestCases(t, binaryExprTestCases)
srcBuilder := bytes.NewBuffer([]byte("package testcase\n"))
for i, tc := range binaryExprTestCases {
srcBuilder.WriteString(fmt.Sprintf(tc.src, i))
}
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
require.NoError(t, err)
for i, tc := range binaryExprTestCases {
v := vm.New()
t.Run(tc.name, func(t *testing.T) {
v.Istack().Clear()
v.Estack().Clear()
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
runAndCheck(t, v, tc.result)
})
}
}
func addBoolExprTestFunc(testCases []testCase, b *bytes.Buffer, val bool, cond string) []testCase {
@ -381,7 +357,7 @@ func TestBooleanExprs(t *testing.T) {
}
t.Run("Triple", func(t *testing.T) {
const step = 256 // empirically found value to make script less than 65536 in size
const step = 350 // empirically found value to make script less than 65536 in size
for start := 0; start < len(triple); start += step {
testCases = testCases[:0]
srcBuilder.Reset()

View file

@ -1,9 +1,15 @@
package compiler_test
import (
"bytes"
"fmt"
"math/big"
"strings"
"testing"
"github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/stretchr/testify/require"
)
type convertTestCase struct {
@ -25,12 +31,11 @@ func getFunctionName(typ string) string {
}
func TestConvert(t *testing.T) {
srcTmpl := `package foo
import "github.com/nspcc-dev/neo-go/pkg/interop/convert"
func Main() %s {
srcTmpl := `func F%d() %s {
arg := %s
return convert.To%s(arg)
}`
}
`
convertTestCases := []convertTestCase{
{"bool", "true", true},
@ -53,11 +58,24 @@ func TestConvert(t *testing.T) {
{"[]byte", "[]byte{0, 1, 0}", []byte{0, 1, 0}},
}
for _, tc := range convertTestCases {
srcBuilder := bytes.NewBuffer([]byte(`package testcase
import "github.com/nspcc-dev/neo-go/pkg/interop/convert"
`))
for i, tc := range convertTestCases {
name := getFunctionName(tc.returnType)
t.Run(tc.argValue+"->"+name, func(t *testing.T) {
src := fmt.Sprintf(srcTmpl, tc.returnType, tc.argValue, name)
eval(t, src, tc.result)
srcBuilder.WriteString(fmt.Sprintf(srcTmpl, i, tc.returnType, tc.argValue, name))
}
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
require.NoError(t, err)
for i, tc := range convertTestCases {
v := vm.New()
t.Run(tc.argValue+getFunctionName(tc.returnType), func(t *testing.T) {
v.Istack().Clear()
v.Estack().Clear()
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
runAndCheck(t, v, tc.result)
})
}
}

View file

@ -1,11 +1,16 @@
package compiler_test
import (
"bytes"
"fmt"
"math/big"
"strings"
"testing"
"github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
func TestEntryPointWithMethod(t *testing.T) {
@ -370,405 +375,368 @@ func TestDec(t *testing.T) {
eval(t, src, big.NewInt(1))
}
func TestForLoopEmpty(t *testing.T) {
src := `
package foo
func Main() int {
x := 0
for {
x++
if x == 2 {
break
var forLoopTestCases = []testCase{
{
"empty for loop",
`func F%d() int {
x := 0
for {
x++
if x == 2 { break }
}
return x
}
return x
}
`
eval(t, src, big.NewInt(2))
}
func TestForLoopBigIter(t *testing.T) {
src := `
package foo
func Main() int {
`,
big.NewInt(2),
}, {
"big iteration count",
`func F%d() int {
x := 0
for i := 0; i < 100000; i++ {
x = i
}
return x
}
`
eval(t, src, big.NewInt(99999))
}
func TestForLoopNoInit(t *testing.T) {
src := `
package foo
func Main() int {
`,
big.NewInt(99999),
},
{
"no init",
`func F%d() int {
i := 0
for ; i < 10; i++ {
}
return i
}
`
eval(t, src, big.NewInt(10))
}
func TestForLoopNoPost(t *testing.T) {
src := `
package foo
func Main() int {
`,
big.NewInt(10),
},
{
"no post",
`func F%d() int {
i := 0
for i < 10 {
i++
}
return i
}
`
eval(t, src, big.NewInt(10))
}
func TestForLoopRange(t *testing.T) {
src := `
package foo
func Main() int {
sum := 0
arr := []int{1, 2, 3}
for i := range arr {
sum += arr[i]
}
return sum
}`
eval(t, src, big.NewInt(6))
}
func TestForLoopRangeGlobalIndex(t *testing.T) {
src := `
package foo
func Main() int {
sum := 0
i := 0
arr := []int{1, 2, 3}
for i = range arr {
sum += arr[i]
}
return sum + i
}`
eval(t, src, big.NewInt(8))
}
func TestForLoopRangeChangeVariable(t *testing.T) {
src := `
package foo
func Main() int {
sum := 0
arr := []int{1, 2, 3}
for i := range arr {
sum += arr[i]
i++
sum += i
}
return sum
}`
eval(t, src, big.NewInt(12))
}
func TestForLoopBreak(t *testing.T) {
src := `
package foo
func Main() int {
var i int
for i < 10 {
i++
if i == 5 {
break
`,
big.NewInt(10),
},
{
"range",
`func F%d() int {
sum := 0
arr := []int{1, 2, 3}
for i := range arr {
sum += arr[i]
}
return sum
}
return i
}`
eval(t, src, big.NewInt(5))
}
func TestForLoopBreakLabel(t *testing.T) {
src := `
package foo
func Main() int {
var i int
loop:
for i < 10 {
i++
if i == 5 {
break loop
`,
big.NewInt(6),
},
{
"range, global index",
`func F%d() int {
sum := 0
i := 0
arr := []int{1, 2, 3}
for i = range arr {
sum += arr[i]
}
return sum + i
}
return i
}`
eval(t, src, big.NewInt(5))
}
func TestForLoopNestedBreak(t *testing.T) {
src := `
package foo
func Main() int {
var i int
for i < 10 {
i++
for j := 0; j < 2; j++ {
`,
big.NewInt(8),
},
{
"range, change variable",
`func F%d() int {
sum := 0
arr := []int{1, 2, 3}
for i := range arr {
sum += arr[i]
i++
sum += i
}
return sum
}
`,
big.NewInt(12),
},
{
"break",
`func F%d() int {
var i int
for i < 10 {
i++
if i == 5 {
break
}
}
return i
}
return i
}`
eval(t, src, big.NewInt(11))
}
func TestForLoopNestedBreakLabel(t *testing.T) {
src := `
package foo
func Main() int {
var i int
loop:
for i < 10 {
i++
for j := 0; j < 2; j++ {
`,
big.NewInt(5),
},
{
"break label",
`func F%d() int {
var i int
loop:
for i < 10 {
i++
if i == 5 {
break loop
}
}
return i
}
`,
big.NewInt(5),
},
{
"nested break",
`func F%d() int {
var i int
for i < 10 {
i++
for j := 0; j < 2; j++ {
i++
if i == 5 {
break
}
}
}
return i
}
return i
}`
eval(t, src, big.NewInt(5))
}
func TestForLoopContinue(t *testing.T) {
src := `
package foo
func Main() int {
var i, j int
for i < 10 {
i++
if i >= 5 {
continue
`,
big.NewInt(11),
},
{
"nested break label",
`func F%d() int {
var i int
loop:
for i < 10 {
i++
for j := 0; j < 2; j++ {
if i == 5 {
break loop
}
i++
}
}
j++
return i
}
return j
}`
eval(t, src, big.NewInt(4))
}
func TestForLoopContinueLabel(t *testing.T) {
src := `
package foo
func Main() int {
var i, j int
loop:
for i < 10 {
i++
if i >= 5 {
continue loop
}
j++
}
return j
}`
eval(t, src, big.NewInt(4))
}
func TestForLoopNestedContinue(t *testing.T) {
src := `
package foo
func Main() int {
var i, k int
for i < 10 {
i++
for j := 0; j < 3; j++ {
if j >= 2 {
`,
big.NewInt(5),
},
{
"continue",
`func F%d() int {
var i, j int
for i < 10 {
i++
if i >= 5 {
continue
}
k++
j++
}
return j
}
return k
}`
eval(t, src, big.NewInt(20))
}
func TestForLoopNestedContinueLabel(t *testing.T) {
src := `
package foo
func Main() int {
var i int
loop:
for ; i < 10; i += 10 {
i++
for j := 0; j < 4; j++ {
if i == 5 {
`,
big.NewInt(4),
},
{
"continue label",
`func F%d() int {
var i, j int
loop:
for i < 10 {
i++
if i >= 5 {
continue loop
}
j++
}
return j
}
`,
big.NewInt(4),
},
{
"nested continue",
`func F%d() int {
var i, k int
for i < 10 {
i++
for j := 0; j < 3; j++ {
if j >= 2 {
continue
}
k++
}
}
return k
}
`,
big.NewInt(20),
},
{
"nested continue label",
`func F%d() int {
var i int
loop:
for ; i < 10; i += 10 {
i++
for j := 0; j < 4; j++ {
if i == 5 {
continue loop
}
i++
}
}
return i
}
`,
big.NewInt(15),
},
{
"range break",
`func F%d() int {
var i int
arr := []int{1, 2, 3}
for i = range arr {
if arr[i] == 2 {
break
}
}
return i
}
`,
big.NewInt(1),
},
{
"range nested break",
`func F%d() int {
k := 5
arr := []int{1, 2, 3}
urr := []int{4, 5, 6, 7}
loop:
for range arr {
k++
for j := range urr {
k++
if j == 3 {
break loop
}
}
}
return k
}
`,
big.NewInt(10),
},
{
"range continue",
`func F%d() int {
i := 6
arr := []int{1, 2, 3}
for j := range arr {
if arr[j] < 2 {
continue
}
i++
}
return i
}
return i
}`
eval(t, src, big.NewInt(15))
}
func TestForLoopRangeBreak(t *testing.T) {
src := `
package foo
func Main() int {
var i int
arr := []int{1, 2, 3}
for i = range arr {
if arr[i] == 2 {
break
`,
big.NewInt(8),
},
{
"range, no variable",
`func F%d() int {
sum := 0
arr := []int{1, 2, 3}
for range arr {
sum += 1
}
return sum
}
return i
}`
eval(t, src, big.NewInt(1))
}
func TestForLoopRangeNestedBreak(t *testing.T) {
src := `
package foo
func Main() int {
k := 5
arr := []int{1, 2, 3}
urr := []int{4, 5, 6, 7}
loop:
for range arr {
k++
for j := range urr {
k++
if j == 3 {
break loop
}
`,
big.NewInt(3),
},
{
"range value",
`func f(a int) int { return a }
func F%d() int {
var sum int
arr := []int{1, 9, 4}
for _, v := range arr {
sum += f(v)
}
return sum
}
return k
}`
eval(t, src, big.NewInt(10))
}
func TestForLoopRangeContinue(t *testing.T) {
src := `
package foo
func Main() int {
i := 6
arr := []int{1, 2, 3}
for j := range arr {
if arr[j] < 2 {
continue
`,
big.NewInt(14),
},
{
"range, map",
`func F%d() int {
m := map[int]int{
1: 13,
11: 17,
}
i++
var sum int
for i, v := range m {
sum += i
sum += v
}
return sum
}
return i
}`
eval(t, src, big.NewInt(8))
`,
big.NewInt(42),
},
{
"range, type conversion",
`type intArr []int
func F%d() int {
a := []int{1, 2, 3}
s := 0
for _, v := range intArr(a) {
s += v
}
return s
}
`,
big.NewInt(6),
},
}
func TestForLoopRangeNoVariable(t *testing.T) {
src := `
package foo
func Main() int {
sum := 0
arr := []int{1, 2, 3}
for range arr {
sum += 1
}
return sum
}`
func TestForLoop(t *testing.T) {
srcBuilder := bytes.NewBuffer([]byte("package testcase\n"))
for i, tc := range forLoopTestCases {
srcBuilder.WriteString(fmt.Sprintf(tc.src, i))
}
eval(t, src, big.NewInt(3))
}
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
require.NoError(t, err)
func TestForLoopRangeValue(t *testing.T) {
src := `
package foo
func f(a int) int { return a }
func Main() int {
var sum int
arr := []int{1, 9, 4}
for _, v := range arr {
sum += f(v)
}
return sum
}`
eval(t, src, big.NewInt(14))
}
func TestForLoopRangeMap(t *testing.T) {
src := `package foo
func Main() int {
m := map[int]int{
1: 13,
11: 17,
}
var sum int
for i, v := range m {
sum += i
sum += v
}
return sum
}`
eval(t, src, big.NewInt(42))
}
func TestForLoopRangeTypeConversion(t *testing.T) {
src := `package foo
type intArr []int
func Main() int {
a := []int{1, 2, 3}
s := 0
for _, v := range intArr(a) {
s += v
}
return s
}`
eval(t, src, big.NewInt(6))
for i, tc := range forLoopTestCases {
v := vm.New()
t.Run(tc.name, func(t *testing.T) {
v.Istack().Clear()
v.Estack().Clear()
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
runAndCheck(t, v, tc.result)
})
}
}
func TestForLoopComplexConditions(t *testing.T) {
src := `
package foo
func Main() int {
var ok bool
_ = ok
i := 0
j := 0
%s
for %s {
i++
j++
%s
}
return i
}`
tests := []struct {
forCondTestCases := []struct {
Name string
Cond string
Assign string
@ -788,14 +756,39 @@ func TestForLoopComplexConditions(t *testing.T) {
{Cond: "ok", Assign: "ok = (i < 2 && j < 4) || (i < 4 && j < 2)", Result: 2},
}
for _, tc := range tests {
tmpl := `func F%d() int {
var ok bool
_ = ok
i := 0
j := 0
%s
for %s {
i++
j++
%s
}
return i
}
`
srcBuilder := bytes.NewBufferString("package foo\n")
for i, tc := range forCondTestCases {
srcBuilder.WriteString(fmt.Sprintf(tmpl, i, tc.Assign, tc.Cond, tc.Assign))
}
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
require.NoError(t, err)
for i, tc := range forCondTestCases {
v := vm.New()
name := tc.Cond
if tc.Assign != "" {
name = tc.Assign
}
t.Run(name, func(t *testing.T) {
s := fmt.Sprintf(src, tc.Assign, tc.Cond, tc.Assign)
eval(t, s, big.NewInt(tc.Result))
v.Istack().Clear()
v.Estack().Clear()
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
runAndCheck(t, v, big.NewInt(tc.Result))
})
}
}

View file

@ -1,9 +1,11 @@
package compiler_test
import (
"bytes"
"errors"
"fmt"
"math/big"
"strconv"
"strings"
"testing"
@ -24,6 +26,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
@ -198,11 +201,23 @@ func TestNativeHelpersCompile(t *testing.T) {
})
}
func runNativeTestCases(t *testing.T, ctr interop.ContractMD, name string, testCases []nativeTestCase) {
func runNativeTestCases(t *testing.T, ctr interop.ContractMD, name string, nativeTestCases []nativeTestCase) {
srcBuilder := bytes.NewBuffer([]byte(`package foo
import "github.com/nspcc-dev/neo-go/pkg/interop/native/` + name + `"
import "github.com/nspcc-dev/neo-go/pkg/interop"
var _ interop.Hash160
`))
for i, tc := range nativeTestCases {
addNativeTestCase(t, srcBuilder, ctr, i, name, tc.method, tc.params...)
}
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
require.NoError(t, err)
t.Run(ctr.Name, func(t *testing.T) {
for _, tc := range testCases {
for i, tc := range nativeTestCases {
t.Run(tc.method, func(t *testing.T) {
runNativeTestCase(t, ctr, name, tc.method, tc.params...)
runNativeTestCase(t, ne, di, ctr, i, tc.method, tc.params...)
})
}
})
@ -233,30 +248,32 @@ func getMethod(t *testing.T, ctr interop.ContractMD, name string, params []strin
return md
}
func runNativeTestCase(t *testing.T, ctr interop.ContractMD, name, method string, params ...string) {
func addNativeTestCase(t *testing.T, srcBuilder *bytes.Buffer, ctr interop.ContractMD, i int, name, method string, params ...string) {
md := getMethod(t, ctr, method, params)
isVoid := md.MD.ReturnType == smartcontract.VoidType
srcTmpl := `package foo
import "github.com/nspcc-dev/neo-go/pkg/interop/native/%s"
import "github.com/nspcc-dev/neo-go/pkg/interop"
var _ interop.Hash160
`
if isVoid {
srcTmpl += `func Main() { %s.%s(%s) }`
srcBuilder.WriteString("func F" + strconv.Itoa(i) + "() ")
if !isVoid {
srcBuilder.WriteString("interface{} { return ")
} else {
srcTmpl += `func Main() interface{} { return %s.%s(%s) }`
srcBuilder.WriteString("{ ")
}
methodUpper := strings.ToUpper(method[:1]) + method[1:] // ASCII only
methodUpper = strings.ReplaceAll(methodUpper, "Gas", "GAS")
methodUpper = strings.ReplaceAll(methodUpper, "Json", "JSON")
src := fmt.Sprintf(srcTmpl, name, name, methodUpper, strings.Join(params, ","))
srcBuilder.WriteString(name)
srcBuilder.WriteRune('.')
srcBuilder.WriteString(methodUpper)
srcBuilder.WriteRune('(')
srcBuilder.WriteString(strings.Join(params, ", "))
srcBuilder.WriteString(") }\n")
}
func runNativeTestCase(t *testing.T, b *nef.File, di *compiler.DebugInfo, ctr interop.ContractMD, i int, method string, params ...string) {
md := getMethod(t, ctr, method, params)
result := getTestStackItem(md.MD.ReturnType)
isVoid := md.MD.ReturnType == smartcontract.VoidType
v := vm.New()
v.GasLimit = -1
b, di, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil)
require.NoError(t, err)
result := getTestStackItem(md.MD.ReturnType)
v.LoadToken = func(id int32) error {
t := b.Tokens[id]
if t.Hash != ctr.Hash {
@ -285,7 +302,7 @@ func runNativeTestCase(t *testing.T, ctr interop.ContractMD, name, method string
}
return nil
}
invokeMethod(t, testMainIdent, b.Script, v, di)
invokeMethod(t, fmt.Sprintf("F%d", i), b.Script, v, di)
require.NoError(t, v.Run())
if isVoid {
require.Equal(t, 0, v.Estack().Len())

View file

@ -1,11 +1,14 @@
package compiler_test
import (
"bytes"
"fmt"
"math/big"
"strings"
"testing"
"github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
@ -13,9 +16,7 @@ import (
var sliceTestCases = []testCase{
{
"constant index",
`
package foo
func Main() int {
`func F%d() int {
a := []int{0,0}
a[1] = 42
return a[1]+0
@ -25,9 +26,7 @@ var sliceTestCases = []testCase{
},
{
"variable index",
`
package foo
func Main() int {
`func F%d() int {
a := []int{0,0}
i := 1
a[i] = 42
@ -38,19 +37,17 @@ var sliceTestCases = []testCase{
},
{
"increase slice element with +=",
`package foo
func Main() int {
`func F%d() int {
a := []int{1, 2, 3}
a[1] += 40
return a[1]
}`,
}
`,
big.NewInt(42),
},
{
"complex test",
`
package foo
func Main() int {
`func F%d() int {
a := []int{1,2,3}
x := a[0]
a[x] = a[x] + 4
@ -62,9 +59,7 @@ var sliceTestCases = []testCase{
},
{
"slice literals with variables",
`
package foo
func Main() int {
`func F%d() int {
elem := 7
a := []int{6, elem, 8}
return a[1]
@ -74,9 +69,7 @@ var sliceTestCases = []testCase{
},
{
"slice literals with expressions",
`
package foo
func Main() int {
`func F%d() int {
elem := []int{3, 7}
a := []int{6, elem[1]*2+1, 24}
return a[1]
@ -86,93 +79,88 @@ var sliceTestCases = []testCase{
},
{
"sub-slice with literal bounds",
`
package foo
func Main() []byte {
`func F%d() []byte {
a := []byte{0, 1, 2, 3}
b := a[1:3]
return b
}`,
}
`,
[]byte{1, 2},
},
{
"sub-slice with constant bounds",
`
package foo
const x = 1
`const x = 1
const y = 3
func Main() []byte {
func F%d() []byte {
a := []byte{0, 1, 2, 3}
b := a[x:y]
return b
}`,
}
`,
[]byte{1, 2},
},
{
"sub-slice with variable bounds",
`
package foo
func Main() []byte {
`func F%d() []byte {
a := []byte{0, 1, 2, 3}
x := 1
y := 3
b := a[x:y]
return b
}`,
}
`,
[]byte{1, 2},
},
{
"sub-slice with no lower bound",
`
package foo
func Main() []byte {
`func F%d() []byte {
a := []byte{0, 1, 2, 3}
b := a[:3]
return b
}`,
}
`,
[]byte{0, 1, 2},
},
{
"sub-slice with no upper bound",
`
package foo
func Main() []byte {
`func F%d() []byte {
a := []byte{0, 1, 2, 3}
b := a[2:]
return b
}`,
}
`,
[]byte{2, 3},
},
{
"declare byte slice",
`package foo
func Main() []byte {
`func F%d() []byte {
var a []byte
a = append(a, 1)
a = append(a, 2)
return a
}`,
}
`,
[]byte{1, 2},
},
{
"append multiple bytes to a slice",
`package foo
func Main() []byte {
`func F%d() []byte {
var a []byte
a = append(a, 1, 2)
return a
}`,
}
`,
[]byte{1, 2},
},
{
"append multiple ints to a slice",
`package foo
func Main() []int {
`func F%d() []int {
var a []int
a = append(a, 1, 2, 3)
a = append(a, 4, 5)
return a
}`,
}
`,
[]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(1)),
stackitem.NewBigInteger(big.NewInt(2)),
@ -183,15 +171,15 @@ var sliceTestCases = []testCase{
},
{
"int slice, append slice",
`package foo
func getByte() byte { return 0x80 }
func Main() []int {
` func getByte() byte { return 0x80 }
func F%d() []int {
x := []int{1}
y := []int{2, 3}
x = append(x, y...)
x = append(x, y...)
return x
}`,
}
`,
[]stackitem.Item{
stackitem.Make(1),
stackitem.Make(2), stackitem.Make(3),
@ -200,13 +188,13 @@ var sliceTestCases = []testCase{
},
{
"declare compound slice",
`package foo
func Main() []string {
`func F%d() []string {
var a []string
a = append(a, "a")
a = append(a, "b")
return a
}`,
}
`,
[]stackitem.Item{
stackitem.NewByteArray([]byte("a")),
stackitem.NewByteArray([]byte("b")),
@ -214,14 +202,14 @@ var sliceTestCases = []testCase{
},
{
"declare compound slice alias",
`package foo
type strs []string
func Main() []string {
`type strs []string
func F%d() []string {
var a strs
a = append(a, "a")
a = append(a, "b")
return a
}`,
}
`,
[]stackitem.Item{
stackitem.NewByteArray([]byte("a")),
stackitem.NewByteArray([]byte("b")),
@ -229,72 +217,70 @@ var sliceTestCases = []testCase{
},
{
"byte-slice assignment",
`package foo
func Main() []byte {
`func F%d() []byte {
a := []byte{0, 1, 2}
a[1] = 42
return a
}`,
}
`,
[]byte{0, 42, 2},
},
{
"byte-slice assignment after string conversion",
`package foo
func Main() []byte {
`func F%d() []byte {
a := "abc"
b := []byte(a)
b[1] = 42
return []byte(a)
}`,
}
`,
[]byte{0x61, 0x62, 0x63},
},
{
"declare and append byte-slice",
`package foo
func Main() []byte {
`func F%d() []byte {
var a []byte
a = append(a, 1)
a = append(a, 2)
return a
}`,
}
`,
[]byte{1, 2},
},
{
"nested slice assignment",
`package foo
func Main() int {
`func F%d() int {
a := [][]int{[]int{1, 2}, []int{3, 4}}
a[1][0] = 42
return a[1][0]
}`,
}
`,
big.NewInt(42),
},
{
"nested slice omitted type (slice)",
`package foo
func Main() int {
`func F%d() int {
a := [][]int{{1, 2}, {3, 4}}
a[1][0] = 42
return a[1][0]
}`,
}
`,
big.NewInt(42),
},
{
"nested slice omitted type (struct)",
`package foo
type pair struct { a, b int }
func Main() int {
a := []pair{{a: 1, b: 2}, {a: 3, b: 4}}
`type pairA struct { a, b int }
func F%d() int {
a := []pairA{{a: 1, b: 2}, {a: 3, b: 4}}
a[1].a = 42
return a[1].a
}`,
}
`,
big.NewInt(42),
},
{
"defaults to nil for byte slice",
`
package foo
func Main() int {
`func F%d() int {
var a []byte
if a != nil { return 1}
return 2
@ -304,9 +290,7 @@ var sliceTestCases = []testCase{
},
{
"defaults to nil for int slice",
`
package foo
func Main() int {
`func F%d() int {
var a []int
if a != nil { return 1}
return 2
@ -316,11 +300,9 @@ var sliceTestCases = []testCase{
},
{
"defaults to nil for struct slice",
`
package foo
type pair struct { a, b int }
func Main() int {
var a []pair
`type pairB struct { a, b int }
func F%d() int {
var a []pairB
if a != nil { return 1}
return 2
}
@ -329,28 +311,42 @@ var sliceTestCases = []testCase{
},
{
"literal byte-slice with variable values",
`package foo
const sym1 = 's'
func Main() []byte {
`const sym1 = 's'
func F%d() []byte {
sym2 := byte('t')
sym4 := byte('i')
return []byte{sym1, sym2, 'r', sym4, 'n', 'g'}
}`,
}
`,
[]byte("string"),
},
{
"literal slice with function call",
`package foo
func fn() byte { return 't' }
func Main() []byte {
`func fn() byte { return 't' }
func F%d() []byte {
return []byte{'s', fn(), 'r'}
}`,
}
`,
[]byte("str"),
},
}
func TestSliceOperations(t *testing.T) {
runTestCases(t, sliceTestCases)
srcBuilder := bytes.NewBuffer([]byte("package testcase\n"))
for i, tc := range sliceTestCases {
srcBuilder.WriteString(fmt.Sprintf(tc.src, i))
}
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
require.NoError(t, err)
for i, tc := range sliceTestCases {
t.Run(tc.name, func(t *testing.T) {
v := vm.New()
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
runAndCheck(t, v, tc.result)
})
}
}
func TestByteSlices(t *testing.T) {

View file

@ -1,19 +1,23 @@
package compiler_test
import (
"bytes"
"fmt"
"math/big"
"strings"
"testing"
"github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
var structTestCases = []testCase{
{
"struct field assign",
`
package foo
func Main() int {
t := token {
`func F%d() int {
t := token1 {
x: 2,
y: 4,
}
@ -22,7 +26,7 @@ var structTestCases = []testCase{
return age
}
type token struct {
type token1 struct {
x int
y int
}
@ -31,11 +35,9 @@ var structTestCases = []testCase{
},
{
"struct field from func result",
`
package foo
type S struct { x int }
`type S struct { x int }
func fn() int { return 2 }
func Main() int {
func F%d() int {
t := S{x: fn()}
return t.x
}
@ -44,15 +46,13 @@ var structTestCases = []testCase{
},
{
"struct field return",
`
package foo
type token struct {
`type token2 struct {
x int
y int
}
func Main() int {
t := token {
func F%d() int {
t := token2 {
x: 2,
y: 4,
}
@ -64,15 +64,13 @@ var structTestCases = []testCase{
},
{
"struct field assign",
`
package foo
type token struct {
`type token3 struct {
x int
y int
}
func Main() int {
t := token {
func F%d() int {
t := token3 {
x: 2,
y: 4,
}
@ -84,17 +82,15 @@ var structTestCases = []testCase{
},
{
"complex struct",
`
package foo
type token struct {
`type token4 struct {
x int
y int
}
func Main() int {
func F%d() int {
x := 10
t := token {
t := token4 {
x: 2,
y: 4,
}
@ -108,95 +104,90 @@ var structTestCases = []testCase{
},
{
"initialize struct field from variable",
`
package foo
type token struct {
`type token5 struct {
x int
y int
}
func Main() int {
func F%d() int {
x := 10
t := token {
t := token5 {
x: x,
y: 4,
}
y := t.x + t.y
return y
}`,
}
`,
big.NewInt(14),
},
{
"assign a variable to a struct field",
`
package foo
type token struct {
`type token6 struct {
x int
y int
}
func Main() int {
func F%d() int {
ten := 10
t := token {
t := token6 {
x: 2,
y: 4,
}
t.x = ten
y := t.y + t.x
return y
}`,
}
`,
big.NewInt(14),
},
{
"increase struct field with +=",
`package foo
type token struct { x int }
func Main() int {
t := token{x: 2}
`type token7 struct { x int }
func F%d() int {
t := token7{x: 2}
t.x += 3
return t.x
}`,
}
`,
big.NewInt(5),
},
{
"assign a struct field to a struct field",
`
package foo
type token struct {
`type token8 struct {
x int
y int
}
func Main() int {
t1 := token {
func F%d() int {
t1 := token8 {
x: 2,
y: 4,
}
t2 := token {
t2 := token8 {
x: 3,
y: 5,
}
t1.x = t2.y
y := t1.x + t2.x
return y
}`,
}
`,
big.NewInt(8),
},
{
"initialize same struct twice",
`
package foo
type token struct {
`type token9 struct {
x int
y int
}
func Main() int {
t1 := token {
func F%d() int {
t1 := token9 {
x: 2,
y: 4,
}
t2 := token {
t2 := token9 {
x: 2,
y: 4,
}
@ -207,18 +198,16 @@ var structTestCases = []testCase{
},
{
"struct methods",
`
package foo
type token struct {
`type token10 struct {
x int
}
func(t token) getInteger() int {
func(t token10) getInteger() int {
return t.x
}
func Main() int {
t := token {
func F%d() int {
t := token10 {
x: 4,
}
someInt := t.getInteger()
@ -229,19 +218,17 @@ var structTestCases = []testCase{
},
{
"struct methods with arguments",
`
package foo
type token struct {
`type token11 struct {
x int
}
// Also tests if x conflicts with t.x
func(t token) addIntegers(x int, y int) int {
func(t token11) addIntegers(x int, y int) int {
return t.x + x + y
}
func Main() int {
t := token {
func F%d() int {
t := token11 {
x: 4,
}
someInt := t.addIntegers(2, 4)
@ -252,17 +239,15 @@ var structTestCases = []testCase{
},
{
"initialize struct partially",
`
package foo
type token struct {
`type token12 struct {
x int
y int
z string
b bool
}
func Main() int {
t := token {
func F%d() int {
t := token12 {
x: 4,
}
return t.y
@ -272,17 +257,15 @@ var structTestCases = []testCase{
},
{
"test return struct from func",
`
package foo
type token struct {
`type token13 struct {
x int
y int
z string
b bool
}
func newToken() token {
return token{
func newToken() token13 {
return token13{
x: 1,
y: 2,
z: "hello",
@ -290,7 +273,7 @@ var structTestCases = []testCase{
}
}
func Main() token {
func F%d() token13 {
return newToken()
}
`,
@ -303,10 +286,7 @@ var structTestCases = []testCase{
},
{
"pass struct as argument",
`
package foo
type Bar struct {
`type Bar struct {
amount int
}
@ -315,7 +295,7 @@ var structTestCases = []testCase{
return bar.amount
}
func Main() int {
func F%d() int {
b := Bar{
amount: 10,
}
@ -328,110 +308,109 @@ var structTestCases = []testCase{
},
{
"declare struct literal",
`package foo
func Main() int {
`func F%d() int {
var x struct {
a int
}
x.a = 2
return x.a
}`,
}
`,
big.NewInt(2),
},
{
"declare struct type",
`package foo
type withA struct {
`type withA struct {
a int
}
func Main() int {
func F%d() int {
var x withA
x.a = 2
return x.a
}`,
}
`,
big.NewInt(2),
},
{
"nested selectors (simple read)",
`package foo
type S1 struct { x, y S2 }
`type S1 struct { x, y S2 }
type S2 struct { a, b int }
func Main() int {
func F%d() int {
var s1 S1
var s2 S2
s2.a = 3
s1.y = s2
return s1.y.a
}`,
}
`,
big.NewInt(3),
},
{
"nested selectors (simple write)",
`package foo
type S1 struct { x S2 }
type S2 struct { a int }
func Main() int {
s1 := S1{
x: S2 {
`type S3 struct { x S4 }
type S4 struct { a int }
func F%d() int {
s1 := S3{
x: S4 {
a: 3,
},
}
s1.x.a = 11
return s1.x.a
}`,
}
`,
big.NewInt(11),
},
{
"complex struct default value",
`package foo
type S1 struct { x S2 }
type S2 struct { y S3 }
type S3 struct { a int }
func Main() int {
var s1 S1
`type S5 struct { x S6 }
type S6 struct { y S7 }
type S7 struct { a int }
func F%d() int {
var s1 S5
s1.x.y.a = 11
return s1.x.y.a
}`,
}
`,
big.NewInt(11),
},
{
"lengthy struct default value",
`package foo
type S struct { x int; y []byte; z bool }
func Main() int {
var s S
`type SS struct { x int; y []byte; z bool }
func F%d() int {
var s SS
return s.x
}`,
}
`,
big.NewInt(0),
},
{
"nested selectors (complex write)",
`package foo
type S1 struct { x S2 }
type S2 struct { y, z S3 }
type S3 struct { a int }
func Main() int {
var s1 S1
`type S8 struct { x S9 }
type S9 struct { y, z S10 }
type S10 struct { a int }
func F%d() int {
var s1 S8
s1.x.y.a, s1.x.z.a = 11, 31
return s1.x.y.a + s1.x.z.a
}`,
}
`,
big.NewInt(42),
},
{
"omit field names",
`package foo
type pair struct { a, b int }
func Main() int {
`type pair struct { a, b int }
func F%d() int {
p := pair{1, 2}
x := p.a * 10
return x + p.b
}`,
}
`,
big.NewInt(12),
},
{
"uninitialized struct fields",
`package foo
type Foo struct {
`type Foo struct {
i int
m map[string]int
b []byte
@ -439,7 +418,7 @@ var structTestCases = []testCase{
s struct { ii int }
}
func NewFoo() Foo { return Foo{} }
func Main() int {
func F%d() int {
foo := NewFoo()
if foo.i != 0 { return 1 }
if len(foo.m) != 0 { return 1 }
@ -448,11 +427,26 @@ var structTestCases = []testCase{
s := foo.s
if s.ii != 0 { return 1 }
return 2
}`,
}
`,
big.NewInt(2),
},
}
func TestStructs(t *testing.T) {
runTestCases(t, structTestCases)
srcBuilder := bytes.NewBuffer([]byte("package testcase\n"))
for i, tc := range structTestCases {
srcBuilder.WriteString(fmt.Sprintf(tc.src, i))
}
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
require.NoError(t, err)
for i, tc := range structTestCases {
t.Run(tc.name, func(t *testing.T) {
v := vm.New()
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
runAndCheck(t, v, tc.result)
})
}
}

View file

@ -1,28 +1,34 @@
package compiler_test
import (
"bytes"
"fmt"
"math/big"
"strings"
"testing"
"github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/stretchr/testify/require"
)
var switchTestCases = []testCase{
{
"simple switch success",
`package main
func Main() int {
`func F%d() int {
a := 5
switch a {
case 5: return 2
}
return 1
}`,
}
`,
big.NewInt(2),
},
{
"switch with no tag",
`package main
func f() bool { return false }
func Main() int {
`func f() bool { return false }
func F%d() int {
switch {
case f():
return 1
@ -30,14 +36,14 @@ var switchTestCases = []testCase{
return 2
}
return 3
}`,
}
`,
big.NewInt(2),
},
{
"type conversion in tag",
`package main
type state int
func Main() int {
`type state int
func F%d() int {
a := 1
switch state(a) {
case 1:
@ -45,52 +51,52 @@ var switchTestCases = []testCase{
default:
return 11
}
}`,
}
`,
big.NewInt(42),
},
{
"simple switch fail",
`package main
func Main() int {
`func F%d() int {
a := 6
switch a {
case 5:
return 2
}
return 1
}`,
}
`,
big.NewInt(1),
},
{
"multiple cases success",
`package main
func Main() int {
`func F%d() int {
a := 6
switch a {
case 5: return 2
case 6: return 3
}
return 1
}`,
}
`,
big.NewInt(3),
},
{
"multiple cases fail",
`package main
func Main() int {
`func F%d() int {
a := 7
switch a {
case 5: return 2
case 6: return 3
}
return 1
}`,
}
`,
big.NewInt(1),
},
{
"default case",
`package main
func Main() int {
`func F%d() int {
a := 7
switch a {
case 5: return 2
@ -98,13 +104,13 @@ var switchTestCases = []testCase{
default: return 4
}
return 1
}`,
}
`,
big.NewInt(4),
},
{
"empty case before default",
`package main
func Main() int {
`func F%d() int {
a := 6
switch a {
case 5: return 2
@ -112,13 +118,13 @@ var switchTestCases = []testCase{
default: return 4
}
return 1
}`,
}
`,
big.NewInt(1),
},
{
"expression in case clause",
`package main
func Main() int {
`func F%d() int {
a := 6
b := 3
switch a {
@ -126,13 +132,13 @@ var switchTestCases = []testCase{
case b*3-3: return 3
}
return 1
}`,
}
`,
big.NewInt(3),
},
{
"multiple expressions in case",
`package main
func Main() int {
`func F%d() int {
a := 8
b := 3
switch a {
@ -140,13 +146,13 @@ var switchTestCases = []testCase{
case b*3-3, 7, 8: return 3
}
return 1
}`,
}
`,
big.NewInt(3),
},
{
"string switch",
`package main
func Main() int {
`func F%d() int {
name := "Valera"
switch name {
case "Misha": return 2
@ -154,13 +160,13 @@ var switchTestCases = []testCase{
case "Lera", "Valer" + "a": return 4
}
return 1
}`,
}
`,
big.NewInt(4),
},
{
"break from switch",
`package main
func Main() int {
`func F%d() int {
i := 3
switch i {
case 2: return 2
@ -171,13 +177,13 @@ var switchTestCases = []testCase{
case 4: return 4
}
return i
}`,
}
`,
big.NewInt(1),
},
{
"break from outer for",
`package main
func Main() int {
`func F%d() int {
i := 3
loop:
for i < 10 {
@ -191,13 +197,13 @@ var switchTestCases = []testCase{
}
}
return i
}`,
}
`,
big.NewInt(7),
},
{
"continue outer for",
`package main
func Main() int {
`func F%d() int {
i := 2
for i < 10 {
i++
@ -214,13 +220,13 @@ var switchTestCases = []testCase{
}
}
return i
}`,
}
`,
big.NewInt(2),
},
{
"simple fallthrough",
`package main
func Main() int {
`func F%d() int {
n := 2
switch n {
case 1: return 5
@ -228,13 +234,13 @@ var switchTestCases = []testCase{
case 3: return 6
}
return 7
}`,
}
`,
big.NewInt(6),
},
{
"double fallthrough",
`package main
func Main() int {
`func F%d() int {
n := 2
k := 5
switch n {
@ -249,15 +255,26 @@ var switchTestCases = []testCase{
return k
}
return k
}`,
}
`,
big.NewInt(6),
},
}
func TestSwitch(t *testing.T) {
for _, tc := range switchTestCases {
srcBuilder := bytes.NewBuffer([]byte("package testcase\n"))
for i, tc := range switchTestCases {
srcBuilder.WriteString(fmt.Sprintf(tc.src, i))
}
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
require.NoError(t, err)
for i, tc := range switchTestCases {
t.Run(tc.name, func(t *testing.T) {
eval(t, tc.src, tc.result)
v := vm.New()
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
runAndCheck(t, v, tc.result)
})
}
}

View file

@ -1,6 +1,7 @@
package compiler_test
import (
"bytes"
"errors"
"fmt"
"math/big"
@ -99,14 +100,41 @@ func TestSyscallExecution(t *testing.T) {
// It will be set in test and we want to fail if calling invalid syscall.
fs.Func = nil
}
srcBuilder := bytes.NewBuffer(nil)
srcBuilder.WriteString(`package foo
import "github.com/nspcc-dev/neo-go/pkg/interop/contract"
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
import "github.com/nspcc-dev/neo-go/pkg/interop/storage"
import "github.com/nspcc-dev/neo-go/pkg/interop/iterator"
import "github.com/nspcc-dev/neo-go/pkg/interop/crypto"
import "github.com/nspcc-dev/neo-go/pkg/interop"
func unused() { var _ interop.Hash160 }
`)
for goName, tc := range interops {
realName := strings.ToTitle(strings.Replace(goName, ".", "", 1))
var tmpl string
if tc.isVoid {
tmpl = "func %s() { %s(%s) }\n"
} else {
tmpl = "func %s() interface{} { return %s(%s) }\n"
}
srcBuilder.WriteString(fmt.Sprintf(tmpl, realName, goName, strings.Join(tc.params, ", ")))
}
nf, di, err := compiler.CompileWithOptions("foo.go", srcBuilder, nil)
require.NoError(t, err)
for goName, tc := range interops {
t.Run(goName, func(t *testing.T) {
runSyscallTestCase(t, ic, goName, tc)
name := strings.ToTitle(strings.Replace(goName, ".", "", 1))
runSyscallTestCase(t, ic, name, nf.Script, di, tc)
})
}
}
func runSyscallTestCase(t *testing.T, ic *interop.Context, goName string, tc syscallTestCase) {
func runSyscallTestCase(t *testing.T, ic *interop.Context, realName string,
script []byte, debugInfo *compiler.DebugInfo, tc syscallTestCase) {
syscallID := interopnames.ToID([]byte(tc.method))
f := ic.GetFunction(syscallID)
require.NotNil(t, f)
@ -127,30 +155,14 @@ func runSyscallTestCase(t *testing.T, ic *interop.Context, goName string, tc sys
}
defer func() { f.Func = nil }()
srcTmpl := `package foo
import "github.com/nspcc-dev/neo-go/pkg/interop/%s"
import "github.com/nspcc-dev/neo-go/pkg/interop"
func unused() { var _ interop.Hash160 }
`
if tc.isVoid {
srcTmpl += `func Main() { %s(%s) }`
} else {
srcTmpl += `func Main() interface{} { return %s(%s) }`
}
ss := strings.Split(goName, ".")
src := fmt.Sprintf(srcTmpl, ss[0], goName, strings.Join(tc.params, ", "))
b, _, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil)
require.NoError(t, err)
v := ic.SpawnVM()
v.LoadScriptWithFlags(b.Script, callflag.All)
require.NoError(t, v.Run())
invokeMethod(t, realName, script, ic.VM, debugInfo)
require.NoError(t, ic.VM.Run())
require.True(t, called)
if tc.isVoid {
require.Equal(t, 0, v.Estack().Len())
require.Equal(t, 0, ic.VM.Estack().Len())
} else {
require.Equal(t, 1, v.Estack().Len())
require.Equal(t, big.NewInt(42), v.Estack().Pop().Value())
require.Equal(t, 1, ic.VM.Estack().Len())
require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Value())
}
}