302 lines
6.6 KiB
Go
302 lines
6.6 KiB
Go
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
|
|
}
|