compiler: speed up boolean expression short-circuit test

The amount of tests done is comparable to all other tests in compiler
package (~2k). After we moved to a new x/tools package this became a
bottleneck. In this commit we reduce the amount of compiled files by
combining multiple tests in a single file.

Signed-off-by: Evgeniy Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgeniy Stratonikov 2022-01-12 13:11:17 +03:00
parent 9871dc8f5a
commit 548a6a06f1
2 changed files with 106 additions and 54 deletions

View file

@ -1,9 +1,15 @@
package compiler_test package compiler_test
import ( import (
"bytes"
"fmt" "fmt"
"math/big" "math/big"
"strings"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/stretchr/testify/require"
) )
var binaryExprTestCases = []testCase{ var binaryExprTestCases = []testCase{
@ -264,34 +270,55 @@ func TestBinaryExprs(t *testing.T) {
runTestCases(t, binaryExprTestCases) runTestCases(t, binaryExprTestCases)
} }
func getBoolExprTestFunc(val bool, cond string) func(t *testing.T) { func addBoolExprTestFunc(testCases []testCase, b *bytes.Buffer, val bool, cond string) []testCase {
srcTmpl := `package foo n := len(testCases)
var s = "str" b.WriteString(fmt.Sprintf(`
var v = 9 func F%d_expr() int {
var cond = %s var cond%d = %s
func Main() int { if cond%d { return 42 }
if %s {
return 42
} %s
return 17 return 17
%s }
}` func F%d_cond() int {
if %s { return 42 }
return 17
}
func F%d_else() int {
if %s { return 42 } else { return 17 }
}
`, n, n, cond, n, n, cond, n, cond))
res := big.NewInt(42) res := big.NewInt(42)
if !val { if !val {
res.SetInt64(17) res.SetInt64(17)
} }
return func(t *testing.T) {
t.Run("AsExpression", func(t *testing.T) { return append(testCases, testCase{
src := fmt.Sprintf(srcTmpl, cond, "cond", "", "") name: cond,
eval(t, src, res) result: res,
}) })
t.Run("InCondition", func(t *testing.T) { }
src := fmt.Sprintf(srcTmpl, "true", cond, "", "")
eval(t, src, res) func runBooleanCases(t *testing.T, testCases []testCase, src string) {
}) ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(src), nil)
t.Run("InConditionWithElse", func(t *testing.T) { require.NoError(t, err)
src := fmt.Sprintf(srcTmpl, "true", cond, " else {", "}")
eval(t, src, res) for i, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Run("AsExpression", func(t *testing.T) {
v := vm.New()
invokeMethod(t, fmt.Sprintf("F%d_expr", i), ne.Script, v, di)
runAndCheck(t, v, tc.result)
})
t.Run("InCondition", func(t *testing.T) {
v := vm.New()
invokeMethod(t, fmt.Sprintf("F%d_cond", i), ne.Script, v, di)
runAndCheck(t, v, tc.result)
})
t.Run("InConditionWithElse", func(t *testing.T) {
v := vm.New()
invokeMethod(t, fmt.Sprintf("F%d_else", i), ne.Script, v, di)
runAndCheck(t, v, tc.result)
})
}) })
} }
} }
@ -299,46 +326,70 @@ func getBoolExprTestFunc(val bool, cond string) func(t *testing.T) {
// TestBooleanExprs enumerates a lot of possible combinations of boolean expressions // TestBooleanExprs enumerates a lot of possible combinations of boolean expressions
// and tests if the result matches to that of Go. // and tests if the result matches to that of Go.
func TestBooleanExprs(t *testing.T) { func TestBooleanExprs(t *testing.T) {
t.Skip() // FIXME this test takes more than 2 minutes to complete header := `package foo
var s = "str"
var v = 9
`
srcBuilder := bytes.NewBuffer([]byte(header))
var testCases []testCase
trueExpr := []string{"true", "v < 10", "v <= 9", "v > 8", "v >= 9", "v == 9", "v != 8", `s == "str"`} trueExpr := []string{"true", "v < 10", "v <= 9", "v > 8", "v >= 9", "v == 9", "v != 8", `s == "str"`}
falseExpr := []string{"false", "v > 9", "v >= 10", "v < 9", "v <= 8", "v == 8", "v != 9", `s == "a"`} falseExpr := []string{"false", "v > 9", "v >= 10", "v < 9", "v <= 8", "v == 8", "v != 9", `s == "a"`}
t.Run("Single", func(t *testing.T) { t.Run("Single", func(t *testing.T) {
for _, s := range trueExpr { for _, s := range trueExpr {
t.Run(s, getBoolExprTestFunc(true, s)) testCases = addBoolExprTestFunc(testCases, srcBuilder, true, s)
} }
for _, s := range falseExpr { for _, s := range falseExpr {
t.Run(s, getBoolExprTestFunc(false, s)) testCases = addBoolExprTestFunc(testCases, srcBuilder, false, s)
} }
runBooleanCases(t, testCases, srcBuilder.String())
}) })
type arg struct { type arg struct {
val bool val bool
s string s string
} }
t.Run("Combine", func(t *testing.T) {
var double []arg
for _, e := range trueExpr {
double = append(double, arg{true, e + " || false"})
double = append(double, arg{true, e + " && true"})
}
for _, e := range falseExpr {
double = append(double, arg{false, e + " && true"})
double = append(double, arg{false, e + " || false"})
}
for i := range double {
t.Run(double[i].s, getBoolExprTestFunc(double[i].val, double[i].s))
}
var triple []arg var double []arg
for _, a1 := range double { for _, e := range trueExpr {
for _, a2 := range double { double = append(double, arg{true, e + " || false"})
triple = append(triple, arg{a1.val || a2.val, fmt.Sprintf("(%s) || (%s)", a1.s, a2.s)}) double = append(double, arg{true, e + " && true"})
triple = append(triple, arg{a1.val && a2.val, fmt.Sprintf("(%s) && (%s)", a1.s, a2.s)}) }
} for _, e := range falseExpr {
double = append(double, arg{false, e + " && true"})
double = append(double, arg{false, e + " || false"})
}
t.Run("Double", func(t *testing.T) {
testCases = testCases[:0]
srcBuilder.Reset()
srcBuilder.WriteString(header)
for i := range double {
testCases = addBoolExprTestFunc(testCases, srcBuilder, double[i].val, double[i].s)
} }
for i := range triple { runBooleanCases(t, testCases, srcBuilder.String())
t.Run(triple[i].s, getBoolExprTestFunc(triple[i].val, triple[i].s)) })
var triple []arg
for _, a1 := range double {
for _, a2 := range double {
triple = append(triple, arg{a1.val || a2.val, fmt.Sprintf("(%s) || (%s)", a1.s, a2.s)})
triple = append(triple, arg{a1.val && a2.val, fmt.Sprintf("(%s) && (%s)", a1.s, a2.s)})
}
}
t.Run("Triple", func(t *testing.T) {
const step = 256 // empirically found value to make script less than 65536 in size
for start := 0; start < len(triple); start += step {
testCases = testCases[:0]
srcBuilder.Reset()
srcBuilder.WriteString(header)
for i := start; i < start+step && i < len(triple); i++ {
testCases = addBoolExprTestFunc(testCases, srcBuilder, triple[i].val, triple[i].s)
}
runBooleanCases(t, testCases, srcBuilder.String())
} }
}) })
} }

View file

@ -34,10 +34,14 @@ func runTestCases(t *testing.T, tcases []testCase) {
func eval(t *testing.T, src string, result interface{}) { func eval(t *testing.T, src string, result interface{}) {
vm, _ := vmAndCompileInterop(t, src) vm, _ := vmAndCompileInterop(t, src)
err := vm.Run() runAndCheck(t, vm, result)
}
func runAndCheck(t *testing.T, v *vm.VM, result interface{}) {
err := v.Run()
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, 1, vm.Estack().Len(), "stack contains unexpected items") assert.Equal(t, 1, v.Estack().Len(), "stack contains unexpected items")
assertResult(t, vm, result) assertResult(t, v, result)
} }
func evalWithArgs(t *testing.T, src string, op []byte, args []stackitem.Item, result interface{}) { func evalWithArgs(t *testing.T, src string, op []byte, args []stackitem.Item, result interface{}) {
@ -48,10 +52,7 @@ func evalWithArgs(t *testing.T, src string, op []byte, args []stackitem.Item, re
if op != nil { if op != nil {
vm.Estack().PushVal(op) vm.Estack().PushVal(op)
} }
err := vm.Run() runAndCheck(t, vm, result)
require.NoError(t, err)
assert.Equal(t, 1, vm.Estack().Len(), "stack contains unexpected items")
assertResult(t, vm, result)
} }
func assertResult(t *testing.T, vm *vm.VM, result interface{}) { func assertResult(t *testing.T, vm *vm.VM, result interface{}) {