Repository: stretchr/testify
Pull request #1055 (2021-02-25 05:24:43)

Update versions supported to include `go 1.16`
Rather than directly adding `1.16`, I thought better to reword to state the minimum version supported... this way the line doesn't have to get updated with every new release, but instead only when dropping support for old releases.

jeffwidman: nudge @boyan-soubachov 

Patch:
diff --git a/.travis.gofmt.sh b/.ci.gofmt.sh
similarity index 100%
rename from .travis.gofmt.sh
rename to .ci.gofmt.sh
diff --git a/.travis.gogenerate.sh b/.ci.gogenerate.sh
similarity index 100%
rename from .travis.gogenerate.sh
rename to .ci.gogenerate.sh
diff --git a/.travis.govet.sh b/.ci.govet.sh
similarity index 100%
rename from .travis.govet.sh
rename to .ci.govet.sh
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..bf6944e
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,10 @@
+version: 2
+updates:
+- package-ecosystem: gomod
+  directory: /
+  schedule:
+    interval: daily
+- package-ecosystem: github-actions
+  directory: /
+  schedule:
+    interval: daily
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..cd3367d
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,19 @@
+name: All builds
+on: [push, pull_request]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        go_version: ["1.17.6", "1.16.5", "1.15.13"]
+    steps:
+      - uses: actions/checkout@v2
+      - name: Setup Go
+        uses: actions/setup-go@v2
+        with:
+          go-version: ${{ matrix.go_version }}
+      - run: ./.ci.gogenerate.sh    
+      - run: ./.ci.gofmt.sh
+      - run: ./.ci.govet.sh
+      - run: go test -v -race ./...
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 7a2128a..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-language: go
-os: linux
-
-jobs:
-  include:
-    - go: "1.13.x"
-      env: GO111MODULE=off
-    - go: "1.13.x"
-      env: GO111MODULE=on
-    - go: "1.14.x"
-      env: GO111MODULE=off
-    - go: "1.14.x"
-      env: GO111MODULE=on
-    - go: master
-      env: GO111MODULE=on
-    - go: master
-      env: GO111MODULE=off
-script:
-  - ./.travis.gogenerate.sh
-  - ./.travis.gofmt.sh
-  - ./.travis.govet.sh
-  - go test -v -race ./...
diff --git a/README.md b/README.md
index c78250e..6b9ff9a 100644
--- a/README.md
+++ b/README.md
@@ -323,7 +323,7 @@ To update Testify to the latest version, use `go get -u github.com/stretchr/test
 Supported go versions
 ==================
 
-We support the three major Go versions, which are 1.13, 1.14 and 1.15 at the moment.
+We currently support the most recent major Go versions from 1.13 onward.
 
 ------
 
diff --git a/assert/assertion_compare.go b/assert/assertion_compare.go
index aff1c4b..3bb22a9 100644
--- a/assert/assertion_compare.go
+++ b/assert/assertion_compare.go
@@ -3,6 +3,7 @@ package assert
 import (
 	"fmt"
 	"reflect"
+	"time"
 )
 
 type CompareType int
@@ -30,6 +31,8 @@ var (
 	float64Type = reflect.TypeOf(float64(1))
 
 	stringType = reflect.TypeOf("")
+
+	timeType = reflect.TypeOf(time.Time{})
 )
 
 func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
@@ -299,6 +302,27 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
 				return compareLess, true
 			}
 		}
+	// Check for known struct types we can check for compare results.
+	case reflect.Struct:
+		{
+			// All structs enter here. We're not interested in most types.
+			if !canConvert(obj1Value, timeType) {
+				break
+			}
+
+			// time.Time can compared!
+			timeObj1, ok := obj1.(time.Time)
+			if !ok {
+				timeObj1 = obj1Value.Convert(timeType).Interface().(time.Time)
+			}
+
+			timeObj2, ok := obj2.(time.Time)
+			if !ok {
+				timeObj2 = obj2Value.Convert(timeType).Interface().(time.Time)
+			}
+
+			return compare(timeObj1.UnixNano(), timeObj2.UnixNano(), reflect.Int64)
+		}
 	}
 
 	return compareEqual, false
@@ -313,7 +337,7 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
 	}
-	return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs)
+	return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...)
 }
 
 // GreaterOrEqual asserts that the first element is greater than or equal to the second
@@ -326,7 +350,7 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
 	}
-	return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs)
+	return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...)
 }
 
 // Less asserts that the first element is less than the second
@@ -338,7 +362,7 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{})
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
 	}
-	return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs)
+	return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...)
 }
 
 // LessOrEqual asserts that the first element is less than or equal to the second
@@ -351,7 +375,7 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
 	}
-	return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs)
+	return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...)
 }
 
 // Positive asserts that the specified element is positive
@@ -363,7 +387,7 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
 		h.Helper()
 	}
 	zero := reflect.Zero(reflect.TypeOf(e))
-	return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs)
+	return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs...)
 }
 
 // Negative asserts that the specified element is negative
@@ -375,7 +399,7 @@ func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
 		h.Helper()
 	}
 	zero := reflect.Zero(reflect.TypeOf(e))
-	return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs)
+	return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs...)
 }
 
 func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
diff --git a/assert/assertion_compare_can_convert.go b/assert/assertion_compare_can_convert.go
new file mode 100644
index 0000000..df22c47
--- /dev/null
+++ b/assert/assertion_compare_can_convert.go
@@ -0,0 +1,16 @@
+//go:build go1.17
+// +build go1.17
+
+// TODO: once support for Go 1.16 is dropped, this file can be
+//       merged/removed with assertion_compare_go1.17_test.go and
+//       assertion_compare_legacy.go
+
+package assert
+
+import "reflect"
+
+// Wrapper around reflect.Value.CanConvert, for compatability
+// reasons.
+func canConvert(value reflect.Value, to reflect.Type) bool {
+	return value.CanConvert(to)
+}
diff --git a/assert/assertion_compare_legacy.go b/assert/assertion_compare_legacy.go
new file mode 100644
index 0000000..1701af2
--- /dev/null
+++ b/assert/assertion_compare_legacy.go
@@ -0,0 +1,16 @@
+//go:build !go1.17
+// +build !go1.17
+
+// TODO: once support for Go 1.16 is dropped, this file can be
+//       merged/removed with assertion_compare_go1.17_test.go and
+//       assertion_compare_can_convert.go
+
+package assert
+
+import "reflect"
+
+// Older versions of Go does not have the reflect.Value.CanConvert
+// method.
+func canConvert(value reflect.Value, to reflect.Type) bool {
+	return false
+}
diff --git a/assert/assertion_order.go b/assert/assertion_order.go
index 1c3b471..7594487 100644
--- a/assert/assertion_order.go
+++ b/assert/assertion_order.go
@@ -50,7 +50,7 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareT
 //    assert.IsIncreasing(t, []float{1, 2})
 //    assert.IsIncreasing(t, []string{"a", "b"})
 func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
-	return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs)
+	return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...)
 }
 
 // IsNonIncreasing asserts that the collection is not increasing
@@ -59,7 +59,7 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo
 //    assert.IsNonIncreasing(t, []float{2, 1})
 //    assert.IsNonIncreasing(t, []string{"b", "a"})
 func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
-	return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs)
+	return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...)
 }
 
 // IsDecreasing asserts that the collection is decreasing
@@ -68,7 +68,7 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{})
 //    assert.IsDecreasing(t, []float{2, 1})
 //    assert.IsDecreasing(t, []string{"b", "a"})
 func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
-	return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs)
+	return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...)
 }
 
 // IsNonDecreasing asserts that the collection is not decreasing
@@ -77,5 +77,5 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo
 //    assert.IsNonDecreasing(t, []float{1, 2})
 //    assert.IsNonDecreasing(t, []string{"a", "b"})
 func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
-	return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs)
+	return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...)
 }
diff --git a/assert/assertions.go b/assert/assertions.go
index 00d97a5..0357b22 100644
--- a/assert/assertions.go
+++ b/assert/assertions.go
@@ -718,10 +718,14 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte
 // 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) {
+func containsElement(list interface{}, element interface{}) (ok, found bool) {
 
 	listValue := reflect.ValueOf(list)
-	listKind := reflect.TypeOf(list).Kind()
+	listType := reflect.TypeOf(list)
+	if listType == nil {
+		return false, false
+	}
+	listKind := listType.Kind()
 	defer func() {
 		if e := recover(); e != nil {
 			ok = false
@@ -764,7 +768,7 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo
 		h.Helper()
 	}
 
-	ok, found := includeElement(s, contains)
+	ok, found := containsElement(s, contains)
 	if !ok {
 		return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...)
 	}
@@ -787,7 +791,7 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{})
 		h.Helper()
 	}
 
-	ok, found := includeElement(s, contains)
+	ok, found := containsElement(s, contains)
 	if !ok {
 		return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
 	}
@@ -831,7 +835,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
 
 	for i := 0; i < subsetValue.Len(); i++ {
 		element := subsetValue.Index(i).Interface()
-		ok, found := includeElement(list, element)
+		ok, found := containsElement(list, element)
 		if !ok {
 			return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
 		}
@@ -852,7 +856,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
 		h.Helper()
 	}
 	if subset == nil {
-		return Fail(t, fmt.Sprintf("nil is the empty set which is a subset of every set"), msgAndArgs...)
+		return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...)
 	}
 
 	subsetValue := reflect.ValueOf(subset)
@@ -875,7 +879,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
 
 	for i := 0; i < subsetValue.Len(); i++ {
 		element := subsetValue.Index(i).Interface()
-		ok, found := includeElement(list, element)
+		ok, found := containsElement(list, element)
 		if !ok {
 			return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
 		}
@@ -1000,27 +1004,21 @@ func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
 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()
+func didPanic(f PanicTestFunc) (didPanic bool, message interface{}, stack string) {
+	didPanic = true
 
+	defer func() {
+		message = recover()
+		if didPanic {
+			stack = string(debug.Stack())
+		}
 	}()
 
-	return didPanic, message, stack
+	// call the target function
+	f()
+	didPanic = false
 
+	return
 }
 
 // Panics asserts that the code inside the specified PanicTestFunc panics.
@@ -1161,11 +1159,15 @@ func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs
 	bf, bok := toFloat(actual)
 
 	if !aok || !bok {
-		return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...)
+		return Fail(t, "Parameters must be numerical", msgAndArgs...)
+	}
+
+	if math.IsNaN(af) && math.IsNaN(bf) {
+		return true
 	}
 
 	if math.IsNaN(af) {
-		return Fail(t, fmt.Sprintf("Expected must not be NaN"), msgAndArgs...)
+		return Fail(t, "Expected must not be NaN", msgAndArgs...)
 	}
 
 	if math.IsNaN(bf) {
@@ -1188,7 +1190,7 @@ func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAn
 	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...)
+		return Fail(t, "Parameters must be slice", msgAndArgs...)
 	}
 
 	actualSlice := reflect.ValueOf(actual)
@@ -1250,8 +1252,12 @@ func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, m
 
 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)
+	bf, bok := toFloat(actual)
+	if !aok || !bok {
+		return 0, fmt.Errorf("Parameters must be numerical")
+	}
+	if math.IsNaN(af) && math.IsNaN(bf) {
+		return 0, nil
 	}
 	if math.IsNaN(af) {
 		return 0, errors.New("expected value must not be NaN")
@@ -1259,10 +1265,6 @@ func calcRelativeError(expected, actual interface{}) (float64, error) {
 	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)
-	}
 	if math.IsNaN(bf) {
 		return 0, errors.New("actual value must not be NaN")
 	}
@@ -1298,7 +1300,7 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m
 	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...)
+		return Fail(t, "Parameters must be slice", msgAndArgs...)
 	}
 
 	actualSlice := reflect.ValueOf(actual)
@@ -1609,12 +1611,17 @@ func diff(expected interface{}, actual interface{}) string {
 	}
 
 	var e, a string
-	if et != reflect.TypeOf("") {
-		e = spewConfig.Sdump(expected)
-		a = spewConfig.Sdump(actual)
-	} else {
+
+	switch et {
+	case reflect.TypeOf(""):
 		e = reflect.ValueOf(expected).String()
 		a = reflect.ValueOf(actual).String()
+	case reflect.TypeOf(time.Time{}):
+		e = spewConfigStringerEnabled.Sdump(expected)
+		a = spewConfigStringerEnabled.Sdump(actual)
+	default:
+		e = spewConfig.Sdump(expected)
+		a = spewConfig.Sdump(actual)
 	}
 
 	diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
@@ -1646,6 +1653,14 @@ var spewConfig = spew.ConfigState{
 	MaxDepth:                10,
 }
 
+var spewConfigStringerEnabled = spew.ConfigState{
+	Indent:                  " ",
+	DisablePointerAddresses: true,
+	DisableCapacities:       true,
+	SortKeys:                true,
+	MaxDepth:                10,
+}
+
 type tHelper interface {
 	Helper()
 }
diff --git a/mock/mock.go b/mock/mock.go
index e2e6a2d..853da6c 100644
--- a/mock/mock.go
+++ b/mock/mock.go
@@ -221,6 +221,14 @@ type Mock struct {
 	mutex sync.Mutex
 }
 
+// String provides a %v format string for Mock.
+// Note: this is used implicitly by Arguments.Diff if a Mock is passed.
+// It exists because go's default %v formatting traverses the struct
+// without acquiring the mutex, which is detected by go test -race.
+func (m *Mock) String() string {
+	return fmt.Sprintf("%[1]T<%[1]p>", m)
+}
+
 // TestData holds any data that might be useful for testing.  Testify ignores
 // this data completely allowing you to do whatever you like with it.
 func (m *Mock) TestData() objx.Map {
@@ -720,7 +728,7 @@ func (f argumentMatcher) Matches(argument interface{}) bool {
 }
 
 func (f argumentMatcher) String() string {
-	return fmt.Sprintf("func(%s) bool", f.fn.Type().In(0).Name())
+	return fmt.Sprintf("func(%s) bool", f.fn.Type().In(0).String())
 }
 
 // MatchedBy can be used to match a mock call based on only certain properties


Test patch:
diff --git a/assert/assertion_compare_go1.17_test.go b/assert/assertion_compare_go1.17_test.go
new file mode 100644
index 0000000..d3ea935
--- /dev/null
+++ b/assert/assertion_compare_go1.17_test.go
@@ -0,0 +1,54 @@
+//go:build go1.17
+// +build go1.17
+
+// TODO: once support for Go 1.16 is dropped, this file can be
+//       merged/removed with assertion_compare_can_convert.go and
+//       assertion_compare_legacy.go
+
+package assert
+
+import (
+	"reflect"
+	"testing"
+	"time"
+)
+
+func TestCompare17(t *testing.T) {
+	type customTime time.Time
+	for _, currCase := range []struct {
+		less    interface{}
+		greater interface{}
+		cType   string
+	}{
+		{less: time.Now(), greater: time.Now().Add(time.Hour), cType: "time.Time"},
+		{less: customTime(time.Now()), greater: customTime(time.Now().Add(time.Hour)), cType: "time.Time"},
+	} {
+		resLess, isComparable := compare(currCase.less, currCase.greater, reflect.ValueOf(currCase.less).Kind())
+		if !isComparable {
+			t.Error("object should be comparable for type " + currCase.cType)
+		}
+
+		if resLess != compareLess {
+			t.Errorf("object less (%v) should be less than greater (%v) for type "+currCase.cType,
+				currCase.less, currCase.greater)
+		}
+
+		resGreater, isComparable := compare(currCase.greater, currCase.less, reflect.ValueOf(currCase.less).Kind())
+		if !isComparable {
+			t.Error("object are comparable for type " + currCase.cType)
+		}
+
+		if resGreater != compareGreater {
+			t.Errorf("object greater should be greater than less for type " + currCase.cType)
+		}
+
+		resEqual, isComparable := compare(currCase.less, currCase.less, reflect.ValueOf(currCase.less).Kind())
+		if !isComparable {
+			t.Error("object are comparable for type " + currCase.cType)
+		}
+
+		if resEqual != 0 {
+			t.Errorf("objects should be equal for type " + currCase.cType)
+		}
+	}
+}
diff --git a/assert/assertion_compare_test.go b/assert/assertion_compare_test.go
index da5a330..a38d880 100644
--- a/assert/assertion_compare_test.go
+++ b/assert/assertion_compare_test.go
@@ -59,7 +59,8 @@ func TestCompare(t *testing.T) {
 		}
 
 		if resLess != compareLess {
-			t.Errorf("object less should be less than greater for type " + currCase.cType)
+			t.Errorf("object less (%v) should be less than greater (%v) for type "+currCase.cType,
+				currCase.less, currCase.greater)
 		}
 
 		resGreater, isComparable := compare(currCase.greater, currCase.less, reflect.ValueOf(currCase.less).Kind())
@@ -150,7 +151,7 @@ func TestGreater(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, Greater(out, currCase.less, currCase.greater))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 		Contains(t, out.helpers, "github.com/stretchr/testify/assert.Greater")
 	}
 }
@@ -191,7 +192,7 @@ func TestGreaterOrEqual(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, GreaterOrEqual(out, currCase.less, currCase.greater))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 		Contains(t, out.helpers, "github.com/stretchr/testify/assert.GreaterOrEqual")
 	}
 }
@@ -232,7 +233,7 @@ func TestLess(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, Less(out, currCase.greater, currCase.less))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 		Contains(t, out.helpers, "github.com/stretchr/testify/assert.Less")
 	}
 }
@@ -273,7 +274,7 @@ func TestLessOrEqual(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, LessOrEqual(out, currCase.greater, currCase.less))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 		Contains(t, out.helpers, "github.com/stretchr/testify/assert.LessOrEqual")
 	}
 }
@@ -312,7 +313,7 @@ func TestPositive(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, Positive(out, currCase.e))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 		Contains(t, out.helpers, "github.com/stretchr/testify/assert.Positive")
 	}
 }
@@ -351,7 +352,7 @@ func TestNegative(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, Negative(out, currCase.e))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 		Contains(t, out.helpers, "github.com/stretchr/testify/assert.Negative")
 	}
 }
@@ -386,7 +387,7 @@ func Test_compareTwoValuesNotComparableValues(t *testing.T) {
 	}{
 		{v1: CompareStruct{}, v2: CompareStruct{}},
 		{v1: map[string]int{}, v2: map[string]int{}},
-		{v1: make([]int, 5, 5), v2: make([]int, 5, 5)},
+		{v1: make([]int, 5), v2: make([]int, 5)},
 	} {
 		compareResult := compareTwoValues(mockT, currCase.v1, currCase.v2, []CompareType{compareLess, compareEqual, compareGreater}, "testFailMessage")
 		False(t, compareResult)
@@ -428,3 +429,21 @@ func Test_containsValue(t *testing.T) {
 		Equal(t, currCase.result, compareResult)
 	}
 }
+
+func TestComparingMsgAndArgsForwarding(t *testing.T) {
+	msgAndArgs := []interface{}{"format %s %x", "this", 0xc001}
+	expectedOutput := "format this c001\n"
+	funcs := []func(t TestingT){
+		func(t TestingT) { Greater(t, 1, 2, msgAndArgs...) },
+		func(t TestingT) { GreaterOrEqual(t, 1, 2, msgAndArgs...) },
+		func(t TestingT) { Less(t, 2, 1, msgAndArgs...) },
+		func(t TestingT) { LessOrEqual(t, 2, 1, msgAndArgs...) },
+		func(t TestingT) { Positive(t, 0, msgAndArgs...) },
+		func(t TestingT) { Negative(t, 0, msgAndArgs...) },
+	}
+	for _, f := range funcs {
+		out := &outputT{buf: bytes.NewBuffer(nil)}
+		f(out)
+		Contains(t, out.buf.String(), expectedOutput)
+	}
+}
diff --git a/assert/assertion_order_test.go b/assert/assertion_order_test.go
index 98cbe0e..eefe061 100644
--- a/assert/assertion_order_test.go
+++ b/assert/assertion_order_test.go
@@ -46,7 +46,7 @@ func TestIsIncreasing(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, IsIncreasing(out, currCase.collection))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 	}
 }
 
@@ -91,7 +91,7 @@ func TestIsNonIncreasing(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, IsNonIncreasing(out, currCase.collection))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 	}
 }
 
@@ -136,7 +136,7 @@ func TestIsDecreasing(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, IsDecreasing(out, currCase.collection))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 	}
 }
 
@@ -181,6 +181,23 @@ func TestIsNonDecreasing(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, IsNonDecreasing(out, currCase.collection))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
+	}
+}
+
+func TestOrderingMsgAndArgsForwarding(t *testing.T) {
+	msgAndArgs := []interface{}{"format %s %x", "this", 0xc001}
+	expectedOutput := "format this c001\n"
+	collection := []int{1, 2, 1}
+	funcs := []func(t TestingT){
+		func(t TestingT) { IsIncreasing(t, collection, msgAndArgs...) },
+		func(t TestingT) { IsNonIncreasing(t, collection, msgAndArgs...) },
+		func(t TestingT) { IsDecreasing(t, collection, msgAndArgs...) },
+		func(t TestingT) { IsNonDecreasing(t, collection, msgAndArgs...) },
+	}
+	for _, f := range funcs {
+		out := &outputT{buf: bytes.NewBuffer(nil)}
+		f(out)
+		Contains(t, out.buf.String(), expectedOutput)
 	}
 }
diff --git a/assert/assertions_test.go b/assert/assertions_test.go
index 3bd418d..0f5de92 100644
--- a/assert/assertions_test.go
+++ b/assert/assertions_test.go
@@ -592,6 +592,7 @@ func TestContainsNotContains(t *testing.T) {
 		{"j", "k"},
 	}
 	simpleMap := map[interface{}]interface{}{"Foo": "Bar"}
+	var zeroMap map[interface{}]interface{}
 
 	cases := []struct {
 		expected interface{}
@@ -606,6 +607,7 @@ func TestContainsNotContains(t *testing.T) {
 		{complexList, &A{"g", "e"}, false},
 		{simpleMap, "Foo", true},
 		{simpleMap, "Bar", false},
+		{zeroMap, "Bar", false},
 	}
 
 	for _, c := range cases {
@@ -652,6 +654,22 @@ func TestContainsFailMessage(t *testing.T) {
 	}
 }
 
+func TestContainsNotContainsOnNilValue(t *testing.T) {
+	mockT := new(mockTestingT)
+
+	Contains(mockT, nil, "key")
+	expectedFail := "<nil> could not be applied builtin len()"
+	actualFail := mockT.errorString()
+	if !strings.Contains(actualFail, expectedFail) {
+		t.Errorf("Contains failure should include %q but was %q", expectedFail, actualFail)
+	}
+
+	NotContains(mockT, nil, "key")
+	if !strings.Contains(actualFail, expectedFail) {
+		t.Errorf("Contains failure should include %q but was %q", expectedFail, actualFail)
+	}
+}
+
 func TestSubsetNotSubset(t *testing.T) {
 
 	// MTestCase adds a custom message to the case
@@ -714,53 +732,53 @@ func TestNotSubsetNil(t *testing.T) {
 	}
 }
 
-func Test_includeElement(t *testing.T) {
+func Test_containsElement(t *testing.T) {
 
 	list1 := []string{"Foo", "Bar"}
 	list2 := []int{1, 2}
 	simpleMap := map[interface{}]interface{}{"Foo": "Bar"}
 
-	ok, found := includeElement("Hello World", "World")
+	ok, found := containsElement("Hello World", "World")
 	True(t, ok)
 	True(t, found)
 
-	ok, found = includeElement(list1, "Foo")
+	ok, found = containsElement(list1, "Foo")
 	True(t, ok)
 	True(t, found)
 
-	ok, found = includeElement(list1, "Bar")
+	ok, found = containsElement(list1, "Bar")
 	True(t, ok)
 	True(t, found)
 
-	ok, found = includeElement(list2, 1)
+	ok, found = containsElement(list2, 1)
 	True(t, ok)
 	True(t, found)
 
-	ok, found = includeElement(list2, 2)
+	ok, found = containsElement(list2, 2)
 	True(t, ok)
 	True(t, found)
 
-	ok, found = includeElement(list1, "Foo!")
+	ok, found = containsElement(list1, "Foo!")
 	True(t, ok)
 	False(t, found)
 
-	ok, found = includeElement(list2, 3)
+	ok, found = containsElement(list2, 3)
 	True(t, ok)
 	False(t, found)
 
-	ok, found = includeElement(list2, "1")
+	ok, found = containsElement(list2, "1")
 	True(t, ok)
 	False(t, found)
 
-	ok, found = includeElement(simpleMap, "Foo")
+	ok, found = containsElement(simpleMap, "Foo")
 	True(t, ok)
 	True(t, found)
 
-	ok, found = includeElement(simpleMap, "Bar")
+	ok, found = containsElement(simpleMap, "Bar")
 	True(t, ok)
 	False(t, found)
 
-	ok, found = includeElement(1433, "1")
+	ok, found = containsElement(1433, "1")
 	False(t, ok)
 	False(t, found)
 }
@@ -905,10 +923,18 @@ func TestCondition(t *testing.T) {
 
 func TestDidPanic(t *testing.T) {
 
-	if funcDidPanic, _, _ := didPanic(func() {
-		panic("Panic!")
-	}); !funcDidPanic {
-		t.Error("didPanic should return true")
+	const panicMsg = "Panic!"
+
+	if funcDidPanic, msg, _ := didPanic(func() {
+		panic(panicMsg)
+	}); !funcDidPanic || msg != panicMsg {
+		t.Error("didPanic should return true, panicMsg")
+	}
+
+	if funcDidPanic, msg, _ := didPanic(func() {
+		panic(nil)
+	}); !funcDidPanic || msg != nil {
+		t.Error("didPanic should return true, nil")
 	}
 
 	if funcDidPanic, _, _ := didPanic(func() {
@@ -945,6 +971,12 @@ func TestPanicsWithValue(t *testing.T) {
 		t.Error("PanicsWithValue should return true")
 	}
 
+	if !PanicsWithValue(mockT, nil, func() {
+		panic(nil)
+	}) {
+		t.Error("PanicsWithValue should return true")
+	}
+
 	if PanicsWithValue(mockT, "Panic!", func() {
 	}) {
 		t.Error("PanicsWithValue should return false")
@@ -1324,6 +1356,7 @@ func TestInDelta(t *testing.T) {
 	False(t, InDelta(mockT, "", nil, 1), "Expected non numerals to fail")
 	False(t, InDelta(mockT, 42, math.NaN(), 0.01), "Expected NaN for actual to fail")
 	False(t, InDelta(mockT, math.NaN(), 42, 0.01), "Expected NaN for expected to fail")
+	True(t, InDelta(mockT, math.NaN(), math.NaN(), 0.01), "Expected NaN for both to pass")
 
 	cases := []struct {
 		a, b  interface{}
@@ -1354,19 +1387,19 @@ func TestInDeltaSlice(t *testing.T) {
 	mockT := new(testing.T)
 
 	True(t, InDeltaSlice(mockT,
-		[]float64{1.001, 0.999},
-		[]float64{1, 1},
-		0.1), "{1.001, 0.009} is element-wise close to {1, 1} in delta=0.1")
+		[]float64{1.001, math.NaN(), 0.999},
+		[]float64{1, math.NaN(), 1},
+		0.1), "{1.001, NaN, 0.009} is element-wise close to {1, NaN, 1} in delta=0.1")
 
 	True(t, InDeltaSlice(mockT,
-		[]float64{1, 2},
-		[]float64{0, 3},
-		1), "{1, 2} is element-wise close to {0, 3} in delta=1")
+		[]float64{1, math.NaN(), 2},
+		[]float64{0, math.NaN(), 3},
+		1), "{1, NaN, 2} is element-wise close to {0, NaN, 3} in delta=1")
 
 	False(t, InDeltaSlice(mockT,
-		[]float64{1, 2},
-		[]float64{0, 3},
-		0.1), "{1, 2} is not element-wise close to {0, 3} in delta=0.1")
+		[]float64{1, math.NaN(), 2},
+		[]float64{0, math.NaN(), 3},
+		0.1), "{1, NaN, 2} is not element-wise close to {0, NaN, 3} in delta=0.1")
 
 	False(t, InDeltaSlice(mockT, "", nil, 1), "Expected non numeral slices to fail")
 }
@@ -1386,10 +1419,12 @@ func TestInDeltaMapValues(t *testing.T) {
 			expect: map[string]float64{
 				"foo": 1.0,
 				"bar": 2.0,
+				"baz": math.NaN(),
 			},
 			actual: map[string]float64{
 				"foo": 1.01,
 				"bar": 1.99,
+				"baz": math.NaN(),
 			},
 			delta: 0.1,
 			f:     True,
@@ -1422,10 +1457,10 @@ func TestInDeltaMapValues(t *testing.T) {
 		{
 			title: "Within delta with zero value",
 			expect: map[string]float64{
-				"zero": 0.0,
+				"zero": 0,
 			},
 			actual: map[string]float64{
-				"zero": 0.0,
+				"zero": 0,
 			},
 			delta: 0.1,
 			f:     True,
@@ -1433,12 +1468,12 @@ func TestInDeltaMapValues(t *testing.T) {
 		{
 			title: "With missing key with zero value",
 			expect: map[string]float64{
-				"zero": 0.0,
-				"foo":  0.0,
+				"zero": 0,
+				"foo":  0,
 			},
 			actual: map[string]float64{
-				"zero": 0.0,
-				"bar":  0.0,
+				"zero": 0,
+				"bar":  0,
 			},
 			f: False,
 		},
@@ -1462,6 +1497,7 @@ func TestInEpsilon(t *testing.T) {
 		{uint64(100), uint8(101), 0.01},
 		{0.1, -0.1, 2},
 		{0.1, 0, 2},
+		{math.NaN(), math.NaN(), 1},
 		{time.Second, time.Second + time.Millisecond, 0.002},
 	}
 
@@ -1497,9 +1533,9 @@ func TestInEpsilonSlice(t *testing.T) {
 	mockT := new(testing.T)
 
 	True(t, InEpsilonSlice(mockT,
-		[]float64{2.2, 2.0},
-		[]float64{2.1, 2.1},
-		0.06), "{2.2, 2.0} is element-wise close to {2.1, 2.1} in espilon=0.06")
+		[]float64{2.2, math.NaN(), 2.0},
+		[]float64{2.1, math.NaN(), 2.1},
+		0.06), "{2.2, NaN, 2.0} is element-wise close to {2.1, NaN, 2.1} in espilon=0.06")
 
 	False(t, InEpsilonSlice(mockT,
 		[]float64{2.2, 2.0},
@@ -1553,8 +1589,7 @@ func testAutogeneratedFunction() {
 	t := struct {
 		io.Closer
 	}{}
-	var c io.Closer
-	c = t
+	c := t
 	c.Close()
 }
 
@@ -1991,6 +2026,23 @@ Diff:
 		diffTestingStruct{A: "some string", B: 15},
 	)
 	Equal(t, expected, actual)
+
+	expected = `
+
+Diff:
+--- Expected
++++ Actual
+@@ -1,2 +1,2 @@
+-(time.Time) 2020-09-24 00:00:00 +0000 UTC
++(time.Time) 2020-09-25 00:00:00 +0000 UTC
+ 
+`
+
+	actual = diff(
+		time.Date(2020, 9, 24, 0, 0, 0, 0, time.UTC),
+		time.Date(2020, 9, 25, 0, 0, 0, 0, time.UTC),
+	)
+	Equal(t, expected, actual)
 }
 
 func TestTimeEqualityErrorFormatting(t *testing.T) {
@@ -2185,7 +2237,7 @@ func ExampleValueAssertionFunc() {
 
 	dumbParse := func(input string) interface{} {
 		var x interface{}
-		json.Unmarshal([]byte(input), &x)
+		_ = json.Unmarshal([]byte(input), &x)
 		return x
 	}
 
diff --git a/assert/http_assertions_test.go b/assert/http_assertions_test.go
index 991866a..413c971 100644
--- a/assert/http_assertions_test.go
+++ b/assert/http_assertions_test.go
@@ -122,7 +122,7 @@ func TestHTTPStatusesWrapper(t *testing.T) {
 
 func httpHelloName(w http.ResponseWriter, r *http.Request) {
 	name := r.FormValue("name")
-	w.Write([]byte(fmt.Sprintf("Hello, %s!", name)))
+	_, _ = w.Write([]byte(fmt.Sprintf("Hello, %s!", name)))
 }
 
 func TestHTTPRequestWithNoParams(t *testing.T) {
diff --git a/mock/mock_test.go b/mock/mock_test.go
index 2b6e704..91828f8 100644
--- a/mock/mock_test.go
+++ b/mock/mock_test.go
@@ -1482,6 +1482,10 @@ func (s *timer) GetTime(i int) string {
 	return s.Called(i).Get(0).(string)
 }
 
+func (s *timer) GetTimes(times []int) string {
+	return s.Called(times).Get(0).(string)
+}
+
 type tCustomLogger struct {
 	*testing.T
 	logs []string
@@ -1554,6 +1558,23 @@ func TestArgumentMatcherToPrintMismatch(t *testing.T) {
 	m.AssertExpectations(t)
 }
 
+func TestArgumentMatcherToPrintMismatchWithReferenceType(t *testing.T) {
+	defer func() {
+		if r := recover(); r != nil {
+			matchingExp := regexp.MustCompile(
+				`\s+mock: Unexpected Method Call\s+-*\s+GetTimes\(\[\]int\)\s+0: \[\]int\{1\}\s+The closest call I have is:\s+GetTimes\(mock.argumentMatcher\)\s+0: mock.argumentMatcher\{.*?\}\s+Diff:.*\(\[\]int=\[1\]\) not matched by func\(\[\]int\) bool`)
+			assert.Regexp(t, matchingExp, r)
+		}
+	}()
+
+	m := new(timer)
+	m.On("GetTimes", MatchedBy(func(_ []int) bool { return false })).Return("SomeTime").Once()
+
+	res := m.GetTimes([]int{1})
+	require.Equal(t, "SomeTime", res)
+	m.AssertExpectations(t)
+}
+
 func TestClosestCallMismatchedArgumentInformationShowsTheClosest(t *testing.T) {
 	defer func() {
 		if r := recover(); r != nil {
@@ -1653,3 +1674,40 @@ func unexpectedCallRegex(method, calledArg, expectedArg, diff string) string {
 func ConcurrencyTestMethod(m *Mock) {
 	m.Called()
 }
+
+func TestConcurrentArgumentRead(t *testing.T) {
+	methodUnderTest := func(c caller, u user) {
+		go u.Use(c)
+		c.Call()
+	}
+
+	c := &mockCaller{}
+	defer c.AssertExpectations(t)
+
+	u := &mockUser{}
+	defer u.AssertExpectations(t)
+
+	done := make(chan struct{})
+
+	c.On("Call").Return().Once()
+	u.On("Use", c).Return().Once().Run(func(args Arguments) { close(done) })
+
+	methodUnderTest(c, u)
+	<-done // wait until Use is called or assertions will fail
+}
+
+type caller interface {
+	Call()
+}
+
+type mockCaller struct{ Mock }
+
+func (m *mockCaller) Call() { m.Called() }
+
+type user interface {
+	Use(caller)
+}
+
+type mockUser struct{ Mock }
+
+func (m *mockUser) Use(c caller) { m.Called(c) }