| package assert |
| |
| import ( |
| "bufio" |
| "bytes" |
| "encoding/json" |
| "fmt" |
| "math" |
| "reflect" |
| "regexp" |
| "runtime" |
| "strings" |
| "time" |
| "unicode" |
| "unicode/utf8" |
| |
| "github.com/davecgh/go-spew/spew" |
| "github.com/pmezard/go-difflib/difflib" |
| ) |
| |
| // TestingT is an interface wrapper around *testing.T |
| type TestingT interface { |
| Errorf(format string, args ...interface{}) |
| } |
| |
| // Comparison a custom function that returns true on success and false on failure |
| type Comparison func() (success bool) |
| |
| /* |
| Helper functions |
| */ |
| |
| // ObjectsAreEqual determines if two objects are considered equal. |
| // |
| // This function does no assertion of any kind. |
| func ObjectsAreEqual(expected, actual interface{}) bool { |
| |
| if expected == nil || actual == nil { |
| return expected == actual |
| } |
| |
| return reflect.DeepEqual(expected, actual) |
| |
| } |
| |
| // ObjectsAreEqualValues gets whether two objects are equal, or if their |
| // values are equal. |
| func ObjectsAreEqualValues(expected, actual interface{}) bool { |
| if ObjectsAreEqual(expected, actual) { |
| return true |
| } |
| |
| actualType := reflect.TypeOf(actual) |
| if actualType == nil { |
| return false |
| } |
| expectedValue := reflect.ValueOf(expected) |
| if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) { |
| // Attempt comparison after type conversion |
| return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual) |
| } |
| |
| return false |
| } |
| |
| /* CallerInfo is necessary because the assert functions use the testing object |
| internally, causing it to print the file:line of the assert method, rather than where |
| the problem actually occured in calling code.*/ |
| |
| // CallerInfo returns an array of strings containing the file and line number |
| // of each stack frame leading from the current test to the assert call that |
| // failed. |
| func CallerInfo() []string { |
| |
| pc := uintptr(0) |
| file := "" |
| line := 0 |
| ok := false |
| name := "" |
| |
| callers := []string{} |
| for i := 0; ; i++ { |
| pc, file, line, ok = runtime.Caller(i) |
| if !ok { |
| return nil |
| } |
| |
| // This is a huge edge case, but it will panic if this is the case, see #180 |
| if file == "<autogenerated>" { |
| break |
| } |
| |
| parts := strings.Split(file, "/") |
| dir := parts[len(parts)-2] |
| file = parts[len(parts)-1] |
| if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" { |
| callers = append(callers, fmt.Sprintf("%s:%d", file, line)) |
| } |
| |
| f := runtime.FuncForPC(pc) |
| if f == nil { |
| break |
| } |
| name = f.Name() |
| // 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 |
| } |
| |
| // Stolen from the `go test` tool. |
| // isTest tells whether name looks like a test (or benchmark, according to prefix). |
| // It is a Test (say) if there is a character after Test that is not a lower-case letter. |
| // We don't want TesticularCancer. |
| func isTest(name, prefix string) bool { |
| if !strings.HasPrefix(name, prefix) { |
| return false |
| } |
| if len(name) == len(prefix) { // "Test" is ok |
| return true |
| } |
| rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) |
| return !unicode.IsLower(rune) |
| } |
| |
| // getWhitespaceString returns a string that is long enough to overwrite the default |
| // output from the go testing framework. |
| func getWhitespaceString() string { |
| |
| _, file, line, ok := runtime.Caller(1) |
| if !ok { |
| return "" |
| } |
| parts := strings.Split(file, "/") |
| file = parts[len(parts)-1] |
| |
| return strings.Repeat(" ", len(fmt.Sprintf("%s:%d: ", file, line))) |
| |
| } |
| |
| func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { |
| if len(msgAndArgs) == 0 || msgAndArgs == nil { |
| return "" |
| } |
| if len(msgAndArgs) == 1 { |
| return msgAndArgs[0].(string) |
| } |
| if len(msgAndArgs) > 1 { |
| return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) |
| } |
| return "" |
| } |
| |
| // Indents all lines of the message by appending a number of tabs to each line, in an output format compatible with Go's |
| // test printing (see inner comment for specifics) |
| func indentMessageLines(message string, tabs int) string { |
| outBuf := new(bytes.Buffer) |
| |
| for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ { |
| if i != 0 { |
| outBuf.WriteRune('\n') |
| } |
| for ii := 0; ii < tabs; ii++ { |
| outBuf.WriteRune('\t') |
| // Bizarrely, all lines except the first need one fewer tabs prepended, so deliberately advance the counter |
| // by 1 prematurely. |
| if ii == 0 && i > 0 { |
| ii++ |
| } |
| } |
| outBuf.WriteString(scanner.Text()) |
| } |
| |
| return outBuf.String() |
| } |
| |
| type failNower interface { |
| FailNow() |
| } |
| |
| // FailNow fails test |
| func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { |
| Fail(t, failureMessage, msgAndArgs...) |
| |
| // We cannot extend TestingT with FailNow() and |
| // maintain backwards compatibility, so we fallback |
| // to panicking when FailNow is not available in |
| // TestingT. |
| // See issue #263 |
| |
| if t, ok := t.(failNower); ok { |
| t.FailNow() |
| } else { |
| panic("test failed and t is missing `FailNow()`") |
| } |
| return false |
| } |
| |
| // Fail reports a failure through |
| func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { |
| |
| message := messageFromMsgAndArgs(msgAndArgs...) |
| |
| errorTrace := strings.Join(CallerInfo(), "\n\r\t\t\t") |
| if len(message) > 0 { |
| t.Errorf("\r%s\r\tError Trace:\t%s\n"+ |
| "\r\tError:%s\n"+ |
| "\r\tMessages:\t%s\n\r", |
| getWhitespaceString(), |
| errorTrace, |
| indentMessageLines(failureMessage, 2), |
| message) |
| } else { |
| t.Errorf("\r%s\r\tError Trace:\t%s\n"+ |
| "\r\tError:%s\n\r", |
| getWhitespaceString(), |
| errorTrace, |
| indentMessageLines(failureMessage, 2)) |
| } |
| |
| return false |
| } |
| |
| // Implements asserts that an object is implemented by the specified interface. |
| // |
| // assert.Implements(t, (*MyInterface)(nil), new(MyObject), "MyObject") |
| func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { |
| |
| interfaceType := reflect.TypeOf(interfaceObject).Elem() |
| |
| if !reflect.TypeOf(object).Implements(interfaceType) { |
| return Fail(t, fmt.Sprintf("%T must implement %v", object, interfaceType), msgAndArgs...) |
| } |
| |
| return true |
| |
| } |
| |
| // IsType asserts that the specified objects are of the same type. |
| func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { |
| |
| if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) { |
| return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...) |
| } |
| |
| return true |
| } |
| |
| // Equal asserts that two objects are equal. |
| // |
| // assert.Equal(t, 123, 123, "123 and 123 should be equal") |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { |
| |
| if !ObjectsAreEqual(expected, actual) { |
| diff := diff(expected, actual) |
| return Fail(t, fmt.Sprintf("Not equal: %#v (expected)\n"+ |
| " != %#v (actual)%s", expected, actual, diff), msgAndArgs...) |
| } |
| |
| return true |
| |
| } |
| |
| // EqualValues asserts that two objects are equal or convertable to the same types |
| // and equal. |
| // |
| // assert.EqualValues(t, uint32(123), int32(123), "123 and 123 should be equal") |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { |
| |
| if !ObjectsAreEqualValues(expected, actual) { |
| return Fail(t, fmt.Sprintf("Not equal: %#v (expected)\n"+ |
| " != %#v (actual)", expected, actual), msgAndArgs...) |
| } |
| |
| return true |
| |
| } |
| |
| // Exactly asserts that two objects are equal is value and type. |
| // |
| // assert.Exactly(t, int32(123), int64(123), "123 and 123 should NOT be equal") |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { |
| |
| aType := reflect.TypeOf(expected) |
| bType := reflect.TypeOf(actual) |
| |
| if aType != bType { |
| return Fail(t, fmt.Sprintf("Types expected to match exactly\n\r\t%v != %v", aType, bType), msgAndArgs...) |
| } |
| |
| return Equal(t, expected, actual, msgAndArgs...) |
| |
| } |
| |
| // NotNil asserts that the specified object is not nil. |
| // |
| // assert.NotNil(t, err, "err should be something") |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { |
| if !isNil(object) { |
| return true |
| } |
| return Fail(t, "Expected value not to be nil.", msgAndArgs...) |
| } |
| |
| // isNil checks if a specified object is nil or not, without Failing. |
| func isNil(object interface{}) bool { |
| if object == nil { |
| return true |
| } |
| |
| value := reflect.ValueOf(object) |
| kind := value.Kind() |
| if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() { |
| return true |
| } |
| |
| return false |
| } |
| |
| // Nil asserts that the specified object is nil. |
| // |
| // assert.Nil(t, err, "err should be nothing") |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { |
| if isNil(object) { |
| return true |
| } |
| return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) |
| } |
| |
| var numericZeros = []interface{}{ |
| int(0), |
| int8(0), |
| int16(0), |
| int32(0), |
| int64(0), |
| uint(0), |
| uint8(0), |
| uint16(0), |
| uint32(0), |
| uint64(0), |
| float32(0), |
| float64(0), |
| } |
| |
| // isEmpty gets whether the specified object is considered empty or not. |
| func isEmpty(object interface{}) bool { |
| |
| if object == nil { |
| return true |
| } else if object == "" { |
| return true |
| } else if object == false { |
| return true |
| } |
| |
| for _, v := range numericZeros { |
| if object == v { |
| return true |
| } |
| } |
| |
| objValue := reflect.ValueOf(object) |
| |
| switch objValue.Kind() { |
| case reflect.Map: |
| fallthrough |
| case reflect.Slice, reflect.Chan: |
| { |
| return (objValue.Len() == 0) |
| } |
| case reflect.Struct: |
| switch object.(type) { |
| case time.Time: |
| return object.(time.Time).IsZero() |
| } |
| case reflect.Ptr: |
| { |
| if objValue.IsNil() { |
| return true |
| } |
| switch object.(type) { |
| case *time.Time: |
| return object.(*time.Time).IsZero() |
| default: |
| return false |
| } |
| } |
| } |
| return false |
| } |
| |
| // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either |
| // a slice or a channel with len == 0. |
| // |
| // assert.Empty(t, obj) |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { |
| |
| pass := isEmpty(object) |
| if !pass { |
| Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...) |
| } |
| |
| return pass |
| |
| } |
| |
| // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either |
| // a slice or a channel with len == 0. |
| // |
| // if assert.NotEmpty(t, obj) { |
| // assert.Equal(t, "two", obj[1]) |
| // } |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { |
| |
| pass := !isEmpty(object) |
| if !pass { |
| Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...) |
| } |
| |
| return pass |
| |
| } |
| |
| // getLen try to get length of object. |
| // return (false, 0) if impossible. |
| func getLen(x interface{}) (ok bool, length int) { |
| v := reflect.ValueOf(x) |
| defer func() { |
| if e := recover(); e != nil { |
| ok = false |
| } |
| }() |
| return true, v.Len() |
| } |
| |
| // Len asserts that the specified object has specific length. |
| // Len also fails if the object has a type that len() not accept. |
| // |
| // assert.Len(t, mySlice, 3, "The size of slice is not 3") |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool { |
| ok, l := getLen(object) |
| if !ok { |
| return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", object), msgAndArgs...) |
| } |
| |
| if l != length { |
| return Fail(t, fmt.Sprintf("\"%s\" should have %d item(s), but has %d", object, length, l), msgAndArgs...) |
| } |
| return true |
| } |
| |
| // True asserts that the specified value is true. |
| // |
| // assert.True(t, myBool, "myBool should be true") |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { |
| |
| if value != true { |
| return Fail(t, "Should be true", msgAndArgs...) |
| } |
| |
| return true |
| |
| } |
| |
| // False asserts that the specified value is false. |
| // |
| // assert.False(t, myBool, "myBool should be false") |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { |
| |
| if value != false { |
| return Fail(t, "Should be false", msgAndArgs...) |
| } |
| |
| return true |
| |
| } |
| |
| // NotEqual asserts that the specified values are NOT equal. |
| // |
| // assert.NotEqual(t, obj1, obj2, "two objects shouldn't be equal") |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { |
| |
| if ObjectsAreEqual(expected, actual) { |
| return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) |
| } |
| |
| return true |
| |
| } |
| |
| // containsElement try loop over the list check if the list includes the element. |
| // return (false, false) if impossible. |
| // return (true, false) if element was not found. |
| // return (true, true) if element was found. |
| func includeElement(list interface{}, element interface{}) (ok, found bool) { |
| |
| listValue := reflect.ValueOf(list) |
| elementValue := reflect.ValueOf(element) |
| defer func() { |
| if e := recover(); e != nil { |
| ok = false |
| found = false |
| } |
| }() |
| |
| if reflect.TypeOf(list).Kind() == reflect.String { |
| return true, strings.Contains(listValue.String(), elementValue.String()) |
| } |
| |
| if reflect.TypeOf(list).Kind() == reflect.Map { |
| mapKeys := listValue.MapKeys() |
| for i := 0; i < len(mapKeys); i++ { |
| if ObjectsAreEqual(mapKeys[i].Interface(), element) { |
| return true, true |
| } |
| } |
| return true, false |
| } |
| |
| for i := 0; i < listValue.Len(); i++ { |
| if ObjectsAreEqual(listValue.Index(i).Interface(), element) { |
| return true, true |
| } |
| } |
| return true, false |
| |
| } |
| |
| // Contains asserts that the specified string, list(array, slice...) or map contains the |
| // specified substring or element. |
| // |
| // assert.Contains(t, "Hello World", "World", "But 'Hello World' does contain 'World'") |
| // assert.Contains(t, ["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'") |
| // assert.Contains(t, {"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'") |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { |
| |
| ok, found := includeElement(s, contains) |
| if !ok { |
| return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) |
| } |
| if !found { |
| return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", s, contains), msgAndArgs...) |
| } |
| |
| return true |
| |
| } |
| |
| // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the |
| // specified substring or element. |
| // |
| // assert.NotContains(t, "Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'") |
| // assert.NotContains(t, ["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'") |
| // assert.NotContains(t, {"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'") |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { |
| |
| ok, found := includeElement(s, contains) |
| if !ok { |
| return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) |
| } |
| if found { |
| return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...) |
| } |
| |
| return true |
| |
| } |
| |
| // Condition uses a Comparison to assert a complex condition. |
| func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { |
| result := comp() |
| if !result { |
| Fail(t, "Condition failed!", msgAndArgs...) |
| } |
| return result |
| } |
| |
| // PanicTestFunc defines a func that should be passed to the assert.Panics and assert.NotPanics |
| // methods, and represents a simple func that takes no arguments, and returns nothing. |
| type PanicTestFunc func() |
| |
| // didPanic returns true if the function passed to it panics. Otherwise, it returns false. |
| func didPanic(f PanicTestFunc) (bool, interface{}) { |
| |
| didPanic := false |
| var message interface{} |
| func() { |
| |
| defer func() { |
| if message = recover(); message != nil { |
| didPanic = true |
| } |
| }() |
| |
| // call the target function |
| f() |
| |
| }() |
| |
| return didPanic, message |
| |
| } |
| |
| // Panics asserts that the code inside the specified PanicTestFunc panics. |
| // |
| // assert.Panics(t, func(){ |
| // GoCrazy() |
| // }, "Calling GoCrazy() should panic") |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { |
| |
| if funcDidPanic, panicValue := didPanic(f); !funcDidPanic { |
| return Fail(t, fmt.Sprintf("func %#v should panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...) |
| } |
| |
| return true |
| } |
| |
| // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. |
| // |
| // assert.NotPanics(t, func(){ |
| // RemainCalm() |
| // }, "Calling RemainCalm() should NOT panic") |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { |
| |
| if funcDidPanic, panicValue := didPanic(f); funcDidPanic { |
| return Fail(t, fmt.Sprintf("func %#v should not panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...) |
| } |
| |
| return true |
| } |
| |
| // WithinDuration asserts that the two times are within duration delta of each other. |
| // |
| // assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s") |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { |
| |
| dt := expected.Sub(actual) |
| if dt < -delta || dt > delta { |
| return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) |
| } |
| |
| return true |
| } |
| |
| func toFloat(x interface{}) (float64, bool) { |
| var xf float64 |
| xok := true |
| |
| switch xn := x.(type) { |
| case uint8: |
| xf = float64(xn) |
| case uint16: |
| xf = float64(xn) |
| case uint32: |
| xf = float64(xn) |
| case uint64: |
| xf = float64(xn) |
| case int: |
| xf = float64(xn) |
| case int8: |
| xf = float64(xn) |
| case int16: |
| xf = float64(xn) |
| case int32: |
| xf = float64(xn) |
| case int64: |
| xf = float64(xn) |
| case float32: |
| xf = float64(xn) |
| case float64: |
| xf = float64(xn) |
| default: |
| xok = false |
| } |
| |
| return xf, xok |
| } |
| |
| // InDelta asserts that the two numerals are within delta of each other. |
| // |
| // assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { |
| |
| af, aok := toFloat(expected) |
| bf, bok := toFloat(actual) |
| |
| if !aok || !bok { |
| return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...) |
| } |
| |
| if math.IsNaN(af) { |
| return Fail(t, fmt.Sprintf("Actual must not be NaN"), msgAndArgs...) |
| } |
| |
| if math.IsNaN(bf) { |
| return Fail(t, fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), msgAndArgs...) |
| } |
| |
| dt := af - bf |
| if dt < -delta || dt > delta { |
| return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) |
| } |
| |
| return true |
| } |
| |
| // InDeltaSlice is the same as InDelta, except it compares two slices. |
| func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { |
| if expected == nil || actual == nil || |
| reflect.TypeOf(actual).Kind() != reflect.Slice || |
| reflect.TypeOf(expected).Kind() != reflect.Slice { |
| return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) |
| } |
| |
| actualSlice := reflect.ValueOf(actual) |
| expectedSlice := reflect.ValueOf(expected) |
| |
| for i := 0; i < actualSlice.Len(); i++ { |
| result := InDelta(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta) |
| if !result { |
| return result |
| } |
| } |
| |
| return true |
| } |
| |
| func calcRelativeError(expected, actual interface{}) (float64, error) { |
| af, aok := toFloat(expected) |
| if !aok { |
| return 0, fmt.Errorf("expected value %q cannot be converted to float", expected) |
| } |
| if af == 0 { |
| return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") |
| } |
| bf, bok := toFloat(actual) |
| if !bok { |
| return 0, fmt.Errorf("expected value %q cannot be converted to float", actual) |
| } |
| |
| return math.Abs(af-bf) / math.Abs(af), nil |
| } |
| |
| // InEpsilon asserts that expected and actual have a relative error less than epsilon |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { |
| actualEpsilon, err := calcRelativeError(expected, actual) |
| if err != nil { |
| return Fail(t, err.Error(), msgAndArgs...) |
| } |
| if actualEpsilon > epsilon { |
| return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ |
| " < %#v (actual)", actualEpsilon, epsilon), msgAndArgs...) |
| } |
| |
| return true |
| } |
| |
| // InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. |
| func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { |
| if expected == nil || actual == nil || |
| reflect.TypeOf(actual).Kind() != reflect.Slice || |
| reflect.TypeOf(expected).Kind() != reflect.Slice { |
| return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) |
| } |
| |
| actualSlice := reflect.ValueOf(actual) |
| expectedSlice := reflect.ValueOf(expected) |
| |
| for i := 0; i < actualSlice.Len(); i++ { |
| result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), epsilon) |
| if !result { |
| return result |
| } |
| } |
| |
| return true |
| } |
| |
| /* |
| Errors |
| */ |
| |
| // NoError asserts that a function returned no error (i.e. `nil`). |
| // |
| // actualObj, err := SomeFunction() |
| // if assert.NoError(t, err) { |
| // assert.Equal(t, actualObj, expectedObj) |
| // } |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { |
| if isNil(err) { |
| return true |
| } |
| |
| return Fail(t, fmt.Sprintf("Received unexpected error %q", err), msgAndArgs...) |
| } |
| |
| // Error asserts that a function returned an error (i.e. not `nil`). |
| // |
| // actualObj, err := SomeFunction() |
| // if assert.Error(t, err, "An error was expected") { |
| // assert.Equal(t, err, expectedError) |
| // } |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { |
| |
| message := messageFromMsgAndArgs(msgAndArgs...) |
| return NotNil(t, err, "An error is expected but got nil. %s", message) |
| |
| } |
| |
| // EqualError asserts that a function returned an error (i.e. not `nil`) |
| // and that it is equal to the provided error. |
| // |
| // actualObj, err := SomeFunction() |
| // if assert.Error(t, err, "An error was expected") { |
| // assert.Equal(t, err, expectedError) |
| // } |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool { |
| |
| message := messageFromMsgAndArgs(msgAndArgs...) |
| if !NotNil(t, theError, "An error is expected but got nil. %s", message) { |
| return false |
| } |
| s := "An error with value \"%s\" is expected but got \"%s\". %s" |
| return Equal(t, errString, theError.Error(), |
| s, errString, theError.Error(), message) |
| } |
| |
| // matchRegexp return true if a specified regexp matches a string. |
| func matchRegexp(rx interface{}, str interface{}) bool { |
| |
| var r *regexp.Regexp |
| if rr, ok := rx.(*regexp.Regexp); ok { |
| r = rr |
| } else { |
| r = regexp.MustCompile(fmt.Sprint(rx)) |
| } |
| |
| return (r.FindStringIndex(fmt.Sprint(str)) != nil) |
| |
| } |
| |
| // Regexp asserts that a specified regexp matches a string. |
| // |
| // assert.Regexp(t, regexp.MustCompile("start"), "it's starting") |
| // assert.Regexp(t, "start...$", "it's not starting") |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { |
| |
| match := matchRegexp(rx, str) |
| |
| if !match { |
| Fail(t, fmt.Sprintf("Expect \"%v\" to match \"%v\"", str, rx), msgAndArgs...) |
| } |
| |
| return match |
| } |
| |
| // NotRegexp asserts that a specified regexp does not match a string. |
| // |
| // assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") |
| // assert.NotRegexp(t, "^start", "it's not starting") |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { |
| match := matchRegexp(rx, str) |
| |
| if match { |
| Fail(t, fmt.Sprintf("Expect \"%v\" to NOT match \"%v\"", str, rx), msgAndArgs...) |
| } |
| |
| return !match |
| |
| } |
| |
| // Zero asserts that i is the zero value for its type and returns the truth. |
| func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { |
| if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { |
| return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...) |
| } |
| return true |
| } |
| |
| // NotZero asserts that i is not the zero value for its type and returns the truth. |
| func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { |
| if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { |
| return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...) |
| } |
| return true |
| } |
| |
| // JSONEq asserts that two JSON strings are equivalent. |
| // |
| // assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) |
| // |
| // Returns whether the assertion was successful (true) or not (false). |
| func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { |
| var expectedJSONAsInterface, actualJSONAsInterface interface{} |
| |
| if err := json.Unmarshal([]byte(expected), &expectedJSONAsInterface); err != nil { |
| return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...) |
| } |
| |
| if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil { |
| return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...) |
| } |
| |
| return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...) |
| } |
| |
| func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { |
| t := reflect.TypeOf(v) |
| k := t.Kind() |
| |
| if k == reflect.Ptr { |
| t = t.Elem() |
| k = t.Kind() |
| } |
| return t, k |
| } |
| |
| // diff returns a diff of both values as long as both are of the same type and |
| // are a struct, map, slice or array. Otherwise it returns an empty string. |
| func diff(expected interface{}, actual interface{}) string { |
| if expected == nil || actual == nil { |
| return "" |
| } |
| |
| et, ek := typeAndKind(expected) |
| at, _ := typeAndKind(actual) |
| |
| if et != at { |
| return "" |
| } |
| |
| if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array { |
| return "" |
| } |
| |
| spew.Config.SortKeys = true |
| e := spew.Sdump(expected) |
| a := spew.Sdump(actual) |
| |
| diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ |
| A: difflib.SplitLines(e), |
| B: difflib.SplitLines(a), |
| FromFile: "Expected", |
| FromDate: "", |
| ToFile: "Actual", |
| ToDate: "", |
| Context: 1, |
| }) |
| |
| return "\n\nDiff:\n" + diff |
| } |