status-go/vendor/github.com/robertkrimen/otto/terst/terst.go

670 lines
15 KiB
Go
Raw Normal View History

// This file was AUTOMATICALLY GENERATED by terst-import (smuggol) from github.com/robertkrimen/terst
/*
Package terst is a terse (terst = test + terse), easy-to-use testing library for Go.
terst is compatible with (and works via) the standard testing package: http://golang.org/pkg/testing
var is = terst.Is
func Test(t *testing.T) {
terst.Terst(t, func() {
is("abc", "abc")
is(1, ">", 0)
var abc []int
is(abc, nil)
}
}
Do not import terst directly, instead use `terst-import` to copy it into your testing environment:
https://github.com/robertkrimen/terst/tree/master/terst-import
$ go get github.com/robertkrimen/terst/terst-import
$ terst-import
*/
package terst
import (
"bytes"
"errors"
"fmt"
"math/big"
"reflect"
"regexp"
"runtime"
"strings"
"sync"
"testing"
"time"
)
// Is compares two values (got & expect) and returns true if the comparison is true,
// false otherwise. In addition, if the comparison is false, Is will report the error
// in a manner similar to testing.T.Error(...). Is also takes an optional argument,
// a comparator, that changes how the comparison is made. The following
// comparators are available:
//
// == # got == expect (default)
// != # got != expect
//
// > # got > expect (float32, uint, uint16, int, int64, ...)
// >= # got >= expect
// < # got < expect
// <= # got <= expect
//
// =~ # regexp.MustCompile(expect).Match{String}(got)
// !~ # !regexp.MustCompile(expect).Match{String}(got)
//
// Basic usage with the default comparator (==):
//
// Is(<got>, <expect>)
//
// Specifying a different comparator:
//
// Is(<got>, <comparator>, <expect>)
//
// A simple comparison:
//
// Is(2 + 2, 4)
//
// A bit trickier:
//
// Is(1, ">", 0)
// Is(2 + 2, "!=", 5)
// Is("Nothing happens.", "=~", `ing(\s+)happens\.$`)
//
// Is should only be called under a Terst(t, ...) call. For a standalone version,
// use IsErr. If no scope is found and the comparison is false, then Is will panic the error.
//
func Is(arguments ...interface{}) bool {
err := IsErr(arguments...)
if err != nil {
call := Caller()
if call == nil {
panic(err)
}
call.Error(err)
return false
}
return true
}
type (
// ErrFail indicates a comparison failure (e.g. 0 > 1).
ErrFail error
// ErrInvalid indicates an invalid comparison (e.g. bool == string).
ErrInvalid error
)
var errInvalid = errors.New("invalid")
var registry = struct {
table map[uintptr]*_scope
lock sync.RWMutex
}{
table: map[uintptr]*_scope{},
}
func registerScope(pc uintptr, scope *_scope) {
registry.lock.Lock()
defer registry.lock.Unlock()
registry.table[pc] = scope
}
func scope() *_scope {
scope, _ := findScope()
return scope
}
func floatCompare(a float64, b float64) int {
if a > b {
return 1
} else if a < b {
return -1
}
// NaN == NaN
return 0
}
func bigIntCompare(a *big.Int, b *big.Int) int {
return a.Cmp(b)
}
func bigInt(value int64) *big.Int {
return big.NewInt(value)
}
func bigUint(value uint64) *big.Int {
return big.NewInt(0).SetUint64(value)
}
type _toString interface {
String() string
}
func toString(value interface{}) (string, error) {
switch value := value.(type) {
case string:
return value, nil
case _toString:
return value.String(), nil
case error:
return value.Error(), nil
}
return "", errInvalid
}
func matchString(got string, expect *regexp.Regexp) (int, error) {
if expect.MatchString(got) {
return 0, nil
}
return -1, nil
}
func match(got []byte, expect *regexp.Regexp) (int, error) {
if expect.Match(got) {
return 0, nil
}
return -1, nil
}
func compareMatch(got, expect interface{}) (int, error) {
switch got := got.(type) {
case []byte:
switch expect := expect.(type) {
case string:
matcher, err := regexp.Compile(expect)
if err != nil {
return 0, err
}
return match(got, matcher)
case *regexp.Regexp:
return match(got, expect)
}
default:
if got, err := toString(got); err == nil {
switch expect := expect.(type) {
case string:
matcher, err := regexp.Compile(expect)
if err != nil {
return 0, err
}
return matchString(got, matcher)
case *regexp.Regexp:
return matchString(got, expect)
}
} else {
return 0, err
}
}
return 0, errInvalid
}
func floatPromote(value reflect.Value) (float64, error) {
kind := value.Kind()
if reflect.Int <= kind && kind <= reflect.Int64 {
return float64(value.Int()), nil
}
if reflect.Uint <= kind && kind <= reflect.Uint64 {
return float64(value.Uint()), nil
}
if reflect.Float32 <= kind && kind <= reflect.Float64 {
return value.Float(), nil
}
return 0, errInvalid
}
func bigIntPromote(value reflect.Value) (*big.Int, error) {
kind := value.Kind()
if reflect.Int <= kind && kind <= reflect.Int64 {
return bigInt(value.Int()), nil
}
if reflect.Uint <= kind && kind <= reflect.Uint64 {
return bigUint(value.Uint()), nil
}
return nil, errInvalid
}
func compareOther(got, expect interface{}) (int, error) {
{
switch expect.(type) {
case float32, float64:
return compareNumber(got, expect)
case uint, uint8, uint16, uint32, uint64:
return compareNumber(got, expect)
case int, int8, int16, int32, int64:
return compareNumber(got, expect)
case string:
var err error
got, err = toString(got)
if err != nil {
return 0, err
}
case nil:
got := reflect.ValueOf(got)
switch got.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface:
if got.IsNil() {
return 0, nil
}
return -1, nil
case reflect.Invalid: // reflect.Invalid: var abc interface{} = nil
return 0, nil
}
return 0, errInvalid
}
}
if reflect.ValueOf(got).Type() != reflect.ValueOf(expect).Type() {
return 0, errInvalid
}
if reflect.DeepEqual(got, expect) {
return 0, nil
}
return -1, nil
}
func compareNumber(got, expect interface{}) (int, error) {
{
got := reflect.ValueOf(got)
k0 := got.Kind()
expect := reflect.ValueOf(expect)
k1 := expect.Kind()
if reflect.Float32 <= k0 && k0 <= reflect.Float64 ||
reflect.Float32 <= k1 && k1 <= reflect.Float64 {
got, err := floatPromote(got)
if err != nil {
return 0, err
}
expect, err := floatPromote(expect)
if err != nil {
return 0, err
}
return floatCompare(got, expect), nil
} else {
got, err := bigIntPromote(got)
if err != nil {
return 0, err
}
expect, err := bigIntPromote(expect)
if err != nil {
return 0, err
}
return got.Cmp(expect), nil
}
}
return 0, errInvalid
}
// IsErr compares two values (got & expect) and returns nil if the comparison is true, an ErrFail if
// the comparison is false, or an ErrInvalid if the comparison is invalid. IsErr also
// takes an optional argument, a comparator, that changes how the comparison is made.
//
// Is & IsErr are similar but different:
//
// Is(...) // Should only be called within a Terst(...) call
// IsErr(...) // A standalone comparator, the same as Is, just without the automatic reporting
//
func IsErr(arguments ...interface{}) error {
var got, expect interface{}
comparator := "=="
switch len(arguments) {
case 0, 1:
return fmt.Errorf("invalid number of arguments to IsErr: %d", len(arguments))
case 2:
got, expect = arguments[0], arguments[1]
default:
if value, ok := arguments[1].(string); ok {
comparator = value
} else {
return fmt.Errorf("invalid comparator: %v", arguments[1])
}
got, expect = arguments[0], arguments[2]
}
var result int
var err error
switch comparator {
case "<", "<=", ">", ">=":
result, err = compareNumber(got, expect)
case "=~", "!~":
result, err = compareMatch(got, expect)
case "==", "!=":
result, err = compareOther(got, expect)
default:
return fmt.Errorf("invalid comparator: %s", comparator)
}
if err == errInvalid {
return ErrInvalid(fmt.Errorf(
"\nINVALID (%s):\n got: %v (%T)\n expected: %v (%T)",
comparator,
got, got,
expect, expect,
))
} else if err != nil {
return err
}
equality, pass := false, false
switch comparator {
case "==", "=~":
equality = true
pass = result == 0
case "!=", "!~":
equality = true
pass = result != 0
case "<":
pass = result < 0
case "<=":
pass = result <= 0
case ">":
pass = result > 0
case ">=":
pass = result >= 0
}
if !pass {
if equality {
if comparator[1] == '~' {
if value, ok := got.([]byte); ok {
return ErrFail(fmt.Errorf(
"\nFAIL (%s)\n got: %s %v%s\nexpected: %v%s",
comparator,
value, got, typeKindString(got),
expect, typeKindString(expect),
))
}
}
return ErrFail(fmt.Errorf(
"\nFAIL (%s)\n got: %v%s\nexpected: %v%s",
comparator,
got, typeKindString(got),
expect, typeKindString(expect),
))
}
return ErrFail(fmt.Errorf(
"\nFAIL (%s)\n got: %v%s\nexpected: %s %v%s",
comparator,
got, typeKindString(got),
comparator, expect, typeKindString(expect),
))
}
return nil
}
func typeKindString(value interface{}) string {
reflectValue := reflect.ValueOf(value)
kind := reflectValue.Kind().String()
result := fmt.Sprintf("%T", value)
if kind == result {
if kind == "string" {
return ""
}
return fmt.Sprintf(" (%T)", value)
}
return fmt.Sprintf(" (%T=%s)", value, kind)
}
func (scope *_scope) reset() {
scope.name = ""
scope.output = scope.output[:]
scope.start = time.Time{}
scope.duration = 0
}
// Terst creates a testing scope, where Is can be called and errors will be reported
// according to the top-level location of the comparison, and not where the Is call
// actually takes place. For example:
//
// func test(value int) {
// Is(value, 5) // <--- This failure is reported below.
// }
//
// Terst(t, func(){
//
// Is(2, ">", 3) // <--- An error is reported here.
//
// test(5) // <--- An error is reported here.
//
// })
//
func Terst(t *testing.T, arguments ...func()) {
scope := &_scope{
t: t,
}
pc, _, _, ok := runtime.Caller(1) // TODO Associate with the Test... func
if !ok {
panic("Here be dragons.")
}
_, scope.testFunc = findTestFunc()
registerScope(pc, scope)
for _, fn := range arguments {
func() {
scope.reset()
name := scope.testFunc.Name()
index := strings.LastIndex(scope.testFunc.Name(), ".")
if index >= 0 {
name = name[index+1:] + "(Terst)"
} else {
name = "(Terst)"
}
name = "(Terst)"
scope.name = name
scope.start = time.Now()
defer func() {
scope.duration = time.Now().Sub(scope.start)
if err := recover(); err != nil {
scope.t.Fail()
scope.report()
panic(err)
}
scope.report()
}()
fn()
}()
}
}
// From "testing"
func (scope *_scope) report() {
format := "~~~ %s: (Terst)\n%s"
if scope.t.Failed() {
fmt.Printf(format, "FAIL", scope.output)
} else if testing.Verbose() && len(scope.output) > 0 {
fmt.Printf(format, "PASS", scope.output)
}
}
func (scope *_scope) log(call _entry, str string) {
scope.mu.Lock()
defer scope.mu.Unlock()
scope.output = append(scope.output, decorate(call, str)...)
}
// decorate prefixes the string with the file and line of the call site
// and inserts the final newline if needed and indentation tabs for formascing.
func decorate(call _entry, s string) string {
file, line := call.File, call.Line
if call.PC > 0 {
// Truncate file name at last file name separator.
if index := strings.LastIndex(file, "/"); index >= 0 {
file = file[index+1:]
} else if index = strings.LastIndex(file, "\\"); index >= 0 {
file = file[index+1:]
}
} else {
file = "???"
line = 1
}
buf := new(bytes.Buffer)
// Every line is indented at least one tab.
buf.WriteByte('\t')
fmt.Fprintf(buf, "%s:%d: ", file, line)
lines := strings.Split(s, "\n")
if l := len(lines); l > 1 && lines[l-1] == "" {
lines = lines[:l-1]
}
for i, line := range lines {
if i > 0 {
// Second and subsequent lines are indented an extra tab.
buf.WriteString("\n\t\t")
}
buf.WriteString(line)
}
buf.WriteByte('\n')
return buf.String()
}
func findScope() (*_scope, _entry) {
registry.lock.RLock()
defer registry.lock.RUnlock()
table := registry.table
depth := 2 // Starting depth
call := _entry{}
for {
pc, _, _, ok := runtime.Caller(depth)
if !ok {
break
}
if scope, exists := table[pc]; exists {
pc, file, line, _ := runtime.Caller(depth - 3) // Terst(...) + func(){}() + fn() => ???()
call.PC = pc
call.File = file
call.Line = line
return scope, call
}
depth++
}
return nil, _entry{}
}
// Call is a reference to a line immediately under a Terst testing scope.
type Call struct {
scope *_scope
entry _entry
}
// Caller will search the stack, looking for a Terst testing scope. If a scope
// is found, then Caller returns a Call for logging errors, accessing testing.T, etc.
// If no scope is found, Caller returns nil.
func Caller() *Call {
scope, entry := findScope()
if scope == nil {
return nil
}
return &Call{
scope: scope,
entry: entry,
}
}
// TestFunc returns the *runtime.Func entry for the top-level Test...(t testing.T)
// function.
func (cl *Call) TestFunc() *runtime.Func {
return cl.scope.testFunc
}
// T returns the original testing.T passed to Terst(...)
func (cl *Call) T() *testing.T {
return cl.scope.t
}
// Log is the terst version of `testing.T.Log`
func (cl *Call) Log(arguments ...interface{}) {
cl.scope.log(cl.entry, fmt.Sprintln(arguments...))
}
// Logf is the terst version of `testing.T.Logf`
func (cl *Call) Logf(format string, arguments ...interface{}) {
cl.scope.log(cl.entry, fmt.Sprintf(format, arguments...))
}
// Error is the terst version of `testing.T.Error`
func (cl *Call) Error(arguments ...interface{}) {
cl.scope.log(cl.entry, fmt.Sprintln(arguments...))
cl.scope.t.Fail()
}
// Errorf is the terst version of `testing.T.Errorf`
func (cl *Call) Errorf(format string, arguments ...interface{}) {
cl.scope.log(cl.entry, fmt.Sprintf(format, arguments...))
cl.scope.t.Fail()
}
// Skip is the terst version of `testing.T.Skip`
func (cl *Call) Skip(arguments ...interface{}) {
cl.scope.log(cl.entry, fmt.Sprintln(arguments...))
cl.scope.t.SkipNow()
}
// Skipf is the terst version of `testing.T.Skipf`
func (cl *Call) Skipf(format string, arguments ...interface{}) {
cl.scope.log(cl.entry, fmt.Sprintf(format, arguments...))
cl.scope.t.SkipNow()
}
type _scope struct {
t *testing.T
testFunc *runtime.Func
name string
mu sync.RWMutex
output []byte
start time.Time
duration time.Duration
}
type _entry struct {
PC uintptr
File string
Line int
Func *runtime.Func
}
func _findFunc(match string) (_entry, *runtime.Func) {
depth := 2 // Starting depth
for {
pc, file, line, ok := runtime.Caller(depth)
if !ok {
break
}
fn := runtime.FuncForPC(pc)
name := fn.Name()
if index := strings.LastIndex(name, match); index >= 0 {
// Assume we have an instance of TestXyzzy in a _test file
return _entry{
PC: pc,
File: file,
Line: line,
Func: fn,
}, fn
}
depth++
}
return _entry{}, nil
}
func findTestFunc() (_entry, *runtime.Func) {
return _findFunc(".Test")
}
func findTerstFunc() (_entry, *runtime.Func) {
return _findFunc(".Terst")
}