123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- package require
- import (
- "bytes"
- "fmt"
- "reflect"
- "runtime"
- "strings"
- "testing"
- "unicode"
- "unicode/utf8"
- "github.com/d5/tengo/v2"
- "github.com/d5/tengo/v2/parser"
- "github.com/d5/tengo/v2/token"
- )
- // NoError asserts err is not an error.
- func NoError(t *testing.T, err error, msg ...interface{}) {
- if err != nil {
- failExpectedActual(t, "no error", err, msg...)
- }
- }
- // Error asserts err is an error.
- func Error(t *testing.T, err error, msg ...interface{}) {
- if err == nil {
- failExpectedActual(t, "error", err, msg...)
- }
- }
- // Nil asserts v is nil.
- func Nil(t *testing.T, v interface{}, msg ...interface{}) {
- if !isNil(v) {
- failExpectedActual(t, "nil", v, msg...)
- }
- }
- // True asserts v is true.
- func True(t *testing.T, v bool, msg ...interface{}) {
- if !v {
- failExpectedActual(t, "true", v, msg...)
- }
- }
- // False asserts vis false.
- func False(t *testing.T, v bool, msg ...interface{}) {
- if v {
- failExpectedActual(t, "false", v, msg...)
- }
- }
- // NotNil asserts v is not nil.
- func NotNil(t *testing.T, v interface{}, msg ...interface{}) {
- if isNil(v) {
- failExpectedActual(t, "not nil", v, msg...)
- }
- }
- // IsType asserts expected and actual are of the same type.
- func IsType(
- t *testing.T,
- expected, actual interface{},
- msg ...interface{},
- ) {
- if reflect.TypeOf(expected) != reflect.TypeOf(actual) {
- failExpectedActual(t, reflect.TypeOf(expected),
- reflect.TypeOf(actual), msg...)
- }
- }
- // Equal asserts expected and actual are equal.
- func Equal(
- t *testing.T,
- expected, actual interface{},
- msg ...interface{},
- ) {
- if isNil(expected) {
- Nil(t, actual, "expected nil, but got not nil")
- return
- }
- NotNil(t, actual, "expected not nil, but got nil")
- IsType(t, expected, actual, msg...)
- switch expected := expected.(type) {
- case int:
- if expected != actual.(int) {
- failExpectedActual(t, expected, actual, msg...)
- }
- case int64:
- if expected != actual.(int64) {
- failExpectedActual(t, expected, actual, msg...)
- }
- case float64:
- if expected != actual.(float64) {
- failExpectedActual(t, expected, actual, msg...)
- }
- case string:
- if expected != actual.(string) {
- failExpectedActual(t, expected, actual, msg...)
- }
- case []byte:
- if !bytes.Equal(expected, actual.([]byte)) {
- failExpectedActual(t, string(expected),
- string(actual.([]byte)), msg...)
- }
- case []string:
- if !equalStringSlice(expected, actual.([]string)) {
- failExpectedActual(t, expected, actual, msg...)
- }
- case []int:
- if !equalIntSlice(expected, actual.([]int)) {
- failExpectedActual(t, expected, actual, msg...)
- }
- case bool:
- if expected != actual.(bool) {
- failExpectedActual(t, expected, actual, msg...)
- }
- case rune:
- if expected != actual.(rune) {
- failExpectedActual(t, expected, actual, msg...)
- }
- case *tengo.Symbol:
- if !equalSymbol(expected, actual.(*tengo.Symbol)) {
- failExpectedActual(t, expected, actual, msg...)
- }
- case parser.Pos:
- if expected != actual.(parser.Pos) {
- failExpectedActual(t, expected, actual, msg...)
- }
- case token.Token:
- if expected != actual.(token.Token) {
- failExpectedActual(t, expected, actual, msg...)
- }
- case []tengo.Object:
- equalObjectSlice(t, expected, actual.([]tengo.Object), msg...)
- case *tengo.Int:
- Equal(t, expected.Value, actual.(*tengo.Int).Value, msg...)
- case *tengo.Float:
- Equal(t, expected.Value, actual.(*tengo.Float).Value, msg...)
- case *tengo.String:
- Equal(t, expected.Value, actual.(*tengo.String).Value, msg...)
- case *tengo.Char:
- Equal(t, expected.Value, actual.(*tengo.Char).Value, msg...)
- case *tengo.Bool:
- if expected != actual {
- failExpectedActual(t, expected, actual, msg...)
- }
- case *tengo.Array:
- equalObjectSlice(t, expected.Value,
- actual.(*tengo.Array).Value, msg...)
- case *tengo.ImmutableArray:
- equalObjectSlice(t, expected.Value,
- actual.(*tengo.ImmutableArray).Value, msg...)
- case *tengo.Bytes:
- if !bytes.Equal(expected.Value, actual.(*tengo.Bytes).Value) {
- failExpectedActual(t, string(expected.Value),
- string(actual.(*tengo.Bytes).Value), msg...)
- }
- case *tengo.Map:
- equalObjectMap(t, expected.Value,
- actual.(*tengo.Map).Value, msg...)
- case *tengo.ImmutableMap:
- equalObjectMap(t, expected.Value,
- actual.(*tengo.ImmutableMap).Value, msg...)
- case *tengo.CompiledFunction:
- equalCompiledFunction(t, expected,
- actual.(*tengo.CompiledFunction), msg...)
- case *tengo.Undefined:
- if expected != actual {
- failExpectedActual(t, expected, actual, msg...)
- }
- case *tengo.Error:
- Equal(t, expected.Value, actual.(*tengo.Error).Value, msg...)
- case tengo.Object:
- if !expected.Equals(actual.(tengo.Object)) {
- failExpectedActual(t, expected, actual, msg...)
- }
- case *parser.SourceFileSet:
- equalFileSet(t, expected, actual.(*parser.SourceFileSet), msg...)
- case *parser.SourceFile:
- Equal(t, expected.Name, actual.(*parser.SourceFile).Name, msg...)
- Equal(t, expected.Base, actual.(*parser.SourceFile).Base, msg...)
- Equal(t, expected.Size, actual.(*parser.SourceFile).Size, msg...)
- True(t, equalIntSlice(expected.Lines,
- actual.(*parser.SourceFile).Lines), msg...)
- case error:
- if expected != actual.(error) {
- failExpectedActual(t, expected, actual, msg...)
- }
- default:
- panic(fmt.Errorf("type not implemented: %T", expected))
- }
- }
- // Fail marks the function as having failed but continues execution.
- func Fail(t *testing.T, msg ...interface{}) {
- t.Logf("\nError trace:\n\t%s\n%s", strings.Join(errorTrace(), "\n\t"),
- message(msg...))
- t.Fail()
- }
- func failExpectedActual(
- t *testing.T,
- expected, actual interface{},
- msg ...interface{},
- ) {
- 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.FailNow()
- }
- 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 equalStringSlice(a, b []string) 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 *tengo.Symbol) bool {
- return a.Name == b.Name &&
- a.Index == b.Index &&
- a.Scope == b.Scope
- }
- func equalObjectSlice(
- t *testing.T,
- expected, actual []tengo.Object,
- msg ...interface{},
- ) {
- Equal(t, len(expected), len(actual), msg...)
- for i := 0; i < len(expected); i++ {
- Equal(t, expected[i], actual[i], msg...)
- }
- }
- func equalFileSet(
- t *testing.T,
- expected, actual *parser.SourceFileSet,
- msg ...interface{},
- ) {
- Equal(t, len(expected.Files), len(actual.Files), msg...)
- for i, f := range expected.Files {
- Equal(t, f, actual.Files[i], msg...)
- }
- Equal(t, expected.Base, actual.Base)
- Equal(t, expected.LastFile, actual.LastFile)
- }
- func equalObjectMap(
- t *testing.T,
- expected, actual map[string]tengo.Object,
- msg ...interface{},
- ) {
- Equal(t, len(expected), len(actual), msg...)
- for key, expectedVal := range expected {
- actualVal := actual[key]
- Equal(t, expectedVal, actualVal, msg...)
- }
- }
- func equalCompiledFunction(
- t *testing.T,
- expected, actual tengo.Object,
- msg ...interface{},
- ) {
- expectedT := expected.(*tengo.CompiledFunction)
- actualT := actual.(*tengo.CompiledFunction)
- Equal(t,
- tengo.FormatInstructions(expectedT.Instructions, 0),
- tengo.FormatInstructions(actualT.Instructions, 0), msg...)
- }
- func isNil(v interface{}) bool {
- if v == nil {
- return true
- }
- value := reflect.ValueOf(v)
- kind := value.Kind()
- return kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil()
- }
- func errorTrace() []string {
- var pc uintptr
- file := ""
- line := 0
- var ok bool
- name := ""
- var callers []string
- for i := 0; ; i++ {
- pc, file, line, ok = runtime.Caller(i)
- if !ok {
- break
- }
- if file == "<autogenerated>" {
- break
- }
- f := runtime.FuncForPC(pc)
- if f == nil {
- break
- }
- name = f.Name()
- if name == "testing.tRunner" {
- break
- }
- parts := strings.Split(file, "/")
- file = parts[len(parts)-1]
- if len(parts) > 1 {
- dir := parts[len(parts)-2]
- if dir != "require" ||
- file == "mock_test.go" {
- callers = append(callers, fmt.Sprintf("%s:%d", file, line))
- }
- }
- // Drop the package
- segments := strings.Split(name, ".")
- name = segments[len(segments)-1]
- if isTest(name, "Test") ||
- isTest(name, "Benchmark") ||
- isTest(name, "Example") {
- break
- }
- }
- return callers
- }
- func isTest(name, prefix string) bool {
- if !strings.HasPrefix(name, prefix) {
- return false
- }
- if len(name) == len(prefix) { // "Test" is ok
- return true
- }
- r, _ := utf8.DecodeRuneInString(name[len(prefix):])
- return !unicode.IsLower(r)
- }
|