diff --git a/pkg/compiler/assign_test.go b/pkg/compiler/assign_test.go index 182224ea4..b770e8a61 100644 --- a/pkg/compiler/assign_test.go +++ b/pkg/compiler/assign_test.go @@ -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) + }) + } } diff --git a/pkg/compiler/binary_expr_test.go b/pkg/compiler/binary_expr_test.go index 936b0e117..b03a33727 100644 --- a/pkg/compiler/binary_expr_test.go +++ b/pkg/compiler/binary_expr_test.go @@ -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() diff --git a/pkg/compiler/convert_test.go b/pkg/compiler/convert_test.go index 61abb068a..fd3552afd 100644 --- a/pkg/compiler/convert_test.go +++ b/pkg/compiler/convert_test.go @@ -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) }) } } diff --git a/pkg/compiler/for_test.go b/pkg/compiler/for_test.go index ab2a29fbe..bf451af1c 100644 --- a/pkg/compiler/for_test.go +++ b/pkg/compiler/for_test.go @@ -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)) }) } } diff --git a/pkg/compiler/native_test.go b/pkg/compiler/native_test.go index 9aa5efa45..7df916dda 100644 --- a/pkg/compiler/native_test.go +++ b/pkg/compiler/native_test.go @@ -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" @@ -196,11 +199,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...) }) } }) @@ -231,30 +246,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 { @@ -283,7 +300,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()) diff --git a/pkg/compiler/slice_test.go b/pkg/compiler/slice_test.go index 4ce08291d..8152959cd 100644 --- a/pkg/compiler/slice_test.go +++ b/pkg/compiler/slice_test.go @@ -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) { diff --git a/pkg/compiler/struct_test.go b/pkg/compiler/struct_test.go index 046cdb629..d799f1805 100644 --- a/pkg/compiler/struct_test.go +++ b/pkg/compiler/struct_test.go @@ -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) + }) + } } diff --git a/pkg/compiler/switch_test.go b/pkg/compiler/switch_test.go index 73c711b79..d88226785 100644 --- a/pkg/compiler/switch_test.go +++ b/pkg/compiler/switch_test.go @@ -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) }) } } diff --git a/pkg/compiler/syscall_test.go b/pkg/compiler/syscall_test.go index fe6ee2626..086ef7621 100644 --- a/pkg/compiler/syscall_test.go +++ b/pkg/compiler/syscall_test.go @@ -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()) } }