mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-26 19:42:23 +00:00
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:
parent
9871dc8f5a
commit
548a6a06f1
2 changed files with 106 additions and 54 deletions
|
@ -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"
|
||||
)
|
||||
|
||||
var binaryExprTestCases = []testCase{
|
||||
|
@ -264,34 +270,55 @@ func TestBinaryExprs(t *testing.T) {
|
|||
runTestCases(t, binaryExprTestCases)
|
||||
}
|
||||
|
||||
func getBoolExprTestFunc(val bool, cond string) func(t *testing.T) {
|
||||
srcTmpl := `package foo
|
||||
var s = "str"
|
||||
var v = 9
|
||||
var cond = %s
|
||||
func Main() int {
|
||||
if %s {
|
||||
return 42
|
||||
} %s
|
||||
func addBoolExprTestFunc(testCases []testCase, b *bytes.Buffer, val bool, cond string) []testCase {
|
||||
n := len(testCases)
|
||||
b.WriteString(fmt.Sprintf(`
|
||||
func F%d_expr() int {
|
||||
var cond%d = %s
|
||||
if cond%d { return 42 }
|
||||
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)
|
||||
if !val {
|
||||
res.SetInt64(17)
|
||||
}
|
||||
return func(t *testing.T) {
|
||||
t.Run("AsExpression", func(t *testing.T) {
|
||||
src := fmt.Sprintf(srcTmpl, cond, "cond", "", "")
|
||||
eval(t, src, res)
|
||||
})
|
||||
t.Run("InCondition", func(t *testing.T) {
|
||||
src := fmt.Sprintf(srcTmpl, "true", cond, "", "")
|
||||
eval(t, src, res)
|
||||
})
|
||||
t.Run("InConditionWithElse", func(t *testing.T) {
|
||||
src := fmt.Sprintf(srcTmpl, "true", cond, " else {", "}")
|
||||
eval(t, src, res)
|
||||
|
||||
return append(testCases, testCase{
|
||||
name: cond,
|
||||
result: res,
|
||||
})
|
||||
}
|
||||
|
||||
func runBooleanCases(t *testing.T, testCases []testCase, src string) {
|
||||
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(src), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
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
|
||||
// and tests if the result matches to that of Go.
|
||||
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"`}
|
||||
falseExpr := []string{"false", "v > 9", "v >= 10", "v < 9", "v <= 8", "v == 8", "v != 9", `s == "a"`}
|
||||
t.Run("Single", func(t *testing.T) {
|
||||
for _, s := range trueExpr {
|
||||
t.Run(s, getBoolExprTestFunc(true, s))
|
||||
testCases = addBoolExprTestFunc(testCases, srcBuilder, true, s)
|
||||
}
|
||||
for _, s := range falseExpr {
|
||||
t.Run(s, getBoolExprTestFunc(false, s))
|
||||
testCases = addBoolExprTestFunc(testCases, srcBuilder, false, s)
|
||||
}
|
||||
runBooleanCases(t, testCases, srcBuilder.String())
|
||||
})
|
||||
|
||||
type arg struct {
|
||||
val bool
|
||||
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
|
||||
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)})
|
||||
}
|
||||
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"})
|
||||
}
|
||||
|
||||
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 {
|
||||
t.Run(triple[i].s, getBoolExprTestFunc(triple[i].val, triple[i].s))
|
||||
runBooleanCases(t, testCases, srcBuilder.String())
|
||||
})
|
||||
|
||||
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())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -34,10 +34,14 @@ func runTestCases(t *testing.T, tcases []testCase) {
|
|||
|
||||
func eval(t *testing.T, src string, result interface{}) {
|
||||
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)
|
||||
assert.Equal(t, 1, vm.Estack().Len(), "stack contains unexpected items")
|
||||
assertResult(t, vm, result)
|
||||
assert.Equal(t, 1, v.Estack().Len(), "stack contains unexpected items")
|
||||
assertResult(t, v, result)
|
||||
}
|
||||
|
||||
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 {
|
||||
vm.Estack().PushVal(op)
|
||||
}
|
||||
err := vm.Run()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, vm.Estack().Len(), "stack contains unexpected items")
|
||||
assertResult(t, vm, result)
|
||||
runAndCheck(t, vm, result)
|
||||
}
|
||||
|
||||
func assertResult(t *testing.T, vm *vm.VM, result interface{}) {
|
||||
|
|
Loading…
Reference in a new issue