mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-12-23 21:40:20 +00:00
91774fc936
Uses github.com/libopenstorage/secrets to communicate with Vault. This removes the need for maintaining our own limited Vault APIs. By adding the new dependency, several other packages got updated in the process. Unused indirect dependencies have been removed from go.mod. Signed-off-by: Niels de Vos <ndevos@redhat.com>
1627 lines
45 KiB
Go
1627 lines
45 KiB
Go
package assert
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"os"
|
|
"reflect"
|
|
"regexp"
|
|
"runtime"
|
|
"runtime/debug"
|
|
"strings"
|
|
"time"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
|
|
"github.com/davecgh/go-spew/spew"
|
|
"github.com/pmezard/go-difflib/difflib"
|
|
yaml "gopkg.in/yaml.v2"
|
|
)
|
|
|
|
//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl"
|
|
|
|
// TestingT is an interface wrapper around *testing.T
|
|
type TestingT interface {
|
|
Errorf(format string, args ...interface{})
|
|
}
|
|
|
|
// ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful
|
|
// for table driven tests.
|
|
type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) bool
|
|
|
|
// ValueAssertionFunc is a common function prototype when validating a single value. Can be useful
|
|
// for table driven tests.
|
|
type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) bool
|
|
|
|
// BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful
|
|
// for table driven tests.
|
|
type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool
|
|
|
|
// ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful
|
|
// for table driven tests.
|
|
type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool
|
|
|
|
// 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
|
|
}
|
|
|
|
exp, ok := expected.([]byte)
|
|
if !ok {
|
|
return reflect.DeepEqual(expected, actual)
|
|
}
|
|
|
|
act, ok := actual.([]byte)
|
|
if !ok {
|
|
return false
|
|
}
|
|
if exp == nil || act == nil {
|
|
return exp == nil && act == nil
|
|
}
|
|
return bytes.Equal(exp, act)
|
|
}
|
|
|
|
// 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 occurred 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 {
|
|
// The breaks below failed to terminate the loop, and we ran off the
|
|
// end of the call stack.
|
|
break
|
|
}
|
|
|
|
// This is a huge edge case, but it will panic if this is the case, see #180
|
|
if file == "<autogenerated>" {
|
|
break
|
|
}
|
|
|
|
f := runtime.FuncForPC(pc)
|
|
if f == nil {
|
|
break
|
|
}
|
|
name = f.Name()
|
|
|
|
// testing.tRunner is the standard library function that calls
|
|
// tests. Subtests are called directly by tRunner, without going through
|
|
// the Test/Benchmark/Example function that contains the t.Run calls, so
|
|
// with subtests we should break when we hit tRunner, without adding it
|
|
// to the list of callers.
|
|
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 != "assert" && dir != "mock" && 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
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
|
|
if len(msgAndArgs) == 0 || msgAndArgs == nil {
|
|
return ""
|
|
}
|
|
if len(msgAndArgs) == 1 {
|
|
msg := msgAndArgs[0]
|
|
if msgAsStr, ok := msg.(string); ok {
|
|
return msgAsStr
|
|
}
|
|
return fmt.Sprintf("%+v", msg)
|
|
}
|
|
if len(msgAndArgs) > 1 {
|
|
return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// Aligns the provided message so that all lines after the first line start at the same location as the first line.
|
|
// Assumes that the first line starts at the correct location (after carriage return, tab, label, spacer and tab).
|
|
// The longestLabelLen parameter specifies the length of the longest label in the output (required becaues this is the
|
|
// basis on which the alignment occurs).
|
|
func indentMessageLines(message string, longestLabelLen int) string {
|
|
outBuf := new(bytes.Buffer)
|
|
|
|
for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ {
|
|
// no need to align first line because it starts at the correct location (after the label)
|
|
if i != 0 {
|
|
// append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab
|
|
outBuf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t")
|
|
}
|
|
outBuf.WriteString(scanner.Text())
|
|
}
|
|
|
|
return outBuf.String()
|
|
}
|
|
|
|
type failNower interface {
|
|
FailNow()
|
|
}
|
|
|
|
// FailNow fails test
|
|
func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
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 {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
content := []labeledContent{
|
|
{"Error Trace", strings.Join(CallerInfo(), "\n\t\t\t")},
|
|
{"Error", failureMessage},
|
|
}
|
|
|
|
// Add test name if the Go version supports it
|
|
if n, ok := t.(interface {
|
|
Name() string
|
|
}); ok {
|
|
content = append(content, labeledContent{"Test", n.Name()})
|
|
}
|
|
|
|
message := messageFromMsgAndArgs(msgAndArgs...)
|
|
if len(message) > 0 {
|
|
content = append(content, labeledContent{"Messages", message})
|
|
}
|
|
|
|
t.Errorf("\n%s", ""+labeledOutput(content...))
|
|
|
|
return false
|
|
}
|
|
|
|
type labeledContent struct {
|
|
label string
|
|
content string
|
|
}
|
|
|
|
// labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner:
|
|
//
|
|
// \t{{label}}:{{align_spaces}}\t{{content}}\n
|
|
//
|
|
// The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label.
|
|
// If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this
|
|
// alignment is achieved, "\t{{content}}\n" is added for the output.
|
|
//
|
|
// If the content of the labeledOutput contains line breaks, the subsequent lines are aligned so that they start at the same location as the first line.
|
|
func labeledOutput(content ...labeledContent) string {
|
|
longestLabel := 0
|
|
for _, v := range content {
|
|
if len(v.label) > longestLabel {
|
|
longestLabel = len(v.label)
|
|
}
|
|
}
|
|
var output string
|
|
for _, v := range content {
|
|
output += "\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n"
|
|
}
|
|
return output
|
|
}
|
|
|
|
// Implements asserts that an object is implemented by the specified interface.
|
|
//
|
|
// assert.Implements(t, (*MyInterface)(nil), new(MyObject))
|
|
func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
interfaceType := reflect.TypeOf(interfaceObject).Elem()
|
|
|
|
if object == nil {
|
|
return Fail(t, fmt.Sprintf("Cannot check if nil implements %v", interfaceType), msgAndArgs...)
|
|
}
|
|
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 h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
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)
|
|
//
|
|
// Pointer variable equality is determined based on the equality of the
|
|
// referenced values (as opposed to the memory addresses). Function equality
|
|
// cannot be determined and will always fail.
|
|
func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
if err := validateEqualArgs(expected, actual); err != nil {
|
|
return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)",
|
|
expected, actual, err), msgAndArgs...)
|
|
}
|
|
|
|
if !ObjectsAreEqual(expected, actual) {
|
|
diff := diff(expected, actual)
|
|
expected, actual = formatUnequalValues(expected, actual)
|
|
return Fail(t, fmt.Sprintf("Not equal: \n"+
|
|
"expected: %s\n"+
|
|
"actual : %s%s", expected, actual, diff), msgAndArgs...)
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
// validateEqualArgs checks whether provided arguments can be safely used in the
|
|
// Equal/NotEqual functions.
|
|
func validateEqualArgs(expected, actual interface{}) error {
|
|
if expected == nil && actual == nil {
|
|
return nil
|
|
}
|
|
|
|
if isFunction(expected) || isFunction(actual) {
|
|
return errors.New("cannot take func type as argument")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Same asserts that two pointers reference the same object.
|
|
//
|
|
// assert.Same(t, ptr1, ptr2)
|
|
//
|
|
// Both arguments must be pointer variables. Pointer variable sameness is
|
|
// determined based on the equality of both type and value.
|
|
func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
if !samePointers(expected, actual) {
|
|
return Fail(t, fmt.Sprintf("Not same: \n"+
|
|
"expected: %p %#v\n"+
|
|
"actual : %p %#v", expected, expected, actual, actual), msgAndArgs...)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// NotSame asserts that two pointers do not reference the same object.
|
|
//
|
|
// assert.NotSame(t, ptr1, ptr2)
|
|
//
|
|
// Both arguments must be pointer variables. Pointer variable sameness is
|
|
// determined based on the equality of both type and value.
|
|
func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
if samePointers(expected, actual) {
|
|
return Fail(t, fmt.Sprintf(
|
|
"Expected and actual point to the same object: %p %#v",
|
|
expected, expected), msgAndArgs...)
|
|
}
|
|
return true
|
|
}
|
|
|
|
// samePointers compares two generic interface objects and returns whether
|
|
// they point to the same object
|
|
func samePointers(first, second interface{}) bool {
|
|
firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second)
|
|
if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr {
|
|
return false
|
|
}
|
|
|
|
firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second)
|
|
if firstType != secondType {
|
|
return false
|
|
}
|
|
|
|
// compare pointer addresses
|
|
return first == second
|
|
}
|
|
|
|
// formatUnequalValues takes two values of arbitrary types and returns string
|
|
// representations appropriate to be presented to the user.
|
|
//
|
|
// If the values are not of like type, the returned strings will be prefixed
|
|
// with the type name, and the value will be enclosed in parenthesis similar
|
|
// to a type conversion in the Go grammar.
|
|
func formatUnequalValues(expected, actual interface{}) (e string, a string) {
|
|
if reflect.TypeOf(expected) != reflect.TypeOf(actual) {
|
|
return fmt.Sprintf("%T(%#v)", expected, expected),
|
|
fmt.Sprintf("%T(%#v)", actual, actual)
|
|
}
|
|
switch expected.(type) {
|
|
case time.Duration:
|
|
return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual)
|
|
}
|
|
return fmt.Sprintf("%#v", expected), fmt.Sprintf("%#v", actual)
|
|
}
|
|
|
|
// EqualValues asserts that two objects are equal or convertable to the same types
|
|
// and equal.
|
|
//
|
|
// assert.EqualValues(t, uint32(123), int32(123))
|
|
func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
if !ObjectsAreEqualValues(expected, actual) {
|
|
diff := diff(expected, actual)
|
|
expected, actual = formatUnequalValues(expected, actual)
|
|
return Fail(t, fmt.Sprintf("Not equal: \n"+
|
|
"expected: %s\n"+
|
|
"actual : %s%s", expected, actual, diff), msgAndArgs...)
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
// Exactly asserts that two objects are equal in value and type.
|
|
//
|
|
// assert.Exactly(t, int32(123), int64(123))
|
|
func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
aType := reflect.TypeOf(expected)
|
|
bType := reflect.TypeOf(actual)
|
|
|
|
if aType != bType {
|
|
return Fail(t, fmt.Sprintf("Types expected to match exactly\n\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)
|
|
func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
if !isNil(object) {
|
|
return true
|
|
}
|
|
return Fail(t, "Expected value not to be nil.", msgAndArgs...)
|
|
}
|
|
|
|
// containsKind checks if a specified kind in the slice of kinds.
|
|
func containsKind(kinds []reflect.Kind, kind reflect.Kind) bool {
|
|
for i := 0; i < len(kinds); i++ {
|
|
if kind == kinds[i] {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// 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()
|
|
isNilableKind := containsKind(
|
|
[]reflect.Kind{
|
|
reflect.Chan, reflect.Func,
|
|
reflect.Interface, reflect.Map,
|
|
reflect.Ptr, reflect.Slice},
|
|
kind)
|
|
|
|
if isNilableKind && value.IsNil() {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Nil asserts that the specified object is nil.
|
|
//
|
|
// assert.Nil(t, err)
|
|
func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
if isNil(object) {
|
|
return true
|
|
}
|
|
return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...)
|
|
}
|
|
|
|
// isEmpty gets whether the specified object is considered empty or not.
|
|
func isEmpty(object interface{}) bool {
|
|
|
|
// get nil case out of the way
|
|
if object == nil {
|
|
return true
|
|
}
|
|
|
|
objValue := reflect.ValueOf(object)
|
|
|
|
switch objValue.Kind() {
|
|
// collection types are empty when they have no element
|
|
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
|
|
return objValue.Len() == 0
|
|
// pointers are empty if nil or if the value they point to is empty
|
|
case reflect.Ptr:
|
|
if objValue.IsNil() {
|
|
return true
|
|
}
|
|
deref := objValue.Elem().Interface()
|
|
return isEmpty(deref)
|
|
// for all other types, compare against the zero value
|
|
default:
|
|
zero := reflect.Zero(objValue.Type())
|
|
return reflect.DeepEqual(object, zero.Interface())
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
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])
|
|
// }
|
|
func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
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)
|
|
func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
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)
|
|
func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
if h, ok := t.(interface {
|
|
Helper()
|
|
}); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
if value != true {
|
|
return Fail(t, "Should be true", msgAndArgs...)
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
// False asserts that the specified value is false.
|
|
//
|
|
// assert.False(t, myBool)
|
|
func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
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)
|
|
//
|
|
// Pointer variable equality is determined based on the equality of the
|
|
// referenced values (as opposed to the memory addresses).
|
|
func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
if err := validateEqualArgs(expected, actual); err != nil {
|
|
return Fail(t, fmt.Sprintf("Invalid operation: %#v != %#v (%s)",
|
|
expected, actual, err), msgAndArgs...)
|
|
}
|
|
|
|
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)
|
|
listKind := reflect.TypeOf(list).Kind()
|
|
defer func() {
|
|
if e := recover(); e != nil {
|
|
ok = false
|
|
found = false
|
|
}
|
|
}()
|
|
|
|
if listKind == reflect.String {
|
|
elementValue := reflect.ValueOf(element)
|
|
return true, strings.Contains(listValue.String(), elementValue.String())
|
|
}
|
|
|
|
if listKind == 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")
|
|
// assert.Contains(t, ["Hello", "World"], "World")
|
|
// assert.Contains(t, {"Hello": "World"}, "Hello")
|
|
func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
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")
|
|
// assert.NotContains(t, ["Hello", "World"], "Earth")
|
|
// assert.NotContains(t, {"Hello": "World"}, "Earth")
|
|
func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
// Subset asserts that the specified list(array, slice...) contains all
|
|
// elements given in the specified subset(array, slice...).
|
|
//
|
|
// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
|
|
func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
if subset == nil {
|
|
return true // we consider nil to be equal to the nil set
|
|
}
|
|
|
|
subsetValue := reflect.ValueOf(subset)
|
|
defer func() {
|
|
if e := recover(); e != nil {
|
|
ok = false
|
|
}
|
|
}()
|
|
|
|
listKind := reflect.TypeOf(list).Kind()
|
|
subsetKind := reflect.TypeOf(subset).Kind()
|
|
|
|
if listKind != reflect.Array && listKind != reflect.Slice {
|
|
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
|
|
}
|
|
|
|
if subsetKind != reflect.Array && subsetKind != reflect.Slice {
|
|
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
|
|
}
|
|
|
|
for i := 0; i < subsetValue.Len(); i++ {
|
|
element := subsetValue.Index(i).Interface()
|
|
ok, found := includeElement(list, element)
|
|
if !ok {
|
|
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
|
|
}
|
|
if !found {
|
|
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, element), msgAndArgs...)
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// NotSubset asserts that the specified list(array, slice...) contains not all
|
|
// elements given in the specified subset(array, slice...).
|
|
//
|
|
// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
|
|
func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
if subset == nil {
|
|
return Fail(t, fmt.Sprintf("nil is the empty set which is a subset of every set"), msgAndArgs...)
|
|
}
|
|
|
|
subsetValue := reflect.ValueOf(subset)
|
|
defer func() {
|
|
if e := recover(); e != nil {
|
|
ok = false
|
|
}
|
|
}()
|
|
|
|
listKind := reflect.TypeOf(list).Kind()
|
|
subsetKind := reflect.TypeOf(subset).Kind()
|
|
|
|
if listKind != reflect.Array && listKind != reflect.Slice {
|
|
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
|
|
}
|
|
|
|
if subsetKind != reflect.Array && subsetKind != reflect.Slice {
|
|
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
|
|
}
|
|
|
|
for i := 0; i < subsetValue.Len(); i++ {
|
|
element := subsetValue.Index(i).Interface()
|
|
ok, found := includeElement(list, element)
|
|
if !ok {
|
|
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
|
|
}
|
|
if !found {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...)
|
|
}
|
|
|
|
// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified
|
|
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
|
|
// the number of appearances of each of them in both lists should match.
|
|
//
|
|
// assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2])
|
|
func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
if isEmpty(listA) && isEmpty(listB) {
|
|
return true
|
|
}
|
|
|
|
aKind := reflect.TypeOf(listA).Kind()
|
|
bKind := reflect.TypeOf(listB).Kind()
|
|
|
|
if aKind != reflect.Array && aKind != reflect.Slice {
|
|
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listA, aKind), msgAndArgs...)
|
|
}
|
|
|
|
if bKind != reflect.Array && bKind != reflect.Slice {
|
|
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listB, bKind), msgAndArgs...)
|
|
}
|
|
|
|
aValue := reflect.ValueOf(listA)
|
|
bValue := reflect.ValueOf(listB)
|
|
|
|
aLen := aValue.Len()
|
|
bLen := bValue.Len()
|
|
|
|
if aLen != bLen {
|
|
return Fail(t, fmt.Sprintf("lengths don't match: %d != %d", aLen, bLen), msgAndArgs...)
|
|
}
|
|
|
|
// Mark indexes in bValue that we already used
|
|
visited := make([]bool, bLen)
|
|
for i := 0; i < aLen; i++ {
|
|
element := aValue.Index(i).Interface()
|
|
found := false
|
|
for j := 0; j < bLen; j++ {
|
|
if visited[j] {
|
|
continue
|
|
}
|
|
if ObjectsAreEqual(bValue.Index(j).Interface(), element) {
|
|
visited[j] = true
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
return Fail(t, fmt.Sprintf("element %s appears more times in %s than in %s", element, aValue, bValue), msgAndArgs...)
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// Condition uses a Comparison to assert a complex condition.
|
|
func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
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{}, string) {
|
|
|
|
didPanic := false
|
|
var message interface{}
|
|
var stack string
|
|
func() {
|
|
|
|
defer func() {
|
|
if message = recover(); message != nil {
|
|
didPanic = true
|
|
stack = string(debug.Stack())
|
|
}
|
|
}()
|
|
|
|
// call the target function
|
|
f()
|
|
|
|
}()
|
|
|
|
return didPanic, message, stack
|
|
|
|
}
|
|
|
|
// Panics asserts that the code inside the specified PanicTestFunc panics.
|
|
//
|
|
// assert.Panics(t, func(){ GoCrazy() })
|
|
func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
if funcDidPanic, panicValue, _ := didPanic(f); !funcDidPanic {
|
|
return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that
|
|
// the recovered panic value equals the expected panic value.
|
|
//
|
|
// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() })
|
|
func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
funcDidPanic, panicValue, panickedStack := didPanic(f)
|
|
if !funcDidPanic {
|
|
return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...)
|
|
}
|
|
if panicValue != expected {
|
|
return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, expected, panicValue, panickedStack), msgAndArgs...)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// PanicsWithError asserts that the code inside the specified PanicTestFunc
|
|
// panics, and that the recovered panic value is an error that satisfies the
|
|
// EqualError comparison.
|
|
//
|
|
// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() })
|
|
func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
funcDidPanic, panicValue, panickedStack := didPanic(f)
|
|
if !funcDidPanic {
|
|
return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...)
|
|
}
|
|
panicErr, ok := panicValue.(error)
|
|
if !ok || panicErr.Error() != errString {
|
|
return Fail(t, fmt.Sprintf("func %#v should panic with error message:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, errString, panicValue, panickedStack), msgAndArgs...)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
|
|
//
|
|
// assert.NotPanics(t, func(){ RemainCalm() })
|
|
func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
if funcDidPanic, panicValue, panickedStack := didPanic(f); funcDidPanic {
|
|
return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v\n\tPanic stack:\t%s", f, panicValue, panickedStack), 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)
|
|
func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
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)
|
|
case time.Duration:
|
|
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)
|
|
func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
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("Expected 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 h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
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, msgAndArgs...)
|
|
if !result {
|
|
return result
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
|
|
func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
if expected == nil || actual == nil ||
|
|
reflect.TypeOf(actual).Kind() != reflect.Map ||
|
|
reflect.TypeOf(expected).Kind() != reflect.Map {
|
|
return Fail(t, "Arguments must be maps", msgAndArgs...)
|
|
}
|
|
|
|
expectedMap := reflect.ValueOf(expected)
|
|
actualMap := reflect.ValueOf(actual)
|
|
|
|
if expectedMap.Len() != actualMap.Len() {
|
|
return Fail(t, "Arguments must have the same number of keys", msgAndArgs...)
|
|
}
|
|
|
|
for _, k := range expectedMap.MapKeys() {
|
|
ev := expectedMap.MapIndex(k)
|
|
av := actualMap.MapIndex(k)
|
|
|
|
if !ev.IsValid() {
|
|
return Fail(t, fmt.Sprintf("missing key %q in expected map", k), msgAndArgs...)
|
|
}
|
|
|
|
if !av.IsValid() {
|
|
return Fail(t, fmt.Sprintf("missing key %q in actual map", k), msgAndArgs...)
|
|
}
|
|
|
|
if !InDelta(
|
|
t,
|
|
ev.Interface(),
|
|
av.Interface(),
|
|
delta,
|
|
msgAndArgs...,
|
|
) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
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("actual 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
|
|
func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
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)", epsilon, actualEpsilon), 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 h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
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, expectedObj, actualObj)
|
|
// }
|
|
func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
if err != nil {
|
|
return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// Error asserts that a function returned an error (i.e. not `nil`).
|
|
//
|
|
// actualObj, err := SomeFunction()
|
|
// if assert.Error(t, err) {
|
|
// assert.Equal(t, expectedError, err)
|
|
// }
|
|
func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
if err == nil {
|
|
return Fail(t, "An error is expected but got nil.", msgAndArgs...)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// EqualError asserts that a function returned an error (i.e. not `nil`)
|
|
// and that it is equal to the provided error.
|
|
//
|
|
// actualObj, err := SomeFunction()
|
|
// assert.EqualError(t, err, expectedErrorString)
|
|
func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
if !Error(t, theError, msgAndArgs...) {
|
|
return false
|
|
}
|
|
expected := errString
|
|
actual := theError.Error()
|
|
// don't need to use deep equals here, we know they are both strings
|
|
if expected != actual {
|
|
return Fail(t, fmt.Sprintf("Error message not equal:\n"+
|
|
"expected: %q\n"+
|
|
"actual : %q", expected, actual), msgAndArgs...)
|
|
}
|
|
return true
|
|
}
|
|
|
|
// 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")
|
|
func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
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")
|
|
func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
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.
|
|
func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
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.
|
|
func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
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
|
|
}
|
|
|
|
// FileExists checks whether a file exists in the given path. It also fails if
|
|
// the path points to a directory or there is an error when trying to check the file.
|
|
func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
info, err := os.Lstat(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...)
|
|
}
|
|
return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...)
|
|
}
|
|
if info.IsDir() {
|
|
return Fail(t, fmt.Sprintf("%q is a directory", path), msgAndArgs...)
|
|
}
|
|
return true
|
|
}
|
|
|
|
// NoFileExists checks whether a file does not exist in a given path. It fails
|
|
// if the path points to an existing _file_ only.
|
|
func NoFileExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
info, err := os.Lstat(path)
|
|
if err != nil {
|
|
return true
|
|
}
|
|
if info.IsDir() {
|
|
return true
|
|
}
|
|
return Fail(t, fmt.Sprintf("file %q exists", path), msgAndArgs...)
|
|
}
|
|
|
|
// DirExists checks whether a directory exists in the given path. It also fails
|
|
// if the path is a file rather a directory or there is an error checking whether it exists.
|
|
func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
info, err := os.Lstat(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...)
|
|
}
|
|
return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...)
|
|
}
|
|
if !info.IsDir() {
|
|
return Fail(t, fmt.Sprintf("%q is a file", path), msgAndArgs...)
|
|
}
|
|
return true
|
|
}
|
|
|
|
// NoDirExists checks whether a directory does not exist in the given path.
|
|
// It fails if the path points to an existing _directory_ only.
|
|
func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
info, err := os.Lstat(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return true
|
|
}
|
|
return true
|
|
}
|
|
if !info.IsDir() {
|
|
return true
|
|
}
|
|
return Fail(t, fmt.Sprintf("directory %q exists", path), msgAndArgs...)
|
|
}
|
|
|
|
// JSONEq asserts that two JSON strings are equivalent.
|
|
//
|
|
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
|
|
func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
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...)
|
|
}
|
|
|
|
// YAMLEq asserts that two YAML strings are equivalent.
|
|
func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
var expectedYAMLAsInterface, actualYAMLAsInterface interface{}
|
|
|
|
if err := yaml.Unmarshal([]byte(expected), &expectedYAMLAsInterface); err != nil {
|
|
return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid yaml.\nYAML parsing error: '%s'", expected, err.Error()), msgAndArgs...)
|
|
}
|
|
|
|
if err := yaml.Unmarshal([]byte(actual), &actualYAMLAsInterface); err != nil {
|
|
return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid yaml.\nYAML error: '%s'", actual, err.Error()), msgAndArgs...)
|
|
}
|
|
|
|
return Equal(t, expectedYAMLAsInterface, actualYAMLAsInterface, 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, array or string. 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 && ek != reflect.String {
|
|
return ""
|
|
}
|
|
|
|
var e, a string
|
|
if et != reflect.TypeOf("") {
|
|
e = spewConfig.Sdump(expected)
|
|
a = spewConfig.Sdump(actual)
|
|
} else {
|
|
e = reflect.ValueOf(expected).String()
|
|
a = reflect.ValueOf(actual).String()
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func isFunction(arg interface{}) bool {
|
|
if arg == nil {
|
|
return false
|
|
}
|
|
return reflect.TypeOf(arg).Kind() == reflect.Func
|
|
}
|
|
|
|
var spewConfig = spew.ConfigState{
|
|
Indent: " ",
|
|
DisablePointerAddresses: true,
|
|
DisableCapacities: true,
|
|
SortKeys: true,
|
|
}
|
|
|
|
type tHelper interface {
|
|
Helper()
|
|
}
|
|
|
|
// Eventually asserts that given condition will be met in waitFor time,
|
|
// periodically checking target function each tick.
|
|
//
|
|
// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond)
|
|
func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
ch := make(chan bool, 1)
|
|
|
|
timer := time.NewTimer(waitFor)
|
|
defer timer.Stop()
|
|
|
|
ticker := time.NewTicker(tick)
|
|
defer ticker.Stop()
|
|
|
|
for tick := ticker.C; ; {
|
|
select {
|
|
case <-timer.C:
|
|
return Fail(t, "Condition never satisfied", msgAndArgs...)
|
|
case <-tick:
|
|
tick = nil
|
|
go func() { ch <- condition() }()
|
|
case v := <-ch:
|
|
if v {
|
|
return true
|
|
}
|
|
tick = ticker.C
|
|
}
|
|
}
|
|
}
|
|
|
|
// Never asserts that the given condition doesn't satisfy in waitFor time,
|
|
// periodically checking the target function each tick.
|
|
//
|
|
// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond)
|
|
func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
|
|
if h, ok := t.(tHelper); ok {
|
|
h.Helper()
|
|
}
|
|
|
|
ch := make(chan bool, 1)
|
|
|
|
timer := time.NewTimer(waitFor)
|
|
defer timer.Stop()
|
|
|
|
ticker := time.NewTicker(tick)
|
|
defer ticker.Stop()
|
|
|
|
for tick := ticker.C; ; {
|
|
select {
|
|
case <-timer.C:
|
|
return true
|
|
case <-tick:
|
|
tick = nil
|
|
go func() { ch <- condition() }()
|
|
case v := <-ch:
|
|
if v {
|
|
return Fail(t, "Condition satisfied", msgAndArgs...)
|
|
}
|
|
tick = ticker.C
|
|
}
|
|
}
|
|
}
|