xgo/assert/assert.go
2019-01-11 01:16:34 -08:00

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
}