From e209ca88af1919e710960205e887e57db33c9be0 Mon Sep 17 00:00:00 2001 From: perrydunn Date: Tue, 4 May 2021 16:42:39 +0100 Subject: [PATCH 01/32] Improve mock.MatchedBy failed comparison Diff message --- mock/mock.go | 2 +- mock/mock_test.go | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/mock/mock.go b/mock/mock.go index 5d445c6d3..853da6cce 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -728,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 diff --git a/mock/mock_test.go b/mock/mock_test.go index 097addc8a..91828f813 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 { From 5c61ef97aee7462f1a509bafce2c752defc88f50 Mon Sep 17 00:00:00 2001 From: Menno Date: Sun, 30 May 2021 21:05:06 +0200 Subject: [PATCH 02/32] fix potential nil-pointer dereference the fix gracefully fails the assertion instead of panicking unresolved. --- assert/assertions.go | 6 +++++- assert/assertions_test.go | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/assert/assertions.go b/assert/assertions.go index 30742a0bb..c91fcbfc0 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -721,7 +721,11 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte func includeElement(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 diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 4fafb512d..c6fbb7679 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 := " 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 From edff5a049b1c4eacd84bec25bcf7e7852b7c1163 Mon Sep 17 00:00:00 2001 From: Menno Date: Sun, 4 Jul 2021 16:20:17 +0200 Subject: [PATCH 03/32] fix funtion name --- assert/assertions.go | 10 +++++----- assert/assertions_test.go | 24 ++++++++++++------------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index c91fcbfc0..79a115088 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -718,7 +718,7 @@ 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) listType := reflect.TypeOf(list) @@ -768,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...) } @@ -791,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...) } @@ -835,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...) } @@ -879,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...) } diff --git a/assert/assertions_test.go b/assert/assertions_test.go index c6fbb7679..880f9cc43 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -732,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) } From ab6dc3262822ed562480c19876b0257ace761e3e Mon Sep 17 00:00:00 2001 From: Menno Date: Sun, 4 Jul 2021 16:25:58 +0200 Subject: [PATCH 04/32] fix linting errors in /assert package --- assert/assertion_compare_test.go | 14 +++++++------- assert/assertion_order_test.go | 8 ++++---- assert/assertions.go | 10 +++++----- assert/assertions_test.go | 5 ++--- assert/http_assertions_test.go | 2 +- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/assert/assertion_compare_test.go b/assert/assertion_compare_test.go index da5a33033..6f7db87c8 100644 --- a/assert/assertion_compare_test.go +++ b/assert/assertion_compare_test.go @@ -150,7 +150,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 +191,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 +232,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 +273,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 +312,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 +351,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 +386,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) diff --git a/assert/assertion_order_test.go b/assert/assertion_order_test.go index 98cbe0eae..7726b82f9 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,6 @@ 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) } } diff --git a/assert/assertions.go b/assert/assertions.go index 79a115088..230118b3b 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -856,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) @@ -1165,7 +1165,7 @@ 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) { @@ -1173,7 +1173,7 @@ func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs } 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) { @@ -1196,7 +1196,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) @@ -1306,7 +1306,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) diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 880f9cc43..0b5b2c187 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -1575,8 +1575,7 @@ func testAutogeneratedFunction() { t := struct { io.Closer }{} - var c io.Closer - c = t + c := t c.Close() } @@ -2224,7 +2223,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 991866ac4..413c9714f 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) { From f87e2b211992baaf0251ae5ad1a530aaa9266570 Mon Sep 17 00:00:00 2001 From: Boyan Soubachov Date: Thu, 10 Feb 2022 20:04:21 +1000 Subject: [PATCH 05/32] Update builds * Add Go 1.17.6 to the build versions * Remove Go 1.14 * Remove unused TravisCI file --- .github/workflows/main.yml | 2 +- .travis.yml | 22 ---------------------- 2 files changed, 1 insertion(+), 23 deletions(-) delete mode 100644 .travis.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f4325027f..cd3367dc9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go_version: ["1.16.5", "1.15.13", "1.14.15"] + go_version: ["1.17.6", "1.16.5", "1.15.13"] steps: - uses: actions/checkout@v2 - name: Setup Go diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7a2128a8e..000000000 --- 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 ./... From c29de713426fdf9068696c483705fdbb24a815ac Mon Sep 17 00:00:00 2001 From: Ilia Kravets Date: Thu, 20 Jan 2022 11:25:52 +0200 Subject: [PATCH 06/32] add tests for correct msgAndArgs forwarding len(msgAndArgs)>1 should lead to fmt.Sprintf() --- assert/assertion_compare_test.go | 18 ++++++++++++++++++ assert/assertion_order_test.go | 17 +++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/assert/assertion_compare_test.go b/assert/assertion_compare_test.go index 6f7db87c8..667dbf7c4 100644 --- a/assert/assertion_compare_test.go +++ b/assert/assertion_compare_test.go @@ -428,3 +428,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 7726b82f9..eefe06127 100644 --- a/assert/assertion_order_test.go +++ b/assert/assertion_order_test.go @@ -184,3 +184,20 @@ func TestIsNonDecreasing(t *testing.T) { 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) + } +} From 7bcf74e94f95af11a6a7c0b9a5d9a719605d4faa Mon Sep 17 00:00:00 2001 From: Ilia Kravets Date: Thu, 20 Jan 2022 11:30:25 +0200 Subject: [PATCH 07/32] fix msgAndArgs forwarding --- assert/assertion_compare.go | 12 ++++++------ assert/assertion_order.go | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/assert/assertion_compare.go b/assert/assertion_compare.go index aff1c4b80..718d58202 100644 --- a/assert/assertion_compare.go +++ b/assert/assertion_compare.go @@ -313,7 +313,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 +326,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 +338,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 +351,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 +363,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 +375,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_order.go b/assert/assertion_order.go index 1c3b47182..759448783 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...) } From 087b655c75372ea14309c2df573a2280c54afac6 Mon Sep 17 00:00:00 2001 From: Torkel Rogstad Date: Mon, 17 Jan 2022 14:10:48 +0100 Subject: [PATCH 08/32] assert: allow comparing time.Time --- assert/assertion_compare.go | 24 ++++++++++++++++++++++++ assert/assertion_compare_test.go | 7 ++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/assert/assertion_compare.go b/assert/assertion_compare.go index 718d58202..96027d1ec 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 !obj1Value.CanConvert(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 diff --git a/assert/assertion_compare_test.go b/assert/assertion_compare_test.go index 667dbf7c4..1af4b5da2 100644 --- a/assert/assertion_compare_test.go +++ b/assert/assertion_compare_test.go @@ -6,6 +6,7 @@ import ( "reflect" "runtime" "testing" + "time" ) func TestCompare(t *testing.T) { @@ -22,6 +23,7 @@ func TestCompare(t *testing.T) { type customFloat32 float32 type customFloat64 float64 type customString string + type customTime time.Time for _, currCase := range []struct { less interface{} greater interface{} @@ -52,6 +54,8 @@ func TestCompare(t *testing.T) { {less: customFloat32(1.23), greater: customFloat32(2.23), cType: "float32"}, {less: float64(1.23), greater: float64(2.34), cType: "float64"}, {less: customFloat64(1.23), greater: customFloat64(2.34), cType: "float64"}, + {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 { @@ -59,7 +63,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()) From 83198c2c50a6190cf9b038c485c65c5ed8b6cd3a Mon Sep 17 00:00:00 2001 From: Torkel Rogstad Date: Mon, 17 Jan 2022 14:34:50 +0100 Subject: [PATCH 09/32] assert: guard CanConvert call in backward compatible wrapper --- assert/assertion_compare.go | 2 +- assert/assertion_compare_can_convert.go | 11 ++++++ assert/assertion_compare_go1.17_test.go | 49 +++++++++++++++++++++++++ assert/assertion_compare_legacy.go | 11 ++++++ assert/assertion_compare_test.go | 4 -- 5 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 assert/assertion_compare_can_convert.go create mode 100644 assert/assertion_compare_go1.17_test.go create mode 100644 assert/assertion_compare_legacy.go diff --git a/assert/assertion_compare.go b/assert/assertion_compare.go index 96027d1ec..3bb22a971 100644 --- a/assert/assertion_compare.go +++ b/assert/assertion_compare.go @@ -306,7 +306,7 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { case reflect.Struct: { // All structs enter here. We're not interested in most types. - if !obj1Value.CanConvert(timeType) { + if !canConvert(obj1Value, timeType) { break } diff --git a/assert/assertion_compare_can_convert.go b/assert/assertion_compare_can_convert.go new file mode 100644 index 000000000..1838be3fe --- /dev/null +++ b/assert/assertion_compare_can_convert.go @@ -0,0 +1,11 @@ +// +build go1.17 + +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_go1.17_test.go b/assert/assertion_compare_go1.17_test.go new file mode 100644 index 000000000..0511e648a --- /dev/null +++ b/assert/assertion_compare_go1.17_test.go @@ -0,0 +1,49 @@ +// +build go1.17 + +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_legacy.go b/assert/assertion_compare_legacy.go new file mode 100644 index 000000000..478e953c0 --- /dev/null +++ b/assert/assertion_compare_legacy.go @@ -0,0 +1,11 @@ +// +build !go1.17 + +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_compare_test.go b/assert/assertion_compare_test.go index 1af4b5da2..a38d88060 100644 --- a/assert/assertion_compare_test.go +++ b/assert/assertion_compare_test.go @@ -6,7 +6,6 @@ import ( "reflect" "runtime" "testing" - "time" ) func TestCompare(t *testing.T) { @@ -23,7 +22,6 @@ func TestCompare(t *testing.T) { type customFloat32 float32 type customFloat64 float64 type customString string - type customTime time.Time for _, currCase := range []struct { less interface{} greater interface{} @@ -54,8 +52,6 @@ func TestCompare(t *testing.T) { {less: customFloat32(1.23), greater: customFloat32(2.23), cType: "float32"}, {less: float64(1.23), greater: float64(2.34), cType: "float64"}, {less: customFloat64(1.23), greater: customFloat64(2.34), cType: "float64"}, - {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 { From e798dc2763edab11eadc45df377ff160c6c86fd1 Mon Sep 17 00:00:00 2001 From: Torkel Rogstad Date: Thu, 10 Feb 2022 16:00:40 +0100 Subject: [PATCH 10/32] Add docs on 1.17 build tags --- assert/assertion_compare_can_convert.go | 4 ++++ assert/assertion_compare_go1.17_test.go | 4 ++++ assert/assertion_compare_legacy.go | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/assert/assertion_compare_can_convert.go b/assert/assertion_compare_can_convert.go index 1838be3fe..4a5627c32 100644 --- a/assert/assertion_compare_can_convert.go +++ b/assert/assertion_compare_can_convert.go @@ -1,5 +1,9 @@ // +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" diff --git a/assert/assertion_compare_go1.17_test.go b/assert/assertion_compare_go1.17_test.go index 0511e648a..bff219de6 100644 --- a/assert/assertion_compare_go1.17_test.go +++ b/assert/assertion_compare_go1.17_test.go @@ -1,5 +1,9 @@ // +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 ( diff --git a/assert/assertion_compare_legacy.go b/assert/assertion_compare_legacy.go index 478e953c0..0bb8ad712 100644 --- a/assert/assertion_compare_legacy.go +++ b/assert/assertion_compare_legacy.go @@ -1,5 +1,9 @@ // +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" From 1e36bfe10404cb77c12f6dfc8665564f3a41ad7e Mon Sep 17 00:00:00 2001 From: Torkel Rogstad Date: Thu, 10 Feb 2022 16:12:31 +0100 Subject: [PATCH 11/32] Use cross Go version compatible build tag syntax --- assert/assertion_compare_can_convert.go | 1 + assert/assertion_compare_go1.17_test.go | 1 + assert/assertion_compare_legacy.go | 1 + 3 files changed, 3 insertions(+) diff --git a/assert/assertion_compare_can_convert.go b/assert/assertion_compare_can_convert.go index 4a5627c32..df22c47fc 100644 --- a/assert/assertion_compare_can_convert.go +++ b/assert/assertion_compare_can_convert.go @@ -1,3 +1,4 @@ +//go:build go1.17 // +build go1.17 // TODO: once support for Go 1.16 is dropped, this file can be diff --git a/assert/assertion_compare_go1.17_test.go b/assert/assertion_compare_go1.17_test.go index bff219de6..d3ea935fa 100644 --- a/assert/assertion_compare_go1.17_test.go +++ b/assert/assertion_compare_go1.17_test.go @@ -1,3 +1,4 @@ +//go:build go1.17 // +build go1.17 // TODO: once support for Go 1.16 is dropped, this file can be diff --git a/assert/assertion_compare_legacy.go b/assert/assertion_compare_legacy.go index 0bb8ad712..1701af2a3 100644 --- a/assert/assertion_compare_legacy.go +++ b/assert/assertion_compare_legacy.go @@ -1,3 +1,4 @@ +//go:build !go1.17 // +build !go1.17 // TODO: once support for Go 1.16 is dropped, this file can be From 083ff1c0449867d0d8d456483ee5fab8e0c0e1e6 Mon Sep 17 00:00:00 2001 From: RmbRT Date: Fri, 26 Jul 2019 14:21:04 +0200 Subject: [PATCH 12/32] Fixed didPanic to now detect panic(nil). Previously, the function would not detect panic(nil) calls. In didPanic, removed the anonymous function call, instead, added named return values. Added extra test cases for the panic(nil) call. --- assert/assertions.go | 28 +++++++++++----------------- assert/assertions_test.go | 22 ++++++++++++++++++---- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index 230118b3b..0357b2231 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1004,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. diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 0b5b2c187..0f5de92ff 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -923,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() { @@ -963,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") From 77977386932ab1866a4b9556a7af1ae347531786 Mon Sep 17 00:00:00 2001 From: Jeff Widman Date: Wed, 24 Feb 2021 21:24:28 -0800 Subject: [PATCH 13/32] 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 releases. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c78250e41..6b9ff9ad3 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. ------ From 35864782d21250c3ff66874e87e5fdc9c79e0be1 Mon Sep 17 00:00:00 2001 From: cuishuang Date: Thu, 3 Mar 2022 15:15:54 +0800 Subject: [PATCH 14/32] assert: fix typo Signed-off-by: cuishuang --- assert/assertion_compare_can_convert.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assert/assertion_compare_can_convert.go b/assert/assertion_compare_can_convert.go index df22c47fc..da867903e 100644 --- a/assert/assertion_compare_can_convert.go +++ b/assert/assertion_compare_can_convert.go @@ -9,7 +9,7 @@ package assert import "reflect" -// Wrapper around reflect.Value.CanConvert, for compatability +// Wrapper around reflect.Value.CanConvert, for compatibility // reasons. func canConvert(value reflect.Value, to reflect.Type) bool { return value.CanConvert(to) From a409ccf19e17d8a90156521ce71c5e8ef8f6bca8 Mon Sep 17 00:00:00 2001 From: Weizhen Wang Date: Thu, 3 Mar 2022 23:43:17 +0800 Subject: [PATCH 15/32] fix data race in the suit Signed-off-by: Weizhen Wang --- suite/suite.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/suite/suite.go b/suite/suite.go index b9b5d1c56..1c835d4e0 100644 --- a/suite/suite.go +++ b/suite/suite.go @@ -7,6 +7,7 @@ import ( "reflect" "regexp" "runtime/debug" + "sync" "testing" "time" @@ -21,17 +22,22 @@ var matchMethod = flag.String("testify.m", "", "regular expression to select tes // retrieving the current *testing.T context. type Suite struct { *assert.Assertions + mu sync.Mutex require *require.Assertions t *testing.T } // T retrieves the current *testing.T context. func (suite *Suite) T() *testing.T { + suite.mu.Lock() + defer suite.mu.Unlock() return suite.t } // SetT sets the current *testing.T context. func (suite *Suite) SetT(t *testing.T) { + suite.mu.Lock() + defer suite.mu.Unlock() suite.t = t suite.Assertions = assert.New(t) suite.require = require.New(t) @@ -39,6 +45,8 @@ func (suite *Suite) SetT(t *testing.T) { // Require returns a require context for suite. func (suite *Suite) Require() *require.Assertions { + suite.mu.Lock() + defer suite.mu.Unlock() if suite.require == nil { suite.require = require.New(suite.T()) } @@ -51,6 +59,8 @@ func (suite *Suite) Require() *require.Assertions { // assert.Assertions with require.Assertions), this method is provided so you // can call `suite.Assert().NoError()`. func (suite *Suite) Assert() *assert.Assertions { + suite.mu.Lock() + defer suite.mu.Unlock() if suite.Assertions == nil { suite.Assertions = assert.New(suite.T()) } From 106ec21d14df07d4c33ec1155328800300c28c7f Mon Sep 17 00:00:00 2001 From: Weizhen Wang Date: Fri, 18 Mar 2022 15:55:34 +0800 Subject: [PATCH 16/32] use RWMutex Signed-off-by: Weizhen Wang --- suite/suite.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/suite/suite.go b/suite/suite.go index 1c835d4e0..1c402e8df 100644 --- a/suite/suite.go +++ b/suite/suite.go @@ -22,15 +22,15 @@ var matchMethod = flag.String("testify.m", "", "regular expression to select tes // retrieving the current *testing.T context. type Suite struct { *assert.Assertions - mu sync.Mutex + mu sync.RWMutex require *require.Assertions t *testing.T } // T retrieves the current *testing.T context. func (suite *Suite) T() *testing.T { - suite.mu.Lock() - defer suite.mu.Unlock() + suite.mu.RLock() + defer suite.mu.RUnlock() return suite.t } From 6e7fab43fc3294f4e7c079e260746edcf8d66639 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 May 2022 11:17:51 +0000 Subject: [PATCH 17/32] Bump actions/setup-go from 2 to 3.1.0 Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2 to 3.1.0. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v2...v3.1.0) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cd3367dc9..f72a324e2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Setup Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3.1.0 with: go-version: ${{ matrix.go_version }} - run: ./.ci.gogenerate.sh From 285adcc5ced0bb267a7c874cfa3ca238266ce14f Mon Sep 17 00:00:00 2001 From: Jeff Widman Date: Thu, 24 Mar 2022 14:19:34 -0700 Subject: [PATCH 18/32] Update go versions in build matrix These are the latest versions listed on https://go.dev/dl/ --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f72a324e2..68ac70f17 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go_version: ["1.17.6", "1.16.5", "1.15.13"] + go_version: ["1.17.8", "1.16.15", "1.15.15"] steps: - uses: actions/checkout@v2 - name: Setup Go From 41453c009af9a942261b7a25a88521d0d6804e7f Mon Sep 17 00:00:00 2001 From: Karol Lassak Date: Fri, 27 May 2022 13:22:05 +0200 Subject: [PATCH 19/32] Update gopkg.in/yaml.v3 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ed0b50bf9..9d165ea0f 100644 --- a/go.mod +++ b/go.mod @@ -6,5 +6,5 @@ require ( github.com/davecgh/go-spew v1.1.0 github.com/pmezard/go-difflib v1.0.0 github.com/stretchr/objx v0.1.0 - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c + gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index e23c32556..2b8f330c7 100644 --- a/go.sum +++ b/go.sum @@ -6,5 +6,5 @@ github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From e2b56b3a384eb40136b2cff13e07e932dd95bf28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jun 2022 09:53:30 +0000 Subject: [PATCH 20/32] Bump github.com/stretchr/objx from 0.1.0 to 0.4.0 Bumps [github.com/stretchr/objx](https://github.com/stretchr/objx) from 0.1.0 to 0.4.0. - [Release notes](https://github.com/stretchr/objx/releases) - [Commits](https://github.com/stretchr/objx/compare/v0.1.0...v0.4.0) --- updated-dependencies: - dependency-name: github.com/stretchr/objx dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 9d165ea0f..c5d3e8890 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/stretchr/testify go 1.13 require ( - github.com/davecgh/go-spew v1.1.0 + github.com/davecgh/go-spew v1.1.1 github.com/pmezard/go-difflib v1.0.0 - github.com/stretchr/objx v0.1.0 + github.com/stretchr/objx v0.4.0 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 2b8f330c7..3be384bf9 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,14 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 3c33e07c4c233bf61e1414f5acb3cda06ffef1d1 Mon Sep 17 00:00:00 2001 From: Boyan Soubachov <20902194+boyan-soubachov@users.noreply.github.com> Date: Tue, 14 Jun 2022 20:50:25 +1000 Subject: [PATCH 21/32] Added Go 1.18.1 as a build/supported version (#1182) * Added Go 1.18.1 as a build/supported version Removed Go 1.15.13 as a build version as it's no longer supported * Fix mutex passed by value for the Mock struct * Add mutex initialisation for Mock Co-authored-by: Boyan Soubachov --- .github/workflows/main.yml | 2 +- mock/mock.go | 36 +++++++++++++++++------------------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 68ac70f17..aa7994f1f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go_version: ["1.17.8", "1.16.15", "1.15.15"] + go_version: ["1.18.1", "1.17.6", "1.16.5"] steps: - uses: actions/checkout@v2 - name: Setup Go diff --git a/mock/mock.go b/mock/mock.go index 853da6cce..646c9b5c5 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -218,7 +218,7 @@ type Mock struct { // this data completely allowing you to do whatever you like with it. testData objx.Map - mutex sync.Mutex + mutex *sync.Mutex } // String provides a %v format string for Mock. @@ -232,7 +232,6 @@ func (m *Mock) String() string { // 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 { - if m.testData == nil { m.testData = make(objx.Map) } @@ -246,6 +245,10 @@ func (m *Mock) TestData() objx.Map { // Test sets the test struct variable of the mock object func (m *Mock) Test(t TestingT) { + if m.mutex == nil { + m.mutex = &sync.Mutex{} + } + m.mutex.Lock() defer m.mutex.Unlock() m.test = t @@ -276,6 +279,9 @@ func (m *Mock) On(methodName string, arguments ...interface{}) *Call { } } + // Since we start mocks with the .On() function, m.mutex should be reset + m.mutex = &sync.Mutex{} + m.mutex.Lock() defer m.mutex.Unlock() c := newCall(m, methodName, assert.CallerInfo(), arguments...) @@ -354,7 +360,6 @@ func (m *Mock) findClosestCall(method string, arguments ...interface{}) (*Call, } func callString(method string, arguments Arguments, includeArgumentValues bool) string { - var argValsString string if includeArgumentValues { var argVals []string @@ -378,10 +383,10 @@ func (m *Mock) Called(arguments ...interface{}) Arguments { panic("Couldn't get the caller information") } functionPath := runtime.FuncForPC(pc).Name() - //Next four lines are required to use GCCGO function naming conventions. - //For Ex: github_com_docker_libkv_store_mock.WatchTree.pN39_github_com_docker_libkv_store_mock.Mock - //uses interface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree - //With GCCGO we need to remove interface information starting from pN
. + // Next four lines are required to use GCCGO function naming conventions. + // For Ex: github_com_docker_libkv_store_mock.WatchTree.pN39_github_com_docker_libkv_store_mock.Mock + // uses interface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree + // With GCCGO we need to remove interface information starting from pN
. re := regexp.MustCompile("\\.pN\\d+_") if re.MatchString(functionPath) { functionPath = re.Split(functionPath, -1)[0] @@ -397,7 +402,7 @@ func (m *Mock) Called(arguments ...interface{}) Arguments { // If Call.WaitFor is set, blocks until the channel is closed or receives a message. func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Arguments { m.mutex.Lock() - //TODO: could combine expected and closes in single loop + // TODO: could combine expected and closes in single loop found, call := m.findExpectedCall(methodName, arguments...) if found < 0 { @@ -781,12 +786,12 @@ func (args Arguments) Is(objects ...interface{}) bool { // // Returns the diff string and number of differences found. func (args Arguments) Diff(objects []interface{}) (string, int) { - //TODO: could return string as error and nil for No difference + // TODO: could return string as error and nil for No difference - var output = "\n" + output := "\n" var differences int - var maxArgCount = len(args) + maxArgCount := len(args) if len(objects) > maxArgCount { maxArgCount = len(objects) } @@ -819,14 +824,12 @@ func (args Arguments) Diff(objects []interface{}) (string, int) { output = fmt.Sprintf("%s\t%d: FAIL: %s not matched by %s\n", output, i, actualFmt, matcher) } } else if reflect.TypeOf(expected) == reflect.TypeOf((*AnythingOfTypeArgument)(nil)).Elem() { - // type checking if reflect.TypeOf(actual).Name() != string(expected.(AnythingOfTypeArgument)) && reflect.TypeOf(actual).String() != string(expected.(AnythingOfTypeArgument)) { // not match differences++ output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt) } - } else if reflect.TypeOf(expected) == reflect.TypeOf((*IsTypeArgument)(nil)) { t := expected.(*IsTypeArgument).t if reflect.TypeOf(t) != reflect.TypeOf(actual) { @@ -834,7 +837,6 @@ func (args Arguments) Diff(objects []interface{}) (string, int) { output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, reflect.TypeOf(t).Name(), reflect.TypeOf(actual).Name(), actualFmt) } } else { - // normal checking if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) { @@ -854,7 +856,6 @@ func (args Arguments) Diff(objects []interface{}) (string, int) { } return output, differences - } // Assert compares the arguments with the specified objects and fails if @@ -876,7 +877,6 @@ func (args Arguments) Assert(t TestingT, objects ...interface{}) bool { t.Errorf("%sArguments do not match.", assert.CallerInfo()) return false - } // String gets the argument at the specified index. Panics if there is no argument, or @@ -885,7 +885,6 @@ func (args Arguments) Assert(t TestingT, objects ...interface{}) bool { // If no index is provided, String() returns a complete string representation // of the arguments. func (args Arguments) String(indexOrNil ...int) string { - if len(indexOrNil) == 0 { // normal String() method - return a string representation of the args var argsStr []string @@ -895,7 +894,7 @@ func (args Arguments) String(indexOrNil ...int) string { return strings.Join(argsStr, ",") } else if len(indexOrNil) == 1 { // Index has been specified - get the argument at that index - var index = indexOrNil[0] + index := indexOrNil[0] var s string var ok bool if s, ok = args.Get(index).(string); !ok { @@ -905,7 +904,6 @@ func (args Arguments) String(indexOrNil ...int) string { } panic(fmt.Sprintf("assert: arguments: Wrong number of arguments passed to String. Must be 0 or 1, not %d", len(indexOrNil))) - } // Int gets the argument at the specified index. Panics if there is no argument, or From c33fc8d30db6f6d2c5d270c5023eb0f2181bbab5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jun 2022 20:23:43 +1000 Subject: [PATCH 22/32] Bump actions/checkout from 2 to 3 (#1163) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index aa7994f1f..9038bbd77 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,7 +8,7 @@ jobs: matrix: go_version: ["1.18.1", "1.17.6", "1.16.5"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup Go uses: actions/setup-go@v3.1.0 with: From 07dc7ee5abe6d1c0ef6449f731ca9bf08e1787f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jun 2022 20:32:41 +1000 Subject: [PATCH 23/32] Bump actions/setup-go from 3.1.0 to 3.2.0 (#1191) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v3.1.0...v3.2.0) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9038bbd77..12a23ea61 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Setup Go - uses: actions/setup-go@v3.1.0 + uses: actions/setup-go@v3.2.0 with: go-version: ${{ matrix.go_version }} - run: ./.ci.gogenerate.sh From 840cb801497147a6d30a568e0874dfefb10867a9 Mon Sep 17 00:00:00 2001 From: Adam Luzsi Date: Mon, 20 Jun 2022 12:44:27 +0200 Subject: [PATCH 24/32] arrays value types in a zero-initialized state are considered empty (#1126) * fix .Empty assertion with Array types * refactor .Empty assertion for array types --- assert/assertions.go | 7 ++++--- assert/assertions_test.go | 7 ++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index 0357b2231..580fdea4c 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -563,16 +563,17 @@ func isEmpty(object interface{}) bool { switch objValue.Kind() { // collection types are empty when they have no element - case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: + case reflect.Chan, reflect.Map, reflect.Slice: return objValue.Len() == 0 - // pointers are empty if nil or if the value they point to is empty + // 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 + // for all other types, compare against the zero value + // array types are empty when they match their zero-initialized state default: zero := reflect.Zero(objValue.Type()) return reflect.DeepEqual(object, zero.Interface()) diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 0f5de92ff..7a35a648e 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -1145,6 +1145,7 @@ func Test_isEmpty(t *testing.T) { True(t, isEmpty(new(time.Time))) True(t, isEmpty(time.Time{})) True(t, isEmpty(make(chan struct{}))) + True(t, isEmpty([1]int{})) False(t, isEmpty("something")) False(t, isEmpty(errors.New("something"))) False(t, isEmpty([]string{"something"})) @@ -1152,7 +1153,7 @@ func Test_isEmpty(t *testing.T) { False(t, isEmpty(true)) False(t, isEmpty(map[string]string{"Hello": "World"})) False(t, isEmpty(chWithValue)) - + False(t, isEmpty([1]int{42})) } func TestEmpty(t *testing.T) { @@ -1186,6 +1187,7 @@ func TestEmpty(t *testing.T) { True(t, Empty(mockT, TStruct{}), "struct with zero values is empty") True(t, Empty(mockT, TString("")), "empty aliased string is empty") True(t, Empty(mockT, sP), "ptr to nil value is empty") + True(t, Empty(mockT, [1]int{}), "array is state") False(t, Empty(mockT, "something"), "Non Empty string is not empty") False(t, Empty(mockT, errors.New("something")), "Non nil object is not empty") @@ -1196,6 +1198,7 @@ func TestEmpty(t *testing.T) { False(t, Empty(mockT, TStruct{x: 1}), "struct with initialized values is empty") False(t, Empty(mockT, TString("abc")), "non-empty aliased string is empty") False(t, Empty(mockT, xP), "ptr to non-nil value is not empty") + False(t, Empty(mockT, [1]int{42}), "array is not state") } func TestNotEmpty(t *testing.T) { @@ -1210,6 +1213,7 @@ func TestNotEmpty(t *testing.T) { False(t, NotEmpty(mockT, 0), "Zero int value is empty") False(t, NotEmpty(mockT, false), "False value is empty") False(t, NotEmpty(mockT, make(chan struct{})), "Channel without values is empty") + False(t, NotEmpty(mockT, [1]int{}), "array is state") True(t, NotEmpty(mockT, "something"), "Non Empty string is not empty") True(t, NotEmpty(mockT, errors.New("something")), "Non nil object is not empty") @@ -1217,6 +1221,7 @@ func TestNotEmpty(t *testing.T) { True(t, NotEmpty(mockT, 1), "Non-zero int value is not empty") True(t, NotEmpty(mockT, true), "True value is not empty") True(t, NotEmpty(mockT, chWithValue), "Channel with values is not empty") + True(t, NotEmpty(mockT, [1]int{42}), "array is not state") } func Test_getLen(t *testing.T) { From 48391ba5eb8c5f49132138e67ec112d180b88f63 Mon Sep 17 00:00:00 2001 From: Tony Abboud Date: Mon, 20 Jun 2022 17:08:00 -0400 Subject: [PATCH 25/32] Fix panic in AssertExpectations for mocks without expectations (#1207) Co-authored-by: Tony Abboud --- mock/mock.go | 4 ++++ mock/mock_test.go | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/mock/mock.go b/mock/mock.go index 646c9b5c5..fefc8e985 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -508,6 +508,10 @@ func (m *Mock) AssertExpectations(t TestingT) bool { if h, ok := t.(tHelper); ok { h.Helper() } + if m.mutex == nil { + m.mutex = &sync.Mutex{} + } + m.mutex.Lock() defer m.mutex.Unlock() var somethingMissing bool diff --git a/mock/mock_test.go b/mock/mock_test.go index 91828f813..f8befa87b 100644 --- a/mock/mock_test.go +++ b/mock/mock_test.go @@ -915,6 +915,7 @@ func Test_AssertExpectationsForObjects_Helper(t *testing.T) { var mockedService1 = new(TestExampleImplementation) var mockedService2 = new(TestExampleImplementation) var mockedService3 = new(TestExampleImplementation) + var mockedService4 = new(TestExampleImplementation) // No expectations does not cause a panic mockedService1.On("Test_AssertExpectationsForObjects_Helper", 1).Return() mockedService2.On("Test_AssertExpectationsForObjects_Helper", 2).Return() @@ -924,8 +925,8 @@ func Test_AssertExpectationsForObjects_Helper(t *testing.T) { mockedService2.Called(2) mockedService3.Called(3) - assert.True(t, AssertExpectationsForObjects(t, &mockedService1.Mock, &mockedService2.Mock, &mockedService3.Mock)) - assert.True(t, AssertExpectationsForObjects(t, mockedService1, mockedService2, mockedService3)) + assert.True(t, AssertExpectationsForObjects(t, &mockedService1.Mock, &mockedService2.Mock, &mockedService3.Mock, &mockedService4.Mock)) + assert.True(t, AssertExpectationsForObjects(t, mockedService1, mockedService2, mockedService3, mockedService4)) } From c31ea0312f8a96ca55801db5ebdf62119800fb70 Mon Sep 17 00:00:00 2001 From: Ryan Leung Date: Tue, 21 Jun 2022 12:54:58 +0800 Subject: [PATCH 26/32] Support comparing byte slice (#1202) * support comparing byte slice Signed-off-by: Ryan Leung * address the comment Signed-off-by: Ryan Leung --- assert/assertion_compare.go | 24 ++++- assert/assertion_compare_go1.17_test.go | 128 ++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 1 deletion(-) diff --git a/assert/assertion_compare.go b/assert/assertion_compare.go index 3bb22a971..95d8e59da 100644 --- a/assert/assertion_compare.go +++ b/assert/assertion_compare.go @@ -1,6 +1,7 @@ package assert import ( + "bytes" "fmt" "reflect" "time" @@ -32,7 +33,8 @@ var ( stringType = reflect.TypeOf("") - timeType = reflect.TypeOf(time.Time{}) + timeType = reflect.TypeOf(time.Time{}) + bytesType = reflect.TypeOf([]byte{}) ) func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { @@ -323,6 +325,26 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { return compare(timeObj1.UnixNano(), timeObj2.UnixNano(), reflect.Int64) } + case reflect.Slice: + { + // We only care about the []byte type. + if !canConvert(obj1Value, bytesType) { + break + } + + // []byte can be compared! + bytesObj1, ok := obj1.([]byte) + if !ok { + bytesObj1 = obj1Value.Convert(bytesType).Interface().([]byte) + + } + bytesObj2, ok := obj2.([]byte) + if !ok { + bytesObj2 = obj2Value.Convert(bytesType).Interface().([]byte) + } + + return CompareType(bytes.Compare(bytesObj1, bytesObj2)), true + } } return compareEqual, false diff --git a/assert/assertion_compare_go1.17_test.go b/assert/assertion_compare_go1.17_test.go index d3ea935fa..53e01ed46 100644 --- a/assert/assertion_compare_go1.17_test.go +++ b/assert/assertion_compare_go1.17_test.go @@ -8,6 +8,7 @@ package assert import ( + "bytes" "reflect" "testing" "time" @@ -15,6 +16,7 @@ import ( func TestCompare17(t *testing.T) { type customTime time.Time + type customBytes []byte for _, currCase := range []struct { less interface{} greater interface{} @@ -22,6 +24,8 @@ func TestCompare17(t *testing.T) { }{ {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"}, + {less: []byte{1, 1}, greater: []byte{1, 2}, cType: "[]byte"}, + {less: customBytes([]byte{1, 1}), greater: customBytes([]byte{1, 2}), cType: "[]byte"}, } { resLess, isComparable := compare(currCase.less, currCase.greater, reflect.ValueOf(currCase.less).Kind()) if !isComparable { @@ -52,3 +56,127 @@ func TestCompare17(t *testing.T) { } } } + +func TestGreater17(t *testing.T) { + mockT := new(testing.T) + + if !Greater(mockT, 2, 1) { + t.Error("Greater should return true") + } + + if Greater(mockT, 1, 1) { + t.Error("Greater should return false") + } + + if Greater(mockT, 1, 2) { + t.Error("Greater should return false") + } + + // Check error report + for _, currCase := range []struct { + less interface{} + greater interface{} + msg string + }{ + {less: []byte{1, 1}, greater: []byte{1, 2}, msg: `"[1 1]" is not greater than "[1 2]"`}, + {less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `"0001-01-01 00:00:00 +0000 UTC" is not greater than "0001-01-01 01:00:00 +0000 UTC"`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, Greater(out, currCase.less, currCase.greater)) + Contains(t, out.buf.String(), currCase.msg) + Contains(t, out.helpers, "github.com/stretchr/testify/assert.Greater") + } +} + +func TestGreaterOrEqual17(t *testing.T) { + mockT := new(testing.T) + + if !GreaterOrEqual(mockT, 2, 1) { + t.Error("GreaterOrEqual should return true") + } + + if !GreaterOrEqual(mockT, 1, 1) { + t.Error("GreaterOrEqual should return true") + } + + if GreaterOrEqual(mockT, 1, 2) { + t.Error("GreaterOrEqual should return false") + } + + // Check error report + for _, currCase := range []struct { + less interface{} + greater interface{} + msg string + }{ + {less: []byte{1, 1}, greater: []byte{1, 2}, msg: `"[1 1]" is not greater than or equal to "[1 2]"`}, + {less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `"0001-01-01 00:00:00 +0000 UTC" is not greater than or equal to "0001-01-01 01:00:00 +0000 UTC"`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, GreaterOrEqual(out, currCase.less, currCase.greater)) + Contains(t, out.buf.String(), currCase.msg) + Contains(t, out.helpers, "github.com/stretchr/testify/assert.GreaterOrEqual") + } +} + +func TestLess17(t *testing.T) { + mockT := new(testing.T) + + if !Less(mockT, 1, 2) { + t.Error("Less should return true") + } + + if Less(mockT, 1, 1) { + t.Error("Less should return false") + } + + if Less(mockT, 2, 1) { + t.Error("Less should return false") + } + + // Check error report + for _, currCase := range []struct { + less interface{} + greater interface{} + msg string + }{ + {less: []byte{1, 1}, greater: []byte{1, 2}, msg: `"[1 2]" is not less than "[1 1]"`}, + {less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `"0001-01-01 01:00:00 +0000 UTC" is not less than "0001-01-01 00:00:00 +0000 UTC"`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, Less(out, currCase.greater, currCase.less)) + Contains(t, out.buf.String(), currCase.msg) + Contains(t, out.helpers, "github.com/stretchr/testify/assert.Less") + } +} + +func TestLessOrEqual17(t *testing.T) { + mockT := new(testing.T) + + if !LessOrEqual(mockT, 1, 2) { + t.Error("LessOrEqual should return true") + } + + if !LessOrEqual(mockT, 1, 1) { + t.Error("LessOrEqual should return true") + } + + if LessOrEqual(mockT, 2, 1) { + t.Error("LessOrEqual should return false") + } + + // Check error report + for _, currCase := range []struct { + less interface{} + greater interface{} + msg string + }{ + {less: []byte{1, 1}, greater: []byte{1, 2}, msg: `"[1 2]" is not less than or equal to "[1 1]"`}, + {less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `"0001-01-01 01:00:00 +0000 UTC" is not less than or equal to "0001-01-01 00:00:00 +0000 UTC"`}, + } { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, LessOrEqual(out, currCase.greater, currCase.less)) + Contains(t, out.buf.String(), currCase.msg) + Contains(t, out.helpers, "github.com/stretchr/testify/assert.LessOrEqual") + } +} From ba1076d8b3b67cdaf7bf92c95b3641636a039be2 Mon Sep 17 00:00:00 2001 From: Paul Dufour Date: Wed, 22 Jun 2022 10:31:35 +0100 Subject: [PATCH 27/32] Add .Unset method to mock (#982) * Add .Off method to mock * Update README.md * Update mock.go * Update mock_test.go * Update README.md * Fix tests * Add unset test * remove prints * fix test * update readme --- README.md | 25 +++++++++++++++ mock/mock.go | 37 ++++++++++++++++++++++ mock/mock_test.go | 81 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) diff --git a/README.md b/README.md index 6b9ff9ad3..ce6d3de28 100644 --- a/README.md +++ b/README.md @@ -190,6 +190,31 @@ func TestSomethingWithPlaceholder(t *testing.T) { } + +// TestSomethingElse2 is a third example that shows how you can use +// the Unset method to cleanup handlers and then add new ones. +func TestSomethingElse2(t *testing.T) { + + // create an instance of our test object + testObj := new(MyMockedObject) + + // setup expectations with a placeholder in the argument list + mockCall := testObj.On("DoSomething", mock.Anything).Return(true, nil) + + // call the code we are testing + targetFuncThatDoesSomethingWithObj(testObj) + + // assert that the expectations were met + testObj.AssertExpectations(t) + + // remove the handler now so we can add another one that takes precedence + mockCall.Unset() + + // return false now instead of true + testObj.On("DoSomething", mock.Anything).Return(false, nil) + + testObj.AssertExpectations(t) +} ``` For more information on how to write mock code, check out the [API documentation for the `mock` package](http://godoc.org/github.com/stretchr/testify/mock). diff --git a/mock/mock.go b/mock/mock.go index fefc8e985..769aed8b3 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -199,6 +199,43 @@ func (c *Call) On(methodName string, arguments ...interface{}) *Call { return c.Parent.On(methodName, arguments...) } +// Unset removes a mock handler from being called. +// test.On("func", mock.Anything).Unset() +func (c *Call) Unset() *Call { + var unlockOnce sync.Once + + for _, arg := range c.Arguments { + if v := reflect.ValueOf(arg); v.Kind() == reflect.Func { + panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg)) + } + } + + c.lock() + defer unlockOnce.Do(c.unlock) + + foundMatchingCall := false + + for i, call := range c.Parent.ExpectedCalls { + if call.Method == c.Method { + _, diffCount := call.Arguments.Diff(c.Arguments) + if diffCount == 0 { + foundMatchingCall = true + // Remove from ExpectedCalls + c.Parent.ExpectedCalls = append(c.Parent.ExpectedCalls[:i], c.Parent.ExpectedCalls[i+1:]...) + } + } + } + + if !foundMatchingCall { + unlockOnce.Do(c.unlock) + c.Parent.fail("\n\nmock: Could not find expected call\n-----------------------------\n\n%s\n\n", + callString(c.Method, c.Arguments, true), + ) + } + + return c +} + // Mock is the workhorse used to track activity on another object. // For an example of its usage, refer to the "Example Usage" section at the top // of this document. diff --git a/mock/mock_test.go b/mock/mock_test.go index f8befa87b..211568690 100644 --- a/mock/mock_test.go +++ b/mock/mock_test.go @@ -462,6 +462,87 @@ func Test_Mock_On_WithFuncTypeArg(t *testing.T) { }) } +func Test_Mock_Unset(t *testing.T) { + // make a test impl object + var mockedService = new(TestExampleImplementation) + + call := mockedService. + On("TheExampleMethodFuncType", "argA"). + Return("blah") + + found, foundCall := mockedService.findExpectedCall("TheExampleMethodFuncType", "argA") + require.NotEqual(t, -1, found) + require.Equal(t, foundCall, call) + + call.Unset() + + found, foundCall = mockedService.findExpectedCall("TheExampleMethodFuncType", "argA") + require.Equal(t, -1, found) + + var expectedCall *Call + require.Equal(t, expectedCall, foundCall) + + fn := func(string) error { return nil } + assert.Panics(t, func() { + mockedService.TheExampleMethodFuncType(fn) + }) +} + +// Since every time you call On it creates a new object +// the last time you call Unset it will only unset the last call +func Test_Mock_Chained_UnsetOnlyUnsetsLastCall(t *testing.T) { + // make a test impl object + var mockedService = new(TestExampleImplementation) + + // determine our current line number so we can assert the expected calls callerInfo properly + _, _, line, _ := runtime.Caller(0) + mockedService. + On("TheExampleMethod1", 1, 1). + Return(0). + On("TheExampleMethod2", 2, 2). + On("TheExampleMethod3", 3, 3, 3). + Return(nil). + Unset() + + expectedCalls := []*Call{ + { + Parent: &mockedService.Mock, + Method: "TheExampleMethod1", + Arguments: []interface{}{1, 1}, + ReturnArguments: []interface{}{0}, + callerInfo: []string{fmt.Sprintf("mock_test.go:%d", line+2)}, + }, + { + Parent: &mockedService.Mock, + Method: "TheExampleMethod2", + Arguments: []interface{}{2, 2}, + ReturnArguments: []interface{}{}, + callerInfo: []string{fmt.Sprintf("mock_test.go:%d", line+4)}, + }, + } + assert.Equal(t, 2, len(expectedCalls)) + assert.Equal(t, expectedCalls, mockedService.ExpectedCalls) +} + +func Test_Mock_UnsetIfAlreadyUnsetFails(t *testing.T) { + // make a test impl object + var mockedService = new(TestExampleImplementation) + + mock1 := mockedService. + On("TheExampleMethod1", 1, 1). + Return(1) + + assert.Equal(t, 1, len(mockedService.ExpectedCalls)) + mock1.Unset() + assert.Equal(t, 0, len(mockedService.ExpectedCalls)) + + assert.Panics(t, func() { + mock1.Unset() + }) + + assert.Equal(t, 0, len(mockedService.ExpectedCalls)) +} + func Test_Mock_Return(t *testing.T) { // make a test impl object From 1b73601ae8d1c3e389e93092f595b1f6e3d68251 Mon Sep 17 00:00:00 2001 From: nicoche <78445450+nicoche@users.noreply.github.com> Date: Thu, 23 Jun 2022 11:40:15 +0200 Subject: [PATCH 28/32] suite: correctly set stats on test panic (#1195) --- suite/suite.go | 15 +++++++++++---- suite/suite_test.go | 29 +++++++++++++++++++++++------ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/suite/suite.go b/suite/suite.go index 1c402e8df..895591878 100644 --- a/suite/suite.go +++ b/suite/suite.go @@ -67,8 +67,12 @@ func (suite *Suite) Assert() *assert.Assertions { return suite.Assertions } -func failOnPanic(t *testing.T) { +func recoverAndFailOnPanic(t *testing.T) { r := recover() + failOnPanic(t, r) +} + +func failOnPanic(t *testing.T, r interface{}) { if r != nil { t.Errorf("test panicked: %v\n%s", r, debug.Stack()) t.FailNow() @@ -91,7 +95,7 @@ func (suite *Suite) Run(name string, subtest func()) bool { // Run takes a testing suite and runs all of the tests attached // to it. func Run(t *testing.T, suite TestingSuite) { - defer failOnPanic(t) + defer recoverAndFailOnPanic(t) suite.SetT(t) @@ -136,10 +140,12 @@ func Run(t *testing.T, suite TestingSuite) { F: func(t *testing.T) { parentT := suite.T() suite.SetT(t) - defer failOnPanic(t) + defer recoverAndFailOnPanic(t) defer func() { + r := recover() + if stats != nil { - passed := !t.Failed() + passed := !t.Failed() && r == nil stats.end(method.Name, passed) } @@ -152,6 +158,7 @@ func Run(t *testing.T, suite TestingSuite) { } suite.SetT(parentT) + failOnPanic(t, r) }() if setupTestSuite, ok := suite.(SetupTestSuite); ok { diff --git a/suite/suite_test.go b/suite/suite_test.go index 963a25258..446029a43 100644 --- a/suite/suite_test.go +++ b/suite/suite_test.go @@ -501,19 +501,36 @@ func (s *suiteWithStats) TestSomething() { s.Equal(1, 1) } +func (s *suiteWithStats) TestPanic() { + panic("oops") +} + func TestSuiteWithStats(t *testing.T) { suiteWithStats := new(suiteWithStats) - Run(t, suiteWithStats) + + testing.RunTests(allTestsFilter, []testing.InternalTest{ + { + Name: "WithStats", + F: func(t *testing.T) { + Run(t, suiteWithStats) + }, + }, + }) assert.True(t, suiteWithStats.wasCalled) assert.NotZero(t, suiteWithStats.stats.Start) assert.NotZero(t, suiteWithStats.stats.End) - assert.True(t, suiteWithStats.stats.Passed()) + assert.False(t, suiteWithStats.stats.Passed()) + + testStats := suiteWithStats.stats.TestStats + + assert.NotZero(t, testStats["TestSomething"].Start) + assert.NotZero(t, testStats["TestSomething"].End) + assert.True(t, testStats["TestSomething"].Passed) - testStats := suiteWithStats.stats.TestStats["TestSomething"] - assert.NotZero(t, testStats.Start) - assert.NotZero(t, testStats.End) - assert.True(t, testStats.Passed) + assert.NotZero(t, testStats["TestPanic"].Start) + assert.NotZero(t, testStats["TestPanic"].End) + assert.False(t, testStats["TestPanic"].Passed) } // FailfastSuite will test the behavior when running with the failfast flag From c206b2e823e70c1e4e7ca8eded9e410acc8f71be Mon Sep 17 00:00:00 2001 From: Bracken Date: Thu, 23 Jun 2022 10:42:21 +0100 Subject: [PATCH 29/32] Mock can be deadlocked by a panic (#1157) If an argumentMatcher function panics and AssertExpectations is deferred then the test would deadlock. --- mock/mock.go | 11 ++++++++++- mock/mock_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/mock/mock.go b/mock/mock.go index 769aed8b3..ff8111a58 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -858,7 +858,16 @@ func (args Arguments) Diff(objects []interface{}) (string, int) { } if matcher, ok := expected.(argumentMatcher); ok { - if matcher.Matches(actual) { + var matches bool + func() { + defer func() { + if r := recover(); r != nil { + actualFmt = fmt.Sprintf("panic in argument matcher: %v", r) + } + }() + matches = matcher.Matches(actual) + }() + if matches { output = fmt.Sprintf("%s\t%d: PASS: %s matched by %s\n", output, i, actualFmt, matcher) } else { differences++ diff --git a/mock/mock_test.go b/mock/mock_test.go index 211568690..06b25012e 100644 --- a/mock/mock_test.go +++ b/mock/mock_test.go @@ -233,6 +233,32 @@ func Test_Mock_On_WithIntArgMatcher(t *testing.T) { }) } +func Test_Mock_On_WithArgMatcherThatPanics(t *testing.T) { + var mockedService TestExampleImplementation + + mockedService.On("TheExampleMethod2", MatchedBy(func(_ interface{}) bool { + panic("try to lock mockedService") + })).Return() + + defer func() { + assertedExpectations := make(chan struct{}) + go func() { + tt := new(testing.T) + mockedService.AssertExpectations(tt) + close(assertedExpectations) + }() + select { + case <-assertedExpectations: + case <-time.After(time.Second): + t.Fatal("AssertExpectations() deadlocked, did the panic leave mockedService locked?") + } + }() + + assert.Panics(t, func() { + mockedService.TheExampleMethod2(false) + }) +} + func TestMock_WithTest(t *testing.T) { var ( mockedService TestExampleImplementation From b5ce16571001d6e96e1693ac891fed5c2510c651 Mon Sep 17 00:00:00 2001 From: Edward Raigosa Date: Thu, 23 Jun 2022 17:11:59 -0700 Subject: [PATCH 30/32] fixing panic in calls to assertion with nil m.mutex (#1212) * fixing panic in calls to assertion with nil m.mutex This reverts a change that was made in https://github.com/stretchr/testify/pull/1182 The PR makes m.mutex a pointer which now needs to be checked but it's not checked for nil everywhere. This should also help with these issues: - https://github.com/stretchr/testify/issues/1208 - https://github.com/stretchr/testify/issues/1210 * Revert throwing out the lock because other concurrent calls can already have it locked * fix go vet copy lock by using pointer * fix obj assignment for passing test --- mock/mock.go | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/mock/mock.go b/mock/mock.go index ff8111a58..3833cb6f0 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -255,7 +255,7 @@ type Mock struct { // this data completely allowing you to do whatever you like with it. testData objx.Map - mutex *sync.Mutex + mutex sync.Mutex } // String provides a %v format string for Mock. @@ -282,10 +282,6 @@ func (m *Mock) TestData() objx.Map { // Test sets the test struct variable of the mock object func (m *Mock) Test(t TestingT) { - if m.mutex == nil { - m.mutex = &sync.Mutex{} - } - m.mutex.Lock() defer m.mutex.Unlock() m.test = t @@ -316,9 +312,6 @@ func (m *Mock) On(methodName string, arguments ...interface{}) *Call { } } - // Since we start mocks with the .On() function, m.mutex should be reset - m.mutex = &sync.Mutex{} - m.mutex.Lock() defer m.mutex.Unlock() c := newCall(m, methodName, assert.CallerInfo(), arguments...) @@ -526,9 +519,9 @@ func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool { h.Helper() } for _, obj := range testObjects { - if m, ok := obj.(Mock); ok { + if m, ok := obj.(*Mock); ok { t.Logf("Deprecated mock.AssertExpectationsForObjects(myMock.Mock) use mock.AssertExpectationsForObjects(myMock)") - obj = &m + obj = m } m := obj.(assertExpectationser) if !m.AssertExpectations(t) { @@ -545,9 +538,6 @@ func (m *Mock) AssertExpectations(t TestingT) bool { if h, ok := t.(tHelper); ok { h.Helper() } - if m.mutex == nil { - m.mutex = &sync.Mutex{} - } m.mutex.Lock() defer m.mutex.Unlock() From 2fab6dffcf101c5a79fc8f3cd67a3541432db5fd Mon Sep 17 00:00:00 2001 From: Ruan Moolman Date: Tue, 28 Jun 2022 13:14:39 +0200 Subject: [PATCH 31/32] Add WithinTimeRange method (#1188) * Add WithinTimeRange method * Run ./.ci.generate.sh * Rename WithinTimeRange to WithinRange * Rename WithinRange expected parameter to actual * Capitalise start parameter at start of error message * Improve WithinRange example --- assert/assertion_format.go | 10 ++++++++++ assert/assertion_forward.go | 20 ++++++++++++++++++++ assert/assertions.go | 21 +++++++++++++++++++++ assert/assertions_test.go | 19 +++++++++++++++++++ require/require.go | 26 ++++++++++++++++++++++++++ require/require_forward.go | 20 ++++++++++++++++++++ 6 files changed, 116 insertions(+) diff --git a/assert/assertion_format.go b/assert/assertion_format.go index 27e2420ed..7880b8f94 100644 --- a/assert/assertion_format.go +++ b/assert/assertion_format.go @@ -736,6 +736,16 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) } +// WithinRangef asserts that a time is within a time range (inclusive). +// +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return WithinRange(t, actual, start, end, append([]interface{}{msg}, args...)...) +} + // YAMLEqf asserts that two YAML strings are equivalent. func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { diff --git a/assert/assertion_forward.go b/assert/assertion_forward.go index d9ea368d0..339515b8b 100644 --- a/assert/assertion_forward.go +++ b/assert/assertion_forward.go @@ -1461,6 +1461,26 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta return WithinDurationf(a.t, expected, actual, delta, msg, args...) } +// WithinRange asserts that a time is within a time range (inclusive). +// +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return WithinRange(a.t, actual, start, end, msgAndArgs...) +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return WithinRangef(a.t, actual, start, end, msg, args...) +} + // YAMLEq asserts that two YAML strings are equivalent. func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { diff --git a/assert/assertions.go b/assert/assertions.go index 580fdea4c..1fcb980f4 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1110,6 +1110,27 @@ func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, return true } +// WithinRange asserts that a time is within a time range (inclusive). +// +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if end.Before(start) { + return Fail(t, "Start should be before end", msgAndArgs...) + } + + if actual.Before(start) { + return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is before the range", actual, start, end), msgAndArgs...) + } else if actual.After(end) { + return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is after the range", actual, start, end), msgAndArgs...) + } + + return true +} + func toFloat(x interface{}) (float64, bool) { var xf float64 xok := true diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 7a35a648e..f407422d8 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -1350,6 +1350,25 @@ func TestWithinDuration(t *testing.T) { False(t, WithinDuration(mockT, b, a, -11*time.Second), "A 10s difference is not within a 9s time difference") } +func TestWithinRange(t *testing.T) { + + mockT := new(testing.T) + n := time.Now() + s := n.Add(-time.Second) + e := n.Add(time.Second) + + True(t, WithinRange(mockT, n, n, n), "Exact same actual, start, and end values return true") + + True(t, WithinRange(mockT, n, s, e), "Time in range is within the time range") + True(t, WithinRange(mockT, s, s, e), "The start time is within the time range") + True(t, WithinRange(mockT, e, s, e), "The end time is within the time range") + + False(t, WithinRange(mockT, s.Add(-time.Nanosecond), s, e, "Just before the start time is not within the time range")) + False(t, WithinRange(mockT, e.Add(time.Nanosecond), s, e, "Just after the end time is not within the time range")) + + False(t, WithinRange(mockT, n, e, s, "Just after the end time is not within the time range")) +} + func TestInDelta(t *testing.T) { mockT := new(testing.T) diff --git a/require/require.go b/require/require.go index 59c48277a..880853f5a 100644 --- a/require/require.go +++ b/require/require.go @@ -1864,6 +1864,32 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim t.FailNow() } +// WithinRange asserts that a time is within a time range (inclusive). +// +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.WithinRange(t, actual, start, end, msgAndArgs...) { + return + } + t.FailNow() +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.WithinRangef(t, actual, start, end, msg, args...) { + return + } + t.FailNow() +} + // YAMLEq asserts that two YAML strings are equivalent. func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { diff --git a/require/require_forward.go b/require/require_forward.go index 5bb07c89c..960bf6f2c 100644 --- a/require/require_forward.go +++ b/require/require_forward.go @@ -1462,6 +1462,26 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta WithinDurationf(a.t, expected, actual, delta, msg, args...) } +// WithinRange asserts that a time is within a time range (inclusive). +// +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinRange(a.t, actual, start, end, msgAndArgs...) +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinRangef(a.t, actual, start, end, msg, args...) +} + // YAMLEq asserts that two YAML strings are equivalent. func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { From 66eef0ef3adb4691cdc275620c9a129f2f98935a Mon Sep 17 00:00:00 2001 From: Oladapo Ajala Date: Tue, 28 Jun 2022 12:24:53 +0100 Subject: [PATCH 32/32] fix: assert.MapSubset (or just support maps in assert.Subset) (#1178) * WIP: added map key value check in subset * upgraded subset & notsubset to check handle maps --- assert/assertions.go | 46 ++++++++++++++++++++++++++++++++++----- assert/assertions_test.go | 16 ++++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index 1fcb980f4..6355dd2cf 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -816,7 +816,6 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok 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 @@ -826,14 +825,32 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok listKind := reflect.TypeOf(list).Kind() subsetKind := reflect.TypeOf(subset).Kind() - if listKind != reflect.Array && listKind != reflect.Slice { + if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } - if subsetKind != reflect.Array && subsetKind != reflect.Slice { + if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } + subsetValue := reflect.ValueOf(subset) + if subsetKind == reflect.Map && listKind == reflect.Map { + listValue := reflect.ValueOf(list) + subsetKeys := subsetValue.MapKeys() + + for i := 0; i < len(subsetKeys); i++ { + subsetKey := subsetKeys[i] + subsetElement := subsetValue.MapIndex(subsetKey).Interface() + listElement := listValue.MapIndex(subsetKey).Interface() + + if !ObjectsAreEqual(subsetElement, listElement) { + return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, subsetElement), msgAndArgs...) + } + } + + return true + } + for i := 0; i < subsetValue.Len(); i++ { element := subsetValue.Index(i).Interface() ok, found := containsElement(list, element) @@ -860,7 +877,6 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) return Fail(t, "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 @@ -870,14 +886,32 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) listKind := reflect.TypeOf(list).Kind() subsetKind := reflect.TypeOf(subset).Kind() - if listKind != reflect.Array && listKind != reflect.Slice { + if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } - if subsetKind != reflect.Array && subsetKind != reflect.Slice { + if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } + subsetValue := reflect.ValueOf(subset) + if subsetKind == reflect.Map && listKind == reflect.Map { + listValue := reflect.ValueOf(list) + subsetKeys := subsetValue.MapKeys() + + for i := 0; i < len(subsetKeys); i++ { + subsetKey := subsetKeys[i] + subsetElement := subsetValue.MapIndex(subsetKey).Interface() + listElement := listValue.MapIndex(subsetKey).Interface() + + if !ObjectsAreEqual(subsetElement, listElement) { + return true + } + } + + return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) + } + for i := 0; i < subsetValue.Len(); i++ { element := subsetValue.Index(i).Interface() ok, found := containsElement(list, element) diff --git a/assert/assertions_test.go b/assert/assertions_test.go index f407422d8..bdab184c1 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -685,11 +685,27 @@ func TestSubsetNotSubset(t *testing.T) { {[]int{1, 2, 3}, []int{1, 2}, true, "[1, 2, 3] contains [1, 2]"}, {[]int{1, 2, 3}, []int{1, 2, 3}, true, "[1, 2, 3] contains [1, 2, 3"}, {[]string{"hello", "world"}, []string{"hello"}, true, "[\"hello\", \"world\"] contains [\"hello\"]"}, + {map[string]string{ + "a": "x", + "c": "z", + "b": "y", + }, map[string]string{ + "a": "x", + "b": "y", + }, true, `{ "a": "x", "b": "y", "c": "z"} contains { "a": "x", "b": "y"}`}, // cases that are expected not to contain {[]string{"hello", "world"}, []string{"hello", "testify"}, false, "[\"hello\", \"world\"] does not contain [\"hello\", \"testify\"]"}, {[]int{1, 2, 3}, []int{4, 5}, false, "[1, 2, 3] does not contain [4, 5"}, {[]int{1, 2, 3}, []int{1, 5}, false, "[1, 2, 3] does not contain [1, 5]"}, + {map[string]string{ + "a": "x", + "c": "z", + "b": "y", + }, map[string]string{ + "a": "x", + "b": "z", + }, false, `{ "a": "x", "b": "y", "c": "z"} does not contain { "a": "x", "b": "z"}`}, } for _, c := range cases {