mirror of
https://github.com/chaisql/chai.git
synced 2025-09-27 03:55:59 +08:00
213 lines
5.5 KiB
Go
213 lines
5.5 KiB
Go
package testutil
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/chaisql/chai/internal/database"
|
|
"github.com/chaisql/chai/internal/environment"
|
|
"github.com/chaisql/chai/internal/expr"
|
|
"github.com/chaisql/chai/internal/expr/functions"
|
|
"github.com/chaisql/chai/internal/sql/parser"
|
|
"github.com/chaisql/chai/internal/testutil/genexprtests"
|
|
"github.com/chaisql/chai/internal/types"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// NullValue creates a literal value of type Null.
|
|
func NullValue() expr.LiteralValue {
|
|
return expr.LiteralValue{Value: types.NewNullValue()}
|
|
}
|
|
|
|
// BoolValue creates a literal value of type Bool.
|
|
func BoolValue(v bool) expr.LiteralValue {
|
|
return expr.LiteralValue{Value: types.NewBooleanValue(v)}
|
|
}
|
|
|
|
// IntegerValue creates a literal value of type Integer.
|
|
func IntegerValue(v int32) expr.LiteralValue {
|
|
return expr.LiteralValue{Value: types.NewIntegerValue(v)}
|
|
}
|
|
|
|
// BigintValue creates a literal value of type Bigint.
|
|
func BigintValue(v int64) expr.LiteralValue {
|
|
return expr.LiteralValue{Value: types.NewBigintValue(v)}
|
|
}
|
|
|
|
// DoubleValue creates a literal value of type Double.
|
|
func DoubleValue(v float64) expr.LiteralValue {
|
|
return expr.LiteralValue{Value: types.NewDoubleValue(v)}
|
|
}
|
|
|
|
// TextValue creates a literal value of type Text.
|
|
func TextValue(v string) expr.LiteralValue {
|
|
return expr.LiteralValue{Value: types.NewTextValue(v)}
|
|
}
|
|
|
|
// ByteaValue creates a literal value of type bytea.
|
|
func ByteaValue(v []byte) expr.LiteralValue {
|
|
return expr.LiteralValue{Value: types.NewByteaValue(v)}
|
|
}
|
|
|
|
func ExprList(t testing.TB, s string) expr.LiteralExprList {
|
|
t.Helper()
|
|
|
|
e, err := parser.ParseExpr(s)
|
|
require.NoError(t, err)
|
|
switch e := e.(type) {
|
|
case expr.LiteralExprList:
|
|
return e
|
|
case expr.Parentheses:
|
|
return expr.LiteralExprList{e.E}
|
|
default:
|
|
t.Fatalf("unexpected expression type: %T", e)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ParseNamedExpr(t testing.TB, s string, name ...string) expr.Expr {
|
|
t.Helper()
|
|
|
|
ne := expr.NamedExpr{
|
|
Expr: ParseExpr(t, s),
|
|
ExprName: s,
|
|
}
|
|
|
|
if len(name) > 0 {
|
|
ne.ExprName = name[0]
|
|
}
|
|
|
|
return &ne
|
|
}
|
|
|
|
func ParseExpr(t testing.TB, s string) expr.Expr {
|
|
t.Helper()
|
|
|
|
e, err := parser.ParseExpr(s)
|
|
require.NoError(t, err)
|
|
|
|
return e
|
|
}
|
|
|
|
func ParseExprs(t testing.TB, s ...string) []expr.Expr {
|
|
t.Helper()
|
|
|
|
ex := make([]expr.Expr, len(s))
|
|
for i, e := range s {
|
|
ex[i] = ParseExpr(t, e)
|
|
}
|
|
|
|
return ex
|
|
}
|
|
|
|
func ParseExprList(t testing.TB, s string) expr.LiteralExprList {
|
|
t.Helper()
|
|
|
|
e, err := parser.ParseExpr(s)
|
|
require.NoError(t, err)
|
|
|
|
switch e := e.(type) {
|
|
case expr.LiteralExprList:
|
|
return e
|
|
case expr.Parentheses:
|
|
return expr.LiteralExprList{e.E}
|
|
default:
|
|
t.Fatalf("unexpected expression type: %T", e)
|
|
}
|
|
|
|
return e.(expr.LiteralExprList)
|
|
}
|
|
|
|
func TestExpr(t testing.TB, exprStr string, env *environment.Environment, want types.Value, fails bool) {
|
|
t.Helper()
|
|
e, err := parser.NewParser(strings.NewReader(exprStr)).ParseExpr()
|
|
require.NoError(t, err)
|
|
res, err := e.Eval(env)
|
|
if fails {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, want, res)
|
|
}
|
|
}
|
|
|
|
func FunctionExpr(t testing.TB, name string, args ...expr.Expr) expr.Expr {
|
|
t.Helper()
|
|
def, err := functions.GetFunc(name)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, def)
|
|
expr, err := def.Function(args...)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, expr)
|
|
return expr
|
|
}
|
|
|
|
func ExprRunner(t *testing.T, testfile string) {
|
|
t.Helper()
|
|
|
|
f, err := os.Open(testfile)
|
|
if err != nil {
|
|
t.Errorf("Failed to open test data, got %v (%s)", err, testfile)
|
|
}
|
|
|
|
ts, err := genexprtests.Parse(f)
|
|
require.NoError(t, err)
|
|
|
|
tx := database.Transaction{
|
|
TxStart: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
}
|
|
|
|
env := environment.New(nil, &tx, nil, nil)
|
|
|
|
for _, test := range ts.Tests {
|
|
t.Run(test.Name, func(t *testing.T) {
|
|
t.Helper()
|
|
testfile, _ := filepath.Abs(testfile)
|
|
for _, stmt := range test.Statements {
|
|
if !stmt.Fail {
|
|
t.Run("OK "+stmt.Expr, func(t *testing.T) {
|
|
t.Helper()
|
|
// parse the expected result
|
|
e, err := parser.NewParser(strings.NewReader(stmt.Res)).ParseExpr()
|
|
require.NoErrorf(t, err, "parse error at %s:%d\n`%s`: %v", testfile, stmt.ResLine, stmt.Res, err)
|
|
|
|
// eval it to get a proper Value
|
|
want, err := e.Eval(env)
|
|
require.NoErrorf(t, err, "eval error at %s:%d\n`%s`: %v", testfile, stmt.ResLine, stmt.Res, err)
|
|
|
|
// parse the given expr
|
|
e, err = parser.NewParser(strings.NewReader(stmt.Expr)).ParseExpr()
|
|
require.NoErrorf(t, err, "parse error at %s:%d\n`%s`: %v", testfile, stmt.ExprLine, stmt.Expr, err)
|
|
|
|
// eval it to get a proper Value
|
|
got, err := e.Eval(env)
|
|
require.NoErrorf(t, err, "eval error at %s:%d\n`%s`: %v", testfile, stmt.ExprLine, stmt.Expr, err)
|
|
|
|
// finally, compare those two
|
|
RequireValueEqual(t, want, got, "assertion error at %s:%d", testfile, stmt.ResLine)
|
|
})
|
|
} else {
|
|
t.Run("NOK "+stmt.Expr, func(t *testing.T) {
|
|
t.Helper()
|
|
// parse the given expr
|
|
e, err := parser.NewParser(strings.NewReader(stmt.Expr)).ParseExpr()
|
|
if err != nil {
|
|
require.Regexp(t, regexp.MustCompile(regexp.QuoteMeta(stmt.Res)), err.Error())
|
|
} else {
|
|
// eval it, it should return an error
|
|
_, err = e.Eval(env)
|
|
require.NotNilf(t, err, "expected expr to return an error at %s:%d\n`%s`, got nil", testfile, stmt.ExprLine, stmt.Expr)
|
|
require.Regexpf(t, regexp.MustCompile(regexp.QuoteMeta(stmt.Res)), err.Error(), "expected error message to match at %s:%d", testfile, stmt.ResLine)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|