package assert import ( "bytes" "fmt" "reflect" "strings" "testing" "github.com/d5/tengo/compiler" "github.com/d5/tengo/objects" "github.com/d5/tengo/source" "github.com/d5/tengo/token" ) func NoError(t *testing.T, err error, msg ...interface{}) bool { t.Helper() if err == nil { return true } return failExpectedActual(t, "no error", err, msg...) } func Error(t *testing.T, err error, msg ...interface{}) bool { t.Helper() if err != nil { return true } return failExpectedActual(t, "error", err, msg...) } func Nil(t *testing.T, v interface{}, msg ...interface{}) bool { t.Helper() if v == nil { return true } return failExpectedActual(t, "nil", v, msg...) } func True(t *testing.T, v bool, msg ...interface{}) bool { t.Helper() if v { return true } return failExpectedActual(t, "true", v, msg...) } func False(t *testing.T, v bool, msg ...interface{}) bool { t.Helper() if !v { return true } return failExpectedActual(t, "false", v, msg...) } func NotNil(t *testing.T, v interface{}, msg ...interface{}) bool { t.Helper() if v != nil { return true } return failExpectedActual(t, "not nil", v, msg...) } func IsType(t *testing.T, expected, actual interface{}, msg ...interface{}) bool { t.Helper() if reflect.TypeOf(expected) == reflect.TypeOf(actual) { return true } return failExpectedActual(t, reflect.TypeOf(expected), reflect.TypeOf(actual), msg...) } func Equal(t *testing.T, expected, actual interface{}, msg ...interface{}) bool { t.Helper() if expected == nil { return Nil(t, actual, "expected nil, but got not nil") } if !NotNil(t, actual, "expected not nil, but got nil") { return false } if !IsType(t, expected, actual) { return false } switch expected := expected.(type) { case int: if expected != actual.(int) { return failExpectedActual(t, expected, actual, msg...) } case int64: if expected != actual.(int64) { return failExpectedActual(t, expected, actual, msg...) } case float64: if expected != actual.(float64) { return failExpectedActual(t, expected, actual, msg...) } case string: if expected != actual.(string) { return failExpectedActual(t, expected, actual, msg...) } case []byte: if bytes.Compare(expected, actual.([]byte)) != 0 { return failExpectedActual(t, expected, actual, msg...) } case []int: if !equalIntSlice(expected, actual.([]int)) { return failExpectedActual(t, expected, actual, msg...) } case bool: if expected != actual.(bool) { return failExpectedActual(t, expected, actual, msg...) } case rune: if expected != actual.(rune) { return failExpectedActual(t, expected, actual, msg...) } case compiler.Symbol: if !equalSymbol(expected, actual.(compiler.Symbol)) { return failExpectedActual(t, expected, actual, msg...) } case source.Pos: if expected != actual.(source.Pos) { return failExpectedActual(t, expected, actual, msg...) } case token.Token: if expected != actual.(token.Token) { return failExpectedActual(t, expected, actual, msg...) } case []objects.Object: return equalObjectSlice(t, expected, actual.([]objects.Object)) case *objects.Int: return Equal(t, expected.Value, actual.(*objects.Int).Value) case *objects.Float: return Equal(t, expected.Value, actual.(*objects.Float).Value) case *objects.String: return Equal(t, expected.Value, actual.(*objects.String).Value) case *objects.Char: return Equal(t, expected.Value, actual.(*objects.Char).Value) case *objects.Bool: return Equal(t, expected.Value, actual.(*objects.Bool).Value) case *objects.ReturnValue: return Equal(t, expected.Value, actual.(objects.ReturnValue).Value) case *objects.Array: return equalArray(t, expected, actual.(*objects.Array)) case *objects.Map: return equalMap(t, expected, actual.(*objects.Map)) case *objects.CompiledFunction: return equalCompiledFunction(t, expected, actual.(*objects.CompiledFunction)) case *objects.Closure: return equalClosure(t, expected, actual.(*objects.Closure)) case *objects.Undefined: return true default: panic(fmt.Errorf("type not implemented: %T", expected)) } return true } func Fail(t *testing.T, msg ...interface{}) bool { t.Helper() t.Logf("\nError trace:\n\t%s\n%s", strings.Join(errorTrace(), "\n\t"), message(msg...)) t.Fail() return false } func failExpectedActual(t *testing.T, expected, actual interface{}, msg ...interface{}) bool { t.Helper() var addMsg string if len(msg) > 0 { addMsg = "\nMessage: " + message(msg...) } t.Logf("\nError trace:\n\t%s\nExpected: %v\nActual: %v%s", strings.Join(errorTrace(), "\n\t"), expected, actual, addMsg) t.Fail() return false } func message(formatArgs ...interface{}) string { var format string var args []interface{} if len(formatArgs) > 0 { format = formatArgs[0].(string) } if len(formatArgs) > 1 { args = formatArgs[1:] } return fmt.Sprintf(format, args...) } func equalIntSlice(a, b []int) bool { if len(a) != len(b) { return false } for i := 0; i < len(a); i++ { if a[i] != b[i] { return false } } return true } func equalSymbol(a, b compiler.Symbol) bool { return a.Name == b.Name && a.Index == b.Index && a.Scope == b.Scope } func equalArray(t *testing.T, expected, actual objects.Object) bool { expectedT := expected.(*objects.Array).Value actualT := actual.(*objects.Array).Value return equalObjectSlice(t, expectedT, actualT) } func equalObjectSlice(t *testing.T, expected, actual []objects.Object) bool { if !Equal(t, len(expected), len(actual)) { return false } for i := 0; i < len(expected); i++ { if !Equal(t, expected[i], actual[i]) { return false } } return true } func equalMap(t *testing.T, expected, actual objects.Object) bool { expectedT := expected.(*objects.Map).Value actualT := actual.(*objects.Map).Value if !Equal(t, len(expectedT), len(actualT)) { return false } for key, expectedVal := range expectedT { actualVal := actualT[key] if !Equal(t, expectedVal, actualVal) { return false } } return true } func equalCompiledFunction(t *testing.T, expected, actual objects.Object) bool { expectedT := expected.(*objects.CompiledFunction) actualT := actual.(*objects.CompiledFunction) return Equal(t, expectedT.Instructions, actualT.Instructions) } func equalClosure(t *testing.T, expected, actual objects.Object) bool { expectedT := expected.(*objects.Closure) actualT := actual.(*objects.Closure) if !Equal(t, expectedT.Fn, actualT.Fn) { return false } if !Equal(t, len(expectedT.Free), len(actualT.Free)) { return false } for i := 0; i < len(expectedT.Free); i++ { if !Equal(t, *expectedT.Free[i], *actualT.Free[i]) { return false } } return true }