From c740480fc1a1692280f2f62cc20af82777301030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Sun, 30 Jul 2023 20:51:12 +0200 Subject: [PATCH 01/34] mock: refactor TestIsArgsEqual Refactor tests to use 'append' to build copies of slices instead of manual loops. Thanks golangci-lint. --- mock/mock_test.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/mock/mock_test.go b/mock/mock_test.go index 77493c5..52d20be 100644 --- a/mock/mock_test.go +++ b/mock/mock_test.go @@ -1616,17 +1616,14 @@ func Test_Mock_IsMethodCallable(t *testing.T) { func TestIsArgsEqual(t *testing.T) { var expected = Arguments{5, 3, 4, 6, 7, 2} - var args = make([]interface{}, 5) - for i := 1; i < len(expected); i++ { - args[i-1] = expected[i] - } + + // Copy elements 1 to 5 + args := append(([]interface{})(nil), expected[1:]...) args[2] = expected[1] assert.False(t, isArgsEqual(expected, args)) - var arr = make([]interface{}, 6) - for i := 0; i < len(expected); i++ { - arr[i] = expected[i] - } + // Clone + arr := append(([]interface{})(nil), expected...) assert.True(t, isArgsEqual(expected, arr)) } From 975128c5e6d4ae1304923f0cb23259562941d362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Wed, 9 Aug 2023 16:18:00 +0200 Subject: [PATCH 02/34] deps: upgrade objx to v0.5.1 to fix dep cycle See https://github.com/stretchr/objx/pull/140 go get github.com/stretchr/objx@v0.5.1 --- go.mod | 2 +- go.sum | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index d3c4d72..6b9e9cd 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,6 @@ go 1.17 require ( github.com/davecgh/go-spew v1.1.1 github.com/pmezard/go-difflib v1.0.0 - github.com/stretchr/objx v0.5.0 + github.com/stretchr/objx v0.5.1 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 4f3ced6..abf818b 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,12 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= +github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 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= From 838caeaac1310237cf55515de2c995f4e4845270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Wed, 9 Aug 2023 16:24:51 +0200 Subject: [PATCH 03/34] deps: exclude old testify to break the dependency cycle from objx In go.mod exclude the old version of testify brought by objx. This allows to break the dependency cycle and completely remove the dependency link to old versions of dependencies (some of which had security issues). Closes #1292. go mod edit -exclude=github.com/stretchr/testify@v1.8.2 && go.mod --- go.mod | 4 ++++ go.sum | 8 -------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 6b9e9cd..9133c4a 100644 --- a/go.mod +++ b/go.mod @@ -10,3 +10,7 @@ require ( github.com/stretchr/objx v0.5.1 gopkg.in/yaml.v3 v3.0.1 ) + +// Break dependency cycle with objx. +// See https://github.com/stretchr/objx/pull/140 +exclude github.com/stretchr/testify v1.8.2 diff --git a/go.sum b/go.sum index abf818b..f218355 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,10 @@ -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/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 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 413628c0f4f661b7be122c9c01cbc858cc701822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Mon, 10 Jul 2023 22:12:34 +0200 Subject: [PATCH 04/34] mock: simplify Arguments.Diff Use a type switch instead of reflect.TypeOf comparisons. --- mock/mock.go | 80 +++++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/mock/mock.go b/mock/mock.go index 3234436..9a81459 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -960,53 +960,55 @@ func (args Arguments) Diff(objects []interface{}) (string, int) { differences++ 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) { - differences++ - 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 if reflect.TypeOf(expected) == reflect.TypeOf((*FunctionalOptionsArgument)(nil)) { - t := expected.(*FunctionalOptionsArgument).value + } else { + switch expected := expected.(type) { + case anythingOfTypeArgument: + // type checking + if reflect.TypeOf(actual).Name() != string(expected) && reflect.TypeOf(actual).String() != string(expected) { + // not match + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt) + } + case *IsTypeArgument: + t := expected.t + if reflect.TypeOf(t) != reflect.TypeOf(actual) { + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, reflect.TypeOf(t).Name(), reflect.TypeOf(actual).Name(), actualFmt) + } + case *FunctionalOptionsArgument: + t := expected.value - var name string - tValue := reflect.ValueOf(t) - if tValue.Len() > 0 { - name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String() - } + var name string + tValue := reflect.ValueOf(t) + if tValue.Len() > 0 { + name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String() + } - tName := reflect.TypeOf(t).Name() - if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 { - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt) - } else { - if ef, af := assertOpts(t, actual); ef == "" && af == "" { + tName := reflect.TypeOf(t).Name() + if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 { + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt) + } else { + if ef, af := assertOpts(t, actual); ef == "" && af == "" { + // match + output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName) + } else { + // not match + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, af, ef) + } + } + + default: + if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) { // match - output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName) + output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, actualFmt, expectedFmt) } else { // not match differences++ - output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, af, ef) + output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, actualFmt, expectedFmt) } } - } else { - // normal checking - - if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) { - // match - output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, actualFmt, expectedFmt) - } else { - // not match - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, actualFmt, expectedFmt) - } } } From 307c9344b84736ff4bc5a03c22908a2450fa131c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Mon, 10 Jul 2023 22:49:33 +0200 Subject: [PATCH 05/34] mock: refactor IsType Reduce calls to reflect.Type in implementation of mock.IsType by extracting the type early. This also avoids to keep alive references to the argument value. --- mock/mock.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mock/mock.go b/mock/mock.go index 9a81459..5e35861 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -786,7 +786,7 @@ func AnythingOfType(t string) AnythingOfTypeArgument { // for use when type checking. This is an alternative to AnythingOfType. // Used in Diff and Assert. type IsTypeArgument struct { - t interface{} + t reflect.Type } // IsType returns an IsTypeArgument object containing the type to check for. @@ -796,7 +796,7 @@ type IsTypeArgument struct { // For example: // Assert(t, IsType(""), IsType(0)) func IsType(t interface{}) *IsTypeArgument { - return &IsTypeArgument{t: t} + return &IsTypeArgument{t: reflect.TypeOf(t)} } // FunctionalOptionsArgument is a struct that contains the type and value of an functional option argument @@ -970,10 +970,10 @@ func (args Arguments) Diff(objects []interface{}) (string, int) { output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt) } case *IsTypeArgument: - t := expected.t - if reflect.TypeOf(t) != reflect.TypeOf(actual) { + actualT := reflect.TypeOf(actual) + if actualT != expected.t { differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, reflect.TypeOf(t).Name(), reflect.TypeOf(actual).Name(), actualFmt) + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected.t.Name(), actualT.Name(), actualFmt) } case *FunctionalOptionsArgument: t := expected.value From ab3b9743a738c228e875cb71b3c285698a908885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Tue, 10 Oct 2023 03:32:57 +0200 Subject: [PATCH 06/34] assert.InEpsilonSlice: refactor --- assert/assertions.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index 1e55fbf..44a1625 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1466,14 +1466,18 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m if h, ok := t.(tHelper); ok { h.Helper() } - if expected == nil || actual == nil || - reflect.TypeOf(actual).Kind() != reflect.Slice || - reflect.TypeOf(expected).Kind() != reflect.Slice { + + if expected == nil || actual == nil { return Fail(t, "Parameters must be slice", msgAndArgs...) } - actualSlice := reflect.ValueOf(actual) expectedSlice := reflect.ValueOf(expected) + actualSlice := reflect.ValueOf(actual) + + if expectedSlice.Type().Kind() != reflect.Slice || + actualSlice.Type().Kind() != reflect.Slice { + return Fail(t, "Parameters must be slice", msgAndArgs...) + } for i := 0; i < actualSlice.Len(); i++ { result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), epsilon) From b5dec80529a3053e277fad8f82f3814b9c495f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Tue, 10 Oct 2023 03:33:27 +0200 Subject: [PATCH 07/34] assert.InEpsilonSlice: add more slice checks --- assert/assertions.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assert/assertions.go b/assert/assertions.go index 44a1625..5f6f742 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1479,6 +1479,10 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m return Fail(t, "Parameters must be slice", msgAndArgs...) } + if !IsType(t, expected, actual) || !Len(t, actual, expectedSlice.Len()) { + return false + } + for i := 0; i < actualSlice.Len(); i++ { result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), epsilon) if !result { From f728d3c50f6131b37d2cfeec52bc33937f1b3aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Tue, 10 Oct 2023 03:37:39 +0200 Subject: [PATCH 08/34] assert.InEpsilonSlice: refactor Remove multiple calls to reflect.Value.Len() --- assert/assertions.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index 5f6f742..be6cc26 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1479,11 +1479,12 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m return Fail(t, "Parameters must be slice", msgAndArgs...) } - if !IsType(t, expected, actual) || !Len(t, actual, expectedSlice.Len()) { + expectedLen := expectedSlice.Len() + if !IsType(t, expected, actual) || !Len(t, actual, expectedLen) { return false } - for i := 0; i < actualSlice.Len(); i++ { + for i := 0; i < expectedLen; i++ { result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), epsilon) if !result { return result From 8fd5aae061631fe8427f9f06e163574032ec1011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Tue, 10 Oct 2023 03:40:29 +0200 Subject: [PATCH 09/34] assert.InEpsilonSlice: remove redundant check Remove check of actual's kind redundant with Type()'s check. --- assert/assertions.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index be6cc26..be3f4f5 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1474,9 +1474,8 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m expectedSlice := reflect.ValueOf(expected) actualSlice := reflect.ValueOf(actual) - if expectedSlice.Type().Kind() != reflect.Slice || - actualSlice.Type().Kind() != reflect.Slice { - return Fail(t, "Parameters must be slice", msgAndArgs...) + if expectedSlice.Type().Kind() != reflect.Slice { + return Fail(t, "Expected value must be slice", msgAndArgs...) } expectedLen := expectedSlice.Len() From f7fbc7da15bc38f2f8ff9b955969e22ec2041dda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Tue, 10 Oct 2023 03:48:32 +0200 Subject: [PATCH 10/34] assert.InEpsilonSlice: fix order of expected vs actual (#1231) --- assert/assertions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assert/assertions.go b/assert/assertions.go index be3f4f5..28f7751 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1484,7 +1484,7 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m } for i := 0; i < expectedLen; i++ { - result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), epsilon) + result := InEpsilon(t, expectedSlice.Index(i).Interface(), actualSlice.Index(i).Interface(), epsilon) if !result { return result } From 7d383ba73269a8751aedc9e8ca0b3280935db217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Tue, 10 Oct 2023 04:18:28 +0200 Subject: [PATCH 11/34] assert.InEpsilonSlice: refactor --- assert/assertions.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index 28f7751..e78ac31 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1484,9 +1484,8 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m } for i := 0; i < expectedLen; i++ { - result := InEpsilon(t, expectedSlice.Index(i).Interface(), actualSlice.Index(i).Interface(), epsilon) - if !result { - return result + if !InEpsilon(t, expectedSlice.Index(i).Interface(), actualSlice.Index(i).Interface(), epsilon) { + return false } } From f8dcfd66187e703f37d04bf2b17a55c20e4029aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Tue, 10 Oct 2023 04:22:18 +0200 Subject: [PATCH 12/34] assert.InEpsilonSlice: mention index of error in fail message --- assert/assertions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assert/assertions.go b/assert/assertions.go index e78ac31..e92f588 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1484,7 +1484,7 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m } for i := 0; i < expectedLen; i++ { - if !InEpsilon(t, expectedSlice.Index(i).Interface(), actualSlice.Index(i).Interface(), epsilon) { + if !InEpsilon(t, expectedSlice.Index(i).Interface(), actualSlice.Index(i).Interface(), epsilon, "at index %d", i) { return false } } From 7f962d56e40d4f3820b7807c08bf72b1e2fdc75b Mon Sep 17 00:00:00 2001 From: Kevin Burke Date: Mon, 16 Oct 2023 10:10:28 -0700 Subject: [PATCH 13/34] .github: use latest Go versions Also update the runners to their latest versions. --- .github/workflows/main.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 08796ae..be8b4af 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,8 +7,8 @@ jobs: strategy: matrix: go_version: - - "1.19" - "1.20" + - "1.21" steps: - uses: actions/checkout@v4 - name: Setup Go @@ -28,6 +28,7 @@ jobs: - "1.18" - "1.19" - "1.20" + - "1.21" steps: - uses: actions/checkout@v4 - name: Setup Go From fc1dee9921753b979dfb6f325823627130685158 Mon Sep 17 00:00:00 2001 From: Harald Nordgren Date: Mon, 16 Oct 2023 17:30:47 +0200 Subject: [PATCH 14/34] Deprecate EqualExportedValues --- assert/assertions.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assert/assertions.go b/assert/assertions.go index 1e55fbf..6843d42 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -150,6 +150,8 @@ func copyExportedFields(expected interface{}) interface{} { // structures. // // This function does no assertion of any kind. +// +// Deprecated: Use [EqualExportedValues] instead. func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool { expectedCleaned := copyExportedFields(expected) actualCleaned := copyExportedFields(actual) From 375474cd3c1f5e46920e0e80227bb52d346b032c Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Tue, 26 Sep 2023 11:17:11 +0300 Subject: [PATCH 15/34] suite: refactor test assertions --- suite/suite_test.go | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/suite/suite_test.go b/suite/suite_test.go index d684f52..8ec505f 100644 --- a/suite/suite_test.go +++ b/suite/suite_test.go @@ -34,7 +34,7 @@ func TestSuiteRequireTwice(t *testing.T) { }, }}, ) - assert.Equal(t, false, ok) + assert.False(t, ok) } func (s *SuiteRequireTwice) TestRequireOne() { @@ -213,7 +213,6 @@ func (suite *SuiteTester) TearDownTest() { func (suite *SuiteTester) TestOne() { beforeCount := suite.TestOneRunCount suite.TestOneRunCount++ - assert.Equal(suite.T(), suite.TestOneRunCount, beforeCount+1) suite.Equal(suite.TestOneRunCount, beforeCount+1) } @@ -221,7 +220,6 @@ func (suite *SuiteTester) TestOne() { func (suite *SuiteTester) TestTwo() { beforeCount := suite.TestTwoRunCount suite.TestTwoRunCount++ - assert.NotEqual(suite.T(), suite.TestTwoRunCount, beforeCount) suite.NotEqual(suite.TestTwoRunCount, beforeCount) } @@ -301,13 +299,13 @@ func TestRunSuite(t *testing.T) { // The suite was only run once, so the SetupSuite and TearDownSuite // methods should have each been run only once. - assert.Equal(t, suiteTester.SetupSuiteRunCount, 1) - assert.Equal(t, suiteTester.TearDownSuiteRunCount, 1) + assert.Equal(t, 1, suiteTester.SetupSuiteRunCount) + assert.Equal(t, 1, suiteTester.TearDownSuiteRunCount) - assert.Equal(t, len(suiteTester.SuiteNameAfter), 4) - assert.Equal(t, len(suiteTester.SuiteNameBefore), 4) - assert.Equal(t, len(suiteTester.TestNameAfter), 4) - assert.Equal(t, len(suiteTester.TestNameBefore), 4) + assert.Len(t, suiteTester.SuiteNameAfter, 4) + assert.Len(t, suiteTester.SuiteNameBefore, 4) + assert.Len(t, suiteTester.TestNameAfter, 4) + assert.Len(t, suiteTester.TestNameBefore, 4) assert.Contains(t, suiteTester.TestNameAfter, "TestOne") assert.Contains(t, suiteTester.TestNameAfter, "TestTwo") @@ -338,20 +336,20 @@ func TestRunSuite(t *testing.T) { // There are four test methods (TestOne, TestTwo, TestSkip, and TestSubtest), so // the SetupTest and TearDownTest methods (which should be run once for // each test) should have been run four times. - assert.Equal(t, suiteTester.SetupTestRunCount, 4) - assert.Equal(t, suiteTester.TearDownTestRunCount, 4) + assert.Equal(t, 4, suiteTester.SetupTestRunCount) + assert.Equal(t, 4, suiteTester.TearDownTestRunCount) // Each test should have been run once. - assert.Equal(t, suiteTester.TestOneRunCount, 1) - assert.Equal(t, suiteTester.TestTwoRunCount, 1) - assert.Equal(t, suiteTester.TestSubtestRunCount, 1) + assert.Equal(t, 1, suiteTester.TestOneRunCount) + assert.Equal(t, 1, suiteTester.TestTwoRunCount) + assert.Equal(t, 1, suiteTester.TestSubtestRunCount) - assert.Equal(t, suiteTester.TearDownSubTestRunCount, 2) - assert.Equal(t, suiteTester.SetupSubTestRunCount, 2) + assert.Equal(t, 2, suiteTester.TearDownSubTestRunCount) + assert.Equal(t, 2, suiteTester.SetupSubTestRunCount) // Methods that don't match the test method identifier shouldn't // have been run at all. - assert.Equal(t, suiteTester.NonTestMethodRunCount, 0) + assert.Equal(t, 0, suiteTester.NonTestMethodRunCount) suiteSkipTester := new(SuiteSkipTester) Run(t, suiteSkipTester) @@ -359,8 +357,8 @@ func TestRunSuite(t *testing.T) { // The suite was only run once, so the SetupSuite and TearDownSuite // methods should have each been run only once, even though SetupSuite // called Skip() - assert.Equal(t, suiteSkipTester.SetupSuiteRunCount, 1) - assert.Equal(t, suiteSkipTester.TearDownSuiteRunCount, 1) + assert.Equal(t, 1, suiteSkipTester.SetupSuiteRunCount) + assert.Equal(t, 1, suiteSkipTester.TearDownSuiteRunCount) } @@ -571,7 +569,7 @@ func TestFailfastSuite(t *testing.T) { }, }}, ) - assert.Equal(t, false, ok) + assert.False(t, ok) if failFast { // Test A Fails and because we are running with failfast Test B never runs and we proceed straight to TearDownSuite assert.Equal(t, "SetupSuite;SetupTest;Test A Fails;TearDownTest;TearDownSuite", strings.Join(s.callOrder, ";")) From 351d2776c66b47ccd2539b45925797874fdc0b21 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Tue, 10 Oct 2023 16:27:05 +0300 Subject: [PATCH 16/34] Revert some changes --- suite/suite_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/suite/suite_test.go b/suite/suite_test.go index 8ec505f..e21fb71 100644 --- a/suite/suite_test.go +++ b/suite/suite_test.go @@ -213,6 +213,7 @@ func (suite *SuiteTester) TearDownTest() { func (suite *SuiteTester) TestOne() { beforeCount := suite.TestOneRunCount suite.TestOneRunCount++ + assert.Equal(suite.T(), suite.TestOneRunCount, beforeCount+1) suite.Equal(suite.TestOneRunCount, beforeCount+1) } @@ -220,6 +221,7 @@ func (suite *SuiteTester) TestOne() { func (suite *SuiteTester) TestTwo() { beforeCount := suite.TestTwoRunCount suite.TestTwoRunCount++ + assert.NotEqual(suite.T(), suite.TestTwoRunCount, beforeCount) suite.NotEqual(suite.TestTwoRunCount, beforeCount) } From 737a765d894418ce651c394a418c0a056c8be459 Mon Sep 17 00:00:00 2001 From: Linus Barth Date: Mon, 16 Oct 2023 02:59:15 +0200 Subject: [PATCH 17/34] fix: SetupSubTest and TearDownSubTest execution order There were two problems with the order of execution in the Suite.Run() method: - One could not access the correct testing context ("s.T()") inside the SetupSubTest and TearDownSubTest methods. If the testing context was used for e.g. assertions of mocks in the TearDownSubTest, the results would not be "attached" to the correct test in the test output. - The behavior was different to the order of execution for "root" tests (see lines 167-201) regarding the SetupTest and TearDownTest methods. This could confuse users of the library. Also the logic to be deferred was joined together. This was fine beforehand because a panic in the TearDownSubTest would have had no influence on the "suite.SetT(oldT)". Now since a panic in the TearDownSubTest would lead to omitting the "suite.SetT(oldT)" this defer was split into two separate defers. --- suite/suite.go | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/suite/suite.go b/suite/suite.go index 8b4202d..54efd4c 100644 --- a/suite/suite.go +++ b/suite/suite.go @@ -96,19 +96,23 @@ func failOnPanic(t *testing.T, r interface{}) { func (suite *Suite) Run(name string, subtest func()) bool { oldT := suite.T() - if setupSubTest, ok := suite.s.(SetupSubTest); ok { - setupSubTest.SetupSubTest() - } - - defer func() { - suite.SetT(oldT) - if tearDownSubTest, ok := suite.s.(TearDownSubTest); ok { - tearDownSubTest.TearDownSubTest() - } - }() - return oldT.Run(name, func(t *testing.T) { suite.SetT(t) + + defer func() { + suite.SetT(oldT) + }() + + if setupSubTest, ok := suite.s.(SetupSubTest); ok { + setupSubTest.SetupSubTest() + } + + defer func() { + if tearDownSubTest, ok := suite.s.(TearDownSubTest); ok { + tearDownSubTest.TearDownSubTest() + } + }() + subtest() }) } From 4526456fa4af4e309486fea62b689df8f0c4714a Mon Sep 17 00:00:00 2001 From: Linus Barth Date: Mon, 16 Oct 2023 02:59:16 +0200 Subject: [PATCH 18/34] improve: defer-style in Suite.Run() --- suite/suite.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/suite/suite.go b/suite/suite.go index 54efd4c..cca7533 100644 --- a/suite/suite.go +++ b/suite/suite.go @@ -99,19 +99,15 @@ func (suite *Suite) Run(name string, subtest func()) bool { return oldT.Run(name, func(t *testing.T) { suite.SetT(t) - defer func() { - suite.SetT(oldT) - }() + defer suite.SetT(oldT) if setupSubTest, ok := suite.s.(SetupSubTest); ok { setupSubTest.SetupSubTest() } - defer func() { - if tearDownSubTest, ok := suite.s.(TearDownSubTest); ok { - tearDownSubTest.TearDownSubTest() - } - }() + if tearDownSubTest, ok := suite.s.(TearDownSubTest); ok { + defer tearDownSubTest.TearDownSubTest() + } subtest() }) From 82022eeb0d35dd1ea3f5a831d6b83ff5c4781ae4 Mon Sep 17 00:00:00 2001 From: Linus Barth Date: Mon, 16 Oct 2023 02:59:16 +0200 Subject: [PATCH 19/34] test: call order of setup/teardown for subtests --- suite/suite_test.go | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/suite/suite_test.go b/suite/suite_test.go index e21fb71..2cbfffc 100644 --- a/suite/suite_test.go +++ b/suite/suite_test.go @@ -481,7 +481,7 @@ func (s *CallOrderSuite) SetupSuite() { func (s *CallOrderSuite) TearDownSuite() { s.call("TearDownSuite") - assert.Equal(s.T(), "SetupSuite;SetupTest;Test A;TearDownTest;SetupTest;Test B;TearDownTest;TearDownSuite", strings.Join(s.callOrder, ";")) + assert.Equal(s.T(), "SetupSuite;SetupTest;Test A;SetupSubTest;SubTest A1;TearDownSubTest;SetupSubTest;SubTest A2;TearDownSubTest;TearDownTest;SetupTest;Test B;SetupSubTest;SubTest B1;TearDownSubTest;SetupSubTest;SubTest B2;TearDownSubTest;TearDownTest;TearDownSuite", strings.Join(s.callOrder, ";")) } func (s *CallOrderSuite) SetupTest() { s.call("SetupTest") @@ -491,12 +491,32 @@ func (s *CallOrderSuite) TearDownTest() { s.call("TearDownTest") } +func (s *CallOrderSuite) SetupSubTest() { + s.call("SetupSubTest") +} + +func (s *CallOrderSuite) TearDownSubTest() { + s.call("TearDownSubTest") +} + func (s *CallOrderSuite) Test_A() { s.call("Test A") + s.Run("SubTest A1", func() { + s.call("SubTest A1") + }) + s.Run("SubTest A2", func() { + s.call("SubTest A2") + }) } func (s *CallOrderSuite) Test_B() { s.call("Test B") + s.Run("SubTest B1", func() { + s.call("SubTest B1") + }) + s.Run("SubTest B2", func() { + s.call("SubTest B2") + }) } type suiteWithStats struct { From 855d1c6784eb5570abc27803338713a9f37b1671 Mon Sep 17 00:00:00 2001 From: Linus Barth Date: Mon, 16 Oct 2023 02:59:16 +0200 Subject: [PATCH 20/34] test: testing.T correctness in subtests setup/teardown --- suite/suite_test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/suite/suite_test.go b/suite/suite_test.go index 2cbfffc..fce7202 100644 --- a/suite/suite_test.go +++ b/suite/suite_test.go @@ -242,8 +242,8 @@ func (suite *SuiteTester) TestSubtest() { for _, t := range []struct { testName string }{ - {"first"}, - {"second"}, + {"first-subtest"}, + {"second-subtest"}, } { suiteT := suite.T() suite.Run(t.testName, func() { @@ -259,10 +259,14 @@ func (suite *SuiteTester) TestSubtest() { func (suite *SuiteTester) TearDownSubTest() { suite.TearDownSubTestRunCount++ + // We should get the *testing.T for the test that is to be torn down + suite.Contains(suite.T().Name(), "subtest") } func (suite *SuiteTester) SetupSubTest() { suite.SetupSubTestRunCount++ + // We should get the *testing.T for the test that is to be set up + suite.Contains(suite.T().Name(), "subtest") } type SuiteSkipTester struct { From 130d34026214558b81491eeb1780b04c370111d7 Mon Sep 17 00:00:00 2001 From: Linus Barth Date: Mon, 16 Oct 2023 02:59:16 +0200 Subject: [PATCH 21/34] improve: move comment to msgAndArgs-param in test --- suite/suite_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/suite/suite_test.go b/suite/suite_test.go index fce7202..bc32b23 100644 --- a/suite/suite_test.go +++ b/suite/suite_test.go @@ -259,14 +259,12 @@ func (suite *SuiteTester) TestSubtest() { func (suite *SuiteTester) TearDownSubTest() { suite.TearDownSubTestRunCount++ - // We should get the *testing.T for the test that is to be torn down - suite.Contains(suite.T().Name(), "subtest") + suite.Contains(suite.T().Name(), "subtest", "We should get the *testing.T for the test that is to be torn down") } func (suite *SuiteTester) SetupSubTest() { suite.SetupSubTestRunCount++ - // We should get the *testing.T for the test that is to be set up - suite.Contains(suite.T().Name(), "subtest") + suite.Contains(suite.T().Name(), "subtest", "We should get the *testing.T for the test that is to be set up") } type SuiteSkipTester struct { From 65318c364a06b1a48ced966934d879a58cfe4add Mon Sep 17 00:00:00 2001 From: Linus Barth Date: Tue, 17 Oct 2023 16:36:33 +0200 Subject: [PATCH 22/34] improve: tests for asserting test names in subtests --- suite/suite_test.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/suite/suite_test.go b/suite/suite_test.go index bc32b23..a3c983a 100644 --- a/suite/suite_test.go +++ b/suite/suite_test.go @@ -162,6 +162,9 @@ type SuiteTester struct { SetupSubTestRunCount int TearDownSubTestRunCount int + SetupSubTestNames []string + TearDownSubTestNames []string + SuiteNameBefore []string TestNameBefore []string @@ -242,8 +245,8 @@ func (suite *SuiteTester) TestSubtest() { for _, t := range []struct { testName string }{ - {"first-subtest"}, - {"second-subtest"}, + {"first"}, + {"second"}, } { suiteT := suite.T() suite.Run(t.testName, func() { @@ -258,13 +261,13 @@ func (suite *SuiteTester) TestSubtest() { } func (suite *SuiteTester) TearDownSubTest() { + suite.TearDownSubTestNames = append(suite.TearDownSubTestNames, suite.T().Name()) suite.TearDownSubTestRunCount++ - suite.Contains(suite.T().Name(), "subtest", "We should get the *testing.T for the test that is to be torn down") } func (suite *SuiteTester) SetupSubTest() { + suite.SetupSubTestNames = append(suite.SetupSubTestNames, suite.T().Name()) suite.SetupSubTestRunCount++ - suite.Contains(suite.T().Name(), "subtest", "We should get the *testing.T for the test that is to be set up") } type SuiteSkipTester struct { @@ -321,6 +324,12 @@ func TestRunSuite(t *testing.T) { assert.Contains(t, suiteTester.TestNameBefore, "TestSkip") assert.Contains(t, suiteTester.TestNameBefore, "TestSubtest") + assert.Contains(t, suiteTester.SetupSubTestNames, "TestRunSuite/TestSubtest/first") + assert.Contains(t, suiteTester.SetupSubTestNames, "TestRunSuite/TestSubtest/second") + + assert.Contains(t, suiteTester.TearDownSubTestNames, "TestRunSuite/TestSubtest/first") + assert.Contains(t, suiteTester.TearDownSubTestNames, "TestRunSuite/TestSubtest/second") + for _, suiteName := range suiteTester.SuiteNameAfter { assert.Equal(t, "SuiteTester", suiteName) } From a4a54a4597c8c0ea19314400cfd9cd6f8e5afe59 Mon Sep 17 00:00:00 2001 From: Linus Barth Date: Wed, 18 Oct 2023 03:22:36 +0200 Subject: [PATCH 23/34] fix: panic behavior for subtests This fix adds panic handling for subtests which will achieve: - subtests will fail for the correct test context when panicking - the test execution is not stopped; the next subtest will be executed --- suite/suite.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/suite/suite.go b/suite/suite.go index cca7533..f3aa9a1 100644 --- a/suite/suite.go +++ b/suite/suite.go @@ -98,9 +98,10 @@ func (suite *Suite) Run(name string, subtest func()) bool { return oldT.Run(name, func(t *testing.T) { suite.SetT(t) - defer suite.SetT(oldT) + defer recoverAndFailOnPanic(t) + if setupSubTest, ok := suite.s.(SetupSubTest); ok { setupSubTest.SetupSubTest() } From 7df1a82a315198ca3c077c97af0c89be3495fcf7 Mon Sep 17 00:00:00 2001 From: Linus Barth Date: Wed, 18 Oct 2023 16:04:50 +0200 Subject: [PATCH 24/34] test: add suite tests for panicking of subtests --- suite/suite_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/suite/suite_test.go b/suite/suite_test.go index a3c983a..a2b6b04 100644 --- a/suite/suite_test.go +++ b/suite/suite_test.go @@ -648,3 +648,45 @@ func (s *FailfastSuite) Test_B_Passes() { s.call("Test B Passes") s.Require().True(true) } + +type subtestPanicSuite struct { + Suite + inTearDownSuite bool + inTearDownTest bool + inTearDownSubTest bool +} + +func (s *subtestPanicSuite) TearDownSuite() { + s.inTearDownSuite = true +} + +func (s *subtestPanicSuite) TearDownTest() { + s.inTearDownTest = true +} + +func (s *subtestPanicSuite) TearDownSubTest() { + s.inTearDownSubTest = true +} + +func (s *subtestPanicSuite) TestSubtestPanic() { + s.Run("subtest", func() { + panic("panic") + }) +} + +func TestSubtestPanic(t *testing.T) { + suite := new(subtestPanicSuite) + ok := testing.RunTests( + allTestsFilter, + []testing.InternalTest{{ + Name: "TestSubtestPanic", + F: func(t *testing.T) { + Run(t, suite) + }, + }}, + ) + assert.False(t, ok) + assert.True(t, suite.inTearDownSubTest) + assert.True(t, suite.inTearDownTest) + assert.True(t, suite.inTearDownSuite) +} From 056e9e6bfa0aebd829b1a82a7cfd64d85fe9c758 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Mon, 30 Jan 2023 19:02:58 +0200 Subject: [PATCH 25/34] docs: Fix deprecation formatting for http --- http/doc.go | 2 +- http/test_response_writer.go | 8 ++++---- http/test_round_tripper.go | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/http/doc.go b/http/doc.go index 695167c..aad0e74 100644 --- a/http/doc.go +++ b/http/doc.go @@ -1,2 +1,2 @@ -// Package http DEPRECATED USE net/http/httptest +// Deprecated: Use net/http/httptest instead. package http diff --git a/http/test_response_writer.go b/http/test_response_writer.go index 300a63b..7fe4fe5 100644 --- a/http/test_response_writer.go +++ b/http/test_response_writer.go @@ -4,7 +4,7 @@ import ( "net/http" ) -// TestResponseWriter DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead. +// Deprecated: Use https://pkg.go.dev/net/http/httptest instead. type TestResponseWriter struct { // StatusCode is the last int written by the call to WriteHeader(int) @@ -17,7 +17,7 @@ type TestResponseWriter struct { header http.Header } -// Header DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead. +// Deprecated: Use https://pkg.go.dev/net/http/httptest instead. func (rw *TestResponseWriter) Header() http.Header { if rw.header == nil { @@ -27,7 +27,7 @@ func (rw *TestResponseWriter) Header() http.Header { return rw.header } -// Write DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead. +// Deprecated: Use https://pkg.go.dev/net/http/httptest instead. func (rw *TestResponseWriter) Write(bytes []byte) (int, error) { // assume 200 success if no header has been set @@ -43,7 +43,7 @@ func (rw *TestResponseWriter) Write(bytes []byte) (int, error) { } -// WriteHeader DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead. +// Deprecated: Use https://pkg.go.dev/net/http/httptest instead. func (rw *TestResponseWriter) WriteHeader(i int) { rw.StatusCode = i } diff --git a/http/test_round_tripper.go b/http/test_round_tripper.go index 11a024e..2802c07 100644 --- a/http/test_round_tripper.go +++ b/http/test_round_tripper.go @@ -6,12 +6,12 @@ import ( "github.com/stretchr/testify/mock" ) -// TestRoundTripper DEPRECATED USE net/http/httptest +// Deprecated: Use https://pkg.go.dev/net/http/httptest instead. type TestRoundTripper struct { mock.Mock } -// RoundTrip DEPRECATED USE net/http/httptest +// Deprecated: Use https://pkg.go.dev/net/http/httptest instead. func (t *TestRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { args := t.Called(req) return args.Get(0).(*http.Response), args.Error(1) From 43b2ef12b3f933c888167b0029430c083837ad65 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Thu, 6 Jul 2023 13:04:04 +0300 Subject: [PATCH 26/34] Change to use godoc --- doc.go | 12 ------------ http/doc.go | 2 +- http/test_response_writer.go | 8 ++++---- http/test_round_tripper.go | 4 ++-- 4 files changed, 7 insertions(+), 19 deletions(-) diff --git a/doc.go b/doc.go index 3460b46..aac5ef3 100644 --- a/doc.go +++ b/doc.go @@ -5,19 +5,7 @@ // // The assert package provides a comprehensive set of assertion functions that tie in to the Go testing system. // -// The http package contains tools to make it easier to test http activity using the Go testing system. -// // The mock package provides a system by which it is possible to mock your objects and verify calls are happening as expected. // // The suite package provides a basic structure for using structs as testing suites, and methods on those structs as tests. It includes setup/teardown functionality in the way of interfaces. package testify - -// blank imports help docs. -import ( - // assert package - _ "github.com/stretchr/testify/assert" - // http package - _ "github.com/stretchr/testify/http" - // mock package - _ "github.com/stretchr/testify/mock" -) diff --git a/http/doc.go b/http/doc.go index aad0e74..7c800aa 100644 --- a/http/doc.go +++ b/http/doc.go @@ -1,2 +1,2 @@ -// Deprecated: Use net/http/httptest instead. +// Deprecated: Use [net/http/httptest] instead. package http diff --git a/http/test_response_writer.go b/http/test_response_writer.go index 7fe4fe5..6744e1c 100644 --- a/http/test_response_writer.go +++ b/http/test_response_writer.go @@ -4,7 +4,7 @@ import ( "net/http" ) -// Deprecated: Use https://pkg.go.dev/net/http/httptest instead. +// Deprecated: Use [net/http/httptest] instead. type TestResponseWriter struct { // StatusCode is the last int written by the call to WriteHeader(int) @@ -17,7 +17,7 @@ type TestResponseWriter struct { header http.Header } -// Deprecated: Use https://pkg.go.dev/net/http/httptest instead. +// Deprecated: Use [net/http/httptest] instead. func (rw *TestResponseWriter) Header() http.Header { if rw.header == nil { @@ -27,7 +27,7 @@ func (rw *TestResponseWriter) Header() http.Header { return rw.header } -// Deprecated: Use https://pkg.go.dev/net/http/httptest instead. +// Deprecated: Use [net/http/httptest] instead. func (rw *TestResponseWriter) Write(bytes []byte) (int, error) { // assume 200 success if no header has been set @@ -43,7 +43,7 @@ func (rw *TestResponseWriter) Write(bytes []byte) (int, error) { } -// Deprecated: Use https://pkg.go.dev/net/http/httptest instead. +// Deprecated: Use [net/http/httptest] instead. func (rw *TestResponseWriter) WriteHeader(i int) { rw.StatusCode = i } diff --git a/http/test_round_tripper.go b/http/test_round_tripper.go index 2802c07..a0bdd71 100644 --- a/http/test_round_tripper.go +++ b/http/test_round_tripper.go @@ -6,12 +6,12 @@ import ( "github.com/stretchr/testify/mock" ) -// Deprecated: Use https://pkg.go.dev/net/http/httptest instead. +// Deprecated: Use [net/http/httptest] instead. type TestRoundTripper struct { mock.Mock } -// Deprecated: Use https://pkg.go.dev/net/http/httptest instead. +// Deprecated: Use [net/http/httptest] instead. func (t *TestRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { args := t.Called(req) return args.Get(0).(*http.Response), args.Error(1) From 5105b61304a2efacddfde39984ccf566be97272b Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Thu, 24 Nov 2022 11:30:50 +0000 Subject: [PATCH 27/34] Add map support doc comments to Subset and NotSubset Signed-off-by: Justin Chadwell --- assert/assertion_format.go | 15 +++++++++------ assert/assertion_forward.go | 30 ++++++++++++++++++------------ assert/assertions.go | 15 +++++++++------ require/require.go | 30 ++++++++++++++++++------------ require/require_forward.go | 30 ++++++++++++++++++------------ 5 files changed, 72 insertions(+), 48 deletions(-) diff --git a/assert/assertion_format.go b/assert/assertion_format.go index 53f9c8e..896cc7f 100644 --- a/assert/assertion_format.go +++ b/assert/assertion_format.go @@ -657,10 +657,12 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...) } -// NotSubsetf asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubsetf asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") +// assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -744,10 +746,11 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg return Same(t, expected, actual, append([]interface{}{msg}, args...)...) } -// Subsetf asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subsetf asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") +// assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/assert/assertion_forward.go b/assert/assertion_forward.go index 5a2974b..8aaa259 100644 --- a/assert/assertion_forward.go +++ b/assert/assertion_forward.go @@ -1306,10 +1306,12 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri return NotSamef(a.t, expected, actual, msg, args...) } -// NotSubset asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubset asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// a.NotSubset([1, 3, 4], [1, 2]) +// a.NotSubset({"x": 1, "y": 2}, {"z": 3}) func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1317,10 +1319,12 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs return NotSubset(a.t, list, subset, msgAndArgs...) } -// NotSubsetf asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubsetf asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted") +// a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1480,10 +1484,11 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, return Samef(a.t, expected, actual, msg, args...) } -// Subset asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subset asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// a.Subset([1, 2, 3], [1, 2]) +// a.Subset({"x": 1, "y": 2}, {"x": 1}) func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1491,10 +1496,11 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... return Subset(a.t, list, subset, msgAndArgs...) } -// Subsetf asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subsetf asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted") +// a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/assert/assertions.go b/assert/assertions.go index 6843d42..bc15101 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -929,10 +929,11 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) } -// Subset asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subset asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// assert.Subset(t, [1, 2, 3], [1, 2]) +// assert.Subset(t, {"x": 1, "y": 2}, {"x": 1}) func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -985,10 +986,12 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok return true } -// NotSubset asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubset asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// assert.NotSubset(t, [1, 3, 4], [1, 2]) +// assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/require/require.go b/require/require.go index fa3792b..fde0833 100644 --- a/require/require.go +++ b/require/require.go @@ -1655,10 +1655,12 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, t.FailNow() } -// NotSubset asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubset asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// assert.NotSubset(t, [1, 3, 4], [1, 2]) +// assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1669,10 +1671,12 @@ func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...i t.FailNow() } -// NotSubsetf asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubsetf asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") +// assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1877,10 +1881,11 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg t.FailNow() } -// Subset asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subset asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// assert.Subset(t, [1, 2, 3], [1, 2]) +// assert.Subset(t, {"x": 1, "y": 2}, {"x": 1}) func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1891,10 +1896,11 @@ func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...inte t.FailNow() } -// Subsetf asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subsetf asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") +// assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/require/require_forward.go b/require/require_forward.go index 99e19ea..8fddd1f 100644 --- a/require/require_forward.go +++ b/require/require_forward.go @@ -1307,10 +1307,12 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri NotSamef(a.t, expected, actual, msg, args...) } -// NotSubset asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubset asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// a.NotSubset([1, 3, 4], [1, 2]) +// a.NotSubset({"x": 1, "y": 2}, {"z": 3}) func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1318,10 +1320,12 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs NotSubset(a.t, list, subset, msgAndArgs...) } -// NotSubsetf asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubsetf asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted") +// a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1481,10 +1485,11 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, Samef(a.t, expected, actual, msg, args...) } -// Subset asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subset asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// a.Subset([1, 2, 3], [1, 2]) +// a.Subset({"x": 1, "y": 2}, {"x": 1}) func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1492,10 +1497,11 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... Subset(a.t, list, subset, msgAndArgs...) } -// Subsetf asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subsetf asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted") +// a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() From 002647e9f8aea5ce96b66b362b16eefac92d84ab Mon Sep 17 00:00:00 2001 From: Craig Davison Date: Wed, 26 Jul 2023 15:13:55 -0600 Subject: [PATCH 28/34] TestErrorIs/TestNotErrorIs: check error contents --- assert/assertions_test.go | 202 +++++++++++++++++++++++++++++++++----- 1 file changed, 176 insertions(+), 26 deletions(-) diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 9a44f95..72908b3 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -2908,52 +2908,202 @@ func Test_truncatingFormat(t *testing.T) { } } +func parseLabeledOutput(output string) []labeledContent { + labelPattern := regexp.MustCompile(`^\t([^\t]*): *\t(.*)$`) + contentPattern := regexp.MustCompile(`^\t *\t(.*)$`) + var contents []labeledContent + lines := strings.Split(output, "\n") + i := -1 + for _, line := range lines { + if line == "" { + // skip blank lines + continue + } + matches := labelPattern.FindStringSubmatch(line) + if len(matches) == 3 { + // a label + contents = append(contents, labeledContent{ + label: matches[1], + content: matches[2] + "\n", + }) + i++ + continue + } + matches = contentPattern.FindStringSubmatch(line) + if len(matches) == 2 { + // just content + if i >= 0 { + contents[i].content += matches[1] + "\n" + continue + } + } + // Couldn't parse output + return nil + } + return contents +} + +type captureTestingT struct { + msg string +} + +func (ctt *captureTestingT) Errorf(format string, args ...interface{}) { + ctt.msg = fmt.Sprintf(format, args...) +} + +func checkResultAndErrMsg(t *testing.T, expectedRes, res bool, expectedErrMsg, rawErrOutput string) { + t.Helper() + if res != expectedRes { + t.Errorf("Should return %t", expectedRes) + return + } + contents := parseLabeledOutput(rawErrOutput) + if res == true { + if contents != nil { + t.Errorf("Should not log an error") + } + return + } + if contents == nil { + t.Errorf("Should log an error. Log output: %v", rawErrOutput) + return + } + for _, content := range contents { + if content.label == "Error" { + if expectedErrMsg == content.content { + return + } + t.Errorf("Logged Error: %v", content.content) + } + } + t.Errorf("Should log Error: %v", expectedErrMsg) +} + func TestErrorIs(t *testing.T) { - mockT := new(testing.T) tests := []struct { - err error - target error - result bool + err error + target error + result bool + resultErrMsg string }{ - {io.EOF, io.EOF, true}, - {fmt.Errorf("wrap: %w", io.EOF), io.EOF, true}, - {io.EOF, io.ErrClosedPipe, false}, - {nil, io.EOF, false}, - {io.EOF, nil, false}, - {nil, nil, true}, + { + err: io.EOF, + target: io.EOF, + result: true, + }, + { + err: fmt.Errorf("wrap: %w", io.EOF), + target: io.EOF, + result: true, + }, + { + err: io.EOF, + target: io.ErrClosedPipe, + result: false, + resultErrMsg: "Target error should be in err chain:\n" + + "expected: \"io: read/write on closed pipe\"\n" + + "in chain: \"EOF\"\n", + }, + { + err: nil, + target: io.EOF, + result: false, + resultErrMsg: "Target error should be in err chain:\n" + + "expected: \"EOF\"\n" + + "in chain: \n", + }, + { + err: io.EOF, + target: nil, + result: false, + resultErrMsg: "Target error should be in err chain:\n" + + "expected: \"\"\n" + + "in chain: \"EOF\"\n", + }, + { + err: nil, + target: nil, + result: true, + }, + { + err: fmt.Errorf("abc: %w", errors.New("def")), + target: io.EOF, + result: false, + resultErrMsg: "Target error should be in err chain:\n" + + "expected: \"EOF\"\n" + + "in chain: \"abc: def\"\n" + + "\t\"def\"\n", + }, } for _, tt := range tests { tt := tt + mockT := new(captureTestingT) t.Run(fmt.Sprintf("ErrorIs(%#v,%#v)", tt.err, tt.target), func(t *testing.T) { res := ErrorIs(mockT, tt.err, tt.target) - if res != tt.result { - t.Errorf("ErrorIs(%#v,%#v) should return %t", tt.err, tt.target, tt.result) - } + checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg, mockT.msg) }) } } func TestNotErrorIs(t *testing.T) { - mockT := new(testing.T) tests := []struct { - err error - target error - result bool + err error + target error + result bool + resultErrMsg string }{ - {io.EOF, io.EOF, false}, - {fmt.Errorf("wrap: %w", io.EOF), io.EOF, false}, - {io.EOF, io.ErrClosedPipe, true}, - {nil, io.EOF, true}, - {io.EOF, nil, true}, - {nil, nil, false}, + { + err: io.EOF, + target: io.EOF, + result: false, + resultErrMsg: "Target error should not be in err chain:\n" + + "found: \"EOF\"\n" + + "in chain: \"EOF\"\n", + }, + { + err: fmt.Errorf("wrap: %w", io.EOF), + target: io.EOF, + result: false, + resultErrMsg: "Target error should not be in err chain:\n" + + "found: \"EOF\"\n" + + "in chain: \"wrap: EOF\"\n" + + "\t\"EOF\"\n", + }, + { + err: io.EOF, + target: io.ErrClosedPipe, + result: true, + }, + { + err: nil, + target: io.EOF, + result: true, + }, + { + err: io.EOF, + target: nil, + result: true, + }, + { + err: nil, + target: nil, + result: false, + resultErrMsg: "Target error should not be in err chain:\n" + + "found: \"\"\n" + + "in chain: \n", + }, + { + err: fmt.Errorf("abc: %w", errors.New("def")), + target: io.EOF, + result: true, + }, } for _, tt := range tests { tt := tt + mockT := new(captureTestingT) t.Run(fmt.Sprintf("NotErrorIs(%#v,%#v)", tt.err, tt.target), func(t *testing.T) { res := NotErrorIs(mockT, tt.err, tt.target) - if res != tt.result { - t.Errorf("NotErrorIs(%#v,%#v) should return %t", tt.err, tt.target, tt.result) - } + checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg, mockT.msg) }) } } From b7c378d6bd9004da7a9b052c7ca584155e99cd07 Mon Sep 17 00:00:00 2001 From: Craig Davison Date: Wed, 26 Jul 2023 15:37:34 -0600 Subject: [PATCH 29/34] captureTestingT.checkResultAndErrMsg --- assert/assertions_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 72908b3..81f5897 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -2951,13 +2951,13 @@ func (ctt *captureTestingT) Errorf(format string, args ...interface{}) { ctt.msg = fmt.Sprintf(format, args...) } -func checkResultAndErrMsg(t *testing.T, expectedRes, res bool, expectedErrMsg, rawErrOutput string) { +func (ctt *captureTestingT) checkResultAndErrMsg(t *testing.T, expectedRes, res bool, expectedErrMsg string) { t.Helper() if res != expectedRes { t.Errorf("Should return %t", expectedRes) return } - contents := parseLabeledOutput(rawErrOutput) + contents := parseLabeledOutput(ctt.msg) if res == true { if contents != nil { t.Errorf("Should not log an error") @@ -2965,7 +2965,7 @@ func checkResultAndErrMsg(t *testing.T, expectedRes, res bool, expectedErrMsg, r return } if contents == nil { - t.Errorf("Should log an error. Log output: %v", rawErrOutput) + t.Errorf("Should log an error. Log output: %v", ctt.msg) return } for _, content := range contents { @@ -3040,7 +3040,7 @@ func TestErrorIs(t *testing.T) { mockT := new(captureTestingT) t.Run(fmt.Sprintf("ErrorIs(%#v,%#v)", tt.err, tt.target), func(t *testing.T) { res := ErrorIs(mockT, tt.err, tt.target) - checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg, mockT.msg) + mockT.checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg) }) } } @@ -3103,7 +3103,7 @@ func TestNotErrorIs(t *testing.T) { mockT := new(captureTestingT) t.Run(fmt.Sprintf("NotErrorIs(%#v,%#v)", tt.err, tt.target), func(t *testing.T) { res := NotErrorIs(mockT, tt.err, tt.target) - checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg, mockT.msg) + mockT.checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg) }) } } From 5f48c62606fee86427a81eceb0b9d8fd4e2ba1d6 Mon Sep 17 00:00:00 2001 From: Craig Davison Date: Mon, 31 Jul 2023 16:41:32 -0600 Subject: [PATCH 30/34] address some review comments --- assert/assertions_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 81f5897..7021382 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -2908,6 +2908,8 @@ func Test_truncatingFormat(t *testing.T) { } } +// parseLabeledOutput does the inverse of labeledOutput - it takes a formatted +// output string and turns it back into a slice of labeledContent. func parseLabeledOutput(output string) []labeledContent { labelPattern := regexp.MustCompile(`^\t([^\t]*): *\t(.*)$`) contentPattern := regexp.MustCompile(`^\t *\t(.*)$`) @@ -3037,8 +3039,8 @@ func TestErrorIs(t *testing.T) { } for _, tt := range tests { tt := tt - mockT := new(captureTestingT) t.Run(fmt.Sprintf("ErrorIs(%#v,%#v)", tt.err, tt.target), func(t *testing.T) { + mockT := new(captureTestingT) res := ErrorIs(mockT, tt.err, tt.target) mockT.checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg) }) @@ -3100,8 +3102,8 @@ func TestNotErrorIs(t *testing.T) { } for _, tt := range tests { tt := tt - mockT := new(captureTestingT) t.Run(fmt.Sprintf("NotErrorIs(%#v,%#v)", tt.err, tt.target), func(t *testing.T) { + mockT := new(captureTestingT) res := NotErrorIs(mockT, tt.err, tt.target) mockT.checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg) }) From 331c520966e81450aef3b50956ea4a5db5e0ccdf Mon Sep 17 00:00:00 2001 From: Craig Davison Date: Mon, 30 Oct 2023 23:12:39 -0600 Subject: [PATCH 31/34] Improve readability --- assert/assertions_test.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 7021382..d2a25c2 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -3002,7 +3002,8 @@ func TestErrorIs(t *testing.T) { err: io.EOF, target: io.ErrClosedPipe, result: false, - resultErrMsg: "Target error should be in err chain:\n" + + resultErrMsg: "" + + "Target error should be in err chain:\n" + "expected: \"io: read/write on closed pipe\"\n" + "in chain: \"EOF\"\n", }, @@ -3010,7 +3011,8 @@ func TestErrorIs(t *testing.T) { err: nil, target: io.EOF, result: false, - resultErrMsg: "Target error should be in err chain:\n" + + resultErrMsg: "" + + "Target error should be in err chain:\n" + "expected: \"EOF\"\n" + "in chain: \n", }, @@ -3018,7 +3020,8 @@ func TestErrorIs(t *testing.T) { err: io.EOF, target: nil, result: false, - resultErrMsg: "Target error should be in err chain:\n" + + resultErrMsg: "" + + "Target error should be in err chain:\n" + "expected: \"\"\n" + "in chain: \"EOF\"\n", }, @@ -3031,7 +3034,8 @@ func TestErrorIs(t *testing.T) { err: fmt.Errorf("abc: %w", errors.New("def")), target: io.EOF, result: false, - resultErrMsg: "Target error should be in err chain:\n" + + resultErrMsg: "" + + "Target error should be in err chain:\n" + "expected: \"EOF\"\n" + "in chain: \"abc: def\"\n" + "\t\"def\"\n", @@ -3058,7 +3062,8 @@ func TestNotErrorIs(t *testing.T) { err: io.EOF, target: io.EOF, result: false, - resultErrMsg: "Target error should not be in err chain:\n" + + resultErrMsg: "" + + "Target error should not be in err chain:\n" + "found: \"EOF\"\n" + "in chain: \"EOF\"\n", }, @@ -3066,7 +3071,8 @@ func TestNotErrorIs(t *testing.T) { err: fmt.Errorf("wrap: %w", io.EOF), target: io.EOF, result: false, - resultErrMsg: "Target error should not be in err chain:\n" + + resultErrMsg: "" + + "Target error should not be in err chain:\n" + "found: \"EOF\"\n" + "in chain: \"wrap: EOF\"\n" + "\t\"EOF\"\n", @@ -3090,7 +3096,8 @@ func TestNotErrorIs(t *testing.T) { err: nil, target: nil, result: false, - resultErrMsg: "Target error should not be in err chain:\n" + + resultErrMsg: "" + + "Target error should not be in err chain:\n" + "found: \"\"\n" + "in chain: \n", }, From db8608ed63f5bd9abdd03f669f5bb80569c114b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Thu, 9 Nov 2023 01:44:27 +0100 Subject: [PATCH 32/34] suite: fix subtest names (fix #1501) (#1504) * suite: fix TestSubtestPanic failure (#1501) The subtest of TestSubtestPanic is expected to fail, but that must not make the testuite of package 'suite' to fail. So call Skip to make 'go test' to ignore that expected failure. * suite: fix subtests names We are doing dirty things with testing.InternalTest. It looks like test names should have the same prefix as the parent test, like if they were true subtests (testing.T.Run). So until we drop our usage of testing.InternamTest, let's rename the tests. Also added a few checks of the testing.RunTests result. --- suite/suite_test.go | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/suite/suite_test.go b/suite/suite_test.go index a2b6b04..292dc29 100644 --- a/suite/suite_test.go +++ b/suite/suite_test.go @@ -27,7 +27,7 @@ func TestSuiteRequireTwice(t *testing.T) { ok := testing.RunTests( allTestsFilter, []testing.InternalTest{{ - Name: "TestSuiteRequireTwice", + Name: t.Name() + "/SuiteRequireTwice", F: func(t *testing.T) { suite := new(SuiteRequireTwice) Run(t, suite) @@ -104,31 +104,31 @@ func TestSuiteRecoverPanic(t *testing.T) { ok := true panickingTests := []testing.InternalTest{ { - Name: "TestPanicInSetupSuite", + Name: t.Name() + "/InSetupSuite", F: func(t *testing.T) { Run(t, &panickingSuite{panicInSetupSuite: true}) }, }, { - Name: "TestPanicInSetupTest", + Name: t.Name() + "/InSetupTest", F: func(t *testing.T) { Run(t, &panickingSuite{panicInSetupTest: true}) }, }, { - Name: "TestPanicInBeforeTest", + Name: t.Name() + "InBeforeTest", F: func(t *testing.T) { Run(t, &panickingSuite{panicInBeforeTest: true}) }, }, { - Name: "TestPanicInTest", + Name: t.Name() + "/InTest", F: func(t *testing.T) { Run(t, &panickingSuite{panicInTest: true}) }, }, { - Name: "TestPanicInAfterTest", + Name: t.Name() + "/InAfterTest", F: func(t *testing.T) { Run(t, &panickingSuite{panicInAfterTest: true}) }, }, { - Name: "TestPanicInTearDownTest", + Name: t.Name() + "/InTearDownTest", F: func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownTest: true}) }, }, { - Name: "TestPanicInTearDownSuite", + Name: t.Name() + "/InTearDownSuite", F: func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownSuite: true}) }, }, } @@ -451,7 +451,7 @@ func TestSuiteLogging(t *testing.T) { suiteLoggingTester := new(SuiteLoggingTester) capture := StdoutCapture{} internalTest := testing.InternalTest{ - Name: "SomeTest", + Name: t.Name() + "/SuiteLoggingTester", F: func(subT *testing.T) { Run(subT, suiteLoggingTester) }, @@ -552,14 +552,15 @@ func (s *suiteWithStats) TestPanic() { func TestSuiteWithStats(t *testing.T) { suiteWithStats := new(suiteWithStats) - testing.RunTests(allTestsFilter, []testing.InternalTest{ + suiteSuccess := testing.RunTests(allTestsFilter, []testing.InternalTest{ { - Name: "WithStats", + Name: t.Name() + "/suiteWithStats", F: func(t *testing.T) { Run(t, suiteWithStats) }, }, }) + require.False(t, suiteSuccess, "suiteWithStats should report test failure because of panic in TestPanic") assert.True(t, suiteWithStats.wasCalled) assert.NotZero(t, suiteWithStats.stats.Start) @@ -596,7 +597,7 @@ func TestFailfastSuite(t *testing.T) { ok := testing.RunTests( allTestsFilter, []testing.InternalTest{{ - Name: "TestFailfastSuite", + Name: t.Name() + "/FailfastSuite", F: func(t *testing.T) { Run(t, s) }, @@ -669,9 +670,10 @@ func (s *subtestPanicSuite) TearDownSubTest() { } func (s *subtestPanicSuite) TestSubtestPanic() { - s.Run("subtest", func() { + ok := s.Run("subtest", func() { panic("panic") }) + s.False(ok, "subtest failure is expected") } func TestSubtestPanic(t *testing.T) { @@ -679,13 +681,13 @@ func TestSubtestPanic(t *testing.T) { ok := testing.RunTests( allTestsFilter, []testing.InternalTest{{ - Name: "TestSubtestPanic", + Name: t.Name() + "/subtestPanicSuite", F: func(t *testing.T) { Run(t, suite) }, }}, ) - assert.False(t, ok) + assert.False(t, ok, "TestSubtestPanic/subtest should make the testsuite fail") assert.True(t, suite.inTearDownSubTest) assert.True(t, suite.inTearDownTest) assert.True(t, suite.inTearDownSuite) From 858080fbab9209e4bc8f2587a0377a964d844be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Thu, 9 Nov 2023 01:07:57 +0100 Subject: [PATCH 33/34] assert: more unsafe.Pointer tests 1. Isolate tests that use the "unsafe" package in a separate package assert/internal/unsafetests. That way the assert package is not tainted with unsafe. 2. Remove one reference to the private assert.isNil() in assert tests. 3. Add more tests of assert.Nil and assert.NotNil with unsafe.Pointer. --- assert/assertions_test.go | 8 ----- assert/internal/unsafetests/doc.go | 4 +++ .../internal/unsafetests/unsafetests_test.go | 34 +++++++++++++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 assert/internal/unsafetests/doc.go create mode 100644 assert/internal/unsafetests/unsafetests_test.go diff --git a/assert/assertions_test.go b/assert/assertions_test.go index d2a25c2..61577e5 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -16,7 +16,6 @@ import ( "strings" "testing" "time" - "unsafe" ) var ( @@ -3138,10 +3137,3 @@ func TestErrorAs(t *testing.T) { }) } } - -func TestIsNil(t *testing.T) { - var n unsafe.Pointer = nil - if !isNil(n) { - t.Fatal("fail") - } -} diff --git a/assert/internal/unsafetests/doc.go b/assert/internal/unsafetests/doc.go new file mode 100644 index 0000000..08172d5 --- /dev/null +++ b/assert/internal/unsafetests/doc.go @@ -0,0 +1,4 @@ +// This package exists just to isolate tests that reference the [unsafe] package. +// +// The tests in this package are totally safe. +package unsafetests diff --git a/assert/internal/unsafetests/unsafetests_test.go b/assert/internal/unsafetests/unsafetests_test.go new file mode 100644 index 0000000..b7f01a6 --- /dev/null +++ b/assert/internal/unsafetests/unsafetests_test.go @@ -0,0 +1,34 @@ +package unsafetests_test + +import ( + "fmt" + "testing" + "unsafe" + + "github.com/stretchr/testify/assert" +) + +type ignoreTestingT struct{} + +var _ assert.TestingT = ignoreTestingT{} + +func (ignoreTestingT) Helper() {} + +func (ignoreTestingT) Errorf(format string, args ...interface{}) { + // Run the formatting, but ignore the result + msg := fmt.Sprintf(format, args...) + _ = msg +} + +func TestUnsafePointers(t *testing.T) { + var ignore ignoreTestingT + + assert.True(t, assert.Nil(t, unsafe.Pointer(nil), "unsafe.Pointer(nil) is nil")) + assert.False(t, assert.NotNil(ignore, unsafe.Pointer(nil), "unsafe.Pointer(nil) is nil")) + + assert.True(t, assert.Nil(t, unsafe.Pointer((*int)(nil)), "unsafe.Pointer((*int)(nil)) is nil")) + assert.False(t, assert.NotNil(ignore, unsafe.Pointer((*int)(nil)), "unsafe.Pointer((*int)(nil)) is nil")) + + assert.False(t, assert.Nil(ignore, unsafe.Pointer(new(int)), "unsafe.Pointer(new(int)) is NOT nil")) + assert.True(t, assert.NotNil(t, unsafe.Pointer(new(int)), "unsafe.Pointer(new(int)) is NOT nil")) +} From cbcc423cdfea8beb19f8020563392537148bcacc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Thu, 9 Nov 2023 01:17:37 +0100 Subject: [PATCH 34/34] assert: simplify isNil implementation Simpler and faster (also less allocations) in internal isNil(). --- assert/assertions.go | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index bc15101..74f22eb 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -632,17 +632,6 @@ func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return Fail(t, "Expected value not to be nil.", msgAndArgs...) } -// containsKind checks if a specified kind in the slice of kinds. -func containsKind(kinds []reflect.Kind, kind reflect.Kind) bool { - for i := 0; i < len(kinds); i++ { - if kind == kinds[i] { - return true - } - } - - return false -} - // isNil checks if a specified object is nil or not, without Failing. func isNil(object interface{}) bool { if object == nil { @@ -650,16 +639,13 @@ func isNil(object interface{}) bool { } value := reflect.ValueOf(object) - kind := value.Kind() - isNilableKind := containsKind( - []reflect.Kind{ - reflect.Chan, reflect.Func, - reflect.Interface, reflect.Map, - reflect.Ptr, reflect.Slice, reflect.UnsafePointer}, - kind) + switch value.Kind() { + case + reflect.Chan, reflect.Func, + reflect.Interface, reflect.Map, + reflect.Ptr, reflect.Slice, reflect.UnsafePointer: - if isNilableKind && value.IsNil() { - return true + return value.IsNil() } return false