diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index 99be99128..a28d616fb 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -11,5 +11,5 @@ jobs: permissions: contents: write pull-requests: write - uses: go-openapi/ci-workflows/.github/workflows/auto-merge.yml@ea0dfc9a8f78335f47355d842b76bcf95d29c846 # v0.2.5 + uses: go-openapi/ci-workflows/.github/workflows/auto-merge.yml@2a3bc27eb79f3f5e5dc7a43954e9f41eeb96ebcb # v0.2.5 secrets: inherit diff --git a/.github/workflows/bump-release.yml b/.github/workflows/bump-release.yml index 4446cf3d4..476337b9a 100644 --- a/.github/workflows/bump-release.yml +++ b/.github/workflows/bump-release.yml @@ -32,7 +32,7 @@ jobs: permissions: contents: write pull-requests: write - uses: go-openapi/ci-workflows/.github/workflows/bump-release-monorepo.yml@ea0dfc9a8f78335f47355d842b76bcf95d29c846 # v0.2.5 + uses: go-openapi/ci-workflows/.github/workflows/bump-release-monorepo.yml@2a3bc27eb79f3f5e5dc7a43954e9f41eeb96ebcb # v0.2.5 with: bump-type: ${{ inputs.bump-type }} tag-message-title: ${{ inputs.tag-message-title }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 73da00599..9d489ef21 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -18,5 +18,5 @@ jobs: permissions: contents: read security-events: write - uses: go-openapi/ci-workflows/.github/workflows/codeql.yml@ea0dfc9a8f78335f47355d842b76bcf95d29c846 # v0.2.5 + uses: go-openapi/ci-workflows/.github/workflows/codeql.yml@2a3bc27eb79f3f5e5dc7a43954e9f41eeb96ebcb # v0.2.5 secrets: inherit diff --git a/.github/workflows/contributors.yml b/.github/workflows/contributors.yml index 65d6f46f5..4f7955aeb 100644 --- a/.github/workflows/contributors.yml +++ b/.github/workflows/contributors.yml @@ -14,5 +14,5 @@ jobs: permissions: pull-requests: write contents: write - uses: go-openapi/ci-workflows/.github/workflows/contributors.yml@ea0dfc9a8f78335f47355d842b76bcf95d29c846 # v0.2.5 + uses: go-openapi/ci-workflows/.github/workflows/contributors.yml@2a3bc27eb79f3f5e5dc7a43954e9f41eeb96ebcb # v0.2.5 secrets: inherit diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml index d1baad85b..f11ef638f 100644 --- a/.github/workflows/go-test.yml +++ b/.github/workflows/go-test.yml @@ -13,7 +13,7 @@ on: jobs: test: - uses: go-openapi/ci-workflows/.github/workflows/go-test-monorepo.yml@ea0dfc9a8f78335f47355d842b76bcf95d29c846 # v0.2.5 + uses: go-openapi/ci-workflows/.github/workflows/go-test-monorepo.yml@2a3bc27eb79f3f5e5dc7a43954e9f41eeb96ebcb # v0.2.5 with: - extra-flags: '-tags testcgo' # this is to trigger extra tests in spew + extra-flags: '-tags testcgo,testcolorized' # (1) this is to trigger extra tests in spew, (2) this is to enable integration test for colorized output secrets: inherit diff --git a/.github/workflows/scanner.yml b/.github/workflows/scanner.yml index c7550a9af..50a06e359 100644 --- a/.github/workflows/scanner.yml +++ b/.github/workflows/scanner.yml @@ -15,5 +15,5 @@ jobs: permissions: contents: read security-events: write - uses: go-openapi/ci-workflows/.github/workflows/scanner.yml@ea0dfc9a8f78335f47355d842b76bcf95d29c846 # V0.2.4 + uses: go-openapi/ci-workflows/.github/workflows/scanner.yml@2a3bc27eb79f3f5e5dc7a43954e9f41eeb96ebcb # V0.2.4 secrets: inherit diff --git a/.github/workflows/tag-release.yml b/.github/workflows/tag-release.yml index fd527bd83..aca8bbe5b 100644 --- a/.github/workflows/tag-release.yml +++ b/.github/workflows/tag-release.yml @@ -13,7 +13,7 @@ jobs: name: Create release permissions: contents: write - uses: go-openapi/ci-workflows/.github/workflows/release.yml@ea0dfc9a8f78335f47355d842b76bcf95d29c846 # v0.2.5 + uses: go-openapi/ci-workflows/.github/workflows/release.yml@2a3bc27eb79f3f5e5dc7a43954e9f41eeb96ebcb # v0.2.5 with: tag: ${{ github.ref_name }} is-monorepo: true diff --git a/assert/assert_adhoc_example_6_test.go b/assert/assert_adhoc_example_6_test.go new file mode 100644 index 000000000..e741c51e8 --- /dev/null +++ b/assert/assert_adhoc_example_6_test.go @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package assert_test + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/assert" +) + +func ExampleAssertions_with_generics() { + t := new(testing.T) // normally provided by test + + a := assert.New(t) + + const expected = "hello" + goodValue := "hello" + wrongValue := "world" + + r0 := a.Equal(expected, goodValue) // classic reflect-based assertion + fmt.Printf("good value is %t\n", r0) + + r1 := assert.EqualT(a.T, expected, goodValue) // usage with generic assertion + fmt.Printf("good value is %t\n", r1) + + r2 := assert.EqualT(a.T, expected, wrongValue) + fmt.Printf("wrong value is %t\n", r2) + + // Output: + // good value is true + // good value is true + // wrong value is false +} diff --git a/assert/assert_assertions.go b/assert/assert_assertions.go index 7d0cb908f..5cd9336a0 100644 --- a/assert/assert_assertions.go +++ b/assert/assert_assertions.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package assert @@ -411,7 +410,7 @@ func Eventually(t T, condition func() bool, waitFor time.Duration, tick time.Dur return assertions.Eventually(t, condition, waitFor, tick, msgAndArgs...) } -// EventuallyWithT asserts that the given condition will be met in waitFor time, +// EventuallyWith asserts that the given condition will be met in waitFor time, // periodically checking the target function at each tick. // // In contrast to [Eventually], the condition function is supplied with a [CollectT] @@ -433,7 +432,7 @@ func Eventually(t T, condition func() bool, waitFor time.Duration, tick time.Dur // externalValue = true // }() // -// assertions.EventuallyWithT(t, func(c *assertions.CollectT) { +// assertions.EventuallyWith(t, func(c *assertions.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assertions.True(c, externalValue, "expected 'externalValue' to be true") // }, @@ -453,11 +452,11 @@ func Eventually(t T, condition func() bool, waitFor time.Duration, tick time.Dur // failure: func(c *CollectT) { False(c,true) }, 100*time.Millisecond, 20*time.Millisecond // // Upon failure, the test [T] is marked as failed and continues execution. -func EventuallyWithT(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { +func EventuallyWith(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.EventuallyWithT(t, condition, waitFor, tick, msgAndArgs...) + return assertions.EventuallyWith(t, condition, waitFor, tick, msgAndArgs...) } // Exactly asserts that two objects are equal in value and type. @@ -1403,6 +1402,68 @@ func JSONEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any return assertions.JSONEqT[EDoc, ADoc](t, expected, actual, msgAndArgs...) } +// JSONMarshalAsT wraps [JSONEq] after [json.Marshal]. +// +// The input JSON may be a string or []byte. +// +// It fails if the marshaling returns an error or if the expected JSON bytes differ semantically +// from the expected ones. +// +// # Usage +// +// actual := struct { +// A int `json:"a"` +// }{ +// A: 10, +// } +// +// assertions.JSONUnmarshalAsT(t,expected, `{"a": 10}`) +// +// # Examples +// +// success: []byte(`{"A": "a"}`), dummyStruct{A: "a"} +// failure: `[{"foo": "bar"}, {"hello": "world"}]`, 1 +// +// Upon failure, the test [T] is marked as failed and continues execution. +func JSONMarshalAsT[EDoc Text](t T, expected EDoc, object any, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.JSONMarshalAsT[EDoc](t, expected, object, msgAndArgs...) +} + +// JSONUnmarshalAsT wraps [Equal] after [json.Unmarshal]. +// +// The input JSON may be a string or []byte. +// +// It fails if the unmarshaling returns an error or if the resulting object is not equal to the expected one. +// +// Be careful not to wrap the expected object into an "any" interface if this is not what you expected: +// the unmarshaling would take this type to unmarshal as a map[string]any. +// +// # Usage +// +// expected := struct { +// A int `json:"a"` +// }{ +// A: 10, +// } +// +// assertions.JSONUnmarshalAsT(t,expected, `{"a": 10}`) +// +// # Examples +// +// success: dummyStruct{A: "a"} , []byte(`{"A": "a"}`) +// failure: 1, `[{"foo": "bar"}, {"hello": "world"}]` +// +// Upon failure, the test [T] is marked as failed and continues execution. +func JSONUnmarshalAsT[Object any, ADoc Text](t T, expected Object, jazon ADoc, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.JSONUnmarshalAsT[Object, ADoc](t, expected, jazon, msgAndArgs...) +} + // Kind asserts that the [reflect.Kind] of a given object matches the expected [reflect.Kind]. // // Kind reflects the concrete value stored in the object. The nil value (or interface with nil value) @@ -1716,6 +1777,16 @@ func NoError(t T, err error, msgAndArgs ...any) bool { return assertions.NoError(t, err, msgAndArgs...) } +// NoGoRoutineLeak ensures that no goroutine did leak from inside the tested function. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func NoGoRoutineLeak(t T, inside func(), msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.NoGoRoutineLeak(t, inside, msgAndArgs...) +} + // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // @@ -2713,6 +2784,68 @@ func YAMLEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any return assertions.YAMLEqT[EDoc, ADoc](t, expected, actual, msgAndArgs...) } +// YAMLMarshalAsT wraps [YAMLEq] after [yaml.Marshal]. +// +// The input YAML may be a string or []byte. +// +// It fails if the marshaling returns an error or if the expected YAML bytes differ semantically +// from the expected ones. +// +// # Usage +// +// actual := struct { +// A int `yaml:"a"` +// }{ +// A: 10, +// } +// +// assertions.YAMLUnmarshalAsT(t,expected, `{"a": 10}`) +// +// # Examples +// +// panic: "key: value", "key: value" +// should panic without the yaml feature enabled. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func YAMLMarshalAsT[EDoc Text](t T, expected EDoc, object any, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.YAMLMarshalAsT[EDoc](t, expected, object, msgAndArgs...) +} + +// YAMLUnmarshalAsT wraps [Equal] after [yaml.Unmarshal]. +// +// The input YAML may be a string or []byte. +// +// It fails if the unmarshaling returns an error or if the resulting object is not equal to the expected one. +// +// Be careful not to wrap the expected object into an "any" interface if this is not what you expected: +// the unmarshaling would take this type to unmarshal as a map[string]any. +// +// # Usage +// +// expected := struct { +// A int `yaml:"a"` +// }{ +// A: 10, +// } +// +// assertions.YAMLUnmarshalAsT(t,expected, `{"a": 10}`) +// +// # Examples +// +// panic: "key: value", "key: value" +// should panic without the yaml feature enabled. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func YAMLUnmarshalAsT[Object any, ADoc Text](t T, expected Object, jazon ADoc, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.YAMLUnmarshalAsT[Object, ADoc](t, expected, jazon, msgAndArgs...) +} + // Zero asserts that i is the zero value for its type. // // # Usage diff --git a/assert/assert_assertions_test.go b/assert/assert_assertions_test.go index b39eebfa4..92705a3ee 100644 --- a/assert/assert_assertions_test.go +++ b/assert/assert_assertions_test.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package assert @@ -426,13 +425,13 @@ func TestEventually(t *testing.T) { }) } -func TestEventuallyWithT(t *testing.T) { +func TestEventuallyWith(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - result := EventuallyWithT(t, func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond) + result := EventuallyWith(t, func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond) if !result { - t.Error("EventuallyWithT should return true on success") + t.Error("EventuallyWith should return true on success") } }) @@ -440,12 +439,12 @@ func TestEventuallyWithT(t *testing.T) { t.Parallel() mock := new(mockT) - result := EventuallyWithT(mock, func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond) + result := EventuallyWith(mock, func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond) if result { - t.Error("EventuallyWithT should return false on failure") + t.Error("EventuallyWith should return false on failure") } if !mock.failed { - t.Error("EventuallyWithT should mark test as failed") + t.Error("EventuallyWith should mark test as failed") } }) } @@ -1442,6 +1441,54 @@ func TestJSONEqT(t *testing.T) { }) } +func TestJSONMarshalAsT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := JSONMarshalAsT(t, []byte(`{"A": "a"}`), dummyStruct{A: "a"}) + if !result { + t.Error("JSONMarshalAsT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := JSONMarshalAsT(mock, `[{"foo": "bar"}, {"hello": "world"}]`, 1) + if result { + t.Error("JSONMarshalAsT should return false on failure") + } + if !mock.failed { + t.Error("JSONMarshalAsT should mark test as failed") + } + }) +} + +func TestJSONUnmarshalAsT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := JSONUnmarshalAsT(t, dummyStruct{A: "a"}, []byte(`{"A": "a"}`)) + if !result { + t.Error("JSONUnmarshalAsT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := JSONUnmarshalAsT(mock, 1, `[{"foo": "bar"}, {"hello": "world"}]`) + if result { + t.Error("JSONUnmarshalAsT should return false on failure") + } + if !mock.failed { + t.Error("JSONUnmarshalAsT should mark test as failed") + } + }) +} + func TestKind(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1754,6 +1801,11 @@ func TestNoError(t *testing.T) { }) } +func TestNoGoRoutineLeak(t *testing.T) { + t.Parallel() + t.Skip() // this function doesn't have tests yet: feed the original function with examples to test. +} + func TestNotContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -2819,6 +2871,28 @@ func TestYAMLEqT(t *testing.T) { }) } +func TestYAMLMarshalAsT(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + Panics(t, func() { + YAMLMarshalAsT(t, "key: value", "key: value") + }, "should panic without the yaml feature enabled.") + }) +} + +func TestYAMLUnmarshalAsT(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + Panics(t, func() { + YAMLUnmarshalAsT(t, "key: value", "key: value") + }, "should panic without the yaml feature enabled.") + }) +} + func TestZero(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { diff --git a/assert/assert_examples_test.go b/assert/assert_examples_test.go index d0044c142..495d7cb36 100644 --- a/assert/assert_examples_test.go +++ b/assert/assert_examples_test.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package assert_test @@ -21,7 +20,7 @@ import ( ) func ExampleCondition() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestCondition(t *testing.T) success := assert.Condition(t, func() bool { return true }) @@ -31,7 +30,7 @@ func ExampleCondition() { } func ExampleContains() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestContains(t *testing.T) success := assert.Contains(t, []string{"A", "B"}, "A") fmt.Printf("success: %t\n", success) @@ -39,7 +38,7 @@ func ExampleContains() { } func ExampleDirExists() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestDirExists(t *testing.T) success := assert.DirExists(t, filepath.Join(testDataPath(), "existing_dir")) fmt.Printf("success: %t\n", success) @@ -47,7 +46,7 @@ func ExampleDirExists() { } func ExampleDirNotExists() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestDirNotExists(t *testing.T) success := assert.DirNotExists(t, filepath.Join(testDataPath(), "non_existing_dir")) fmt.Printf("success: %t\n", success) @@ -55,7 +54,7 @@ func ExampleDirNotExists() { } func ExampleElementsMatch() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestElementsMatch(t *testing.T) success := assert.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) fmt.Printf("success: %t\n", success) @@ -63,7 +62,7 @@ func ExampleElementsMatch() { } func ExampleElementsMatchT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestElementsMatchT(t *testing.T) success := assert.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) fmt.Printf("success: %t\n", success) @@ -71,7 +70,7 @@ func ExampleElementsMatchT() { } func ExampleEmpty() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestEmpty(t *testing.T) success := assert.Empty(t, "") fmt.Printf("success: %t\n", success) @@ -79,7 +78,7 @@ func ExampleEmpty() { } func ExampleEqual() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestEqual(t *testing.T) success := assert.Equal(t, 123, 123) fmt.Printf("success: %t\n", success) @@ -87,7 +86,7 @@ func ExampleEqual() { } func ExampleEqualError() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestEqualError(t *testing.T) success := assert.EqualError(t, assert.ErrTest, "assert.ErrTest general error for testing") fmt.Printf("success: %t\n", success) @@ -95,7 +94,7 @@ func ExampleEqualError() { } func ExampleEqualExportedValues() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestEqualExportedValues(t *testing.T) success := assert.EqualExportedValues(t, &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2}) fmt.Printf("success: %t\n", success) @@ -103,7 +102,7 @@ func ExampleEqualExportedValues() { } func ExampleEqualT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestEqualT(t *testing.T) success := assert.EqualT(t, 123, 123) fmt.Printf("success: %t\n", success) @@ -111,7 +110,7 @@ func ExampleEqualT() { } func ExampleEqualValues() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestEqualValues(t *testing.T) success := assert.EqualValues(t, uint32(123), int32(123)) fmt.Printf("success: %t\n", success) @@ -119,7 +118,7 @@ func ExampleEqualValues() { } func ExampleError() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestError(t *testing.T) success := assert.Error(t, assert.ErrTest) fmt.Printf("success: %t\n", success) @@ -127,7 +126,7 @@ func ExampleError() { } func ExampleErrorAs() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestErrorAs(t *testing.T) success := assert.ErrorAs(t, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) fmt.Printf("success: %t\n", success) @@ -135,7 +134,7 @@ func ExampleErrorAs() { } func ExampleErrorContains() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestErrorContains(t *testing.T) success := assert.ErrorContains(t, assert.ErrTest, "general error") fmt.Printf("success: %t\n", success) @@ -143,7 +142,7 @@ func ExampleErrorContains() { } func ExampleErrorIs() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestErrorIs(t *testing.T) success := assert.ErrorIs(t, fmt.Errorf("wrap: %w", io.EOF), io.EOF) fmt.Printf("success: %t\n", success) @@ -151,7 +150,7 @@ func ExampleErrorIs() { } func ExampleEventually() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestEventually(t *testing.T) success := assert.Eventually(t, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond) @@ -160,9 +159,9 @@ func ExampleEventually() { // Output: success: true } -func ExampleEventuallyWithT() { - t := new(testing.T) - success := assert.EventuallyWithT(t, func(c *assert.CollectT) { +func ExampleEventuallyWith() { + t := new(testing.T) // should come from testing, e.g. func TestEventuallyWith(t *testing.T) + success := assert.EventuallyWith(t, func(c *assert.CollectT) { assert.True(c, true) }, 100*time.Millisecond, 20*time.Millisecond) fmt.Printf("success: %t\n", success) @@ -171,7 +170,7 @@ func ExampleEventuallyWithT() { } func ExampleExactly() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestExactly(t *testing.T) success := assert.Exactly(t, int32(123), int32(123)) fmt.Printf("success: %t\n", success) @@ -187,7 +186,7 @@ func ExampleExactly() { // } func ExampleFalse() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestFalse(t *testing.T) success := assert.False(t, 1 == 0) fmt.Printf("success: %t\n", success) @@ -195,7 +194,7 @@ func ExampleFalse() { } func ExampleFalseT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestFalseT(t *testing.T) success := assert.FalseT(t, 1 == 0) fmt.Printf("success: %t\n", success) @@ -203,7 +202,7 @@ func ExampleFalseT() { } func ExampleFileEmpty() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestFileEmpty(t *testing.T) success := assert.FileEmpty(t, filepath.Join(testDataPath(), "empty_file")) fmt.Printf("success: %t\n", success) @@ -211,7 +210,7 @@ func ExampleFileEmpty() { } func ExampleFileExists() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestFileExists(t *testing.T) success := assert.FileExists(t, filepath.Join(testDataPath(), "existing_file")) fmt.Printf("success: %t\n", success) @@ -219,7 +218,7 @@ func ExampleFileExists() { } func ExampleFileNotEmpty() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestFileNotEmpty(t *testing.T) success := assert.FileNotEmpty(t, filepath.Join(testDataPath(), "existing_file")) fmt.Printf("success: %t\n", success) @@ -227,7 +226,7 @@ func ExampleFileNotEmpty() { } func ExampleFileNotExists() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestFileNotExists(t *testing.T) success := assert.FileNotExists(t, filepath.Join(testDataPath(), "non_existing_file")) fmt.Printf("success: %t\n", success) @@ -235,7 +234,7 @@ func ExampleFileNotExists() { } func ExampleGreater() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestGreater(t *testing.T) success := assert.Greater(t, 2, 1) fmt.Printf("success: %t\n", success) @@ -243,7 +242,7 @@ func ExampleGreater() { } func ExampleGreaterOrEqual() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestGreaterOrEqual(t *testing.T) success := assert.GreaterOrEqual(t, 2, 1) fmt.Printf("success: %t\n", success) @@ -251,7 +250,7 @@ func ExampleGreaterOrEqual() { } func ExampleGreaterOrEqualT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestGreaterOrEqualT(t *testing.T) success := assert.GreaterOrEqualT(t, 2, 1) fmt.Printf("success: %t\n", success) @@ -259,7 +258,7 @@ func ExampleGreaterOrEqualT() { } func ExampleGreaterT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestGreaterT(t *testing.T) success := assert.GreaterT(t, 2, 1) fmt.Printf("success: %t\n", success) @@ -267,7 +266,7 @@ func ExampleGreaterT() { } func ExampleHTTPBodyContains() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestHTTPBodyContains(t *testing.T) success := assert.HTTPBodyContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!") fmt.Printf("success: %t\n", success) @@ -275,7 +274,7 @@ func ExampleHTTPBodyContains() { } func ExampleHTTPBodyNotContains() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestHTTPBodyNotContains(t *testing.T) success := assert.HTTPBodyNotContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!") fmt.Printf("success: %t\n", success) @@ -283,7 +282,7 @@ func ExampleHTTPBodyNotContains() { } func ExampleHTTPError() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestHTTPError(t *testing.T) success := assert.HTTPError(t, httpError, "GET", "/", nil) fmt.Printf("success: %t\n", success) @@ -291,7 +290,7 @@ func ExampleHTTPError() { } func ExampleHTTPRedirect() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestHTTPRedirect(t *testing.T) success := assert.HTTPRedirect(t, httpRedirect, "GET", "/", nil) fmt.Printf("success: %t\n", success) @@ -299,7 +298,7 @@ func ExampleHTTPRedirect() { } func ExampleHTTPStatusCode() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestHTTPStatusCode(t *testing.T) success := assert.HTTPStatusCode(t, httpOK, "GET", "/", nil, http.StatusOK) fmt.Printf("success: %t\n", success) @@ -307,7 +306,7 @@ func ExampleHTTPStatusCode() { } func ExampleHTTPSuccess() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestHTTPSuccess(t *testing.T) success := assert.HTTPSuccess(t, httpOK, "GET", "/", nil) fmt.Printf("success: %t\n", success) @@ -315,7 +314,7 @@ func ExampleHTTPSuccess() { } func ExampleImplements() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestImplements(t *testing.T) success := assert.Implements(t, ptr(dummyInterface), new(testing.T)) fmt.Printf("success: %t\n", success) @@ -323,7 +322,7 @@ func ExampleImplements() { } func ExampleInDelta() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestInDelta(t *testing.T) success := assert.InDelta(t, 1.0, 1.01, 0.02) fmt.Printf("success: %t\n", success) @@ -331,7 +330,7 @@ func ExampleInDelta() { } func ExampleInDeltaMapValues() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestInDeltaMapValues(t *testing.T) success := assert.InDeltaMapValues(t, map[string]float64{"a": 1.0}, map[string]float64{"a": 1.01}, 0.02) fmt.Printf("success: %t\n", success) @@ -339,7 +338,7 @@ func ExampleInDeltaMapValues() { } func ExampleInDeltaSlice() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestInDeltaSlice(t *testing.T) success := assert.InDeltaSlice(t, []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02) fmt.Printf("success: %t\n", success) @@ -347,7 +346,7 @@ func ExampleInDeltaSlice() { } func ExampleInDeltaT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestInDeltaT(t *testing.T) success := assert.InDeltaT(t, 1.0, 1.01, 0.02) fmt.Printf("success: %t\n", success) @@ -355,7 +354,7 @@ func ExampleInDeltaT() { } func ExampleInEpsilon() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestInEpsilon(t *testing.T) success := assert.InEpsilon(t, 100.0, 101.0, 0.02) fmt.Printf("success: %t\n", success) @@ -363,7 +362,7 @@ func ExampleInEpsilon() { } func ExampleInEpsilonSlice() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestInEpsilonSlice(t *testing.T) success := assert.InEpsilonSlice(t, []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02) fmt.Printf("success: %t\n", success) @@ -371,7 +370,7 @@ func ExampleInEpsilonSlice() { } func ExampleInEpsilonT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestInEpsilonT(t *testing.T) success := assert.InEpsilonT(t, 100.0, 101.0, 0.02) fmt.Printf("success: %t\n", success) @@ -379,7 +378,7 @@ func ExampleInEpsilonT() { } func ExampleIsDecreasing() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsDecreasing(t *testing.T) success := assert.IsDecreasing(t, []int{3, 2, 1}) fmt.Printf("success: %t\n", success) @@ -387,7 +386,7 @@ func ExampleIsDecreasing() { } func ExampleIsDecreasingT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsDecreasingT(t *testing.T) success := assert.IsDecreasingT(t, []int{3, 2, 1}) fmt.Printf("success: %t\n", success) @@ -395,7 +394,7 @@ func ExampleIsDecreasingT() { } func ExampleIsIncreasing() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsIncreasing(t *testing.T) success := assert.IsIncreasing(t, []int{1, 2, 3}) fmt.Printf("success: %t\n", success) @@ -403,7 +402,7 @@ func ExampleIsIncreasing() { } func ExampleIsIncreasingT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsIncreasingT(t *testing.T) success := assert.IsIncreasingT(t, []int{1, 2, 3}) fmt.Printf("success: %t\n", success) @@ -411,7 +410,7 @@ func ExampleIsIncreasingT() { } func ExampleIsNonDecreasing() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsNonDecreasing(t *testing.T) success := assert.IsNonDecreasing(t, []int{1, 1, 2}) fmt.Printf("success: %t\n", success) @@ -419,7 +418,7 @@ func ExampleIsNonDecreasing() { } func ExampleIsNonDecreasingT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsNonDecreasingT(t *testing.T) success := assert.IsNonDecreasingT(t, []int{1, 1, 2}) fmt.Printf("success: %t\n", success) @@ -427,7 +426,7 @@ func ExampleIsNonDecreasingT() { } func ExampleIsNonIncreasing() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsNonIncreasing(t *testing.T) success := assert.IsNonIncreasing(t, []int{2, 1, 1}) fmt.Printf("success: %t\n", success) @@ -435,7 +434,7 @@ func ExampleIsNonIncreasing() { } func ExampleIsNonIncreasingT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsNonIncreasingT(t *testing.T) success := assert.IsNonIncreasingT(t, []int{2, 1, 1}) fmt.Printf("success: %t\n", success) @@ -443,7 +442,7 @@ func ExampleIsNonIncreasingT() { } func ExampleIsNotOfTypeT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsNotOfTypeT(t *testing.T) success := assert.IsNotOfTypeT[myType](t, 123.123) fmt.Printf("success: %t\n", success) @@ -451,7 +450,7 @@ func ExampleIsNotOfTypeT() { } func ExampleIsNotType() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsNotType(t *testing.T) success := assert.IsNotType(t, int32(123), int64(456)) fmt.Printf("success: %t\n", success) @@ -459,7 +458,7 @@ func ExampleIsNotType() { } func ExampleIsOfTypeT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsOfTypeT(t *testing.T) success := assert.IsOfTypeT[myType](t, myType(123.123)) fmt.Printf("success: %t\n", success) @@ -467,7 +466,7 @@ func ExampleIsOfTypeT() { } func ExampleIsType() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsType(t *testing.T) success := assert.IsType(t, 123, 456) fmt.Printf("success: %t\n", success) @@ -475,7 +474,7 @@ func ExampleIsType() { } func ExampleJSONEq() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestJSONEq(t *testing.T) success := assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) fmt.Printf("success: %t\n", success) @@ -483,7 +482,7 @@ func ExampleJSONEq() { } func ExampleJSONEqBytes() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestJSONEqBytes(t *testing.T) success := assert.JSONEqBytes(t, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`)) fmt.Printf("success: %t\n", success) @@ -491,15 +490,31 @@ func ExampleJSONEqBytes() { } func ExampleJSONEqT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestJSONEqT(t *testing.T) success := assert.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) fmt.Printf("success: %t\n", success) // Output: success: true } +func ExampleJSONMarshalAsT() { + t := new(testing.T) // should come from testing, e.g. func TestJSONMarshalAsT(t *testing.T) + success := assert.JSONMarshalAsT(t, []byte(`{"A": "a"}`), dummyStruct{A: "a"}) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + +func ExampleJSONUnmarshalAsT() { + t := new(testing.T) // should come from testing, e.g. func TestJSONUnmarshalAsT(t *testing.T) + success := assert.JSONUnmarshalAsT(t, dummyStruct{A: "a"}, []byte(`{"A": "a"}`)) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleKind() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestKind(t *testing.T) success := assert.Kind(t, reflect.String, "hello") fmt.Printf("success: %t\n", success) @@ -507,7 +522,7 @@ func ExampleKind() { } func ExampleLen() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestLen(t *testing.T) success := assert.Len(t, []string{"A", "B"}, 2) fmt.Printf("success: %t\n", success) @@ -515,7 +530,7 @@ func ExampleLen() { } func ExampleLess() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestLess(t *testing.T) success := assert.Less(t, 1, 2) fmt.Printf("success: %t\n", success) @@ -523,7 +538,7 @@ func ExampleLess() { } func ExampleLessOrEqual() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestLessOrEqual(t *testing.T) success := assert.LessOrEqual(t, 1, 2) fmt.Printf("success: %t\n", success) @@ -531,7 +546,7 @@ func ExampleLessOrEqual() { } func ExampleLessOrEqualT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestLessOrEqualT(t *testing.T) success := assert.LessOrEqualT(t, 1, 2) fmt.Printf("success: %t\n", success) @@ -539,7 +554,7 @@ func ExampleLessOrEqualT() { } func ExampleLessT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestLessT(t *testing.T) success := assert.LessT(t, 1, 2) fmt.Printf("success: %t\n", success) @@ -547,7 +562,7 @@ func ExampleLessT() { } func ExampleMapContainsT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestMapContainsT(t *testing.T) success := assert.MapContainsT(t, map[string]string{"A": "B"}, "A") fmt.Printf("success: %t\n", success) @@ -555,7 +570,7 @@ func ExampleMapContainsT() { } func ExampleMapNotContainsT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestMapNotContainsT(t *testing.T) success := assert.MapNotContainsT(t, map[string]string{"A": "B"}, "C") fmt.Printf("success: %t\n", success) @@ -563,7 +578,7 @@ func ExampleMapNotContainsT() { } func ExampleNegative() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNegative(t *testing.T) success := assert.Negative(t, -1) fmt.Printf("success: %t\n", success) @@ -571,7 +586,7 @@ func ExampleNegative() { } func ExampleNegativeT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNegativeT(t *testing.T) success := assert.NegativeT(t, -1) fmt.Printf("success: %t\n", success) @@ -579,7 +594,7 @@ func ExampleNegativeT() { } func ExampleNever() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNever(t *testing.T) success := assert.Never(t, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond) @@ -589,7 +604,7 @@ func ExampleNever() { } func ExampleNil() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNil(t *testing.T) success := assert.Nil(t, nil) fmt.Printf("success: %t\n", success) @@ -597,15 +612,19 @@ func ExampleNil() { } func ExampleNoError() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNoError(t *testing.T) success := assert.NoError(t, nil) fmt.Printf("success: %t\n", success) // Output: success: true } +// func ExampleNoGoRoutineLeak() { +// no success example available. Please add some examples to produce a testable example. +// } + func ExampleNotContains() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotContains(t *testing.T) success := assert.NotContains(t, []string{"A", "B"}, "C") fmt.Printf("success: %t\n", success) @@ -613,7 +632,7 @@ func ExampleNotContains() { } func ExampleNotElementsMatch() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotElementsMatch(t *testing.T) success := assert.NotElementsMatch(t, []int{1, 2, 3}, []int{1, 2, 4}) fmt.Printf("success: %t\n", success) @@ -621,7 +640,7 @@ func ExampleNotElementsMatch() { } func ExampleNotElementsMatchT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotElementsMatchT(t *testing.T) success := assert.NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4}) fmt.Printf("success: %t\n", success) @@ -629,7 +648,7 @@ func ExampleNotElementsMatchT() { } func ExampleNotEmpty() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotEmpty(t *testing.T) success := assert.NotEmpty(t, "not empty") fmt.Printf("success: %t\n", success) @@ -637,7 +656,7 @@ func ExampleNotEmpty() { } func ExampleNotEqual() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotEqual(t *testing.T) success := assert.NotEqual(t, 123, 456) fmt.Printf("success: %t\n", success) @@ -645,7 +664,7 @@ func ExampleNotEqual() { } func ExampleNotEqualT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotEqualT(t *testing.T) success := assert.NotEqualT(t, 123, 456) fmt.Printf("success: %t\n", success) @@ -653,7 +672,7 @@ func ExampleNotEqualT() { } func ExampleNotEqualValues() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotEqualValues(t *testing.T) success := assert.NotEqualValues(t, uint32(123), int32(456)) fmt.Printf("success: %t\n", success) @@ -661,7 +680,7 @@ func ExampleNotEqualValues() { } func ExampleNotErrorAs() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotErrorAs(t *testing.T) success := assert.NotErrorAs(t, assert.ErrTest, new(*dummyError)) fmt.Printf("success: %t\n", success) @@ -669,7 +688,7 @@ func ExampleNotErrorAs() { } func ExampleNotErrorIs() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotErrorIs(t *testing.T) success := assert.NotErrorIs(t, assert.ErrTest, io.EOF) fmt.Printf("success: %t\n", success) @@ -677,7 +696,7 @@ func ExampleNotErrorIs() { } func ExampleNotImplements() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotImplements(t *testing.T) success := assert.NotImplements(t, (*error)(nil), new(testing.T)) fmt.Printf("success: %t\n", success) @@ -685,7 +704,7 @@ func ExampleNotImplements() { } func ExampleNotKind() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotKind(t *testing.T) success := assert.NotKind(t, reflect.String, 0) fmt.Printf("success: %t\n", success) @@ -693,7 +712,7 @@ func ExampleNotKind() { } func ExampleNotNil() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotNil(t *testing.T) success := assert.NotNil(t, "not nil") fmt.Printf("success: %t\n", success) @@ -701,7 +720,7 @@ func ExampleNotNil() { } func ExampleNotPanics() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotPanics(t *testing.T) success := assert.NotPanics(t, func() { }) fmt.Printf("success: %t\n", success) @@ -710,7 +729,7 @@ func ExampleNotPanics() { } func ExampleNotRegexp() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotRegexp(t *testing.T) success := assert.NotRegexp(t, "^start", "not starting") fmt.Printf("success: %t\n", success) @@ -718,7 +737,7 @@ func ExampleNotRegexp() { } func ExampleNotRegexpT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotRegexpT(t *testing.T) success := assert.NotRegexpT(t, "^start", "not starting") fmt.Printf("success: %t\n", success) @@ -726,7 +745,7 @@ func ExampleNotRegexpT() { } func ExampleNotSame() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotSame(t *testing.T) success := assert.NotSame(t, &staticVar, ptr("static string")) fmt.Printf("success: %t\n", success) @@ -734,7 +753,7 @@ func ExampleNotSame() { } func ExampleNotSameT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotSameT(t *testing.T) success := assert.NotSameT(t, &staticVar, ptr("static string")) fmt.Printf("success: %t\n", success) @@ -742,7 +761,7 @@ func ExampleNotSameT() { } func ExampleNotSortedT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotSortedT(t *testing.T) success := assert.NotSortedT(t, []int{3, 1, 3}) fmt.Printf("success: %t\n", success) @@ -750,7 +769,7 @@ func ExampleNotSortedT() { } func ExampleNotSubset() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotSubset(t *testing.T) success := assert.NotSubset(t, []int{1, 2, 3}, []int{4, 5}) fmt.Printf("success: %t\n", success) @@ -758,7 +777,7 @@ func ExampleNotSubset() { } func ExampleNotZero() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotZero(t *testing.T) success := assert.NotZero(t, 1) fmt.Printf("success: %t\n", success) @@ -766,7 +785,7 @@ func ExampleNotZero() { } func ExamplePanics() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestPanics(t *testing.T) success := assert.Panics(t, func() { panic("panicking") }) @@ -776,7 +795,7 @@ func ExamplePanics() { } func ExamplePanicsWithError() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestPanicsWithError(t *testing.T) success := assert.PanicsWithError(t, assert.ErrTest.Error(), func() { panic(assert.ErrTest) }) @@ -786,7 +805,7 @@ func ExamplePanicsWithError() { } func ExamplePanicsWithValue() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestPanicsWithValue(t *testing.T) success := assert.PanicsWithValue(t, "panicking", func() { panic("panicking") }) @@ -796,7 +815,7 @@ func ExamplePanicsWithValue() { } func ExamplePositive() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestPositive(t *testing.T) success := assert.Positive(t, 1) fmt.Printf("success: %t\n", success) @@ -804,7 +823,7 @@ func ExamplePositive() { } func ExamplePositiveT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestPositiveT(t *testing.T) success := assert.PositiveT(t, 1) fmt.Printf("success: %t\n", success) @@ -812,7 +831,7 @@ func ExamplePositiveT() { } func ExampleRegexp() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestRegexp(t *testing.T) success := assert.Regexp(t, "^start", "starting") fmt.Printf("success: %t\n", success) @@ -820,7 +839,7 @@ func ExampleRegexp() { } func ExampleRegexpT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestRegexpT(t *testing.T) success := assert.RegexpT(t, "^start", "starting") fmt.Printf("success: %t\n", success) @@ -828,7 +847,7 @@ func ExampleRegexpT() { } func ExampleSame() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSame(t *testing.T) success := assert.Same(t, &staticVar, staticVarPtr) fmt.Printf("success: %t\n", success) @@ -836,7 +855,7 @@ func ExampleSame() { } func ExampleSameT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSameT(t *testing.T) success := assert.SameT(t, &staticVar, staticVarPtr) fmt.Printf("success: %t\n", success) @@ -844,7 +863,7 @@ func ExampleSameT() { } func ExampleSeqContainsT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSeqContainsT(t *testing.T) success := assert.SeqContainsT(t, slices.Values([]string{"A", "B"}), "A") fmt.Printf("success: %t\n", success) @@ -852,7 +871,7 @@ func ExampleSeqContainsT() { } func ExampleSeqNotContainsT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSeqNotContainsT(t *testing.T) success := assert.SeqNotContainsT(t, slices.Values([]string{"A", "B"}), "C") fmt.Printf("success: %t\n", success) @@ -860,7 +879,7 @@ func ExampleSeqNotContainsT() { } func ExampleSliceContainsT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSliceContainsT(t *testing.T) success := assert.SliceContainsT(t, []string{"A", "B"}, "A") fmt.Printf("success: %t\n", success) @@ -868,7 +887,7 @@ func ExampleSliceContainsT() { } func ExampleSliceNotContainsT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSliceNotContainsT(t *testing.T) success := assert.SliceNotContainsT(t, []string{"A", "B"}, "C") fmt.Printf("success: %t\n", success) @@ -876,7 +895,7 @@ func ExampleSliceNotContainsT() { } func ExampleSliceNotSubsetT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSliceNotSubsetT(t *testing.T) success := assert.SliceNotSubsetT(t, []int{1, 2, 3}, []int{4, 5}) fmt.Printf("success: %t\n", success) @@ -884,7 +903,7 @@ func ExampleSliceNotSubsetT() { } func ExampleSliceSubsetT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSliceSubsetT(t *testing.T) success := assert.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) fmt.Printf("success: %t\n", success) @@ -892,7 +911,7 @@ func ExampleSliceSubsetT() { } func ExampleSortedT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSortedT(t *testing.T) success := assert.SortedT(t, []int{1, 1, 3}) fmt.Printf("success: %t\n", success) @@ -900,7 +919,7 @@ func ExampleSortedT() { } func ExampleStringContainsT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestStringContainsT(t *testing.T) success := assert.StringContainsT(t, "AB", "A") fmt.Printf("success: %t\n", success) @@ -908,7 +927,7 @@ func ExampleStringContainsT() { } func ExampleStringNotContainsT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestStringNotContainsT(t *testing.T) success := assert.StringNotContainsT(t, "AB", "C") fmt.Printf("success: %t\n", success) @@ -916,7 +935,7 @@ func ExampleStringNotContainsT() { } func ExampleSubset() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSubset(t *testing.T) success := assert.Subset(t, []int{1, 2, 3}, []int{1, 2}) fmt.Printf("success: %t\n", success) @@ -924,7 +943,7 @@ func ExampleSubset() { } func ExampleTrue() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestTrue(t *testing.T) success := assert.True(t, 1 == 1) fmt.Printf("success: %t\n", success) @@ -932,7 +951,7 @@ func ExampleTrue() { } func ExampleTrueT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestTrueT(t *testing.T) success := assert.TrueT(t, 1 == 1) fmt.Printf("success: %t\n", success) @@ -940,7 +959,7 @@ func ExampleTrueT() { } func ExampleWithinDuration() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestWithinDuration(t *testing.T) success := assert.WithinDuration(t, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second) fmt.Printf("success: %t\n", success) @@ -948,7 +967,7 @@ func ExampleWithinDuration() { } func ExampleWithinRange() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestWithinRange(t *testing.T) success := assert.WithinRange(t, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC)) fmt.Printf("success: %t\n", success) @@ -967,8 +986,16 @@ func ExampleWithinRange() { // no success example available. Please add some examples to produce a testable example. // } +// func ExampleYAMLMarshalAsT() { +// no success example available. Please add some examples to produce a testable example. +// } + +// func ExampleYAMLUnmarshalAsT() { +// no success example available. Please add some examples to produce a testable example. +// } + func ExampleZero() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestZero(t *testing.T) success := assert.Zero(t, 0) fmt.Printf("success: %t\n", success) diff --git a/assert/assert_format.go b/assert/assert_format.go index b83f7f7ba..5d3740d84 100644 --- a/assert/assert_format.go +++ b/assert/assert_format.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package assert @@ -186,14 +185,14 @@ func Eventuallyf(t T, condition func() bool, waitFor time.Duration, tick time.Du return assertions.Eventually(t, condition, waitFor, tick, forwardArgs(msg, args)) } -// EventuallyWithTf is the same as [EventuallyWithT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// EventuallyWithf is the same as [EventuallyWith], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. -func EventuallyWithTf(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...any) bool { +func EventuallyWithf(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.EventuallyWithT(t, condition, waitFor, tick, forwardArgs(msg, args)) + return assertions.EventuallyWith(t, condition, waitFor, tick, forwardArgs(msg, args)) } // Exactlyf is the same as [Exactly], but it accepts a format msg string to format arguments like [fmt.Printf]. @@ -616,6 +615,26 @@ func JSONEqTf[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msg string, args return assertions.JSONEqT[EDoc, ADoc](t, expected, actual, forwardArgs(msg, args)) } +// JSONMarshalAsTf is the same as [JSONMarshalAsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func JSONMarshalAsTf[EDoc Text](t T, expected EDoc, object any, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.JSONMarshalAsT[EDoc](t, expected, object, forwardArgs(msg, args)) +} + +// JSONUnmarshalAsTf is the same as [JSONUnmarshalAsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func JSONUnmarshalAsTf[Object any, ADoc Text](t T, expected Object, jazon ADoc, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.JSONUnmarshalAsT[Object, ADoc](t, expected, jazon, forwardArgs(msg, args)) +} + // Kindf is the same as [Kind], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. @@ -746,6 +765,16 @@ func NoErrorf(t T, err error, msg string, args ...any) bool { return assertions.NoError(t, err, forwardArgs(msg, args)) } +// NoGoRoutineLeakf is the same as [NoGoRoutineLeak], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func NoGoRoutineLeakf(t T, inside func(), msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.NoGoRoutineLeak(t, inside, forwardArgs(msg, args)) +} + // NotContainsf is the same as [NotContains], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. @@ -1206,6 +1235,26 @@ func YAMLEqTf[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msg string, args return assertions.YAMLEqT[EDoc, ADoc](t, expected, actual, forwardArgs(msg, args)) } +// YAMLMarshalAsTf is the same as [YAMLMarshalAsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func YAMLMarshalAsTf[EDoc Text](t T, expected EDoc, object any, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.YAMLMarshalAsT[EDoc](t, expected, object, forwardArgs(msg, args)) +} + +// YAMLUnmarshalAsTf is the same as [YAMLUnmarshalAsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func YAMLUnmarshalAsTf[Object any, ADoc Text](t T, expected Object, jazon ADoc, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.YAMLUnmarshalAsT[Object, ADoc](t, expected, jazon, forwardArgs(msg, args)) +} + // Zerof is the same as [Zero], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. diff --git a/assert/assert_format_test.go b/assert/assert_format_test.go index 708337fbc..30b72f0ec 100644 --- a/assert/assert_format_test.go +++ b/assert/assert_format_test.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package assert @@ -426,13 +425,13 @@ func TestEventuallyf(t *testing.T) { }) } -func TestEventuallyWithTf(t *testing.T) { +func TestEventuallyWithf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - result := EventuallyWithTf(t, func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") + result := EventuallyWithf(t, func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") if !result { - t.Error("EventuallyWithTf should return true on success") + t.Error("EventuallyWithf should return true on success") } }) @@ -440,12 +439,12 @@ func TestEventuallyWithTf(t *testing.T) { t.Parallel() mock := new(mockT) - result := EventuallyWithTf(mock, func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") + result := EventuallyWithf(mock, func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") if result { - t.Error("EventuallyWithTf should return false on failure") + t.Error("EventuallyWithf should return false on failure") } if !mock.failed { - t.Error("EventuallyWithT should mark test as failed") + t.Error("EventuallyWith should mark test as failed") } }) } @@ -1442,6 +1441,54 @@ func TestJSONEqTf(t *testing.T) { }) } +func TestJSONMarshalAsTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := JSONMarshalAsTf(t, []byte(`{"A": "a"}`), dummyStruct{A: "a"}, "test message") + if !result { + t.Error("JSONMarshalAsTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := JSONMarshalAsTf(mock, `[{"foo": "bar"}, {"hello": "world"}]`, 1, "test message") + if result { + t.Error("JSONMarshalAsTf should return false on failure") + } + if !mock.failed { + t.Error("JSONMarshalAsT should mark test as failed") + } + }) +} + +func TestJSONUnmarshalAsTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := JSONUnmarshalAsTf(t, dummyStruct{A: "a"}, []byte(`{"A": "a"}`), "test message") + if !result { + t.Error("JSONUnmarshalAsTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := JSONUnmarshalAsTf(mock, 1, `[{"foo": "bar"}, {"hello": "world"}]`, "test message") + if result { + t.Error("JSONUnmarshalAsTf should return false on failure") + } + if !mock.failed { + t.Error("JSONUnmarshalAsT should mark test as failed") + } + }) +} + func TestKindf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1754,6 +1801,11 @@ func TestNoErrorf(t *testing.T) { }) } +func TestNoGoRoutineLeakf(t *testing.T) { + t.Parallel() + t.Skip() // this function doesn't have tests yet: feed the original function with examples to test. +} + func TestNotContainsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -2819,6 +2871,28 @@ func TestYAMLEqTf(t *testing.T) { }) } +func TestYAMLMarshalAsTf(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + Panicsf(t, func() { + YAMLMarshalAsTf(t, "key: value", "key: value", "test message") + }, "should panic without the yaml feature enabled.") + }) +} + +func TestYAMLUnmarshalAsTf(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + Panicsf(t, func() { + YAMLUnmarshalAsTf(t, "key: value", "key: value", "test message") + }, "should panic without the yaml feature enabled.") + }) +} + func TestZerof(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { diff --git a/assert/assert_forward.go b/assert/assert_forward.go index 1bc3a7bc2..3159ca081 100644 --- a/assert/assert_forward.go +++ b/assert/assert_forward.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package assert @@ -21,13 +20,13 @@ import ( // // Upon failure, the test [T] is marked as failed and continues execution. type Assertions struct { - t T + T } // New makes a new [Assertions] object for the specified [T] (e.g. [testing.T]). func New(t T) *Assertions { return &Assertions{ - t: t, + T: t, } } @@ -35,1638 +34,1658 @@ func New(t T) *Assertions { // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Condition(comp Comparison, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Condition(a.t, comp, msgAndArgs...) + return assertions.Condition(a.T, comp, msgAndArgs...) } // Conditionf is the same as [Assertions.Condition], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Conditionf(comp Comparison, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Condition(a.t, comp, forwardArgs(msg, args)) + return assertions.Condition(a.T, comp, forwardArgs(msg, args)) } // Contains is the same as [Contains], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Contains(s any, contains any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Contains(a.t, s, contains, msgAndArgs...) + return assertions.Contains(a.T, s, contains, msgAndArgs...) } // Containsf is the same as [Assertions.Contains], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Containsf(s any, contains any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Contains(a.t, s, contains, forwardArgs(msg, args)) + return assertions.Contains(a.T, s, contains, forwardArgs(msg, args)) } // DirExists is the same as [DirExists], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) DirExists(path string, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.DirExists(a.t, path, msgAndArgs...) + return assertions.DirExists(a.T, path, msgAndArgs...) } // DirExistsf is the same as [Assertions.DirExists], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) DirExistsf(path string, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.DirExists(a.t, path, forwardArgs(msg, args)) + return assertions.DirExists(a.T, path, forwardArgs(msg, args)) } // DirNotExists is the same as [DirNotExists], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) DirNotExists(path string, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.DirNotExists(a.t, path, msgAndArgs...) + return assertions.DirNotExists(a.T, path, msgAndArgs...) } // DirNotExistsf is the same as [Assertions.DirNotExists], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) DirNotExistsf(path string, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.DirNotExists(a.t, path, forwardArgs(msg, args)) + return assertions.DirNotExists(a.T, path, forwardArgs(msg, args)) } // ElementsMatch is the same as [ElementsMatch], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) ElementsMatch(listA any, listB any, msgAndArgs ...any) (ok bool) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.ElementsMatch(a.t, listA, listB, msgAndArgs...) + return assertions.ElementsMatch(a.T, listA, listB, msgAndArgs...) } // ElementsMatchf is the same as [Assertions.ElementsMatch], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) ElementsMatchf(listA any, listB any, msg string, args ...any) (ok bool) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.ElementsMatch(a.t, listA, listB, forwardArgs(msg, args)) + return assertions.ElementsMatch(a.T, listA, listB, forwardArgs(msg, args)) } // Empty is the same as [Empty], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Empty(object any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Empty(a.t, object, msgAndArgs...) + return assertions.Empty(a.T, object, msgAndArgs...) } // Emptyf is the same as [Assertions.Empty], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Emptyf(object any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Empty(a.t, object, forwardArgs(msg, args)) + return assertions.Empty(a.T, object, forwardArgs(msg, args)) } // Equal is the same as [Equal], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Equal(expected any, actual any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Equal(a.t, expected, actual, msgAndArgs...) + return assertions.Equal(a.T, expected, actual, msgAndArgs...) } // Equalf is the same as [Assertions.Equal], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Equalf(expected any, actual any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Equal(a.t, expected, actual, forwardArgs(msg, args)) + return assertions.Equal(a.T, expected, actual, forwardArgs(msg, args)) } // EqualError is the same as [EqualError], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) EqualError(err error, errString string, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.EqualError(a.t, err, errString, msgAndArgs...) + return assertions.EqualError(a.T, err, errString, msgAndArgs...) } // EqualErrorf is the same as [Assertions.EqualError], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) EqualErrorf(err error, errString string, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.EqualError(a.t, err, errString, forwardArgs(msg, args)) + return assertions.EqualError(a.T, err, errString, forwardArgs(msg, args)) } // EqualExportedValues is the same as [EqualExportedValues], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) EqualExportedValues(expected any, actual any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.EqualExportedValues(a.t, expected, actual, msgAndArgs...) + return assertions.EqualExportedValues(a.T, expected, actual, msgAndArgs...) } // EqualExportedValuesf is the same as [Assertions.EqualExportedValues], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) EqualExportedValuesf(expected any, actual any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.EqualExportedValues(a.t, expected, actual, forwardArgs(msg, args)) + return assertions.EqualExportedValues(a.T, expected, actual, forwardArgs(msg, args)) } // EqualValues is the same as [EqualValues], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) EqualValues(expected any, actual any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.EqualValues(a.t, expected, actual, msgAndArgs...) + return assertions.EqualValues(a.T, expected, actual, msgAndArgs...) } // EqualValuesf is the same as [Assertions.EqualValues], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) EqualValuesf(expected any, actual any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.EqualValues(a.t, expected, actual, forwardArgs(msg, args)) + return assertions.EqualValues(a.T, expected, actual, forwardArgs(msg, args)) } // Error is the same as [Error], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Error(err error, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Error(a.t, err, msgAndArgs...) + return assertions.Error(a.T, err, msgAndArgs...) } // Errorf is the same as [Assertions.Error], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Errorf(err error, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Error(a.t, err, forwardArgs(msg, args)) + return assertions.Error(a.T, err, forwardArgs(msg, args)) } // ErrorAs is the same as [ErrorAs], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) ErrorAs(err error, target any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.ErrorAs(a.t, err, target, msgAndArgs...) + return assertions.ErrorAs(a.T, err, target, msgAndArgs...) } // ErrorAsf is the same as [Assertions.ErrorAs], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) ErrorAsf(err error, target any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.ErrorAs(a.t, err, target, forwardArgs(msg, args)) + return assertions.ErrorAs(a.T, err, target, forwardArgs(msg, args)) } // ErrorContains is the same as [ErrorContains], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) ErrorContains(err error, contains string, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.ErrorContains(a.t, err, contains, msgAndArgs...) + return assertions.ErrorContains(a.T, err, contains, msgAndArgs...) } // ErrorContainsf is the same as [Assertions.ErrorContains], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) ErrorContainsf(err error, contains string, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.ErrorContains(a.t, err, contains, forwardArgs(msg, args)) + return assertions.ErrorContains(a.T, err, contains, forwardArgs(msg, args)) } // ErrorIs is the same as [ErrorIs], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.ErrorIs(a.t, err, target, msgAndArgs...) + return assertions.ErrorIs(a.T, err, target, msgAndArgs...) } // ErrorIsf is the same as [Assertions.ErrorIs], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.ErrorIs(a.t, err, target, forwardArgs(msg, args)) + return assertions.ErrorIs(a.T, err, target, forwardArgs(msg, args)) } // Eventually is the same as [Eventually], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Eventually(a.t, condition, waitFor, tick, msgAndArgs...) + return assertions.Eventually(a.T, condition, waitFor, tick, msgAndArgs...) } // Eventuallyf is the same as [Assertions.Eventually], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Eventually(a.t, condition, waitFor, tick, forwardArgs(msg, args)) + return assertions.Eventually(a.T, condition, waitFor, tick, forwardArgs(msg, args)) } -// EventuallyWithT is the same as [EventuallyWithT], as a method rather than a package-level function. +// EventuallyWith is the same as [EventuallyWith], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. -func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { +func (a *Assertions) EventuallyWith(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) + return assertions.EventuallyWith(a.T, condition, waitFor, tick, msgAndArgs...) } -// EventuallyWithTf is the same as [Assertions.EventuallyWithT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// EventuallyWithf is the same as [Assertions.EventuallyWith], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. -func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { +func (a *Assertions) EventuallyWithf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...any) bool { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.EventuallyWithT(a.t, condition, waitFor, tick, forwardArgs(msg, args)) + return assertions.EventuallyWith(a.T, condition, waitFor, tick, forwardArgs(msg, args)) } // Exactly is the same as [Exactly], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Exactly(expected any, actual any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Exactly(a.t, expected, actual, msgAndArgs...) + return assertions.Exactly(a.T, expected, actual, msgAndArgs...) } // Exactlyf is the same as [Assertions.Exactly], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Exactlyf(expected any, actual any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Exactly(a.t, expected, actual, forwardArgs(msg, args)) + return assertions.Exactly(a.T, expected, actual, forwardArgs(msg, args)) } // Fail is the same as [Fail], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Fail(failureMessage string, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Fail(a.t, failureMessage, msgAndArgs...) + return assertions.Fail(a.T, failureMessage, msgAndArgs...) } // Failf is the same as [Assertions.Fail], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Failf(failureMessage string, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Fail(a.t, failureMessage, forwardArgs(msg, args)) + return assertions.Fail(a.T, failureMessage, forwardArgs(msg, args)) } // FailNow is the same as [FailNow], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.FailNow(a.t, failureMessage, msgAndArgs...) + return assertions.FailNow(a.T, failureMessage, msgAndArgs...) } // FailNowf is the same as [Assertions.FailNow], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FailNowf(failureMessage string, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.FailNow(a.t, failureMessage, forwardArgs(msg, args)) + return assertions.FailNow(a.T, failureMessage, forwardArgs(msg, args)) } // False is the same as [False], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) False(value bool, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.False(a.t, value, msgAndArgs...) + return assertions.False(a.T, value, msgAndArgs...) } // Falsef is the same as [Assertions.False], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Falsef(value bool, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.False(a.t, value, forwardArgs(msg, args)) + return assertions.False(a.T, value, forwardArgs(msg, args)) } // FileEmpty is the same as [FileEmpty], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FileEmpty(path string, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.FileEmpty(a.t, path, msgAndArgs...) + return assertions.FileEmpty(a.T, path, msgAndArgs...) } // FileEmptyf is the same as [Assertions.FileEmpty], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FileEmptyf(path string, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.FileEmpty(a.t, path, forwardArgs(msg, args)) + return assertions.FileEmpty(a.T, path, forwardArgs(msg, args)) } // FileExists is the same as [FileExists], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FileExists(path string, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.FileExists(a.t, path, msgAndArgs...) + return assertions.FileExists(a.T, path, msgAndArgs...) } // FileExistsf is the same as [Assertions.FileExists], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FileExistsf(path string, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.FileExists(a.t, path, forwardArgs(msg, args)) + return assertions.FileExists(a.T, path, forwardArgs(msg, args)) } // FileNotEmpty is the same as [FileNotEmpty], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FileNotEmpty(path string, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.FileNotEmpty(a.t, path, msgAndArgs...) + return assertions.FileNotEmpty(a.T, path, msgAndArgs...) } // FileNotEmptyf is the same as [Assertions.FileNotEmpty], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FileNotEmptyf(path string, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.FileNotEmpty(a.t, path, forwardArgs(msg, args)) + return assertions.FileNotEmpty(a.T, path, forwardArgs(msg, args)) } // FileNotExists is the same as [FileNotExists], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FileNotExists(path string, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.FileNotExists(a.t, path, msgAndArgs...) + return assertions.FileNotExists(a.T, path, msgAndArgs...) } // FileNotExistsf is the same as [Assertions.FileNotExists], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FileNotExistsf(path string, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.FileNotExists(a.t, path, forwardArgs(msg, args)) + return assertions.FileNotExists(a.T, path, forwardArgs(msg, args)) } // Greater is the same as [Greater], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Greater(e1 any, e2 any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Greater(a.t, e1, e2, msgAndArgs...) + return assertions.Greater(a.T, e1, e2, msgAndArgs...) } // Greaterf is the same as [Assertions.Greater], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Greaterf(e1 any, e2 any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Greater(a.t, e1, e2, forwardArgs(msg, args)) + return assertions.Greater(a.T, e1, e2, forwardArgs(msg, args)) } // GreaterOrEqual is the same as [GreaterOrEqual], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) GreaterOrEqual(e1 any, e2 any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.GreaterOrEqual(a.t, e1, e2, msgAndArgs...) + return assertions.GreaterOrEqual(a.T, e1, e2, msgAndArgs...) } // GreaterOrEqualf is the same as [Assertions.GreaterOrEqual], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) GreaterOrEqualf(e1 any, e2 any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.GreaterOrEqual(a.t, e1, e2, forwardArgs(msg, args)) + return assertions.GreaterOrEqual(a.T, e1, e2, forwardArgs(msg, args)) } // HTTPBodyContains is the same as [HTTPBodyContains], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...) + return assertions.HTTPBodyContains(a.T, handler, method, url, values, str, msgAndArgs...) } // HTTPBodyContainsf is the same as [Assertions.HTTPBodyContains], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.HTTPBodyContains(a.t, handler, method, url, values, str, forwardArgs(msg, args)) + return assertions.HTTPBodyContains(a.T, handler, method, url, values, str, forwardArgs(msg, args)) } // HTTPBodyNotContains is the same as [HTTPBodyNotContains], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...) + return assertions.HTTPBodyNotContains(a.T, handler, method, url, values, str, msgAndArgs...) } // HTTPBodyNotContainsf is the same as [Assertions.HTTPBodyNotContains], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.HTTPBodyNotContains(a.t, handler, method, url, values, str, forwardArgs(msg, args)) + return assertions.HTTPBodyNotContains(a.T, handler, method, url, values, str, forwardArgs(msg, args)) } // HTTPError is the same as [HTTPError], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.HTTPError(a.t, handler, method, url, values, msgAndArgs...) + return assertions.HTTPError(a.T, handler, method, url, values, msgAndArgs...) } // HTTPErrorf is the same as [Assertions.HTTPError], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.HTTPError(a.t, handler, method, url, values, forwardArgs(msg, args)) + return assertions.HTTPError(a.T, handler, method, url, values, forwardArgs(msg, args)) } // HTTPRedirect is the same as [HTTPRedirect], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...) + return assertions.HTTPRedirect(a.T, handler, method, url, values, msgAndArgs...) } // HTTPRedirectf is the same as [Assertions.HTTPRedirect], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.HTTPRedirect(a.t, handler, method, url, values, forwardArgs(msg, args)) + return assertions.HTTPRedirect(a.T, handler, method, url, values, forwardArgs(msg, args)) } // HTTPStatusCode is the same as [HTTPStatusCode], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...) + return assertions.HTTPStatusCode(a.T, handler, method, url, values, statuscode, msgAndArgs...) } // HTTPStatusCodef is the same as [Assertions.HTTPStatusCode], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.HTTPStatusCode(a.t, handler, method, url, values, statuscode, forwardArgs(msg, args)) + return assertions.HTTPStatusCode(a.T, handler, method, url, values, statuscode, forwardArgs(msg, args)) } // HTTPSuccess is the same as [HTTPSuccess], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...) + return assertions.HTTPSuccess(a.T, handler, method, url, values, msgAndArgs...) } // HTTPSuccessf is the same as [Assertions.HTTPSuccess], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.HTTPSuccess(a.t, handler, method, url, values, forwardArgs(msg, args)) + return assertions.HTTPSuccess(a.T, handler, method, url, values, forwardArgs(msg, args)) } // Implements is the same as [Implements], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Implements(interfaceObject any, object any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Implements(a.t, interfaceObject, object, msgAndArgs...) + return assertions.Implements(a.T, interfaceObject, object, msgAndArgs...) } // Implementsf is the same as [Assertions.Implements], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Implementsf(interfaceObject any, object any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Implements(a.t, interfaceObject, object, forwardArgs(msg, args)) + return assertions.Implements(a.T, interfaceObject, object, forwardArgs(msg, args)) } // InDelta is the same as [InDelta], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InDelta(expected any, actual any, delta float64, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.InDelta(a.t, expected, actual, delta, msgAndArgs...) + return assertions.InDelta(a.T, expected, actual, delta, msgAndArgs...) } // InDeltaf is the same as [Assertions.InDelta], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InDeltaf(expected any, actual any, delta float64, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.InDelta(a.t, expected, actual, delta, forwardArgs(msg, args)) + return assertions.InDelta(a.T, expected, actual, delta, forwardArgs(msg, args)) } // InDeltaMapValues is the same as [InDeltaMapValues], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InDeltaMapValues(expected any, actual any, delta float64, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...) + return assertions.InDeltaMapValues(a.T, expected, actual, delta, msgAndArgs...) } // InDeltaMapValuesf is the same as [Assertions.InDeltaMapValues], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InDeltaMapValuesf(expected any, actual any, delta float64, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.InDeltaMapValues(a.t, expected, actual, delta, forwardArgs(msg, args)) + return assertions.InDeltaMapValues(a.T, expected, actual, delta, forwardArgs(msg, args)) } // InDeltaSlice is the same as [InDeltaSlice], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InDeltaSlice(expected any, actual any, delta float64, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) + return assertions.InDeltaSlice(a.T, expected, actual, delta, msgAndArgs...) } // InDeltaSlicef is the same as [Assertions.InDeltaSlice], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InDeltaSlicef(expected any, actual any, delta float64, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.InDeltaSlice(a.t, expected, actual, delta, forwardArgs(msg, args)) + return assertions.InDeltaSlice(a.T, expected, actual, delta, forwardArgs(msg, args)) } // InEpsilon is the same as [InEpsilon], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InEpsilon(expected any, actual any, epsilon float64, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) + return assertions.InEpsilon(a.T, expected, actual, epsilon, msgAndArgs...) } // InEpsilonf is the same as [Assertions.InEpsilon], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InEpsilonf(expected any, actual any, epsilon float64, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.InEpsilon(a.t, expected, actual, epsilon, forwardArgs(msg, args)) + return assertions.InEpsilon(a.T, expected, actual, epsilon, forwardArgs(msg, args)) } // InEpsilonSlice is the same as [InEpsilonSlice], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InEpsilonSlice(expected any, actual any, epsilon float64, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) + return assertions.InEpsilonSlice(a.T, expected, actual, epsilon, msgAndArgs...) } // InEpsilonSlicef is the same as [Assertions.InEpsilonSlice], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InEpsilonSlicef(expected any, actual any, epsilon float64, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.InEpsilonSlice(a.t, expected, actual, epsilon, forwardArgs(msg, args)) + return assertions.InEpsilonSlice(a.T, expected, actual, epsilon, forwardArgs(msg, args)) } // IsDecreasing is the same as [IsDecreasing], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsDecreasing(collection any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.IsDecreasing(a.t, collection, msgAndArgs...) + return assertions.IsDecreasing(a.T, collection, msgAndArgs...) } // IsDecreasingf is the same as [Assertions.IsDecreasing], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsDecreasingf(collection any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.IsDecreasing(a.t, collection, forwardArgs(msg, args)) + return assertions.IsDecreasing(a.T, collection, forwardArgs(msg, args)) } // IsIncreasing is the same as [IsIncreasing], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsIncreasing(collection any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.IsIncreasing(a.t, collection, msgAndArgs...) + return assertions.IsIncreasing(a.T, collection, msgAndArgs...) } // IsIncreasingf is the same as [Assertions.IsIncreasing], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsIncreasingf(collection any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.IsIncreasing(a.t, collection, forwardArgs(msg, args)) + return assertions.IsIncreasing(a.T, collection, forwardArgs(msg, args)) } // IsNonDecreasing is the same as [IsNonDecreasing], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsNonDecreasing(collection any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.IsNonDecreasing(a.t, collection, msgAndArgs...) + return assertions.IsNonDecreasing(a.T, collection, msgAndArgs...) } // IsNonDecreasingf is the same as [Assertions.IsNonDecreasing], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsNonDecreasingf(collection any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.IsNonDecreasing(a.t, collection, forwardArgs(msg, args)) + return assertions.IsNonDecreasing(a.T, collection, forwardArgs(msg, args)) } // IsNonIncreasing is the same as [IsNonIncreasing], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsNonIncreasing(collection any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.IsNonIncreasing(a.t, collection, msgAndArgs...) + return assertions.IsNonIncreasing(a.T, collection, msgAndArgs...) } // IsNonIncreasingf is the same as [Assertions.IsNonIncreasing], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsNonIncreasingf(collection any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.IsNonIncreasing(a.t, collection, forwardArgs(msg, args)) + return assertions.IsNonIncreasing(a.T, collection, forwardArgs(msg, args)) } // IsNotType is the same as [IsNotType], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsNotType(theType any, object any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.IsNotType(a.t, theType, object, msgAndArgs...) + return assertions.IsNotType(a.T, theType, object, msgAndArgs...) } // IsNotTypef is the same as [Assertions.IsNotType], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsNotTypef(theType any, object any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.IsNotType(a.t, theType, object, forwardArgs(msg, args)) + return assertions.IsNotType(a.T, theType, object, forwardArgs(msg, args)) } // IsType is the same as [IsType], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsType(expectedType any, object any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.IsType(a.t, expectedType, object, msgAndArgs...) + return assertions.IsType(a.T, expectedType, object, msgAndArgs...) } // IsTypef is the same as [Assertions.IsType], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsTypef(expectedType any, object any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.IsType(a.t, expectedType, object, forwardArgs(msg, args)) + return assertions.IsType(a.T, expectedType, object, forwardArgs(msg, args)) } // JSONEq is the same as [JSONEq], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.JSONEq(a.t, expected, actual, msgAndArgs...) + return assertions.JSONEq(a.T, expected, actual, msgAndArgs...) } // JSONEqf is the same as [Assertions.JSONEq], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.JSONEq(a.t, expected, actual, forwardArgs(msg, args)) + return assertions.JSONEq(a.T, expected, actual, forwardArgs(msg, args)) } // JSONEqBytes is the same as [JSONEqBytes], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) JSONEqBytes(expected []byte, actual []byte, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.JSONEqBytes(a.t, expected, actual, msgAndArgs...) + return assertions.JSONEqBytes(a.T, expected, actual, msgAndArgs...) } // JSONEqBytesf is the same as [Assertions.JSONEqBytes], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) JSONEqBytesf(expected []byte, actual []byte, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.JSONEqBytes(a.t, expected, actual, forwardArgs(msg, args)) + return assertions.JSONEqBytes(a.T, expected, actual, forwardArgs(msg, args)) } // Kind is the same as [Kind], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Kind(expectedKind reflect.Kind, object any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Kind(a.t, expectedKind, object, msgAndArgs...) + return assertions.Kind(a.T, expectedKind, object, msgAndArgs...) } // Kindf is the same as [Assertions.Kind], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Kindf(expectedKind reflect.Kind, object any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Kind(a.t, expectedKind, object, forwardArgs(msg, args)) + return assertions.Kind(a.T, expectedKind, object, forwardArgs(msg, args)) } // Len is the same as [Len], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Len(object any, length int, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Len(a.t, object, length, msgAndArgs...) + return assertions.Len(a.T, object, length, msgAndArgs...) } // Lenf is the same as [Assertions.Len], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Lenf(object any, length int, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Len(a.t, object, length, forwardArgs(msg, args)) + return assertions.Len(a.T, object, length, forwardArgs(msg, args)) } // Less is the same as [Less], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Less(e1 any, e2 any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Less(a.t, e1, e2, msgAndArgs...) + return assertions.Less(a.T, e1, e2, msgAndArgs...) } // Lessf is the same as [Assertions.Less], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Lessf(e1 any, e2 any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Less(a.t, e1, e2, forwardArgs(msg, args)) + return assertions.Less(a.T, e1, e2, forwardArgs(msg, args)) } // LessOrEqual is the same as [LessOrEqual], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) LessOrEqual(e1 any, e2 any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.LessOrEqual(a.t, e1, e2, msgAndArgs...) + return assertions.LessOrEqual(a.T, e1, e2, msgAndArgs...) } // LessOrEqualf is the same as [Assertions.LessOrEqual], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) LessOrEqualf(e1 any, e2 any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.LessOrEqual(a.t, e1, e2, forwardArgs(msg, args)) + return assertions.LessOrEqual(a.T, e1, e2, forwardArgs(msg, args)) } // Negative is the same as [Negative], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Negative(e any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Negative(a.t, e, msgAndArgs...) + return assertions.Negative(a.T, e, msgAndArgs...) } // Negativef is the same as [Assertions.Negative], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Negativef(e any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Negative(a.t, e, forwardArgs(msg, args)) + return assertions.Negative(a.T, e, forwardArgs(msg, args)) } // Never is the same as [Never], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Never(a.t, condition, waitFor, tick, msgAndArgs...) + return assertions.Never(a.T, condition, waitFor, tick, msgAndArgs...) } // Neverf is the same as [Assertions.Never], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Never(a.t, condition, waitFor, tick, forwardArgs(msg, args)) + return assertions.Never(a.T, condition, waitFor, tick, forwardArgs(msg, args)) } // Nil is the same as [Nil], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Nil(object any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Nil(a.t, object, msgAndArgs...) + return assertions.Nil(a.T, object, msgAndArgs...) } // Nilf is the same as [Assertions.Nil], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Nilf(object any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Nil(a.t, object, forwardArgs(msg, args)) + return assertions.Nil(a.T, object, forwardArgs(msg, args)) } // NoError is the same as [NoError], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NoError(err error, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NoError(a.t, err, msgAndArgs...) + return assertions.NoError(a.T, err, msgAndArgs...) } // NoErrorf is the same as [Assertions.NoError], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NoErrorf(err error, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NoError(a.t, err, forwardArgs(msg, args)) + return assertions.NoError(a.T, err, forwardArgs(msg, args)) +} + +// NoGoRoutineLeak is the same as [NoGoRoutineLeak], as a method rather than a package-level function. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func (a *Assertions) NoGoRoutineLeak(inside func(), msgAndArgs ...any) bool { + if h, ok := a.T.(H); ok { + h.Helper() + } + return assertions.NoGoRoutineLeak(a.T, inside, msgAndArgs...) +} + +// NoGoRoutineLeakf is the same as [Assertions.NoGoRoutineLeak], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func (a *Assertions) NoGoRoutineLeakf(inside func(), msg string, args ...any) bool { + if h, ok := a.T.(H); ok { + h.Helper() + } + return assertions.NoGoRoutineLeak(a.T, inside, forwardArgs(msg, args)) } // NotContains is the same as [NotContains], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotContains(s any, contains any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotContains(a.t, s, contains, msgAndArgs...) + return assertions.NotContains(a.T, s, contains, msgAndArgs...) } // NotContainsf is the same as [Assertions.NotContains], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotContainsf(s any, contains any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotContains(a.t, s, contains, forwardArgs(msg, args)) + return assertions.NotContains(a.T, s, contains, forwardArgs(msg, args)) } // NotElementsMatch is the same as [NotElementsMatch], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotElementsMatch(listA any, listB any, msgAndArgs ...any) (ok bool) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotElementsMatch(a.t, listA, listB, msgAndArgs...) + return assertions.NotElementsMatch(a.T, listA, listB, msgAndArgs...) } // NotElementsMatchf is the same as [Assertions.NotElementsMatch], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotElementsMatchf(listA any, listB any, msg string, args ...any) (ok bool) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotElementsMatch(a.t, listA, listB, forwardArgs(msg, args)) + return assertions.NotElementsMatch(a.T, listA, listB, forwardArgs(msg, args)) } // NotEmpty is the same as [NotEmpty], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotEmpty(object any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotEmpty(a.t, object, msgAndArgs...) + return assertions.NotEmpty(a.T, object, msgAndArgs...) } // NotEmptyf is the same as [Assertions.NotEmpty], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotEmptyf(object any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotEmpty(a.t, object, forwardArgs(msg, args)) + return assertions.NotEmpty(a.T, object, forwardArgs(msg, args)) } // NotEqual is the same as [NotEqual], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotEqual(expected any, actual any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotEqual(a.t, expected, actual, msgAndArgs...) + return assertions.NotEqual(a.T, expected, actual, msgAndArgs...) } // NotEqualf is the same as [Assertions.NotEqual], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotEqualf(expected any, actual any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotEqual(a.t, expected, actual, forwardArgs(msg, args)) + return assertions.NotEqual(a.T, expected, actual, forwardArgs(msg, args)) } // NotEqualValues is the same as [NotEqualValues], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotEqualValues(expected any, actual any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotEqualValues(a.t, expected, actual, msgAndArgs...) + return assertions.NotEqualValues(a.T, expected, actual, msgAndArgs...) } // NotEqualValuesf is the same as [Assertions.NotEqualValues], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotEqualValuesf(expected any, actual any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotEqualValues(a.t, expected, actual, forwardArgs(msg, args)) + return assertions.NotEqualValues(a.T, expected, actual, forwardArgs(msg, args)) } // NotErrorAs is the same as [NotErrorAs], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotErrorAs(err error, target any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotErrorAs(a.t, err, target, msgAndArgs...) + return assertions.NotErrorAs(a.T, err, target, msgAndArgs...) } // NotErrorAsf is the same as [Assertions.NotErrorAs], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotErrorAsf(err error, target any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotErrorAs(a.t, err, target, forwardArgs(msg, args)) + return assertions.NotErrorAs(a.T, err, target, forwardArgs(msg, args)) } // NotErrorIs is the same as [NotErrorIs], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotErrorIs(a.t, err, target, msgAndArgs...) + return assertions.NotErrorIs(a.T, err, target, msgAndArgs...) } // NotErrorIsf is the same as [Assertions.NotErrorIs], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotErrorIs(a.t, err, target, forwardArgs(msg, args)) + return assertions.NotErrorIs(a.T, err, target, forwardArgs(msg, args)) } // NotImplements is the same as [NotImplements], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotImplements(interfaceObject any, object any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotImplements(a.t, interfaceObject, object, msgAndArgs...) + return assertions.NotImplements(a.T, interfaceObject, object, msgAndArgs...) } // NotImplementsf is the same as [Assertions.NotImplements], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotImplementsf(interfaceObject any, object any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotImplements(a.t, interfaceObject, object, forwardArgs(msg, args)) + return assertions.NotImplements(a.T, interfaceObject, object, forwardArgs(msg, args)) } // NotKind is the same as [NotKind], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotKind(expectedKind reflect.Kind, object any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotKind(a.t, expectedKind, object, msgAndArgs...) + return assertions.NotKind(a.T, expectedKind, object, msgAndArgs...) } // NotKindf is the same as [Assertions.NotKind], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotKindf(expectedKind reflect.Kind, object any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotKind(a.t, expectedKind, object, forwardArgs(msg, args)) + return assertions.NotKind(a.T, expectedKind, object, forwardArgs(msg, args)) } // NotNil is the same as [NotNil], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotNil(object any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotNil(a.t, object, msgAndArgs...) + return assertions.NotNil(a.T, object, msgAndArgs...) } // NotNilf is the same as [Assertions.NotNil], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotNilf(object any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotNil(a.t, object, forwardArgs(msg, args)) + return assertions.NotNil(a.T, object, forwardArgs(msg, args)) } // NotPanics is the same as [NotPanics], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotPanics(f func(), msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotPanics(a.t, f, msgAndArgs...) + return assertions.NotPanics(a.T, f, msgAndArgs...) } // NotPanicsf is the same as [Assertions.NotPanics], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotPanicsf(f func(), msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotPanics(a.t, f, forwardArgs(msg, args)) + return assertions.NotPanics(a.T, f, forwardArgs(msg, args)) } // NotRegexp is the same as [NotRegexp], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotRegexp(rx any, actual any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotRegexp(a.t, rx, actual, msgAndArgs...) + return assertions.NotRegexp(a.T, rx, actual, msgAndArgs...) } // NotRegexpf is the same as [Assertions.NotRegexp], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotRegexpf(rx any, actual any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotRegexp(a.t, rx, actual, forwardArgs(msg, args)) + return assertions.NotRegexp(a.T, rx, actual, forwardArgs(msg, args)) } // NotSame is the same as [NotSame], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotSame(expected any, actual any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotSame(a.t, expected, actual, msgAndArgs...) + return assertions.NotSame(a.T, expected, actual, msgAndArgs...) } // NotSamef is the same as [Assertions.NotSame], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotSamef(expected any, actual any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotSame(a.t, expected, actual, forwardArgs(msg, args)) + return assertions.NotSame(a.T, expected, actual, forwardArgs(msg, args)) } // NotSubset is the same as [NotSubset], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotSubset(list any, subset any, msgAndArgs ...any) (ok bool) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotSubset(a.t, list, subset, msgAndArgs...) + return assertions.NotSubset(a.T, list, subset, msgAndArgs...) } // NotSubsetf is the same as [Assertions.NotSubset], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotSubsetf(list any, subset any, msg string, args ...any) (ok bool) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotSubset(a.t, list, subset, forwardArgs(msg, args)) + return assertions.NotSubset(a.T, list, subset, forwardArgs(msg, args)) } // NotZero is the same as [NotZero], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotZero(i any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotZero(a.t, i, msgAndArgs...) + return assertions.NotZero(a.T, i, msgAndArgs...) } // NotZerof is the same as [Assertions.NotZero], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotZerof(i any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.NotZero(a.t, i, forwardArgs(msg, args)) + return assertions.NotZero(a.T, i, forwardArgs(msg, args)) } // Panics is the same as [Panics], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Panics(f func(), msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Panics(a.t, f, msgAndArgs...) + return assertions.Panics(a.T, f, msgAndArgs...) } // Panicsf is the same as [Assertions.Panics], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Panicsf(f func(), msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Panics(a.t, f, forwardArgs(msg, args)) + return assertions.Panics(a.T, f, forwardArgs(msg, args)) } // PanicsWithError is the same as [PanicsWithError], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) PanicsWithError(errString string, f func(), msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.PanicsWithError(a.t, errString, f, msgAndArgs...) + return assertions.PanicsWithError(a.T, errString, f, msgAndArgs...) } // PanicsWithErrorf is the same as [Assertions.PanicsWithError], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) PanicsWithErrorf(errString string, f func(), msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.PanicsWithError(a.t, errString, f, forwardArgs(msg, args)) + return assertions.PanicsWithError(a.T, errString, f, forwardArgs(msg, args)) } // PanicsWithValue is the same as [PanicsWithValue], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) PanicsWithValue(expected any, f func(), msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.PanicsWithValue(a.t, expected, f, msgAndArgs...) + return assertions.PanicsWithValue(a.T, expected, f, msgAndArgs...) } // PanicsWithValuef is the same as [Assertions.PanicsWithValue], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) PanicsWithValuef(expected any, f func(), msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.PanicsWithValue(a.t, expected, f, forwardArgs(msg, args)) + return assertions.PanicsWithValue(a.T, expected, f, forwardArgs(msg, args)) } // Positive is the same as [Positive], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Positive(e any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Positive(a.t, e, msgAndArgs...) + return assertions.Positive(a.T, e, msgAndArgs...) } // Positivef is the same as [Assertions.Positive], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Positivef(e any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Positive(a.t, e, forwardArgs(msg, args)) + return assertions.Positive(a.T, e, forwardArgs(msg, args)) } // Regexp is the same as [Regexp], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Regexp(rx any, actual any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Regexp(a.t, rx, actual, msgAndArgs...) + return assertions.Regexp(a.T, rx, actual, msgAndArgs...) } // Regexpf is the same as [Assertions.Regexp], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Regexpf(rx any, actual any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Regexp(a.t, rx, actual, forwardArgs(msg, args)) + return assertions.Regexp(a.T, rx, actual, forwardArgs(msg, args)) } // Same is the same as [Same], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Same(expected any, actual any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Same(a.t, expected, actual, msgAndArgs...) + return assertions.Same(a.T, expected, actual, msgAndArgs...) } // Samef is the same as [Assertions.Same], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Samef(expected any, actual any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Same(a.t, expected, actual, forwardArgs(msg, args)) + return assertions.Same(a.T, expected, actual, forwardArgs(msg, args)) } // Subset is the same as [Subset], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Subset(list any, subset any, msgAndArgs ...any) (ok bool) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Subset(a.t, list, subset, msgAndArgs...) + return assertions.Subset(a.T, list, subset, msgAndArgs...) } // Subsetf is the same as [Assertions.Subset], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Subsetf(list any, subset any, msg string, args ...any) (ok bool) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Subset(a.t, list, subset, forwardArgs(msg, args)) + return assertions.Subset(a.T, list, subset, forwardArgs(msg, args)) } // True is the same as [True], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) True(value bool, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.True(a.t, value, msgAndArgs...) + return assertions.True(a.T, value, msgAndArgs...) } // Truef is the same as [Assertions.True], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Truef(value bool, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.True(a.t, value, forwardArgs(msg, args)) + return assertions.True(a.T, value, forwardArgs(msg, args)) } // WithinDuration is the same as [WithinDuration], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.WithinDuration(a.t, expected, actual, delta, msgAndArgs...) + return assertions.WithinDuration(a.T, expected, actual, delta, msgAndArgs...) } // WithinDurationf is the same as [Assertions.WithinDuration], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.WithinDuration(a.t, expected, actual, delta, forwardArgs(msg, args)) + return assertions.WithinDuration(a.T, expected, actual, delta, forwardArgs(msg, args)) } // WithinRange is the same as [WithinRange], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.WithinRange(a.t, actual, start, end, msgAndArgs...) + return assertions.WithinRange(a.T, actual, start, end, msgAndArgs...) } // WithinRangef is the same as [Assertions.WithinRange], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.WithinRange(a.t, actual, start, end, forwardArgs(msg, args)) + return assertions.WithinRange(a.T, actual, start, end, forwardArgs(msg, args)) } // YAMLEq is the same as [YAMLEq], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.YAMLEq(a.t, expected, actual, msgAndArgs...) + return assertions.YAMLEq(a.T, expected, actual, msgAndArgs...) } // YAMLEqf is the same as [Assertions.YAMLEq], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.YAMLEq(a.t, expected, actual, forwardArgs(msg, args)) + return assertions.YAMLEq(a.T, expected, actual, forwardArgs(msg, args)) } // YAMLEqBytes is the same as [YAMLEqBytes], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) YAMLEqBytes(expected []byte, actual []byte, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.YAMLEqBytes(a.t, expected, actual, msgAndArgs...) + return assertions.YAMLEqBytes(a.T, expected, actual, msgAndArgs...) } // YAMLEqBytesf is the same as [Assertions.YAMLEqBytes], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) YAMLEqBytesf(expected []byte, actual []byte, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.YAMLEqBytes(a.t, expected, actual, forwardArgs(msg, args)) + return assertions.YAMLEqBytes(a.T, expected, actual, forwardArgs(msg, args)) } // Zero is the same as [Zero], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Zero(i any, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Zero(a.t, i, msgAndArgs...) + return assertions.Zero(a.T, i, msgAndArgs...) } // Zerof is the same as [Assertions.Zero], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Zerof(i any, msg string, args ...any) bool { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - return assertions.Zero(a.t, i, forwardArgs(msg, args)) + return assertions.Zero(a.T, i, forwardArgs(msg, args)) } diff --git a/assert/assert_forward_test.go b/assert/assert_forward_test.go index 22d6a764f..77bc818b0 100644 --- a/assert/assert_forward_test.go +++ b/assert/assert_forward_test.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package assert @@ -827,15 +826,15 @@ func TestAssertionsEventuallyf(t *testing.T) { }) } -func TestAssertionsEventuallyWithT(t *testing.T) { +func TestAssertionsEventuallyWith(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() a := New(t) - result := a.EventuallyWithT(func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond) + result := a.EventuallyWith(func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond) if !result { - t.Error("Assertions.EventuallyWithT should return true on success") + t.Error("Assertions.EventuallyWith should return true on success") } }) @@ -844,25 +843,25 @@ func TestAssertionsEventuallyWithT(t *testing.T) { mock := new(mockT) a := New(mock) - result := a.EventuallyWithT(func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond) + result := a.EventuallyWith(func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond) if result { - t.Error("Assertions.EventuallyWithT should return false on failure") + t.Error("Assertions.EventuallyWith should return false on failure") } if !mock.failed { - t.Error("EventuallyWithT should mark test as failed") + t.Error("EventuallyWith should mark test as failed") } }) } -func TestAssertionsEventuallyWithTf(t *testing.T) { +func TestAssertionsEventuallyWithf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() a := New(t) - result := a.EventuallyWithTf(func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") + result := a.EventuallyWithf(func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") if !result { - t.Error("Assertions.EventuallyWithT should return true on success") + t.Error("Assertions.EventuallyWith should return true on success") } }) @@ -871,12 +870,12 @@ func TestAssertionsEventuallyWithTf(t *testing.T) { mock := new(mockT) a := New(mock) - result := a.EventuallyWithTf(func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") + result := a.EventuallyWithf(func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") if result { - t.Error("Assertions.EventuallyWithT should return false on failure") + t.Error("Assertions.EventuallyWith should return false on failure") } if !mock.failed { - t.Error("Assertions.EventuallyWithT should mark test as failed") + t.Error("Assertions.EventuallyWith should mark test as failed") } }) } @@ -2896,6 +2895,16 @@ func TestAssertionsNoErrorf(t *testing.T) { }) } +func TestAssertionsNoGoRoutineLeak(t *testing.T) { + t.Parallel() + t.Skip() // this function doesn't have tests yet: feed the original function with examples to test. +} + +func TestAssertionsNoGoRoutineLeakf(t *testing.T) { + t.Parallel() + t.Skip() // this function doesn't have tests yet: feed the original function with examples to test. +} + func TestAssertionsNotContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { diff --git a/assert/assert_helpers.go b/assert/assert_helpers.go index e130b9d60..0e742c1c1 100644 --- a/assert/assert_helpers.go +++ b/assert/assert_helpers.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package assert diff --git a/assert/assert_helpers_test.go b/assert/assert_helpers_test.go index e24414c1e..14286d463 100644 --- a/assert/assert_helpers_test.go +++ b/assert/assert_helpers_test.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package assert diff --git a/assert/assert_types.go b/assert/assert_types.go index d303fdf10..4bf5888ca 100644 --- a/assert/assert_types.go +++ b/assert/assert_types.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package assert @@ -29,7 +28,7 @@ type ( // CollectT implements the [T] interface and collects all errors. // - // [CollectT] is specifically intended to be used with [EventuallyWithT] and + // [CollectT] is specifically intended to be used with [EventuallyWith] and // should not be used outside of that context. CollectT = assertions.CollectT diff --git a/codegen/internal/generator/doc_generator.go b/codegen/internal/generator/doc_generator.go index a11598175..e4cbccc8c 100644 --- a/codegen/internal/generator/doc_generator.go +++ b/codegen/internal/generator/doc_generator.go @@ -14,6 +14,7 @@ import ( "github.com/go-openapi/testify/codegen/v2/internal/generator/domains" "github.com/go-openapi/testify/codegen/v2/internal/generator/funcmaps" "github.com/go-openapi/testify/codegen/v2/internal/model" + exparser "github.com/go-openapi/testify/codegen/v2/internal/scanner/examples-parser" ) const ( @@ -53,8 +54,11 @@ func (d *DocGenerator) Generate(opts ...GenerateOption) error { return err } - // Proposal for enhancement: other fun stuff - // - capture testable examples and render their source code + // capture testable examples from generated packages and attach them to + // the model so templates may render their source code. + if err := d.populateExamples(); err != nil { + return err + } // reorganize accumulated package-based docs into domain-based docs // @@ -223,6 +227,93 @@ func (d *DocGenerator) loadTemplates() error { return nil } +// populateExamples runs the examples-parser against all generated packages in the +// merged Documentation and attaches the discovered testable examples to the +// corresponding Function and Ident objects. +// +// This must run before [reorganizeByDomain] because domain discovery copies +// functions and types into domain entries. +func (d *DocGenerator) populateExamples() error { + if !d.ctx.runnableExamples { + return nil + } + + docs := domains.FlattenDocumentation(d.doc) + + // derive the module root from the assertions import that every generated + // package carries (e.g. "github.com/go-openapi/testify/v2"). + var rootPkg string + for _, doc := range docs { + if doc.Package != nil && doc.Package.Imports != nil { + if assertionsPath, ok := doc.Package.Imports[assertions]; ok { + rootPkg = path.Dir(path.Dir(assertionsPath)) + + break + } + } + } + if rootPkg == "" { + return nil // nothing to do + } + + workDir, err := filepath.Abs(d.ctx.targetRoot) + if err != nil { + return fmt.Errorf("resolving target root: %w", err) + } + + for _, doc := range docs { + pkg := doc.Package + if pkg == nil { + continue + } + + // Skip the internal assertions package: testable examples live in the + // generated packages (assert, require), not in the source package. + if path.Base(pkg.Package) == assertions { + continue + } + + importPath := rootPkg + "/" + pkg.Package + examples, parseErr := exparser.New(importPath, exparser.WithWorkDir(workDir)).Parse() + if parseErr != nil { + return fmt.Errorf("parsing examples for %s: %w", pkg.Package, parseErr) + } + + populateFunctionExamples(pkg, examples) + populateIdentExamples(pkg.Types, examples) + } + + return nil +} + +func populateFunctionExamples(pkg *model.AssertionPackage, examples exparser.Examples) { + for i, fn := range pkg.Functions { + exs, ok := examples[fn.Name] + if !ok { + continue + } + renderables := make([]model.Renderable, len(exs)) + for j := range exs { + renderables[j] = exs[j] + } + pkg.Functions[i].Examples = renderables + } +} + +func populateIdentExamples(idents []model.Ident, examples exparser.Examples) { + for i, id := range idents { + exs, ok := examples[id.Name] + if !ok { + continue + } + renderables := make([]model.Renderable, len(exs)) + for j := range exs { + renderables[j] = exs[j] + } + idents[i].Examples = renderables + } +} + func (d *DocGenerator) render(name string, target string, data any) error { return renderTemplate( d.ctx.index, diff --git a/codegen/internal/generator/funcmaps/markdown.go b/codegen/internal/generator/funcmaps/markdown.go index ce0ad46bd..d4bf8f851 100644 --- a/codegen/internal/generator/funcmaps/markdown.go +++ b/codegen/internal/generator/funcmaps/markdown.go @@ -7,6 +7,8 @@ import ( "fmt" "regexp" "strings" + + "github.com/go-openapi/testify/codegen/v2/internal/model" ) var ( @@ -24,7 +26,18 @@ const sensiblePrealloc = 20 // // 1. Reference-style markdown links: [text]: url // 2. Godoc-style links: [errors.Is], [testing.T], etc. -func FormatMarkdown(in string) string { +// +//nolint:gocognit,gocyclo,cyclop // will refactor later this highly complex function +func FormatMarkdown(in string, object any) string { + var ( + testableExamples []model.Renderable + funcName string + ) + if function, ok := (object).(model.Function); ok { + testableExamples = function.Examples + funcName = function.Name + } + // Step 1: Extract reference-style link definitions // Pattern: [text]: url (at start of line or after whitespace) refLinks := make(map[string]string) @@ -112,6 +125,7 @@ func FormatMarkdown(in string) string { tabsCollection := false expanded := false tab := false + hasTestableExamples := false for line := range strings.SplitSeq(processed, "\n") { if expanded && len(strings.TrimSpace(line)) == 0 { @@ -147,12 +161,16 @@ func FormatMarkdown(in string) string { tabsCollection = true } + if strings.EqualFold(section, "Examples") && len(testableExamples) > 0 { + hasTestableExamples = true + continue // skip : we'll add testable examples below + } + title := titleize(section) if tab { trailer = append(trailer, "```") trailer = append(trailer, `{{< /tab >}}`) } - trailer = append(trailer, fmt.Sprintf(`{{%% tab title="%s" %%}}`, title)) trailer = append(trailer, "```go") tab = true @@ -163,6 +181,28 @@ func FormatMarkdown(in string) string { trailer = append(trailer, `{{< /tab >}}`) } + if hasTestableExamples { + trailer = append(trailer, `{{% tab title="Testable Examples" %}}`) + trailer = append(trailer, `{{% cards %}}`) + tabsCollection = true + + for _, example := range testableExamples { + trailer = append(trailer, `{{% card href="https://go.dev/play/" %}}`) + trailer = append(trailer, "\n") + trailer = append(trailer, `*Copy and click to open Go Playground*`) + trailer = append(trailer, "\n") + trailer = append(trailer, "```go") + trailer = append(trailer, fmt.Sprintf("// real-world test would inject *testing.T from Test%s(t *testing.T)", funcName)) + trailer = append(trailer, example.Render()) + trailer = append(trailer, "```") + trailer = append(trailer, `{{% /card %}}`) + trailer = append(trailer, "\n") + } + trailer = append(trailer, `{{% /cards %}}`) + trailer = append(trailer, `{{< /tab >}}`) + trailer = append(trailer, "\n") + } + if tabsCollection { trailer = append(trailer, `{{< /tabs >}}`) trailer = append(trailer, `{{% /expand %}}`) diff --git a/codegen/internal/generator/funcmaps/markdown_test.go b/codegen/internal/generator/funcmaps/markdown_test.go index fa455f500..fe7a2a466 100644 --- a/codegen/internal/generator/funcmaps/markdown_test.go +++ b/codegen/internal/generator/funcmaps/markdown_test.go @@ -13,7 +13,7 @@ import ( func TestMarkdownFormatEnhanced(t *testing.T) { for tt := range markdownTestCases() { t.Run(tt.name, func(t *testing.T) { - result := FormatMarkdown(tt.input) + result := FormatMarkdown(tt.input, nil) for _, want := range tt.contains { if !strings.Contains(result, want) { @@ -47,7 +47,7 @@ Values can be of type [strings.Builder] or [Boolean]. failure: "not empty" [Zero values]: https://go.dev/ref/spec#The_zero_value` - result := FormatMarkdown(input) + result := FormatMarkdown(input, nil) t.Logf("Output:\n%s", result) } diff --git a/codegen/internal/generator/generator.go b/codegen/internal/generator/generator.go index 25225ba3c..c7580539e 100644 --- a/codegen/internal/generator/generator.go +++ b/codegen/internal/generator/generator.go @@ -268,7 +268,7 @@ func (g *Generator) transformModel() error { tgt.EnableForward = g.ctx.enableForward tgt.EnableGenerics = g.ctx.enableGenerics tgt.EnableExamples = g.ctx.generateExamples - tgt.RunnableExamples = g.ctx.runnableExamples + tgt.RunnableExamples = g.ctx.runnableExamples /// instructs the doc generator to scan the generated packages to collect runnable examples if tgt.Imports == nil { tgt.Imports = make(model.ImportMap, 1) } diff --git a/codegen/internal/generator/templates/assertion_assertions.gotmpl b/codegen/internal/generator/templates/assertion_assertions.gotmpl index 22ca06fd7..107899cc7 100644 --- a/codegen/internal/generator/templates/assertion_assertions.gotmpl +++ b/codegen/internal/generator/templates/assertion_assertions.gotmpl @@ -1,7 +1,6 @@ {{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. -// {{ .Header }} package {{ .Package }} diff --git a/codegen/internal/generator/templates/assertion_assertions_test.gotmpl b/codegen/internal/generator/templates/assertion_assertions_test.gotmpl index bb946f231..f2f1ee850 100644 --- a/codegen/internal/generator/templates/assertion_assertions_test.gotmpl +++ b/codegen/internal/generator/templates/assertion_assertions_test.gotmpl @@ -1,7 +1,6 @@ {{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. -// {{ .Header }} package {{ .Package }} diff --git a/codegen/internal/generator/templates/assertion_examples_test.gotmpl b/codegen/internal/generator/templates/assertion_examples_test.gotmpl index 66380aeb2..b755f827c 100644 --- a/codegen/internal/generator/templates/assertion_examples_test.gotmpl +++ b/codegen/internal/generator/templates/assertion_examples_test.gotmpl @@ -1,7 +1,6 @@ {{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. -// {{ .Header }} package {{ .Package }}_test @@ -33,7 +32,7 @@ func Example{{ .Name }}() { {{- range .Tests }} {{- if eq .ExpectedOutcome 1 }}{{/* TestSuccess */}} {{- if (not $first) }}{{ println "" }}{{- end }}{{ $first = false }} - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func Test{{ $fn.Name }}(t *testing.T) {{- if $isRequire }} {{ $pkg }}.{{ $fn.Name }}{{ if hasSuffix $fn.Name "OfTypeT" }}[myType]{{ end }}(t, {{ relocate .TestedValues $pkg }}) fmt.Println("passed") diff --git a/codegen/internal/generator/templates/assertion_format.gotmpl b/codegen/internal/generator/templates/assertion_format.gotmpl index 791fc75ac..4df059bee 100644 --- a/codegen/internal/generator/templates/assertion_format.gotmpl +++ b/codegen/internal/generator/templates/assertion_format.gotmpl @@ -1,7 +1,6 @@ {{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. -// {{ .Header }} package {{ .Package }} diff --git a/codegen/internal/generator/templates/assertion_format_test.gotmpl b/codegen/internal/generator/templates/assertion_format_test.gotmpl index 49ca09325..bfb08330d 100644 --- a/codegen/internal/generator/templates/assertion_format_test.gotmpl +++ b/codegen/internal/generator/templates/assertion_format_test.gotmpl @@ -1,7 +1,6 @@ {{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. -// {{ .Header }} package {{ .Package }} diff --git a/codegen/internal/generator/templates/assertion_forward.gotmpl b/codegen/internal/generator/templates/assertion_forward.gotmpl index 42ecab630..cf0c6df11 100644 --- a/codegen/internal/generator/templates/assertion_forward.gotmpl +++ b/codegen/internal/generator/templates/assertion_forward.gotmpl @@ -1,7 +1,6 @@ {{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. -// {{ .Header }} package {{ .Package }} @@ -18,13 +17,13 @@ import ( // {{ docStringPackage .Package }} type {{ .Receiver }} struct { - t T + T } // New makes a new [{{ .Receiver }}] object for the specified [T] (e.g. [testing.T]). func New(t T) *{{ .Receiver }} { return &{{ .Receiver }}{ - t: t, + T: t, } } @@ -35,8 +34,8 @@ func New(t T) *{{ .Receiver }} { // {{ docStringPackage $.Package }} func (a *{{ $.Receiver }}) {{.Name}}({{ params .Params }}, msgAndArgs ...any) {{ returns .Returns }} { - if h, ok := a.t.(H); ok { h.Helper() } - return {{ .TargetPackage }}.{{.Name}}(a.t, {{ forward .Params }}, msgAndArgs...) + if h, ok := a.T.(H); ok { h.Helper() } + return {{ .TargetPackage }}.{{.Name}}(a.T, {{ forward .Params }}, msgAndArgs...) } {{- if $.EnableFormat }} @@ -44,8 +43,8 @@ func (a *{{ $.Receiver }}) {{.Name}}({{ params .Params }}, msgAndArgs ...any) {{ // {{ docStringPackage $.Package }} func (a *{{ $.Receiver }}){{ .Name }}f({{ params .Params }}, msg string, args ...any) {{ returns .Returns }} { - if h, ok := a.t.(H); ok { h.Helper() } - return {{ .TargetPackage }}.{{ .Name }}(a.t, {{ forward .Params }}, forwardArgs(msg, args)) + if h, ok := a.T.(H); ok { h.Helper() } + return {{ .TargetPackage }}.{{ .Name }}(a.T, {{ forward .Params }}, forwardArgs(msg, args)) } {{- end }} {{- end }} diff --git a/codegen/internal/generator/templates/assertion_forward_test.gotmpl b/codegen/internal/generator/templates/assertion_forward_test.gotmpl index 0759aca2e..24c6e5f5d 100644 --- a/codegen/internal/generator/templates/assertion_forward_test.gotmpl +++ b/codegen/internal/generator/templates/assertion_forward_test.gotmpl @@ -1,7 +1,6 @@ {{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. -// {{ .Header }} package {{ .Package }} diff --git a/codegen/internal/generator/templates/assertion_helpers.gotmpl b/codegen/internal/generator/templates/assertion_helpers.gotmpl index cc5737835..bbea35ef8 100644 --- a/codegen/internal/generator/templates/assertion_helpers.gotmpl +++ b/codegen/internal/generator/templates/assertion_helpers.gotmpl @@ -1,7 +1,6 @@ {{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. -// {{ .Header }} package {{ .Package }} diff --git a/codegen/internal/generator/templates/assertion_helpers_test.gotmpl b/codegen/internal/generator/templates/assertion_helpers_test.gotmpl index a48d797a3..bd7ca5a2c 100644 --- a/codegen/internal/generator/templates/assertion_helpers_test.gotmpl +++ b/codegen/internal/generator/templates/assertion_helpers_test.gotmpl @@ -1,7 +1,6 @@ {{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. -// {{ .Header }} package {{ .Package }} diff --git a/codegen/internal/generator/templates/assertion_types.gotmpl b/codegen/internal/generator/templates/assertion_types.gotmpl index 35f3dc5f9..d298221a5 100644 --- a/codegen/internal/generator/templates/assertion_types.gotmpl +++ b/codegen/internal/generator/templates/assertion_types.gotmpl @@ -1,7 +1,6 @@ {{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. -// {{ .Header }} package {{ .Package }} diff --git a/codegen/internal/generator/templates/doc_index.md.gotmpl b/codegen/internal/generator/templates/doc_index.md.gotmpl index af6c39265..88062edf8 100644 --- a/codegen/internal/generator/templates/doc_index.md.gotmpl +++ b/codegen/internal/generator/templates/doc_index.md.gotmpl @@ -6,7 +6,6 @@ description: | Find the assertion function you need for your data. weight: 1 -modified: {{ date }} --- **The v2 our tests wanted** @@ -48,6 +47,4 @@ Generated with {{ .Package.Tool }} {{ .Package.Copyright }} Document generated by {{ .Package.Tool }} DO NOT EDIT. - -{{ .Package.Header }} --> diff --git a/codegen/internal/generator/templates/doc_page.md.gotmpl b/codegen/internal/generator/templates/doc_page.md.gotmpl index a90bc2aa6..0e11e4277 100644 --- a/codegen/internal/generator/templates/doc_page.md.gotmpl +++ b/codegen/internal/generator/templates/doc_page.md.gotmpl @@ -1,7 +1,6 @@ --- title: {{ .Title | quote }} description: {{ titleize .Description | quote }} -modified: {{ date }} weight: {{ .Weight }} domains: - {{ .Domain | quote }} @@ -61,7 +60,7 @@ Generic assertions are marked with a {{ print "{{" }}% icon icon="star" color=or
{{/* like godoc TODO: use css */}} {{- end }} -{{ mdformat .DocString }}{{/* reformat inner markdown and reformat */}} +{{ mdformat .DocString . }}{{/* reformat inner markdown and reformat */}} {{- $funcName := .Name }} {{- with $extraPackages }} @@ -89,7 +88,7 @@ Generic assertions are marked with a {{ print "{{" }}% icon icon="star" color=or | Signature | Usage | |--|--| {{- $pkg := $.Package.Package }} -| [`{{ $pkg }}.{{ .Name }}({{ params .AllParams }}) {{ returns .Returns }}`]({{ $godoc }}/internal/{{ $pkg }}#{{ .Name }}) | internal implementation | +| [`{{ $pkg }}.{{ .GenericName }}({{ params .AllParams }}) {{ returns .Returns }}`]({{ $godoc }}/internal/{{ $pkg }}#{{ .Name }}) | internal implementation | **Source:** [{{ .SourcePackage }}#{{ .Name }}]({{ sourceLink $github .SourceLink }}) @@ -134,7 +133,7 @@ Generic assertions are marked with a {{ print "{{" }}% icon icon="star" color=or
{{/* like godoc */}} {{- end }} -{{ mdformat .DocString }} +{{ mdformat .DocString . }} {{- $funcName := .Name }} {{- with $extraPackages }} @@ -192,6 +191,4 @@ Generated with {{ .Package.Tool }} {{ .Package.Copyright }} Document generated by {{ .Package.Tool }} DO NOT EDIT. - -{{ .Package.Header }} --> diff --git a/codegen/internal/generator/templates/requirement_assertions.gotmpl b/codegen/internal/generator/templates/requirement_assertions.gotmpl index 8fcc24dc8..eda21b21f 100644 --- a/codegen/internal/generator/templates/requirement_assertions.gotmpl +++ b/codegen/internal/generator/templates/requirement_assertions.gotmpl @@ -1,7 +1,6 @@ {{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. -// {{ .Header }} package {{ .Package }} diff --git a/codegen/internal/generator/templates/requirement_format.gotmpl b/codegen/internal/generator/templates/requirement_format.gotmpl index d2a311724..0f4cac32e 100644 --- a/codegen/internal/generator/templates/requirement_format.gotmpl +++ b/codegen/internal/generator/templates/requirement_format.gotmpl @@ -1,7 +1,6 @@ {{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. -// {{ .Header }} package {{ .Package }} diff --git a/codegen/internal/generator/templates/requirement_forward.gotmpl b/codegen/internal/generator/templates/requirement_forward.gotmpl index 0b994d540..457237be1 100644 --- a/codegen/internal/generator/templates/requirement_forward.gotmpl +++ b/codegen/internal/generator/templates/requirement_forward.gotmpl @@ -1,7 +1,6 @@ {{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. -// {{ .Header }} package {{ .Package }} @@ -18,13 +17,13 @@ import ( // {{ docStringPackage .Package }} type {{ .Receiver }} struct { - t T + T } // New makes a new [{{ .Receiver }}] object for the specified [T] (e.g. [testing.T]). func New(t T) *{{ .Receiver }} { return &{{ .Receiver }}{ - t: t, + T: t, } } @@ -35,16 +34,16 @@ func New(t T) *{{ .Receiver }} { // {{ docStringPackage $.Package }} func (a *{{ $.Receiver }}) {{.Name}}({{ params .Params }}, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { h.Helper() } - {{- if or (eq .Name "Fail") (eq .Name "FailNow") }}{{/* special semantics for these two, which can only fail */}} - _ = {{ .TargetPackage }}.{{.Name}}(a.t, {{ forward .Params }}, msgAndArgs...) + if h, ok := a.T.(H); ok { h.Helper() } + {{- if or (eq .Name "Fail") (eq .Name "FailNow") }}{{/* special sema.Tics for these two, which can only fail */}} + _ = {{ .TargetPackage }}.{{.Name}}(a.T, {{ forward .Params }}, msgAndArgs...) {{- else }} - if {{ .TargetPackage }}.{{.Name}}(a.t, {{ forward .Params }}, msgAndArgs...) { + if {{ .TargetPackage }}.{{.Name}}(a.T, {{ forward .Params }}, msgAndArgs...) { return } {{- end }} - a.t.FailNow() + a.T.FailNow() } {{- if $.EnableFormat }} @@ -52,18 +51,18 @@ func (a *{{ $.Receiver }}) {{.Name}}({{ params .Params }}, msgAndArgs ...any) { // {{ docStringPackage $.Package }} func (a *{{ $.Receiver }}){{ .Name }}f({{ params .Params }}, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - {{- if or (eq .Name "Fail") (eq .Name "FailNow") }}{{/* special semantics for these two, which can only fail */}} - _ = {{ .TargetPackage }}.{{ .Name }}(a.t, {{ forward .Params }}, forwardArgs(msg, args)) + {{- if or (eq .Name "Fail") (eq .Name "FailNow") }}{{/* special sema.Tics for these two, which can only fail */}} + _ = {{ .TargetPackage }}.{{ .Name }}(a.T, {{ forward .Params }}, forwardArgs(msg, args)) {{- else }} - if {{ .TargetPackage }}.{{ .Name }}(a.t, {{ forward .Params }}, forwardArgs(msg, args)) { + if {{ .TargetPackage }}.{{ .Name }}(a.T, {{ forward .Params }}, forwardArgs(msg, args)) { return } {{- end }} - a.t.FailNow() + a.T.FailNow() } {{- end }} {{- end }} diff --git a/codegen/internal/model/model.go b/codegen/internal/model/model.go index 965d4a8f0..ef74061fb 100644 --- a/codegen/internal/model/model.go +++ b/codegen/internal/model/model.go @@ -11,6 +11,10 @@ import ( "strings" ) +type Renderable interface { + Render() string +} + // AssertionPackage describes the internal/assertions package. type AssertionPackage struct { Tool string @@ -97,6 +101,7 @@ type Function struct { Domain string SourceLink *token.Position ExtraComments []ExtraComment + Examples []Renderable // testable examples as a collection of [Renderable] examples } // GenericName renders the function name with one or more suffixes, @@ -198,6 +203,7 @@ type Ident struct { Domain string SourceLink *token.Position ExtraComments []ExtraComment + Examples []Renderable // testable examples as a collection of [Renderable] examples } // TestValue represents a single parsed test value expression. diff --git a/codegen/internal/scanner/examples-parser/doc.go b/codegen/internal/scanner/examples-parser/doc.go new file mode 100644 index 000000000..36ab68330 --- /dev/null +++ b/codegen/internal/scanner/examples-parser/doc.go @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +// Package parser scans a package of go source +// and extracts testable examples. +// +// The outcome of the parser is an index of testable examples by exported function or type. +// +// Provided testable examples are a structure that may be rendered as go code. +// +// This package is freely inspired by go team's pkgsite tool (github.com/golang/pkgsite). +package parser diff --git a/codegen/internal/scanner/examples-parser/helpers_test.go b/codegen/internal/scanner/examples-parser/helpers_test.go new file mode 100644 index 000000000..bd045ac61 --- /dev/null +++ b/codegen/internal/scanner/examples-parser/helpers_test.go @@ -0,0 +1,597 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package parser + +import ( + "go/ast" + "go/parser" + "go/token" + "iter" + "slices" + "strings" + "testing" +) + +func TestExampleFuncName(t *testing.T) { + t.Parallel() + + for c := range exampleFuncNameCases() { + t.Run(c.name, func(t *testing.T) { + t.Parallel() + + got := exampleFuncName(c.input) + if got != c.expected { + t.Errorf("exampleFuncName(%q) = %q, expected %q", c.input, got, c.expected) + } + }) + } +} + +type exampleFuncNameCase struct { + name string + input string + expected string +} + +func exampleFuncNameCases() iter.Seq[exampleFuncNameCase] { + return slices.Values([]exampleFuncNameCase{ + { + name: "empty string", + input: "", + expected: "", + }, + { + name: "simple function name", + input: "Equal", + expected: "Equal", + }, + { + name: "function with suffix", + input: "Equal_basic", + expected: "Equal", + }, + { + name: "underscore followed by uppercase is part of name", + input: "T_Method", + expected: "T_Method", + }, + { + name: "multiple underscores with suffix", + input: "Foo_Bar_baz", + expected: "Foo_Bar", + }, + { + name: "trailing underscore without next char", + input: "Foo_", + expected: "Foo_", + }, + { + name: "single character", + input: "X", + expected: "X", + }, + { + name: "underscore only", + input: "_", + expected: "_", + }, + { + name: "suffix starts immediately after underscore", + input: "Contains_slice", + expected: "Contains", + }, + { + name: "multiple suffixes only first split applies", + input: "HTTPStatusCode_redirect_permanent", + expected: "HTTPStatusCode", + }, + }) +} + +func TestExampleSuffix(t *testing.T) { + t.Parallel() + + for c := range exampleSuffixCases() { + t.Run(c.name, func(t *testing.T) { + t.Parallel() + + got := exampleSuffix(c.exampleName, c.funcName) + if got != c.expected { + t.Errorf("exampleSuffix(%q, %q) = %q, expected %q", c.exampleName, c.funcName, got, c.expected) + } + }) + } +} + +type exampleSuffixCase struct { + name string + exampleName string + funcName string + expected string +} + +func exampleSuffixCases() iter.Seq[exampleSuffixCase] { + return slices.Values([]exampleSuffixCase{ + { + name: "no suffix", + exampleName: "Equal", + funcName: "Equal", + expected: "", + }, + { + name: "simple suffix", + exampleName: "Equal_basic", + funcName: "Equal", + expected: "basic", + }, + { + name: "suffix with uppercase name", + exampleName: "Foo_Bar_baz", + funcName: "Foo_Bar", + expected: "baz", + }, + { + name: "multi-word suffix", + exampleName: "Contains_with_custom_message", + funcName: "Contains", + expected: "with_custom_message", + }, + }) +} + +func TestStripOutputComments(t *testing.T) { + t.Parallel() + + for c := range stripOutputCommentsCases() { + t.Run(c.name, func(t *testing.T) { + t.Parallel() + + got := stripOutputComments(c.input) + if got != c.expected { + t.Errorf("stripOutputComments() =\n%q\nexpected:\n%q", got, c.expected) + } + }) + } +} + +type stripOutputCommentsCase struct { + name string + input string + expected string +} + +func stripOutputCommentsCases() iter.Seq[stripOutputCommentsCase] { + return slices.Values([]stripOutputCommentsCase{ + { + name: "no output comment", + input: "x := 1\ny := 2", + expected: "x := 1\ny := 2", + }, + { + name: "trailing output comment", + input: "fmt.Println(x)\n// Output: 42", + expected: "fmt.Println(x)", + }, + { + name: "lowercase output comment", + input: "fmt.Println(x)\n// output: 42", + expected: "fmt.Println(x)", + }, + { + name: "output comment followed by blank lines", + input: "fmt.Println(x)\n// Output: 42\n\n", + expected: "fmt.Println(x)", + }, + { + name: "only trailing blanks stripped", + input: "// Output: first\nfmt.Println(x)\n// Output: 42", + expected: "// Output: first\nfmt.Println(x)", + }, + { + name: "empty string", + input: "", + expected: "", + }, + { + name: "only output comment", + input: "// Output: 42", + expected: "", + }, + { + name: "multiple trailing output lines", + input: "x := 1\n// Output:\n// output: result", + expected: "x := 1", + }, + }) +} + +func TestExtractFuncBody(t *testing.T) { + t.Parallel() + + for c := range extractFuncBodyCases() { + t.Run(c.name, func(t *testing.T) { + t.Parallel() + + got := extractFuncBody(c.input) + if got != c.expected { + t.Errorf("extractFuncBody() =\n%q\nexpected:\n%q", got, c.expected) + } + }) + } +} + +type extractFuncBodyCase struct { + name string + input string + expected string +} + +func extractFuncBodyCases() iter.Seq[extractFuncBodyCase] { + return slices.Values([]extractFuncBodyCase{ + { + name: "standard synthetic file", + input: "package p\n\nfunc f() {\n\tx := 1\n\ty := 2\n}\n", + expected: "x := 1\ny := 2", + }, + { + name: "no function marker", + input: "package p\n\nfunc g() {\n\tx := 1\n}\n", + expected: "package p\n\nfunc g() {\n\tx := 1\n}", + }, + { + name: "empty function body", + input: "package p\n\nfunc f() {\n}\n", + expected: "", + }, + { + name: "single statement", + input: "package p\n\nfunc f() {\n\treturn\n}\n", + expected: "return", + }, + { + name: "no closing brace", + input: "package p\n\nfunc f() {\n\tx := 1\n", + expected: "x := 1", + }, + }) +} + +func TestExtractWholeFileBody(t *testing.T) { + t.Parallel() + + for c := range extractWholeFileBodyCases() { + t.Run(c.name, func(t *testing.T) { + t.Parallel() + + got := extractWholeFileBody(c.input, c.funcName) + if got != c.expected { + t.Errorf("extractWholeFileBody() =\n%q\nexpected:\n%q", got, c.expected) + } + }) + } +} + +type extractWholeFileBodyCase struct { + name string + input string + funcName string + expected string +} + +func extractWholeFileBodyCases() iter.Seq[extractWholeFileBodyCase] { + return slices.Values([]extractWholeFileBodyCase{ + { + name: "strips package imports and renames main", + input: `package main + +import "fmt" + +func main() { + fmt.Println("hello") +}`, + funcName: "ExampleFoo", + expected: `func ExampleFoo() { + fmt.Println("hello") +}`, + }, + { + name: "strips grouped imports", + input: `package main + +import ( + "fmt" + "strings" +) + +func main() { + fmt.Println(strings.ToUpper("hello")) +}`, + funcName: "ExampleBar", + expected: `func ExampleBar() { + fmt.Println(strings.ToUpper("hello")) +}`, + }, + { + name: "preserves supporting declarations", + input: `package main + +import "fmt" + +type myStruct struct { + value int +} + +func main() { + s := myStruct{value: 42} + fmt.Println(s.value) +} + +func helper() int { + return 1 +}`, + funcName: "ExampleBaz", + expected: `type myStruct struct { + value int +} + +func ExampleBaz() { + s := myStruct{value: 42} + fmt.Println(s.value) +} + +func helper() int { + return 1 +}`, + }, + { + name: "handles single-line import", + input: `package main + +import "fmt" + +func main() { + fmt.Println("ok") +}`, + funcName: "ExampleSingle", + expected: `func ExampleSingle() { + fmt.Println("ok") +}`, + }, + { + name: "empty input", + input: "", + funcName: "ExampleEmpty", + expected: "", + }, + }) +} + +func TestIsWholeFileExample(t *testing.T) { + t.Parallel() + + for c := range isWholeFileExampleCases() { + t.Run(c.name, func(t *testing.T) { + t.Parallel() + + got := isWholeFileExample(c.file) + if got != c.expected { + t.Errorf("isWholeFileExample() = %v, expected %v", got, c.expected) + } + }) + } +} + +type isWholeFileExampleCase struct { + name string + file *ast.File + expected bool +} + +func isWholeFileExampleCases() iter.Seq[isWholeFileExampleCase] { + return slices.Values([]isWholeFileExampleCase{ + { + name: "nil file", + file: nil, + expected: false, + }, + { + name: "main only with imports", + file: parseFile(`package main; import "fmt"; func main() { fmt.Println() }`), + expected: false, + }, + { + name: "has helper function", + file: parseFile(`package main; func main() {}; func helper() {}`), + expected: true, + }, + { + name: "has type declaration", + file: parseFile(`package main; type Foo struct{}; func main() {}`), + expected: true, + }, + { + name: "has const declaration", + file: parseFile(`package main; const x = 1; func main() {}`), + expected: true, + }, + { + name: "has var declaration", + file: parseFile(`package main; var x int; func main() {}`), + expected: true, + }, + { + name: "only main and imports", + file: parseFile(`package main; import "os"; func main() { _ = os.Args }`), + expected: false, + }, + }) +} + +func TestCollectExportedSymbols(t *testing.T) { + t.Parallel() + + for c := range collectExportedSymbolsCases() { + t.Run(c.name, func(t *testing.T) { + t.Parallel() + + exported := make(map[string]bool) + file := parseFile(c.src) + collectExportedSymbols(file, exported) + + for _, name := range c.want { + if !exported[name] { + t.Errorf("expected %q to be exported, but it was not collected", name) + } + } + for _, name := range c.dontWant { + if exported[name] { + t.Errorf("expected %q to NOT be collected, but it was", name) + } + } + + if len(exported) != len(c.want) { + t.Errorf("collected %d symbols, expected %d: %v", len(exported), len(c.want), exported) + } + }) + } +} + +type collectExportedSymbolsCase struct { + name string + src string + want []string + dontWant []string +} + +func collectExportedSymbolsCases() iter.Seq[collectExportedSymbolsCase] { + return slices.Values([]collectExportedSymbolsCase{ + { + name: "exported functions", + src: `package p; func Exported() {}; func another() {}`, + want: []string{"Exported"}, + dontWant: []string{"another"}, + }, + { + name: "methods are skipped", + src: `package p; type T struct{}; func (t T) Method() {}; func Standalone() {}`, + want: []string{"T", "Standalone"}, + dontWant: []string{"Method"}, + }, + { + name: "exported types", + src: `package p; type Public struct{}; type private struct{}`, + want: []string{"Public"}, + dontWant: []string{"private"}, + }, + { + name: "mixed functions and types", + src: `package p; func Alpha() {}; type Beta int; func gamma() {}; type delta struct{}`, + want: []string{"Alpha", "Beta"}, + dontWant: []string{"gamma", "delta"}, + }, + { + name: "no exported symbols", + src: `package p; func hidden() {}; type secret struct{}`, + want: nil, + dontWant: []string{"hidden", "secret"}, + }, + { + name: "multiple types in one declaration", + src: `package p; type ( Foo int; Bar string; baz float64 )`, + want: []string{"Foo", "Bar"}, + dontWant: []string{"baz"}, + }, + }) +} + +// parseFile is a test helper that parses a Go source string into an *ast.File. +func parseFile(src string) *ast.File { + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, "test.go", src, 0) + if err != nil { + // Only used in test case setup; a parse error indicates a broken test case. + panic("parseFile: " + err.Error()) + } + + return file +} + +func TestRenderBody_FormatsCode(t *testing.T) { + t.Parallel() + + // Parse a real example from the assert package and verify Render + // produces output that: + // 1. does not contain outer braces + // 2. does not contain "// Output:" lines + // 3. is non-empty + + for c := range renderBodyPropertyCases() { + t.Run(c.name, func(t *testing.T) { + t.Parallel() + + rendered := c.example.Render() + if rendered == "" { + t.Fatal("Render() returned empty string") + } + + t.Run("should not start with outer braces", func(t *testing.T) { + trimmed := strings.TrimSpace(rendered) + if strings.HasPrefix(trimmed, "{") { + t.Errorf("got:\n%s", rendered) + } + }) + + t.Run("should not contain output comments", func(t *testing.T) { + if strings.Contains(rendered, "// Output:") { + t.Errorf("got:\n%s", rendered) + } + }) + + t.Run("should not contain package clause", func(t *testing.T) { + if strings.Contains(rendered, "package ") { + t.Errorf("got:\n%s", rendered) + } + }) + }) + } +} + +type renderBodyPropertyCase struct { + name string + example TestableExample +} + +func renderBodyPropertyCases() iter.Seq[renderBodyPropertyCase] { + // Build simple AST examples programmatically to avoid depending on the + // full package loader in unit tests. + fset := token.NewFileSet() + src := `package p + +func Example() { + x := 1 + _ = x + // Output: 1 +} +` + file, err := parser.ParseFile(fset, "ex_test.go", src, parser.ParseComments) + if err != nil { + panic("renderBodyPropertyCases: " + err.Error()) + } + + fn, ok := file.Decls[0].(*ast.FuncDecl) + if !ok { + panic("renderBodyPropertyCases: expected *ast.FuncDecl") + } + + return slices.Values([]renderBodyPropertyCase{ + { + name: "simple body example", + example: TestableExample{ + Name: "Example", + code: fn.Body, + fset: fset, + }, + }, + }) +} diff --git a/codegen/internal/scanner/examples-parser/options.go b/codegen/internal/scanner/examples-parser/options.go new file mode 100644 index 000000000..2b3e94535 --- /dev/null +++ b/codegen/internal/scanner/examples-parser/options.go @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package parser + +// Option configures the [Extractor]. +type Option func(*Extractor) + +// WithWorkDir sets the working directory for package resolution. +func WithWorkDir(dir string) Option { + return func(e *Extractor) { + e.dir = dir + } +} + +// WithBuildTags sets build tags for package loading (e.g. "integrationtest"). +func WithBuildTags(tags ...string) Option { + return func(e *Extractor) { + e.buildTags = tags + } +} diff --git a/codegen/internal/scanner/examples-parser/parser.go b/codegen/internal/scanner/examples-parser/parser.go new file mode 100644 index 000000000..3f4a64fac --- /dev/null +++ b/codegen/internal/scanner/examples-parser/parser.go @@ -0,0 +1,497 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package parser + +import ( + "bytes" + "fmt" + "go/ast" + "go/doc" + "go/printer" + "go/token" + "strings" + "unicode" + + "golang.org/x/tools/go/packages" + "golang.org/x/tools/imports" +) + +const tabWidth = 4 + +// Extractor explores a go package (including test code) and looks for testable examples. +type Extractor struct { + dir string + pkg string + buildTags []string +} + +// New [Extractor] for a given source package to be scanned. +// +// pkg is an import path or a relative pattern (e.g., "./assert") resolved from the working directory. +func New(pkg string, opts ...Option) *Extractor { + e := &Extractor{ + pkg: pkg, + dir: ".", + } + for _, opt := range opts { + opt(e) + } + + return e +} + +// Parse source code including test code. +// +// Builds an index of exported symbols (functions and types). +// +// Attaches all identified testable examples to the index. +// +// It may fail if the code doesn't compile. +func (e *Extractor) Parse() (Examples, error) { + cfg := &packages.Config{ + Dir: e.dir, + Mode: packages.NeedName | packages.NeedSyntax | packages.NeedCompiledGoFiles, + Tests: true, + } + if len(e.buildTags) > 0 { + cfg.BuildFlags = []string{"-tags", strings.Join(e.buildTags, ",")} + } + + pkgs, err := packages.Load(cfg, e.pkg) + if err != nil { + return nil, err + } + + if len(pkgs) == 0 { + return nil, fmt.Errorf("package not found: %s", e.pkg) + } + + // packages.Load reports resolution failures as package-level errors. + for _, pkg := range pkgs { + for _, pkgErr := range pkg.Errors { + return nil, fmt.Errorf("loading %s: %s", pkg.ID, pkgErr.Msg) + } + } + + // With Tests: true, packages.Load returns: + // - the production package (ID = import path) + // - internal test variant (ID contains "[", Name = pkg name) + // - external test variant (ID contains "[", Name = pkg_test) + var ( + exported = make(map[string]bool) + testFiles []*ast.File + fset *token.FileSet + ) + + for _, pkg := range pkgs { + if fset == nil { + fset = pkg.Fset + } + + isTestVariant := strings.Contains(pkg.ID, "[") + + // From the production package, collect exported symbol names + // (functions and types, so examples for types are linked too). + if !isTestVariant { + for _, file := range pkg.Syntax { + collectExportedSymbols(file, exported) + } + } + + // From test variant packages, collect _test.go files. + if isTestVariant { + for i, file := range pkg.Syntax { + if strings.HasSuffix(pkg.CompiledGoFiles[i], "_test.go") { + testFiles = append(testFiles, file) + } + } + } + } + + if len(testFiles) == 0 { + return make(Examples), nil + } + + // Extract examples using go/doc. + docExamples := doc.Examples(testFiles...) + + // Build the index: match examples to exported symbols. + index := make(Examples) + for _, ex := range docExamples { + funcName := exampleFuncName(ex.Name) + if funcName == "" { + continue // package-level example, skip + } + if !exported[funcName] { + continue // no matching exported symbol + } + + te := TestableExample{ + Name: ex.Name, + Suffix: exampleSuffix(ex.Name, funcName), + Doc: ex.Doc, + Output: ex.Output, + WholeFile: isWholeFileExample(ex.Play), + code: ex.Code, + play: ex.Play, + fset: fset, + } + index[funcName] = append(index[funcName], te) + } + + return index, nil +} + +// collectExportedSymbols walks a file's declarations and records exported function +// and type names into the provided set. +func collectExportedSymbols(file *ast.File, exported map[string]bool) { + for _, decl := range file.Decls { + switch d := decl.(type) { + case *ast.FuncDecl: + if d.Recv != nil { + continue // skip methods + } + if d.Name.IsExported() { + exported[d.Name.Name] = true + } + + case *ast.GenDecl: + for _, spec := range d.Specs { + ts, ok := spec.(*ast.TypeSpec) + if !ok { + continue + } + if ts.Name.IsExported() { + exported[ts.Name.Name] = true + } + } + } + } +} + +// exampleFuncName extracts the symbol name from a testable example name. +// +// Go naming convention: +// +// "Equal" -> "Equal" (ExampleEqual) +// "Equal_basic" -> "Equal" (ExampleEqual_basic) +// "T_Method" -> "T" (type method, if T is a type) +// +// We take the leading identifier segment: everything before the first '_' +// that is followed by a lowercase letter (which marks a suffix). +func exampleFuncName(name string) string { + if name == "" { + return "" + } + + // Find the first '_' that separates the identifier from the suffix. + // The suffix must start with a lowercase letter (Go convention). + for i, r := range name { + if r == '_' && i+1 < len(name) { + next := rune(name[i+1]) + if unicode.IsLower(next) { + return name[:i] + } + } + } + + return name +} + +// exampleSuffix extracts the suffix part from a testable example name +// given the already-identified function name. +func exampleSuffix(name, funcName string) string { + rest := strings.TrimPrefix(name, funcName) + rest = strings.TrimPrefix(rest, "_") + + return rest +} + +// isWholeFileExample reports whether a Play AST represents a whole-file example. +// +// A whole-file example has top-level declarations beyond the package clause, +// imports, and the main function (i.e., supporting types, helpers, etc.). +func isWholeFileExample(play *ast.File) bool { + if play == nil { + return false + } + + for _, decl := range play.Decls { + switch d := decl.(type) { + case *ast.FuncDecl: + if d.Name.Name != "main" { + return true + } + case *ast.GenDecl: + if d.Tok != token.IMPORT { + return true + } + } + } + + return false +} + +// Examples is an index of [TestableExample]. +// +// Keys are exported symbol names (not fully qualified names). +// +// Each key may have 1 or several examples attached to it. +// +// Example: +// +// for function {package}.MyFunction, the key is "MyFunction". +type Examples map[string][]TestableExample + +// TestableExample describes a go testable example and knows how to render as formatted go code. +type TestableExample struct { + // Name is the full example name after the "Example" prefix. + // + // For ExampleEqual, Name is "Equal". + // For ExampleEqual_basic, Name is "Equal_basic". + Name string + + // Suffix is the example suffix, without leading '_'. + // + // For ExampleEqual, Suffix is "". + // For ExampleEqual_basic, Suffix is "basic". + Suffix string + + // Doc is the doc comment on the example function. + Doc string + + // Output is the expected output string (from "// Output:" comments). + Output string + + // WholeFile indicates this is a whole-file example with supporting + // declarations (types, helper functions) outside the example function. + WholeFile bool + + // unexported fields for rendering + code ast.Node + play *ast.File + fset *token.FileSet +} + +// Render the example as a formatted go code snippet. +// +// For whole-file examples, the complete file is rendered (minus package and imports), +// preserving all supporting declarations. +// +// For regular examples, only the function body is rendered. +// +// In both cases, the code is formatted with [imports.Process] (goimports). +func (x TestableExample) Render() string { + // Render the full Play file as a standalone main program when available. + if x.play != nil { + return x.renderPlay() + } + + return x.renderBody() + + // Previous routing: stripped package/imports/main scaffolding. + // if x.WholeFile && x.play != nil { + // return x.renderWholeFile() + // } + // return x.renderBody() +} + +// renderPlay renders the Play AST as-is: a complete runnable program +// with package clause, imports, and func main(). +func (x TestableExample) renderPlay() string { + var buf bytes.Buffer + p := printer.Config{Mode: printer.UseSpaces, Tabwidth: tabWidth} + if err := p.Fprint(&buf, x.fset, x.play); err != nil { + return "" + } + + raw := buf.String() + + formatted, err := imports.Process("example.go", []byte(raw), &imports.Options{ + Fragment: true, + FormatOnly: true, + }) + if err != nil { + return raw + } + + return string(formatted) +} + +// renderBody renders the example function body only. +func (x TestableExample) renderBody() string { + if x.code == nil { + return "" + } + + // Print the raw AST node. + var buf bytes.Buffer + p := printer.Config{Mode: printer.UseSpaces, Tabwidth: tabWidth} + if err := p.Fprint(&buf, x.fset, x.code); err != nil { + return "" + } + + body := buf.String() + + // Strip outer braces: the Code node is a *ast.BlockStmt. + body = strings.TrimSpace(body) + if strings.HasPrefix(body, "{") && strings.HasSuffix(body, "}") { + body = body[1 : len(body)-1] + } + + // Remove trailing "// Output:" comment lines before formatting. + body = stripOutputComments(body) + + // Wrap in a synthetic file so imports.Process can handle formatting. + synthetic := "package p\n\nfunc f() {\n" + body + "\n}\n" + + formatted, err := imports.Process("example.go", []byte(synthetic), &imports.Options{ + Fragment: true, + FormatOnly: true, + }) + if err != nil { + return strings.TrimSpace(body) + } + + return extractFuncBody(string(formatted)) +} + +/* +// renderWholeFile renders a whole-file example, stripping the package clause +// and imports, and renaming "func main()" back to the example function name. +func (x TestableExample) renderWholeFile() string { + // Print the entire Play file. + var buf bytes.Buffer + p := printer.Config{Mode: printer.UseSpaces, Tabwidth: tabWidth} + if err := p.Fprint(&buf, x.fset, x.play); err != nil { + return "" + } + + raw := buf.String() + + // Remove "// Output:" comments. + raw = stripOutputComments(raw) + + // Format with goimports. + formatted, err := imports.Process("example.go", []byte(raw), &imports.Options{ + Fragment: true, + FormatOnly: true, + }) + if err != nil { + formatted = []byte(raw) + } + + // Strip package clause and imports, rename main -> Example function. + return extractWholeFileBody(string(formatted), "Example"+x.Name) +} +*/ + +// extractWholeFileBody strips the package clause and import blocks from a +// formatted Go file, and renames "func main()" to the given example function name. +func extractWholeFileBody(src, exampleFuncName string) string { + var result []string + lines := strings.Split(src, "\n") + + inImportBlock := false + for _, line := range lines { + trimmed := strings.TrimSpace(line) + + // Skip package declaration. + if strings.HasPrefix(trimmed, "package ") { + continue + } + + // Skip import blocks. + if trimmed == "import (" { + inImportBlock = true + continue + } + if inImportBlock { + if trimmed == ")" { + inImportBlock = false + } + continue + } + + // Skip single-line imports. + if strings.HasPrefix(trimmed, "import ") { + continue + } + + // Rename "func main()" to the example function name. + if strings.HasPrefix(trimmed, "func main()") { + line = strings.Replace(line, "func main()", "func "+exampleFuncName+"()", 1) + } + + result = append(result, line) + } + + // Trim leading/trailing blank lines. + for len(result) > 0 && strings.TrimSpace(result[0]) == "" { + result = result[1:] + } + for len(result) > 0 && strings.TrimSpace(result[len(result)-1]) == "" { + result = result[:len(result)-1] + } + + return strings.Join(result, "\n") +} + +// stripOutputComments removes trailing "// Output:" lines and any +// blank lines that follow them at the end of the code block. +func stripOutputComments(code string) string { + lines := strings.Split(code, "\n") + + for len(lines) > 0 { + trimmed := strings.TrimSpace(lines[len(lines)-1]) + if trimmed == "" || strings.HasPrefix(trimmed, "// Output:") || strings.HasPrefix(trimmed, "// output:") { + lines = lines[:len(lines)-1] + continue + } + break + } + + return strings.Join(lines, "\n") +} + +// extractFuncBody extracts the body of "func f()" from a formatted synthetic file, +// stripping the wrapper and dedenting. +func extractFuncBody(src string) string { + // Find "func f() {" and the closing "}". + const openMarker = "func f() {" + _, body, found := strings.Cut(src, openMarker) + if !found { + return strings.TrimSpace(src) + } + + // Find the last "}" which closes the function. + closeIdx := strings.LastIndex(body, "}") + if closeIdx < 0 { + return strings.TrimSpace(body) + } + body = body[:closeIdx] + + // Dedent by one tab (goimports uses tabs). + lines := strings.Split(body, "\n") + + // Skip leading/trailing empty lines. + start := 0 + for start < len(lines) && strings.TrimSpace(lines[start]) == "" { + start++ + } + end := len(lines) + for end > start && strings.TrimSpace(lines[end-1]) == "" { + end-- + } + lines = lines[start:end] + + // Remove one leading tab from each line (goimports indents function body by one tab). + for i, line := range lines { + if strings.HasPrefix(line, "\t") { + lines[i] = line[1:] + } + } + + return strings.Join(lines, "\n") +} diff --git a/codegen/internal/scanner/examples-parser/parser_test.go b/codegen/internal/scanner/examples-parser/parser_test.go new file mode 100644 index 000000000..d91fff951 --- /dev/null +++ b/codegen/internal/scanner/examples-parser/parser_test.go @@ -0,0 +1,252 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package parser_test + +import ( + "flag" + "strings" + "sync" + "testing" + + parser "github.com/go-openapi/testify/codegen/v2/internal/scanner/examples-parser" +) + +//nolint:gochecknoglobals // it is okay to use globals for test flag and [sync.Once] cache for parsed testdata +var ( + render = flag.Bool("render", false, "log rendered example output") + parseTestExamplesOnce sync.Once + cachedTestExamples parser.Examples +) + +const buildTag = "integrationtest" + +func TestParse(t *testing.T) { + t.Parallel() + + examples := loadTestExamples(t) + + if len(examples) == 0 { + t.Fatal("expected examples, got none") + } + + t.Run("Verify known function examples exist", func(t *testing.T) { + for _, funcName := range []string{"Greet", "Add"} { + t.Run("with "+funcName, func(t *testing.T) { + exs, ok := examples[funcName] + if !ok { + t.Errorf("expected examples for %q, found none", funcName) + return + } + if len(exs) == 0 { + t.Errorf("expected at least one example for %q", funcName) + } + }) + } + }) + + t.Run("NoExample is exported but has no example", func(t *testing.T) { + if _, ok := examples["NoExample"]; ok { + t.Error("expected no examples for NoExample") + } + }) + + t.Run("unexported must not appear", func(t *testing.T) { + if _, ok := examples["unexported"]; ok { + t.Error("expected no examples for unexported") + } + }) + + if *render { + t.Logf("found %d symbols with examples", len(examples)) + } +} + +func TestParse_TypeExamples(t *testing.T) { + t.Parallel() + + examples := loadTestExamples(t) + + exs, ok := examples["Formatter"] + if !ok { + t.Fatal("expected example for type Formatter, found none") + } + if len(exs) != 1 { + t.Fatalf("expected 1 example for Formatter, got %d", len(exs)) + } + if !exs[0].WholeFile { + t.Error("expected WholeFile=true for Formatter") + } +} + +func TestParse_ExampleDetails(t *testing.T) { + t.Parallel() + + examples := loadTestExamples(t) + + greetExamples, ok := examples["Greet"] + if !ok || len(greetExamples) == 0 { + t.Fatal("expected examples for Greet") + } + + ex := greetExamples[0] + if ex.Name != "Greet" { + t.Errorf("expected Name=%q, got %q", "Greet", ex.Name) + } + if ex.Suffix != "" { + t.Errorf("expected empty Suffix, got %q", ex.Suffix) + } + if ex.Output != "Hello, World!\n" { + t.Errorf("expected Output=%q, got %q", "Hello, World!\n", ex.Output) + } + if ex.WholeFile { + t.Error("expected WholeFile=false for ExampleGreet") + } +} + +func TestParse_SuffixedExample(t *testing.T) { + t.Parallel() + + examples := loadTestExamples(t) + + addExamples, ok := examples["Add"] + if !ok { + t.Fatal("expected examples for Add") + } + + // Add has two examples: ExampleAdd and ExampleAdd_negative. + if len(addExamples) != 2 { + t.Fatalf("expected 2 examples for Add, got %d", len(addExamples)) + } + + var found bool + for _, ex := range addExamples { + if ex.Suffix == "negative" { + found = true + + break + } + } + if !found { + t.Error("expected an example with Suffix=\"negative\" for Add") + } +} + +func TestParse_Render(t *testing.T) { + t.Parallel() + + examples := loadTestExamples(t) + + greetExamples := examples["Greet"] + if len(greetExamples) == 0 { + t.Fatal("expected examples for Greet") + } + + rendered := greetExamples[0].Render() + if rendered == "" { + t.Fatal("Render() returned empty string") + } + + t.Run("Should contain the function call", func(t *testing.T) { + if !strings.Contains(rendered, "Greet") { + t.Errorf("expected rendered code to contain 'Greet', got:\n%s", rendered) + } + }) + + t.Run(`Should NOT contain "// Output:" lines`, func(t *testing.T) { + if strings.Contains(rendered, "// Output:") { + t.Errorf("expected rendered code to NOT contain '// Output:', got:\n%s", rendered) + } + }) + + t.Run("Should NOT have outer braces", func(t *testing.T) { + trimmed := strings.TrimSpace(rendered) + if strings.HasPrefix(trimmed, "{") { + t.Errorf("expected rendered code without outer braces, got:\n%s", rendered) + } + }) + + if *render { + t.Logf("Rendered ExampleGreet:\n%s", rendered) + } +} + +func TestParse_RenderWholeFile(t *testing.T) { + t.Parallel() + + examples := loadTestExamples(t) + + exs := examples["Formatter"] + if len(exs) == 0 { + t.Fatal("expected example for Formatter") + } + + rendered := exs[0].Render() + if rendered == "" { + t.Fatal("Render() returned empty string") + } + + t.Run("Should contain a main function", func(t *testing.T) { + if !strings.Contains(rendered, "func main()") { + t.Errorf("expected rendered code to contain main function declaration, got:\n%s", rendered) + } + }) + + t.Run("Should contain the supporting type declaration", func(t *testing.T) { + if !strings.Contains(rendered, "type helper struct") { + t.Errorf("expected rendered code to contain 'type helper struct', got:\n%s", rendered) + } + }) + + t.Run("Should contain package clause or imports", func(t *testing.T) { + if !strings.Contains(rendered, "package ") { + t.Errorf("expected rendered code without package clause, got:\n%s", rendered) + } + if !strings.Contains(rendered, "import ") { + t.Errorf("expected rendered code without imports, got:\n%s", rendered) + } + }) + + if *render { + t.Logf("Rendered ExampleFormatter:\n%s", rendered) + } +} + +func TestParse_NonExistentPackage(t *testing.T) { + t.Parallel() + + ext := parser.New("./nonexistent/package/path") + + _, err := ext.Parse() + if err == nil { + t.Error("expected error for non-existent package") + } +} + +func TestRender_NilCode(t *testing.T) { + t.Parallel() + + ex := parser.TestableExample{} + if got := ex.Render(); got != "" { + t.Errorf("expected empty string for nil code, got %q", got) + } +} + +// loadTestExamples parses the testdata package once and caches the result. +func loadTestExamples(t *testing.T) parser.Examples { + t.Helper() + + parseTestExamplesOnce.Do(func() { + examples, err := parser.New( + "./testdata/examplespkg", + parser.WithBuildTags(buildTag), + ).Parse() + if err != nil { + t.Fatalf("Parse() error: %v", err) + } + + cachedTestExamples = examples + }) + + return cachedTestExamples +} diff --git a/codegen/internal/scanner/examples-parser/testdata/examplespkg/examplespkg.go b/codegen/internal/scanner/examples-parser/testdata/examplespkg/examplespkg.go new file mode 100644 index 000000000..a835b27c5 --- /dev/null +++ b/codegen/internal/scanner/examples-parser/testdata/examplespkg/examplespkg.go @@ -0,0 +1,26 @@ +//go:build integrationtest + +// Package examplespkg is a test fixture for the examples-parser unit tests. +package examplespkg + +import "fmt" + +// Greet returns a greeting for the given name. +func Greet(name string) string { + return fmt.Sprintf("Hello, %s!", name) +} + +// Add returns the sum of two integers. +func Add(a, b int) int { + return a + b +} + +// NoExample is exported but has no testable example. +func NoExample() {} + +func unexported() {} //nolint:unused // fixture: verifies unexported symbols are skipped + +// Formatter is an exported type with a whole-file example. +type Formatter struct { + Prefix string +} diff --git a/codegen/internal/scanner/examples-parser/testdata/examplespkg/examplespkg_test.go b/codegen/internal/scanner/examples-parser/testdata/examplespkg/examplespkg_test.go new file mode 100644 index 000000000..7a0f1d91b --- /dev/null +++ b/codegen/internal/scanner/examples-parser/testdata/examplespkg/examplespkg_test.go @@ -0,0 +1,20 @@ +//go:build integrationtest + +package examplespkg + +import "fmt" + +func ExampleGreet() { + fmt.Println(Greet("World")) + // Output: Hello, World! +} + +func ExampleAdd() { + fmt.Println(Add(1, 2)) + // Output: 3 +} + +func ExampleAdd_negative() { + fmt.Println(Add(-1, -2)) + // Output: -3 +} diff --git a/codegen/internal/scanner/examples-parser/testdata/examplespkg/wholefile_test.go b/codegen/internal/scanner/examples-parser/testdata/examplespkg/wholefile_test.go new file mode 100644 index 000000000..c6181abcb --- /dev/null +++ b/codegen/internal/scanner/examples-parser/testdata/examplespkg/wholefile_test.go @@ -0,0 +1,16 @@ +//go:build integrationtest + +package examplespkg + +import "fmt" + +type helper struct { + value string +} + +func ExampleFormatter() { + f := Formatter{Prefix: ">>"} + h := helper{value: "test"} + fmt.Printf("%s %s", f.Prefix, h.value) + // Output: >> test +} diff --git a/codegen/main.go b/codegen/main.go index 350a455f6..821ac151a 100644 --- a/codegen/main.go +++ b/codegen/main.go @@ -120,6 +120,7 @@ func execute(cfg *config) error { generator.WithIncludeForwardFuncs(cfg.includeFwd), generator.WithIncludeGenerics(cfg.includeGen), generator.WithIncludeHelpers(cfg.includeHlp), + generator.WithRunnableExamples(cfg.runExa), // extracts runnable examples from generated sourc ) if err != nil { return err diff --git a/docs/doc-site/api/_index.md b/docs/doc-site/api/_index.md index 620926b7f..7e406a506 100644 --- a/docs/doc-site/api/_index.md +++ b/docs/doc-site/api/_index.md @@ -6,7 +6,6 @@ description: | Find the assertion function you need for your data. weight: 1 -modified: 2026-01-27 --- **The v2 our tests wanted** @@ -25,7 +24,7 @@ with all documented exported variants documented in a more concise form than the ## Domains -The `testify` API is organized in 18 logical domains shown below. +The `testify` API is organized in 19 logical domains shown below. Each domain contains assertions regrouped by their use case (e.g. http, json, error). {{< children type="card" description="true" >}} @@ -40,15 +39,16 @@ Each domain contains assertions regrouped by their use case (e.g. http, json, er - [Error](./error.md) - Asserting Errors (8) - [File](./file.md) - Asserting OS Files (6) - [Http](./http.md) - Asserting HTTP Response And Body (7) -- [Json](./json.md) - Asserting JSON Documents (3) +- [Json](./json.md) - Asserting JSON Documents (5) - [Number](./number.md) - Asserting Numbers (7) - [Ordering](./ordering.md) - Asserting How Collections Are Ordered (10) - [Panic](./panic.md) - Asserting A Panic Behavior (4) +- [Safety](./safety.md) - (1) - [String](./string.md) - Asserting Strings (4) - [Testing](./testing.md) - Mimics Methods From The Testing Standard Library (2) - [Time](./time.md) - Asserting Times And Durations (2) - [Type](./type.md) - Asserting Types Rather Than Values (10) -- [Yaml](./yaml.md) - Asserting Yaml Documents (3) +- [Yaml](./yaml.md) - Asserting Yaml Documents (5) - [Common](./common.md) - Other Uncategorized Helpers (3) --- @@ -66,6 +66,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/boolean.md b/docs/doc-site/api/boolean.md index c25f88fce..919595fb1 100644 --- a/docs/doc-site/api/boolean.md +++ b/docs/doc-site/api/boolean.md @@ -1,7 +1,6 @@ --- title: "Boolean" description: "Asserting Boolean Values" -modified: 2026-01-27 weight: 1 domains: - "boolean" @@ -44,14 +43,44 @@ False asserts that the specified value is false. {{% tab title="Usage" %}} ```go assertions.False(t, myBool) + success: 1 == 0 + failure: 1 == 1 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 1 == 0 - failure: 1 == 1 +// real-world test would inject *testing.T from TestFalse(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.False(t, 1 == 0) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -95,14 +124,44 @@ The type constraint [Boolean](https://pkg.go.dev/github.com/go-openapi/testify/v type B bool var b B = true assertions.FalseT(t, b) + success: 1 == 0 + failure: 1 == 1 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 1 == 0 - failure: 1 == 1 +// real-world test would inject *testing.T from TestFalseT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.FalseT(t, 1 == 0) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -123,7 +182,7 @@ The type constraint [Boolean](https://pkg.go.dev/github.com/go-openapi/testify/v {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.FalseT(t T, value B, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#FalseT) | internal implementation | +| [`assertions.FalseT[B Boolean](t T, value B, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#FalseT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#FalseT](https://github.com/go-openapi/testify/blob/master/internal/assertions/boolean.go#L92) {{% /tab %}} @@ -138,14 +197,44 @@ True asserts that the specified value is true. {{% tab title="Usage" %}} ```go assertions.True(t, myBool) + success: 1 == 1 + failure: 1 == 0 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 1 == 1 - failure: 1 == 0 +// real-world test would inject *testing.T from TestTrue(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.True(t, 1 == 1) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -189,14 +278,44 @@ The type constraint [Boolean](https://pkg.go.dev/github.com/go-openapi/testify/v type B bool var b B = true assertions.True(t, b) + success: 1 == 1 + failure: 1 == 0 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 1 == 1 - failure: 1 == 0 +// real-world test would inject *testing.T from TestTrueT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.TrueT(t, 1 == 1) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -217,7 +336,7 @@ The type constraint [Boolean](https://pkg.go.dev/github.com/go-openapi/testify/v {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.TrueT(t T, value B, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#TrueT) | internal implementation | +| [`assertions.TrueT[B Boolean](t T, value B, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#TrueT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#TrueT](https://github.com/go-openapi/testify/blob/master/internal/assertions/boolean.go#L43) {{% /tab %}} @@ -238,6 +357,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/collection.md b/docs/doc-site/api/collection.md index da49b672a..0d42748e6 100644 --- a/docs/doc-site/api/collection.md +++ b/docs/doc-site/api/collection.md @@ -1,7 +1,6 @@ --- title: "Collection" description: "Asserting Slices And Maps" -modified: 2026-01-27 weight: 2 domains: - "collection" @@ -92,14 +91,44 @@ specified substring or element. assertions.Contains(t, "Hello World", "World") assertions.Contains(t, []string{"Hello", "World"}, "World") assertions.Contains(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"Hello": "World"}, "Hello") + success: []string{"A","B"}, "A" + failure: []string{"A","B"}, "C" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []string{"A","B"}, "A" - failure: []string{"A","B"}, "C" +// real-world test would inject *testing.T from TestContains(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Contains(t, []string{"A", "B"}, "A") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -141,14 +170,44 @@ the number of appearances of each of them in both lists should match. {{% tab title="Usage" %}} ```go assertions.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) + success: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} + failure: []int{1, 2, 3}, []int{1, 2, 4} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} - failure: []int{1, 2, 3}, []int{1, 2, 4} +// real-world test would inject *testing.T from TestElementsMatch(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -190,14 +249,44 @@ the number of appearances of each of them in both lists should match. {{% tab title="Usage" %}} ```go assertions.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) + success: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} + failure: []int{1, 2, 3}, []int{1, 2, 4} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} - failure: []int{1, 2, 3}, []int{1, 2, 4} +// real-world test would inject *testing.T from TestElementsMatchT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -218,7 +307,7 @@ the number of appearances of each of them in both lists should match. {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.ElementsMatchT(t T, listA []E, listB []E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#ElementsMatchT) | internal implementation | +| [`assertions.ElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#ElementsMatchT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#ElementsMatchT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L613) {{% /tab %}} @@ -241,14 +330,44 @@ See also [reflect.Len](https://pkg.go.dev/reflect#Len). assertions.Len(t, mySlice, 3) assertions.Len(t, myString, 4) assertions.Len(t, myMap, 5) + success: []string{"A","B"}, 2 + failure: []string{"A","B"}, 1 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []string{"A","B"}, 2 - failure: []string{"A","B"}, 1 +// real-world test would inject *testing.T from TestLen(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Len(t, []string{"A", "B"}, 2) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -279,7 +398,7 @@ See also [reflect.Len](https://pkg.go.dev/reflect#Len). > **Note** > -> (proposals) this does not currently support iterators, or collection objects that have a Len() method. +> (proposal for enhancement) this does not currently support iterators, or collection objects that have a Len() method. > {{% /tab %}} {{< /tabs >}} @@ -295,14 +414,44 @@ Go native comparable types are explained there: [comparable-types](https://go.de {{% tab title="Usage" %}} ```go assertions.MapContainsT(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"Hello": "x","World": "y"}, "World") + success: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"A": "B"}, "A" + failure: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"A": "B"}, "C" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"A": "B"}, "A" - failure: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"A": "B"}, "C" +// real-world test would inject *testing.T from TestMapContainsT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.MapContainsT(t, map[string]string{"A": "B"}, "A") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -323,7 +472,7 @@ Go native comparable types are explained there: [comparable-types](https://go.de {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.MapContainsT(t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#MapContainsT) | internal implementation | +| [`assertions.MapContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#MapContainsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#MapContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L179) {{% /tab %}} @@ -338,14 +487,44 @@ MapNotContainsT asserts that the specified map does not contain a key. {{% tab title="Usage" %}} ```go assertions.MapNotContainsT(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"Hello": "x","World": "y"}, "hi") + success: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"A": "B"}, "C" + failure: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"A": "B"}, "A" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"A": "B"}, "C" - failure: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"A": "B"}, "A" +// real-world test would inject *testing.T from TestMapNotContainsT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.MapNotContainsT(t, map[string]string{"A": "B"}, "C") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -366,7 +545,7 @@ MapNotContainsT asserts that the specified map does not contain a key. {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.MapNotContainsT(t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#MapNotContainsT) | internal implementation | +| [`assertions.MapNotContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#MapNotContainsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#MapNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L310) {{% /tab %}} @@ -384,14 +563,44 @@ specified substring or element. assertions.NotContains(t, "Hello World", "Earth") assertions.NotContains(t, ["Hello", "World"], "Earth") assertions.NotContains(t, {"Hello": "World"}, "Earth") + success: []string{"A","B"}, "C" + failure: []string{"A","B"}, "B" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []string{"A","B"}, "C" - failure: []string{"A","B"}, "B" +// real-world test would inject *testing.T from TestNotContains(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotContains(t, []string{"A", "B"}, "C") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -436,14 +645,44 @@ This is an inverse of ElementsMatch. assertions.NotElementsMatch(t, []int{1, 1, 2, 3}, []int{1, 1, 2, 3}) -> false assertions.NotElementsMatch(t, []int{1, 1, 2, 3}, []int{1, 2, 3}) -> true assertions.NotElementsMatch(t, []int{1, 2, 3}, []int{1, 2, 4}) -> true + success: []int{1, 2, 3}, []int{1, 2, 4} + failure: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{1, 2, 3}, []int{1, 2, 4} - failure: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} +// real-world test would inject *testing.T from TestNotElementsMatch(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotElementsMatch(t, []int{1, 2, 3}, []int{1, 2, 4}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -488,14 +727,44 @@ This is an inverse of ElementsMatch. assertions.NotElementsMatchT(t, []int{1, 1, 2, 3}, []int{1, 1, 2, 3}) -> false assertions.NotElementsMatchT(t, []int{1, 1, 2, 3}, []int{1, 2, 3}) -> true assertions.NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4}) -> true + success: []int{1, 2, 3}, []int{1, 2, 4} + failure: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{1, 2, 3}, []int{1, 2, 4} - failure: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} +// real-world test would inject *testing.T from TestNotElementsMatchT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -516,7 +785,7 @@ This is an inverse of ElementsMatch. {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.NotElementsMatchT(t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatchT) | internal implementation | +| [`assertions.NotElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatchT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatchT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L649) {{% /tab %}} @@ -537,14 +806,44 @@ only the map key is evaluated. assertions.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) assertions.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"}) assertions.NotSubset(t, {"x": 1, "y": 2}, ["z"]) + success: []int{1, 2, 3}, []int{4, 5} + failure: []int{1, 2, 3}, []int{1, 2} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{1, 2, 3}, []int{4, 5} - failure: []int{1, 2, 3}, []int{1, 2} +// real-world test would inject *testing.T from TestNotSubset(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotSubset(t, []int{1, 2, 3}, []int{4, 5}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -588,14 +887,45 @@ Go native comparable types are explained there: [comparable-types](https://go.de {{% tab title="Usage" %}} ```go assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World") + success: slices.Values([]string{"A","B"}), "A" + failure: slices.Values([]string{"A","B"}), "C" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: slices.Values([]string{"A","B"}), "A" - failure: slices.Values([]string{"A","B"}), "C" +// real-world test would inject *testing.T from TestSeqContainsT(t *testing.T) +package main + +import ( + "fmt" + "slices" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.SeqContainsT(t, slices.Values([]string{"A", "B"}), "A") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -616,7 +946,7 @@ Go native comparable types are explained there: [comparable-types](https://go.de {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.SeqContainsT(t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SeqContainsT) | internal implementation | +| [`assertions.SeqContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SeqContainsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#SeqContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L150) {{% /tab %}} @@ -633,14 +963,45 @@ See [SeqContainsT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Se {{% tab title="Usage" %}} ```go assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World") + success: slices.Values([]string{"A","B"}), "C" + failure: slices.Values([]string{"A","B"}), "A" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: slices.Values([]string{"A","B"}), "C" - failure: slices.Values([]string{"A","B"}), "A" +// real-world test would inject *testing.T from TestSeqNotContainsT(t *testing.T) +package main + +import ( + "fmt" + "slices" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.SeqNotContainsT(t, slices.Values([]string{"A", "B"}), "C") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -661,7 +1022,7 @@ See [SeqContainsT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Se {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.SeqNotContainsT(t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SeqNotContainsT) | internal implementation | +| [`assertions.SeqNotContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SeqNotContainsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#SeqNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L285) {{% /tab %}} @@ -678,14 +1039,44 @@ Go native comparable types are explained there: [comparable-types](https://go.de {{% tab title="Usage" %}} ```go assertions.SliceContainsT(t, []{"Hello","World"}, "World") + success: []string{"A","B"}, "A" + failure: []string{"A","B"}, "C" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []string{"A","B"}, "A" - failure: []string{"A","B"}, "C" +// real-world test would inject *testing.T from TestSliceContainsT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.SliceContainsT(t, []string{"A", "B"}, "A") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -706,7 +1097,7 @@ Go native comparable types are explained there: [comparable-types](https://go.de {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.SliceContainsT(t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceContainsT) | internal implementation | +| [`assertions.SliceContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceContainsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L121) {{% /tab %}} @@ -723,14 +1114,44 @@ See [SliceContainsT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert# {{% tab title="Usage" %}} ```go assertions.SliceNotContainsT(t, []{"Hello","World"}, "hi") + success: []string{"A","B"}, "C" + failure: []string{"A","B"}, "A" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []string{"A","B"}, "C" - failure: []string{"A","B"}, "A" +// real-world test would inject *testing.T from TestSliceNotContainsT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.SliceNotContainsT(t, []string{"A", "B"}, "C") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -751,7 +1172,7 @@ See [SliceContainsT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert# {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.SliceNotContainsT(t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceNotContainsT) | internal implementation | +| [`assertions.SliceNotContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceNotContainsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L260) {{% /tab %}} @@ -766,14 +1187,44 @@ SliceNotSubsetT asserts that a slice of comparable elements does not contain all {{% tab title="Usage" %}} ```go assertions.SliceNotSubsetT(t, []int{1, 2, 3}, []int{1, 4}) + success: []int{1, 2, 3}, []int{4, 5} + failure: []int{1, 2, 3}, []int{1, 2} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{1, 2, 3}, []int{4, 5} - failure: []int{1, 2, 3}, []int{1, 2} +// real-world test would inject *testing.T from TestSliceNotSubsetT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.SliceNotSubsetT(t, []int{1, 2, 3}, []int{4, 5}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -794,7 +1245,7 @@ SliceNotSubsetT asserts that a slice of comparable elements does not contain all {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.SliceNotSubsetT(t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceNotSubsetT) | internal implementation | +| [`assertions.SliceNotSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceNotSubsetT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceNotSubsetT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L513) {{% /tab %}} @@ -809,14 +1260,44 @@ SliceSubsetT asserts that a slice of comparable elements contains all the elemen {{% tab title="Usage" %}} ```go assertions.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) + success: []int{1, 2, 3}, []int{1, 2} + failure: []int{1, 2, 3}, []int{4, 5} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{1, 2, 3}, []int{1, 2} - failure: []int{1, 2, 3}, []int{4, 5} +// real-world test would inject *testing.T from TestSliceSubsetT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -837,7 +1318,7 @@ SliceSubsetT asserts that a slice of comparable elements contains all the elemen {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.SliceSubsetT(t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceSubsetT) | internal implementation | +| [`assertions.SliceSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceSubsetT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceSubsetT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L415) {{% /tab %}} @@ -854,14 +1335,44 @@ Strings may be go strings or []byte according to the type constraint [Text](http {{% tab title="Usage" %}} ```go assertions.StringContainsT(t, "Hello World", "World") + success: "AB", "A" + failure: "AB", "C" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: "AB", "A" - failure: "AB", "C" +// real-world test would inject *testing.T from TestStringContainsT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.StringContainsT(t, "AB", "A") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -882,7 +1393,7 @@ Strings may be go strings or []byte according to the type constraint [Text](http {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.StringContainsT(t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#StringContainsT) | internal implementation | +| [`assertions.StringContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#StringContainsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#StringContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L94) {{% /tab %}} @@ -899,14 +1410,44 @@ See [StringContainsT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert {{% tab title="Usage" %}} ```go assertions.StringNotContainsT(t, "Hello World", "hi") + success: "AB", "C" + failure: "AB", "A" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: "AB", "C" - failure: "AB", "A" +// real-world test would inject *testing.T from TestStringNotContainsT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.StringNotContainsT(t, "AB", "C") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -927,7 +1468,7 @@ See [StringContainsT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.StringNotContainsT(t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#StringNotContainsT) | internal implementation | +| [`assertions.StringNotContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#StringNotContainsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#StringNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L235) {{% /tab %}} @@ -951,14 +1492,44 @@ nil values are considered as empty sets. assertions.Subset(t, []string{"x": 1, "y": 2}, []string{"x": 1}) assertions.Subset(t, []int{1, 2, 3}, map[int](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#int)string{1: "one", 2: "two"}) assertions.Subset(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)int{"x": 1, "y": 2}, []string{"x"}) + success: []int{1, 2, 3}, []int{1, 2} + failure: []int{1, 2, 3}, []int{4, 5} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{1, 2, 3}, []int{1, 2} - failure: []int{1, 2, 3}, []int{4, 5} +// real-world test would inject *testing.T from TestSubset(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Subset(t, []int{1, 2, 3}, []int{1, 2}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -1004,6 +1575,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/common.md b/docs/doc-site/api/common.md index 70c0da4f2..d494fc6e5 100644 --- a/docs/doc-site/api/common.md +++ b/docs/doc-site/api/common.md @@ -1,8 +1,7 @@ --- title: "Common" description: "Other Uncategorized Helpers" -modified: 2026-01-27 -weight: 18 +weight: 19 domains: - "common" keywords: @@ -133,6 +132,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/comparison.md b/docs/doc-site/api/comparison.md index 311e29896..0972f5c08 100644 --- a/docs/doc-site/api/comparison.md +++ b/docs/doc-site/api/comparison.md @@ -1,7 +1,6 @@ --- title: "Comparison" description: "Comparing Ordered Values" -modified: 2026-01-27 weight: 3 domains: - "comparison" @@ -73,14 +72,44 @@ To compare values that need a type conversion (e.g. float32 against float64), yo assertions.Greater(t, 2, 1) assertions.Greater(t, float64(2), float64(1)) assertions.Greater(t, "b", "a") + success: 2, 1 + failure: 1, 2 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 2, 1 - failure: 1, 2 +// real-world test would inject *testing.T from TestGreater(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Greater(t, 2, 1) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -125,14 +154,44 @@ See also [Greater](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Gr assertions.GreaterOrEqual(t, 2, 2) assertions.GreaterOrEqual(t, "b", "a") assertions.GreaterOrEqual(t, "b", "b") + success: 2, 1 + failure: 1, 2 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 2, 1 - failure: 1, 2 +// real-world test would inject *testing.T from TestGreaterOrEqual(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.GreaterOrEqual(t, 2, 1) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -186,14 +245,44 @@ To compare values that need a type conversion (e.g. float32 against float64), yo assertions.GreaterOrEqualT(t, 2, 2) assertions.GreaterOrEqualT(t, "b", "a") assertions.GreaterOrEqualT(t, "b", "b") + success: 2, 1 + failure: 1, 2 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 2, 1 - failure: 1, 2 +// real-world test would inject *testing.T from TestGreaterOrEqualT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.GreaterOrEqualT(t, 2, 1) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -214,7 +303,7 @@ To compare values that need a type conversion (e.g. float32 against float64), yo {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.GreaterOrEqualT(t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#GreaterOrEqualT) | internal implementation | +| [`assertions.GreaterOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#GreaterOrEqualT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#GreaterOrEqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L124) {{% /tab %}} @@ -242,14 +331,44 @@ To compare values that need a type conversion (e.g. float32 against float64), yo assertions.GreaterT(t, float64(2), float64(1)) assertions.GreaterT(t, "b", "a") assertions.GreaterT(t, time.Date(2026,1,1,0,0,0,0,nil), time.Now()) + success: 2, 1 + failure: 1, 2 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 2, 1 - failure: 1, 2 +// real-world test would inject *testing.T from TestGreaterT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.GreaterT(t, 2, 1) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -270,7 +389,7 @@ To compare values that need a type conversion (e.g. float32 against float64), yo {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.GreaterT(t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#GreaterT) | internal implementation | +| [`assertions.GreaterT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#GreaterT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#GreaterT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L62) {{% /tab %}} @@ -290,14 +409,44 @@ To compare values that need a type conversion (e.g. float32 against float64), yo assertions.Less(t, 1, 2) assertions.Less(t, float64(1), float64(2)) assertions.Less(t, "a", "b") + success: 1, 2 + failure: 2, 1 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 1, 2 - failure: 2, 1 +// real-world test would inject *testing.T from TestLess(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Less(t, 1, 2) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -340,14 +489,44 @@ LessOrEqual asserts that the first element is less than or equal to the second. assertions.LessOrEqual(t, 2, 2) assertions.LessOrEqual(t, "a", "b") assertions.LessOrEqual(t, "b", "b") + success: 1, 2 + failure: 2, 1 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 1, 2 - failure: 2, 1 +// real-world test would inject *testing.T from TestLessOrEqual(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.LessOrEqual(t, 1, 2) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -400,14 +579,44 @@ To compare values that need a type conversion (e.g. float32 against float64), yo assertions.LessOrEqualT(t, 2, 2) assertions.LessOrEqualT(t, "a", "b") assertions.LessOrEqualT(t, "b", "b") + success: 1, 2 + failure: 2, 1 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 1, 2 - failure: 2, 1 +// real-world test would inject *testing.T from TestLessOrEqualT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.LessOrEqualT(t, 1, 2) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -428,7 +637,7 @@ To compare values that need a type conversion (e.g. float32 against float64), yo {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.LessOrEqualT(t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#LessOrEqualT) | internal implementation | +| [`assertions.LessOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#LessOrEqualT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#LessOrEqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L243) {{% /tab %}} @@ -455,14 +664,44 @@ To compare values that need a type conversion (e.g. float32 against float64), yo assertions.LessT(t, 1, 2) assertions.LessT(t, float64(1), float64(2)) assertions.LessT(t, "a", "b") + success: 1, 2 + failure: 2, 1 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 1, 2 - failure: 2, 1 +// real-world test would inject *testing.T from TestLessT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.LessT(t, 1, 2) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -483,7 +722,7 @@ To compare values that need a type conversion (e.g. float32 against float64), yo {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.LessT(t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#LessT) | internal implementation | +| [`assertions.LessT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#LessT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#LessT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L184) {{% /tab %}} @@ -499,14 +738,44 @@ Negative asserts that the specified element is strictly negative. ```go assertions.Negative(t, -1) assertions.Negative(t, -1.23) + success: -1 + failure: 1 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: -1 - failure: 1 +// real-world test would inject *testing.T from TestNegative(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Negative(t, -1) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -547,14 +816,44 @@ NegativeT asserts that the specified element of a signed numeric type is strictl ```go assertions.NegativeT(t, -1) assertions.NegativeT(t, -1.23) + success: -1 + failure: 1 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: -1 - failure: 1 +// real-world test would inject *testing.T from TestNegativeT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NegativeT(t, -1) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -575,7 +874,7 @@ NegativeT asserts that the specified element of a signed numeric type is strictl {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.NegativeT(t T, e SignedNumber, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NegativeT) | internal implementation | +| [`assertions.NegativeT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NegativeT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NegativeT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L335) {{% /tab %}} @@ -591,14 +890,44 @@ Positive asserts that the specified element is strictly positive. ```go assertions.Positive(t, 1) assertions.Positive(t, 1.23) + success: 1 + failure: -1 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 1 - failure: -1 +// real-world test would inject *testing.T from TestPositive(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Positive(t, 1) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -639,14 +968,44 @@ PositiveT asserts that the specified element of a signed numeric type is strictl ```go assertions.PositiveT(t, 1) assertions.PositiveT(t, 1.23) + success: 1 + failure: -1 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 1 - failure: -1 +// real-world test would inject *testing.T from TestPositiveT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.PositiveT(t, 1) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -667,7 +1026,7 @@ PositiveT asserts that the specified element of a signed numeric type is strictl {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.PositiveT(t T, e SignedNumber, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#PositiveT) | internal implementation | +| [`assertions.PositiveT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#PositiveT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#PositiveT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L289) {{% /tab %}} @@ -688,6 +1047,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/condition.md b/docs/doc-site/api/condition.md index 2a93a5e21..a2c6c461b 100644 --- a/docs/doc-site/api/condition.md +++ b/docs/doc-site/api/condition.md @@ -1,7 +1,6 @@ --- title: "Condition" description: "Expressing Assertions Using Conditions" -modified: 2026-01-27 weight: 4 domains: - "condition" @@ -10,8 +9,8 @@ keywords: - "Conditionf" - "Eventually" - "Eventuallyf" - - "EventuallyWithT" - - "EventuallyWithTf" + - "EventuallyWith" + - "EventuallyWithf" - "Never" - "Neverf" --- @@ -30,7 +29,7 @@ This domain exposes 4 functionalities. ```tree - [Condition](#condition) | angles-right - [Eventually](#eventually) | angles-right -- [EventuallyWithT](#eventuallywitht) | angles-right +- [EventuallyWith](#eventuallywith) | angles-right - [Never](#never) | angles-right ``` @@ -43,14 +42,46 @@ Condition uses a [Comparison](https://pkg.go.dev/github.com/go-openapi/testify/v {{% tab title="Usage" %}} ```go assertions.Condition(t, func() bool { return myCondition }) + success: func() bool { return true } + failure: func() bool { return false } ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: func() bool { return true } - failure: func() bool { return false } +// real-world test would inject *testing.T from TestCondition(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Condition(t, func() bool { + return true + }) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -107,14 +138,47 @@ A blocking condition will cause [Eventually](https://pkg.go.dev/github.com/go-op {{% tab title="Usage" %}} ```go assertions.Eventually(t, func() bool { return true }, time.Second, 10*time.Millisecond) + success: func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond + failure: func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond - failure: func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond +// real-world test would inject *testing.T from TestEventually(t *testing.T) +package main + +import ( + "fmt" + "testing" + "time" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Eventually(t, func() bool { + return true + }, 100*time.Millisecond, 20*time.Millisecond) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -145,9 +209,9 @@ A blocking condition will cause [Eventually](https://pkg.go.dev/github.com/go-op {{% /tab %}} {{< /tabs >}} -### EventuallyWithT{#eventuallywitht} +### EventuallyWith{#eventuallywith} -EventuallyWithT asserts that the given condition will be met in waitFor time, +EventuallyWith asserts that the given condition will be met in waitFor time, periodically checking the target function at each tick. In contrast to [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually), the condition function is supplied with a [CollectT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#CollectT) @@ -175,7 +239,7 @@ It may write to variables outside its scope without triggering race conditions. time.Sleep(8*time.Second) externalValue = true }() - assertions.EventuallyWithT(t, func(c *assertions.CollectT) { + assertions.EventuallyWith(t, func(c *assertions.CollectT) { // add assertions as needed; any assertion failure will fail the current tick assertions.True(c, externalValue, "expected 'externalValue' to be true") }, @@ -183,14 +247,48 @@ It may write to variables outside its scope without triggering race conditions. 1*time.Second, "external state has not changed to 'true'; still false", ) + success: func(c *CollectT) { True(c,true) }, 100*time.Millisecond, 20*time.Millisecond + failure: func(c *CollectT) { False(c,true) }, 100*time.Millisecond, 20*time.Millisecond ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: func(c *CollectT) { True(c,true) }, 100*time.Millisecond, 20*time.Millisecond - failure: func(c *CollectT) { False(c,true) }, 100*time.Millisecond, 20*time.Millisecond +// real-world test would inject *testing.T from TestEventuallyWith(t *testing.T) +package main + +import ( + "fmt" + "testing" + "time" + + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.EventuallyWith(t, func(c *assert.CollectT) { + assert.True(c, true) + }, 100*time.Millisecond, 20*time.Millisecond) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -198,26 +296,26 @@ It may write to variables outside its scope without triggering race conditions. {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| -| [`assert.EventuallyWithT(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EventuallyWithT) | package-level function | -| [`assert.EventuallyWithTf(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EventuallyWithTf) | formatted variant | -| [`assert.(*Assertions).EventuallyWithT(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.EventuallyWithT) | method variant | -| [`assert.(*Assertions).EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.EventuallyWithTf) | method formatted variant | +| [`assert.EventuallyWith(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EventuallyWith) | package-level function | +| [`assert.EventuallyWithf(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EventuallyWithf) | formatted variant | +| [`assert.(*Assertions).EventuallyWith(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.EventuallyWith) | method variant | +| [`assert.(*Assertions).EventuallyWithf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.EventuallyWithf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| -| [`require.EventuallyWithT(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EventuallyWithT) | package-level function | -| [`require.EventuallyWithTf(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EventuallyWithTf) | formatted variant | -| [`require.(*Assertions).EventuallyWithT(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.EventuallyWithT) | method variant | -| [`require.(*Assertions).EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.EventuallyWithTf) | method formatted variant | +| [`require.EventuallyWith(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EventuallyWith) | package-level function | +| [`require.EventuallyWithf(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EventuallyWithf) | formatted variant | +| [`require.(*Assertions).EventuallyWith(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.EventuallyWith) | method variant | +| [`require.(*Assertions).EventuallyWithf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.EventuallyWithf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.EventuallyWithT(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#EventuallyWithT) | internal implementation | +| [`assertions.EventuallyWith(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#EventuallyWith) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#EventuallyWithT](https://github.com/go-openapi/testify/blob/master/internal/assertions/condition.go#L148) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#EventuallyWith](https://github.com/go-openapi/testify/blob/master/internal/assertions/condition.go#L148) {{% /tab %}} {{< /tabs >}} @@ -243,14 +341,47 @@ A blocking condition will cause [Never](https://pkg.go.dev/github.com/go-openapi {{% tab title="Usage" %}} ```go assertions.Never(t, func() bool { return false }, time.Second, 10*time.Millisecond) + success: func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond + failure: func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond - failure: func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond +// real-world test would inject *testing.T from TestNever(t *testing.T) +package main + +import ( + "fmt" + "testing" + "time" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Never(t, func() bool { + return false + }, 100*time.Millisecond, 20*time.Millisecond) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -296,6 +427,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/equality.md b/docs/doc-site/api/equality.md index 3ed5c6b21..a8604fb42 100644 --- a/docs/doc-site/api/equality.md +++ b/docs/doc-site/api/equality.md @@ -1,7 +1,6 @@ --- title: "Equality" description: "Asserting Two Things Are Equal" -modified: 2026-01-27 weight: 5 domains: - "equality" @@ -90,14 +89,44 @@ Pointer values are "empty" if the pointer is nil or if the pointed value is "emp {{% tab title="Usage" %}} ```go assertions.Empty(t, obj) + success: "" + failure: "not empty" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: "" - failure: "not empty" +// real-world test would inject *testing.T from TestEmpty(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Empty(t, "") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -142,14 +171,44 @@ Function equality cannot be determined and will always fail. {{% tab title="Usage" %}} ```go assertions.Equal(t, 123, 123) + success: 123, 123 + failure: 123, 456 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 123, 123 - failure: 123, 456 +// real-world test would inject *testing.T from TestEqual(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Equal(t, 123, 123) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -199,14 +258,49 @@ Function equality cannot be determined and will always fail. } assertions.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true assertions.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false + success: &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2} + failure: &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "b", b: 1} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2} - failure: &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "b", b: 1} +// real-world test would inject *testing.T from TestEqualExportedValues(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.EqualExportedValues(t, &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2}) + fmt.Println("passed") + +} + +type dummyStruct struct { + A string + b int +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -253,14 +347,44 @@ use [Equal](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Equal) in {{% tab title="Usage" %}} ```go assertions.EqualT(t, 123, 123) + success: 123, 123 + failure: 123, 456 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 123, 123 - failure: 123, 456 +// real-world test would inject *testing.T from TestEqualT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.EqualT(t, 123, 123) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -281,7 +405,7 @@ use [Equal](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Equal) in {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.EqualT(t T, expected V, actual V, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#EqualT) | internal implementation | +| [`assertions.EqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#EqualT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#EqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L67) {{% /tab %}} @@ -299,14 +423,44 @@ Function equality cannot be determined and will always fail. {{% tab title="Usage" %}} ```go assertions.EqualValues(t, uint32(123), int32(123)) + success: uint32(123), int32(123) + failure: uint32(123), int32(456) ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: uint32(123), int32(123) - failure: uint32(123), int32(456) +// real-world test would inject *testing.T from TestEqualValues(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.EqualValues(t, uint32(123), int32(123)) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -346,14 +500,44 @@ Exactly asserts that two objects are equal in value and type. {{% tab title="Usage" %}} ```go assertions.Exactly(t, int32(123), int64(123)) + success: int32(123), int32(123) + failure: int32(123), int64(123) ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: int32(123), int32(123) - failure: int32(123), int64(123) +// real-world test would inject *testing.T from TestExactly(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Exactly(t, int32(123), int32(123)) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -393,14 +577,44 @@ Nil asserts that the specified object is nil. {{% tab title="Usage" %}} ```go assertions.Nil(t, err) + success: nil + failure: "not nil" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: nil - failure: "not nil" +// real-world test would inject *testing.T from TestNil(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Nil(t, nil) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -442,14 +656,44 @@ NotEmpty asserts that the specified object is NOT [Empty](https://pkg.go.dev/git if assert.NotEmpty(t, obj) { assertions.Equal(t, "two", obj[1](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#1)) } + success: "not empty" + failure: "" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: "not empty" - failure: "" +// real-world test would inject *testing.T from TestNotEmpty(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotEmpty(t, "not empty") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -492,14 +736,44 @@ NotEqual asserts that the specified values are NOT equal. Pointer variable equality is determined based on the equality of the referenced values (as opposed to the memory addresses). Function equality cannot be determined and will always fail. + success: 123, 456 + failure: 123, 123 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 123, 456 - failure: 123, 123 +// real-world test would inject *testing.T from TestNotEqual(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotEqual(t, 123, 456) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -541,14 +815,44 @@ See [EqualT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualT). {{% tab title="Usage" %}} ```go assertions.NotEqualT(t, obj1, obj2) + success: 123, 456 + failure: 123, 123 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 123, 456 - failure: 123, 123 +// real-world test would inject *testing.T from TestNotEqualT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotEqualT(t, 123, 456) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -569,7 +873,7 @@ See [EqualT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualT). {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.NotEqualT(t T, expected V, actual V, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotEqualT) | internal implementation | +| [`assertions.NotEqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotEqualT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotEqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L120) {{% /tab %}} @@ -586,14 +890,44 @@ Function equality cannot be determined and will always fail. {{% tab title="Usage" %}} ```go assertions.NotEqualValues(t, obj1, obj2) + success: uint32(123), int32(456) + failure: uint32(123), int32(123) ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: uint32(123), int32(456) - failure: uint32(123), int32(123) +// real-world test would inject *testing.T from TestNotEqualValues(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotEqualValues(t, uint32(123), int32(456)) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -633,14 +967,44 @@ NotNil asserts that the specified object is not nil. {{% tab title="Usage" %}} ```go assertions.NotNil(t, err) + success: "not nil" + failure: nil ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: "not nil" - failure: nil +// real-world test would inject *testing.T from TestNotNil(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotNil(t, "not nil") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -682,14 +1046,52 @@ See [Same](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Same). {{% tab title="Usage" %}} ```go assertions.NotSame(t, ptr1, ptr2) + success: &staticVar, ptr("static string") + failure: &staticVar, staticVarPtr ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: &staticVar, ptr("static string") - failure: &staticVar, staticVarPtr +// real-world test would inject *testing.T from TestNotSame(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotSame(t, &staticVar, ptr("static string")) + fmt.Println("passed") + +} + +var staticVar = "static string" + +func ptr[T any](value T) *T { + p := value + + return &p +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -731,14 +1133,52 @@ See [SameT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SameT). {{% tab title="Usage" %}} ```go assertions.NotSameT(t, ptr1, ptr2) + success: &staticVar, ptr("static string") + failure: &staticVar, staticVarPtr ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: &staticVar, ptr("static string") - failure: &staticVar, staticVarPtr +// real-world test would inject *testing.T from TestNotSameT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotSameT(t, &staticVar, ptr("static string")) + fmt.Println("passed") + +} + +var staticVar = "static string" + +func ptr[T any](value T) *T { + p := value + + return &p +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -759,7 +1199,7 @@ See [SameT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SameT). {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.NotSameT(t T, expected *P, actual *P, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotSameT) | internal implementation | +| [`assertions.NotSameT[P any](t T, expected *P, actual *P, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotSameT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSameT](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_pointer.go#L116) {{% /tab %}} @@ -780,14 +1220,49 @@ Unlike [Equal](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Equal) {{% tab title="Usage" %}} ```go assertions.Same(t, ptr1, ptr2) + success: &staticVar, staticVarPtr + failure: &staticVar, ptr("static string") ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: &staticVar, staticVarPtr - failure: &staticVar, ptr("static string") +// real-world test would inject *testing.T from TestSame(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Same(t, &staticVar, staticVarPtr) + fmt.Println("passed") + +} + +var ( + staticVar = "static string" + staticVarPtr = &staticVar +) + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -829,14 +1304,49 @@ See [Same](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Same). {{% tab title="Usage" %}} ```go assertions.SameT(t, ptr1, ptr2) + success: &staticVar, staticVarPtr + failure: &staticVar, ptr("static string") ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: &staticVar, staticVarPtr - failure: &staticVar, ptr("static string") +// real-world test would inject *testing.T from TestSameT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.SameT(t, &staticVar, staticVarPtr) + fmt.Println("passed") + +} + +var ( + staticVar = "static string" + staticVarPtr = &staticVar +) + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -857,7 +1367,7 @@ See [Same](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Same). {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.SameT(t T, expected *P, actual *P, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SameT) | internal implementation | +| [`assertions.SameT[P any](t T, expected *P, actual *P, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SameT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#SameT](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_pointer.go#L57) {{% /tab %}} @@ -878,6 +1388,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/error.md b/docs/doc-site/api/error.md index 4d60c4f9b..3bd4dfeb1 100644 --- a/docs/doc-site/api/error.md +++ b/docs/doc-site/api/error.md @@ -1,7 +1,6 @@ --- title: "Error" description: "Asserting Errors" -modified: 2026-01-27 weight: 6 domains: - "error" @@ -57,14 +56,44 @@ and that it is equal to the provided error. ```go actualObj, err := SomeFunction() assertions.EqualError(t, err, expectedErrorString) + success: ErrTest, "assert.ErrTest general error for testing" + failure: ErrTest, "wrong error message" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: ErrTest, "assert.ErrTest general error for testing" - failure: ErrTest, "wrong error message" +// real-world test would inject *testing.T from TestEqualError(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.EqualError(t, require.ErrTest, "assert.ErrTest general error for testing") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -91,7 +120,7 @@ and that it is equal to the provided error. |--|--| | [`assertions.EqualError(t T, err error, errString string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#EqualError) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#EqualError](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L87) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#EqualError](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L89) {{% /tab %}} {{< /tabs >}} @@ -105,14 +134,44 @@ Error asserts that a function returned a non-nil error (ie. an error). ```go actualObj, err := SomeFunction() assertions.Error(t, err) + success: ErrTest + failure: nil ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: ErrTest - failure: nil +// real-world test would inject *testing.T from TestError(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Error(t, require.ErrTest) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -139,7 +198,7 @@ Error asserts that a function returned a non-nil error (ie. an error). |--|--| | [`assertions.Error(t T, err error, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Error) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Error](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L63) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Error](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L65) {{% /tab %}} {{< /tabs >}} @@ -154,14 +213,51 @@ This is a wrapper for [errors.As](https://pkg.go.dev/errors#As). {{% tab title="Usage" %}} ```go assertions.ErrorAs(t, err, &target) + success: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) + failure: ErrTest, new(*dummyError) ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) - failure: ErrTest, new(*dummyError) +// real-world test would inject *testing.T from TestErrorAs(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.ErrorAs(t, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) + fmt.Println("passed") + +} + +type dummyError struct { +} + +func (d *dummyError) Error() string { + return "dummy error" +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -188,7 +284,7 @@ This is a wrapper for [errors.As](https://pkg.go.dev/errors#As). |--|--| | [`assertions.ErrorAs(t T, err error, target any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#ErrorAs) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ErrorAs](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L218) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ErrorAs](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L220) {{% /tab %}} {{< /tabs >}} @@ -203,14 +299,44 @@ error) and that the error contains the specified substring. ```go actualObj, err := SomeFunction() assertions.ErrorContains(t, err, expectedErrorSubString) + success: ErrTest, "general error" + failure: ErrTest, "not in message" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: ErrTest, "general error" - failure: ErrTest, "not in message" +// real-world test would inject *testing.T from TestErrorContains(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.ErrorContains(t, require.ErrTest, "general error") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -237,7 +363,7 @@ error) and that the error contains the specified substring. |--|--| | [`assertions.ErrorContains(t T, err error, contains string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#ErrorContains) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ErrorContains](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L118) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ErrorContains](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L120) {{% /tab %}} {{< /tabs >}} @@ -252,14 +378,45 @@ This is a wrapper for [errors.Is](https://pkg.go.dev/errors#Is). {{% tab title="Usage" %}} ```go assertions.ErrorIs(t, err, io.EOF) + success: fmt.Errorf("wrap: %w", io.EOF), io.EOF + failure: ErrTest, io.EOF ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: fmt.Errorf("wrap: %w", io.EOF), io.EOF - failure: ErrTest, io.EOF +// real-world test would inject *testing.T from TestErrorIs(t *testing.T) +package main + +import ( + "fmt" + "io" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.ErrorIs(t, fmt.Errorf("wrap: %w", io.EOF), io.EOF) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -286,7 +443,7 @@ This is a wrapper for [errors.Is](https://pkg.go.dev/errors#Is). |--|--| | [`assertions.ErrorIs(t T, err error, target error, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#ErrorIs) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ErrorIs](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L147) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ErrorIs](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L149) {{% /tab %}} {{< /tabs >}} @@ -302,14 +459,44 @@ NoError asserts that a function returned a nil error (ie. no error). if assert.NoError(t, err) { assertions.Equal(t, expectedObj, actualObj) } + success: nil + failure: ErrTest ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: nil - failure: ErrTest +// real-world test would inject *testing.T from TestNoError(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NoError(t, nil) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -336,7 +523,7 @@ NoError asserts that a function returned a nil error (ie. no error). |--|--| | [`assertions.NoError(t T, err error, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NoError) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NoError](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L40) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NoError](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L42) {{% /tab %}} {{< /tabs >}} @@ -350,14 +537,51 @@ but if so, sets target to that error value. {{% tab title="Usage" %}} ```go assertions.NotErrorAs(t, err, &target) + success: ErrTest, new(*dummyError) + failure: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: ErrTest, new(*dummyError) - failure: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) +// real-world test would inject *testing.T from TestNotErrorAs(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotErrorAs(t, require.ErrTest, new(*dummyError)) + fmt.Println("passed") + +} + +type dummyError struct { +} + +func (d *dummyError) Error() string { + return "dummy error" +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -384,7 +608,7 @@ but if so, sets target to that error value. |--|--| | [`assertions.NotErrorAs(t T, err error, target any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotErrorAs) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotErrorAs](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L252) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotErrorAs](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L254) {{% /tab %}} {{< /tabs >}} @@ -399,14 +623,45 @@ This is a wrapper for [errors.Is](https://pkg.go.dev/errors#Is). {{% tab title="Usage" %}} ```go assertions.NotErrorIs(t, err, io.EOF) + success: ErrTest, io.EOF + failure: fmt.Errorf("wrap: %w", io.EOF), io.EOF ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: ErrTest, io.EOF - failure: fmt.Errorf("wrap: %w", io.EOF), io.EOF +// real-world test would inject *testing.T from TestNotErrorIs(t *testing.T) +package main + +import ( + "fmt" + "io" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotErrorIs(t, require.ErrTest, io.EOF) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -433,7 +688,7 @@ This is a wrapper for [errors.Is](https://pkg.go.dev/errors#Is). |--|--| | [`assertions.NotErrorIs(t T, err error, target error, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotErrorIs) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotErrorIs](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L184) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotErrorIs](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L186) {{% /tab %}} {{< /tabs >}} @@ -452,6 +707,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/file.md b/docs/doc-site/api/file.md index 28426a741..fbbb220d4 100644 --- a/docs/doc-site/api/file.md +++ b/docs/doc-site/api/file.md @@ -1,7 +1,6 @@ --- title: "File" description: "Asserting OS Files" -modified: 2026-01-27 weight: 7 domains: - "file" @@ -50,14 +49,49 @@ if the path is a file rather a directory or there is an error checking whether i {{% tab title="Usage" %}} ```go assertions.DirExists(t, "path/to/directory") + success: filepath.Join(testDataPath(),"existing_dir") + failure: filepath.Join(testDataPath(),"non_existing_dir") ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: filepath.Join(testDataPath(),"existing_dir") - failure: filepath.Join(testDataPath(),"non_existing_dir") +// real-world test would inject *testing.T from TestDirExists(t *testing.T) +package main + +import ( + "fmt" + "path/filepath" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.DirExists(t, filepath.Join(testDataPath(), "existing_dir")) + fmt.Println("passed") + +} + +func testDataPath() string { + return filepath.Join("..", "internal", "assertions", "testdata") +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -98,14 +132,49 @@ It fails if the path points to an existing _directory_ only. {{% tab title="Usage" %}} ```go assertions.DirNotExists(t, "path/to/directory") + success: filepath.Join(testDataPath(),"non_existing_dir") + failure: filepath.Join(testDataPath(),"existing_dir") ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: filepath.Join(testDataPath(),"non_existing_dir") - failure: filepath.Join(testDataPath(),"existing_dir") +// real-world test would inject *testing.T from TestDirNotExists(t *testing.T) +package main + +import ( + "fmt" + "path/filepath" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.DirNotExists(t, filepath.Join(testDataPath(), "non_existing_dir")) + fmt.Println("passed") + +} + +func testDataPath() string { + return filepath.Join("..", "internal", "assertions", "testdata") +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -146,14 +215,49 @@ It fails if the file is not empty, if the path points to a directory or there is {{% tab title="Usage" %}} ```go assertions.FileEmpty(t, "path/to/file") + success: filepath.Join(testDataPath(),"empty_file") + failure: filepath.Join(testDataPath(),"existing_file") ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: filepath.Join(testDataPath(),"empty_file") - failure: filepath.Join(testDataPath(),"existing_file") +// real-world test would inject *testing.T from TestFileEmpty(t *testing.T) +package main + +import ( + "fmt" + "path/filepath" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.FileEmpty(t, filepath.Join(testDataPath(), "empty_file")) + fmt.Println("passed") + +} + +func testDataPath() string { + return filepath.Join("..", "internal", "assertions", "testdata") +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -194,14 +298,49 @@ the path points to a directory or there is an error when trying to check the fil {{% tab title="Usage" %}} ```go assertions.FileExists(t, "path/to/file") + success: filepath.Join(testDataPath(),"existing_file") + failure: filepath.Join(testDataPath(),"non_existing_file") ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: filepath.Join(testDataPath(),"existing_file") - failure: filepath.Join(testDataPath(),"non_existing_file") +// real-world test would inject *testing.T from TestFileExists(t *testing.T) +package main + +import ( + "fmt" + "path/filepath" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.FileExists(t, filepath.Join(testDataPath(), "existing_file")) + fmt.Println("passed") + +} + +func testDataPath() string { + return filepath.Join("..", "internal", "assertions", "testdata") +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -242,14 +381,49 @@ It fails if the file is empty, if the path points to a directory or there is an {{% tab title="Usage" %}} ```go assertions.FileNotEmpty(t, "path/to/file") + success: filepath.Join(testDataPath(),"existing_file") + failure: filepath.Join(testDataPath(),"empty_file") ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: filepath.Join(testDataPath(),"existing_file") - failure: filepath.Join(testDataPath(),"empty_file") +// real-world test would inject *testing.T from TestFileNotEmpty(t *testing.T) +package main + +import ( + "fmt" + "path/filepath" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.FileNotEmpty(t, filepath.Join(testDataPath(), "existing_file")) + fmt.Println("passed") + +} + +func testDataPath() string { + return filepath.Join("..", "internal", "assertions", "testdata") +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -290,14 +464,49 @@ if the path points to an existing _file_ only. {{% tab title="Usage" %}} ```go assertions.FileNotExists(t, "path/to/file") + success: filepath.Join(testDataPath(),"non_existing_file") + failure: filepath.Join(testDataPath(),"existing_file") ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: filepath.Join(testDataPath(),"non_existing_file") - failure: filepath.Join(testDataPath(),"existing_file") +// real-world test would inject *testing.T from TestFileNotExists(t *testing.T) +package main + +import ( + "fmt" + "path/filepath" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.FileNotExists(t, filepath.Join(testDataPath(), "non_existing_file")) + fmt.Println("passed") + +} + +func testDataPath() string { + return filepath.Join("..", "internal", "assertions", "testdata") +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -343,6 +552,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/http.md b/docs/doc-site/api/http.md index cb19f4eef..023ad7451 100644 --- a/docs/doc-site/api/http.md +++ b/docs/doc-site/api/http.md @@ -1,7 +1,6 @@ --- title: "Http" description: "Asserting HTTP Response And Body" -modified: 2026-01-27 weight: 8 domains: - "http" @@ -53,14 +52,51 @@ Returns whether the assertion was successful (true) or not (false). {{% tab title="Usage" %}} ```go assertions.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") + success: httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!" + failure: httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, World!" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!" - failure: httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, World!" +// real-world test would inject *testing.T from TestHTTPBodyContains(t *testing.T) +package main + +import ( + "fmt" + "net/http" + "net/url" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.HTTPBodyContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!") + fmt.Println("passed") + +} + +func httpBody(w http.ResponseWriter, r *http.Request) { + name := r.FormValue("name") + _, _ = fmt.Fprintf(w, "Hello, %s!", name) +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -103,14 +139,51 @@ Returns whether the assertion was successful (true) or not (false). {{% tab title="Usage" %}} ```go assertions.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") + success: httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!" + failure: httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, Bob!" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!" - failure: httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, Bob!" +// real-world test would inject *testing.T from TestHTTPBodyNotContains(t *testing.T) +package main + +import ( + "fmt" + "net/http" + "net/url" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.HTTPBodyNotContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!") + fmt.Println("passed") + +} + +func httpBody(w http.ResponseWriter, r *http.Request) { + name := r.FormValue("name") + _, _ = fmt.Fprintf(w, "Hello, %s!", name) +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -152,14 +225,49 @@ Returns whether the assertion was successful (true) or not (false). {{% tab title="Usage" %}} ```go assertions.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} + success: httpError, "GET", "/", nil + failure: httpOK, "GET", "/", nil ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: httpError, "GET", "/", nil - failure: httpOK, "GET", "/", nil +// real-world test would inject *testing.T from TestHTTPError(t *testing.T) +package main + +import ( + "fmt" + "net/http" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.HTTPError(t, httpError, "GET", "/", nil) + fmt.Println("passed") + +} + +func httpError(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusInternalServerError) +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -201,14 +309,49 @@ Returns whether the assertion was successful (true) or not (false). {{% tab title="Usage" %}} ```go assertions.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} + success: httpRedirect, "GET", "/", nil + failure: httpError, "GET", "/", nil ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: httpRedirect, "GET", "/", nil - failure: httpError, "GET", "/", nil +// real-world test would inject *testing.T from TestHTTPRedirect(t *testing.T) +package main + +import ( + "fmt" + "net/http" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.HTTPRedirect(t, httpRedirect, "GET", "/", nil) + fmt.Println("passed") + +} + +func httpRedirect(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusTemporaryRedirect) +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -250,14 +393,49 @@ Returns whether the assertion was successful (true) or not (false). {{% tab title="Usage" %}} ```go assertions.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) + success: httpOK, "GET", "/", nil, http.StatusOK + failure: httpError, "GET", "/", nil, http.StatusOK ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: httpOK, "GET", "/", nil, http.StatusOK - failure: httpError, "GET", "/", nil, http.StatusOK +// real-world test would inject *testing.T from TestHTTPStatusCode(t *testing.T) +package main + +import ( + "fmt" + "net/http" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.HTTPStatusCode(t, httpOK, "GET", "/", nil, http.StatusOK) + fmt.Println("passed") + +} + +func httpOK(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -299,14 +477,49 @@ Returns whether the assertion was successful (true) or not (false). {{% tab title="Usage" %}} ```go assertions.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) + success: httpOK, "GET", "/", nil + failure: httpError, "GET", "/", nil ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: httpOK, "GET", "/", nil - failure: httpError, "GET", "/", nil +// real-world test would inject *testing.T from TestHTTPSuccess(t *testing.T) +package main + +import ( + "fmt" + "net/http" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.HTTPSuccess(t, httpOK, "GET", "/", nil) + fmt.Println("passed") + +} + +func httpOK(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -381,6 +594,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/json.md b/docs/doc-site/api/json.md index 1e51b69e2..35e441056 100644 --- a/docs/doc-site/api/json.md +++ b/docs/doc-site/api/json.md @@ -1,7 +1,6 @@ --- title: "Json" description: "Asserting JSON Documents" -modified: 2026-01-27 weight: 9 domains: - "json" @@ -12,6 +11,10 @@ keywords: - "JSONEqBytesf" - "JSONEqT" - "JSONEqTf" + - "JSONMarshalAsT" + - "JSONMarshalAsTf" + - "JSONUnmarshalAsT" + - "JSONUnmarshalAsTf" --- Asserting JSON Documents @@ -23,13 +26,15 @@ Asserting JSON Documents _All links point to _ -This domain exposes 3 functionalities. +This domain exposes 5 functionalities. Generic assertions are marked with a {{% icon icon="star" color=orange %}}. ```tree - [JSONEq](#jsoneq) | angles-right - [JSONEqBytes](#jsoneqbytes) | angles-right - [JSONEqT[EDoc, ADoc Text]](#jsoneqtedoc-adoc-text) | star | orange +- [JSONMarshalAsT[EDoc Text]](#jsonmarshalastedoc-text) | star | orange +- [JSONUnmarshalAsT[Object any, ADoc Text]](#jsonunmarshalastobject-any-adoc-text) | star | orange ``` ### JSONEq{#jsoneq} @@ -43,14 +48,44 @@ Expected and actual must be valid JSON. {{% tab title="Usage" %}} ```go assertions.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + success: `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}` + failure: `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]` ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}` - failure: `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]` +// real-world test would inject *testing.T from TestJSONEq(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -92,14 +127,44 @@ Expected and actual must be valid JSON. {{% tab title="Usage" %}} ```go assertions.JSONEqBytes(t, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`)) + success: []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`) + failure: []byte(`{"hello": "world", "foo": "bar"}`), []byte(`[{"foo": "bar"}, {"hello": "world"}]`) ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`) - failure: []byte(`{"hello": "world", "foo": "bar"}`), []byte(`[{"foo": "bar"}, {"hello": "world"}]`) +// real-world test would inject *testing.T from TestJSONEqBytes(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.JSONEqBytes(t, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`)) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -151,14 +216,44 @@ Expected and actual must be valid JSON. {{% tab title="Usage" %}} ```go assertions.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) + success: `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`) + failure: `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]` ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`) - failure: `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]` +// real-world test would inject *testing.T from TestJSONEqT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -179,12 +274,190 @@ Expected and actual must be valid JSON. {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.JSONEqT(t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#JSONEqT) | internal implementation | +| [`assertions.JSONEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#JSONEqT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#JSONEqT](https://github.com/go-openapi/testify/blob/master/internal/assertions/json.go#L86) {{% /tab %}} {{< /tabs >}} +### JSONMarshalAsT[EDoc Text] {{% icon icon="star" color=orange %}}{#jsonmarshalastedoc-text} + +JSONMarshalAsT wraps [JSONEq](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONEq) after [json.Marshal](https://pkg.go.dev/json#Marshal). + +The input JSON may be a string or []byte. + +It fails if the marshaling returns an error or if the expected JSON bytes differ semantically +from the expected ones. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + actual := struct { + A int `json:"a"` + }{ + A: 10, + } + assertions.JSONUnmarshalAsT(t,expected, `{"a": 10}`) + success: []byte(`{"A": "a"}`), dummyStruct{A: "a"} + failure: `[{"foo": "bar"}, {"hello": "world"}]`, 1 +``` +{{< /tab >}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + +```go +// real-world test would inject *testing.T from TestJSONMarshalAsT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.JSONMarshalAsT(t, []byte(`{"A": "a"}`), dummyStruct{A: "a"}) + fmt.Println("passed") + +} + +type dummyStruct struct { + A string + b int +} + +``` +{{% /card %}} + + +{{% /cards %}} +{{< /tab >}} + + +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.JSONMarshalAsT[EDoc Text](t T, expected EDoc, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONMarshalAsT) | package-level function | +| [`assert.JSONMarshalAsTf[EDoc Text](t T, expected EDoc, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONMarshalAsTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.JSONMarshalAsT[EDoc Text](t T, expected EDoc, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#JSONMarshalAsT) | package-level function | +| [`require.JSONMarshalAsTf[EDoc Text](t T, expected EDoc, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#JSONMarshalAsTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.JSONMarshalAsT[EDoc Text](t T, expected EDoc, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#JSONMarshalAsT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#JSONMarshalAsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/json.go#L153) +{{% /tab %}} +{{< /tabs >}} + +### JSONUnmarshalAsT[Object any, ADoc Text] {{% icon icon="star" color=orange %}}{#jsonunmarshalastobject-any-adoc-text} + +JSONUnmarshalAsT wraps [Equal](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Equal) after [json.Unmarshal](https://pkg.go.dev/json#Unmarshal). + +The input JSON may be a string or []byte. + +It fails if the unmarshaling returns an error or if the resulting object is not equal to the expected one. + +Be careful not to wrap the expected object into an "any" interface if this is not what you expected: +the unmarshaling would take this type to unmarshal as a map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)any. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + expected := struct { + A int `json:"a"` + }{ + A: 10, + } + assertions.JSONUnmarshalAsT(t,expected, `{"a": 10}`) + success: dummyStruct{A: "a"} , []byte(`{"A": "a"}`) + failure: 1, `[{"foo": "bar"}, {"hello": "world"}]` +``` +{{< /tab >}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + +```go +// real-world test would inject *testing.T from TestJSONUnmarshalAsT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.JSONUnmarshalAsT(t, dummyStruct{A: "a"}, []byte(`{"A": "a"}`)) + fmt.Println("passed") + +} + +type dummyStruct struct { + A string + b int +} + +``` +{{% /card %}} + + +{{% /cards %}} +{{< /tab >}} + + +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.JSONUnmarshalAsT[Object any, ADoc Text](t T, expected Object, jazon ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONUnmarshalAsT) | package-level function | +| [`assert.JSONUnmarshalAsTf[Object any, ADoc Text](t T, expected Object, jazon ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONUnmarshalAsTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.JSONUnmarshalAsT[Object any, ADoc Text](t T, expected Object, jazon ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#JSONUnmarshalAsT) | package-level function | +| [`require.JSONUnmarshalAsTf[Object any, ADoc Text](t T, expected Object, jazon ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#JSONUnmarshalAsTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.JSONUnmarshalAsT[Object any, ADoc Text](t T, expected Object, jazon ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#JSONUnmarshalAsT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#JSONUnmarshalAsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/json.go#L118) +{{% /tab %}} +{{< /tabs >}} + --- --- @@ -200,6 +473,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/number.md b/docs/doc-site/api/number.md index 005f3c515..2bb39da81 100644 --- a/docs/doc-site/api/number.md +++ b/docs/doc-site/api/number.md @@ -1,7 +1,6 @@ --- title: "Number" description: "Asserting Numbers" -modified: 2026-01-27 weight: 10 domains: - "number" @@ -65,14 +64,44 @@ prefer [InDeltaT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InD {{% tab title="Usage" %}} ```go assertions.InDelta(t, math.Pi, 22/7.0, 0.01) + success: 1.0, 1.01, 0.02 + failure: 1.0, 1.1, 0.05 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 1.0, 1.01, 0.02 - failure: 1.0, 1.1, 0.05 +// real-world test would inject *testing.T from TestInDelta(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.InDelta(t, 1.0, 1.01, 0.02) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -114,14 +143,44 @@ See [InDelta](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDelta {{% tab title="Usage" %}} ```go assertions.InDeltaMapValues(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.0}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.01}, 0.02) + success: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.0}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.01}, 0.02 + failure: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.0}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.1}, 0.05 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.0}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.01}, 0.02 - failure: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.0}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.1}, 0.05 +// real-world test would inject *testing.T from TestInDeltaMapValues(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.InDeltaMapValues(t, map[string]float64{"a": 1.0}, map[string]float64{"a": 1.01}, 0.02) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -163,14 +222,44 @@ See [InDelta](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDelta {{% tab title="Usage" %}} ```go assertions.InDeltaSlice(t, []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02) + success: []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02 + failure: []float64{1.0, 2.0}, []float64{1.1, 2.1}, 0.05 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02 - failure: []float64{1.0, 2.0}, []float64{1.1, 2.1}, 0.05 +// real-world test would inject *testing.T from TestInDeltaSlice(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.InDeltaSlice(t, []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -222,14 +311,44 @@ Delta must be greater than or equal to zero. {{% tab title="Usage" %}} ```go assertions.InDeltaT(t, math.Pi, 22/7.0, 0.01) + success: 1.0, 1.01, 0.02 + failure: 1.0, 1.1, 0.05 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 1.0, 1.01, 0.02 - failure: 1.0, 1.1, 0.05 +// real-world test would inject *testing.T from TestInDeltaT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.InDeltaT(t, 1.0, 1.01, 0.02) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -250,7 +369,7 @@ assertions.InDeltaT(t, math.Pi, 22/7.0, 0.01) {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.InDeltaT(t T, expected Number, actual Number, delta Number, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InDeltaT) | internal implementation | +| [`assertions.InDeltaT[Number Measurable](t T, expected Number, actual Number, delta Number, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InDeltaT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#InDeltaT](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L85) {{% /tab %}} @@ -279,14 +398,44 @@ This allows [InEpsilonT](https://pkg.go.dev/github.com/go-openapi/testify/v2/ass {{% tab title="Usage" %}} ```go assertions.InEpsilon(t, 100.0, 101.0, 0.02) + success: 100.0, 101.0, 0.02 + failure: 100.0, 110.0, 0.05 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 100.0, 101.0, 0.02 - failure: 100.0, 110.0, 0.05 +// real-world test would inject *testing.T from TestInEpsilon(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.InEpsilon(t, 100.0, 101.0, 0.02) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -328,14 +477,44 @@ See [InEpsilon](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEps {{% tab title="Usage" %}} ```go assertions.InEpsilonSlice(t, []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02) + success: []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02 + failure: []float64{100.0, 200.0}, []float64{110.0, 220.0}, 0.05 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02 - failure: []float64{100.0, 200.0}, []float64{110.0, 220.0}, 0.05 +// real-world test would inject *testing.T from TestInEpsilonSlice(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.InEpsilonSlice(t, []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -395,14 +574,44 @@ This allows [InEpsilonT](https://pkg.go.dev/github.com/go-openapi/testify/v2/ass {{% tab title="Usage" %}} ```go assertions.InEpsilon(t, 100.0, 101.0, 0.02) + success: 100.0, 101.0, 0.02 + failure: 100.0, 110.0, 0.05 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 100.0, 101.0, 0.02 - failure: 100.0, 110.0, 0.05 +// real-world test would inject *testing.T from TestInEpsilonT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.InEpsilonT(t, 100.0, 101.0, 0.02) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -423,7 +632,7 @@ This allows [InEpsilonT](https://pkg.go.dev/github.com/go-openapi/testify/v2/ass {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.InEpsilonT(t T, expected Number, actual Number, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InEpsilonT) | internal implementation | +| [`assertions.InEpsilonT[Number Measurable](t T, expected Number, actual Number, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InEpsilonT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#InEpsilonT](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L199) {{% /tab %}} @@ -444,6 +653,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/ordering.md b/docs/doc-site/api/ordering.md index a7300cb10..6f6da2ea1 100644 --- a/docs/doc-site/api/ordering.md +++ b/docs/doc-site/api/ordering.md @@ -1,7 +1,6 @@ --- title: "Ordering" description: "Asserting How Collections Are Ordered" -modified: 2026-01-27 weight: 11 domains: - "ordering" @@ -64,14 +63,44 @@ IsDecreasing asserts that the collection is strictly decreasing. assertions.IsDecreasing(t, []int{2, 1, 0}) assertions.IsDecreasing(t, []float{2, 1}) assertions.IsDecreasing(t, []string{"b", "a"}) + success: []int{3, 2, 1} + failure: []int{1, 2, 3} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{3, 2, 1} - failure: []int{1, 2, 3} +// real-world test would inject *testing.T from TestIsDecreasing(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.IsDecreasing(t, []int{3, 2, 1}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -113,14 +142,44 @@ IsDecreasingT asserts that a slice of [Ordered](https://pkg.go.dev/github.com/go assertions.IsDecreasingT(t, []int{2, 1, 0}) assertions.IsDecreasingT(t, []float{2, 1}) assertions.IsDecreasingT(t, []string{"b", "a"}) + success: []int{3, 2, 1} + failure: []int{1, 2, 3} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{3, 2, 1} - failure: []int{1, 2, 3} +// real-world test would inject *testing.T from TestIsDecreasingT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.IsDecreasingT(t, []int{3, 2, 1}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -141,7 +200,7 @@ IsDecreasingT asserts that a slice of [Ordered](https://pkg.go.dev/github.com/go {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.IsDecreasingT(t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsDecreasingT) | internal implementation | +| [`assertions.IsDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsDecreasingT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsDecreasingT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L220) {{% /tab %}} @@ -158,14 +217,44 @@ IsIncreasing asserts that the collection is strictly increasing. assertions.IsIncreasing(t, []int{1, 2, 3}) assertions.IsIncreasing(t, []float{1, 2}) assertions.IsIncreasing(t, []string{"a", "b"}) + success: []int{1, 2, 3} + failure: []int{1, 1, 2} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{1, 2, 3} - failure: []int{1, 1, 2} +// real-world test would inject *testing.T from TestIsIncreasing(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.IsIncreasing(t, []int{1, 2, 3}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -207,14 +296,44 @@ IsIncreasingT asserts that a slice of [Ordered](https://pkg.go.dev/github.com/go assertions.IsIncreasingT(t, []int{1, 2, 3}) assertions.IsIncreasingT(t, []float{1, 2}) assertions.IsIncreasingT(t, []string{"a", "b"}) + success: []int{1, 2, 3} + failure: []int{1, 1, 2} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{1, 2, 3} - failure: []int{1, 1, 2} +// real-world test would inject *testing.T from TestIsIncreasingT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.IsIncreasingT(t, []int{1, 2, 3}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -235,7 +354,7 @@ IsIncreasingT asserts that a slice of [Ordered](https://pkg.go.dev/github.com/go {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.IsIncreasingT(t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsIncreasingT) | internal implementation | +| [`assertions.IsIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsIncreasingT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsIncreasingT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L53) {{% /tab %}} @@ -252,14 +371,44 @@ IsNonDecreasing asserts that the collection is not strictly decreasing. assertions.IsNonDecreasing(t, []int{1, 1, 2}) assertions.IsNonDecreasing(t, []float{1, 2}) assertions.IsNonDecreasing(t, []string{"a", "b"}) + success: []int{1, 1, 2} + failure: []int{2, 1, 0} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{1, 1, 2} - failure: []int{2, 1, 0} +// real-world test would inject *testing.T from TestIsNonDecreasing(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.IsNonDecreasing(t, []int{1, 1, 2}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -301,14 +450,44 @@ IsNonDecreasingT asserts that a slice of [Ordered](https://pkg.go.dev/github.com assertions.IsNonDecreasingT(t, []int{1, 1, 2}) assertions.IsNonDecreasingT(t, []float{1, 2}) assertions.IsNonDecreasingT(t, []string{"a", "b"}) + success: []int{1, 1, 2} + failure: []int{2, 1, 0} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{1, 1, 2} - failure: []int{2, 1, 0} +// real-world test would inject *testing.T from TestIsNonDecreasingT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.IsNonDecreasingT(t, []int{1, 1, 2}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -329,7 +508,7 @@ IsNonDecreasingT asserts that a slice of [Ordered](https://pkg.go.dev/github.com {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.IsNonDecreasingT(t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNonDecreasingT) | internal implementation | +| [`assertions.IsNonDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNonDecreasingT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNonDecreasingT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L275) {{% /tab %}} @@ -346,14 +525,44 @@ IsNonIncreasing asserts that the collection is not increasing. assertions.IsNonIncreasing(t, []int{2, 1, 1}) assertions.IsNonIncreasing(t, []float{2, 1}) assertions.IsNonIncreasing(t, []string{"b", "a"}) + success: []int{2, 1, 1} + failure: []int{1, 2, 3} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{2, 1, 1} - failure: []int{1, 2, 3} +// real-world test would inject *testing.T from TestIsNonIncreasing(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.IsNonIncreasing(t, []int{2, 1, 1}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -395,14 +604,44 @@ IsNonIncreasingT asserts that a slice of [Ordered](https://pkg.go.dev/github.com assertions.IsNonIncreasing(t, []int{2, 1, 1}) assertions.IsNonIncreasing(t, []float{2, 1}) assertions.IsNonIncreasing(t, []string{"b", "a"}) + success: []int{2, 1, 1} + failure: []int{1, 2, 3} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{2, 1, 1} - failure: []int{1, 2, 3} +// real-world test would inject *testing.T from TestIsNonIncreasingT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.IsNonIncreasingT(t, []int{2, 1, 1}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -423,7 +662,7 @@ IsNonIncreasingT asserts that a slice of [Ordered](https://pkg.go.dev/github.com {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.IsNonIncreasingT(t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNonIncreasingT) | internal implementation | +| [`assertions.IsNonIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNonIncreasingT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNonIncreasingT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L164) {{% /tab %}} @@ -442,14 +681,44 @@ Unlike [IsDecreasingT](https://pkg.go.dev/github.com/go-openapi/testify/v2/asser assertions.NotSortedT(t, []int{3, 2, 3}) assertions.NotSortedT(t, []float{2, 1}) assertions.NotSortedT(t, []string{"b", "a"}) + success: []int{3, 1, 3} + failure: []int{1, 4, 8} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{3, 1, 3} - failure: []int{1, 4, 8} +// real-world test would inject *testing.T from TestNotSortedT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotSortedT(t, []int{3, 1, 3}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -470,7 +739,7 @@ Unlike [IsDecreasingT](https://pkg.go.dev/github.com/go-openapi/testify/v2/asser {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.NotSortedT(t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotSortedT) | internal implementation | +| [`assertions.NotSortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotSortedT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSortedT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L109) {{% /tab %}} @@ -489,14 +758,44 @@ Unlike [IsIncreasingT](https://pkg.go.dev/github.com/go-openapi/testify/v2/asser assertions.SortedT(t, []int{1, 2, 3}) assertions.SortedT(t, []float{1, 2}) assertions.SortedT(t, []string{"a", "b"}) + success: []int{1, 1, 3} + failure: []int{1, 4, 2} ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: []int{1, 1, 3} - failure: []int{1, 4, 2} +// real-world test would inject *testing.T from TestSortedT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.SortedT(t, []int{1, 1, 3}) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -517,7 +816,7 @@ Unlike [IsIncreasingT](https://pkg.go.dev/github.com/go-openapi/testify/v2/asser {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.SortedT(t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SortedT) | internal implementation | +| [`assertions.SortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SortedT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#SortedT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L81) {{% /tab %}} @@ -538,6 +837,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/panic.md b/docs/doc-site/api/panic.md index d83b73668..9255fbcbf 100644 --- a/docs/doc-site/api/panic.md +++ b/docs/doc-site/api/panic.md @@ -1,7 +1,6 @@ --- title: "Panic" description: "Asserting A Panic Behavior" -modified: 2026-01-27 weight: 12 domains: - "panic" @@ -43,14 +42,45 @@ NotPanics asserts that the code inside the specified function does NOT panic. {{% tab title="Usage" %}} ```go assertions.NotPanics(t, func(){ RemainCalm() }) + success: func() { } + failure: func() { panic("panicking") } ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: func() { } - failure: func() { panic("panicking") } +// real-world test would inject *testing.T from TestNotPanics(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotPanics(t, func() { + }) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -90,14 +120,46 @@ Panics asserts that the code inside the specified function panics. {{% tab title="Usage" %}} ```go assertions.Panics(t, func(){ GoCrazy() }) + success: func() { panic("panicking") } + failure: func() { } ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: func() { panic("panicking") } - failure: func() { } +// real-world test would inject *testing.T from TestPanics(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Panics(t, func() { + panic("panicking") + }) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -138,14 +200,47 @@ and that the recovered panic value is an error that satisfies the EqualError com {{% tab title="Usage" %}} ```go assertions.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) + success: ErrTest.Error(), func() { panic(ErrTest) } + failure: ErrTest.Error(), func() { } ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: ErrTest.Error(), func() { panic(ErrTest) } - failure: ErrTest.Error(), func() { } +// real-world test would inject *testing.T from TestPanicsWithError(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.PanicsWithError(t, assert.ErrTest.Error(), func() { + panic(assert.ErrTest) + }) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -186,14 +281,46 @@ and that the recovered panic value equals the expected panic value. {{% tab title="Usage" %}} ```go assertions.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) + success: "panicking", func() { panic("panicking") } + failure: "panicking", func() { } ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: "panicking", func() { panic("panicking") } - failure: "panicking", func() { } +// real-world test would inject *testing.T from TestPanicsWithValue(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.PanicsWithValue(t, "panicking", func() { + panic("panicking") + }) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -239,6 +366,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/safety.md b/docs/doc-site/api/safety.md new file mode 100644 index 000000000..b9e519fc5 --- /dev/null +++ b/docs/doc-site/api/safety.md @@ -0,0 +1,74 @@ +--- +title: "Safety" +description: "" +weight: 13 +domains: + - "safety" +keywords: + - "NoGoRoutineLeak" + - "NoGoRoutineLeakf" +--- + + + +## Assertions + +[![GoDoc][godoc-badge]][godoc-url] +{class="inline-badge"} + +_All links point to _ + +This domain exposes 1 functionalities. + +```tree +- [NoGoRoutineLeak](#nogoroutineleak) | angles-right +``` + +### NoGoRoutineLeak{#nogoroutineleak} + +NoGoRoutineLeak ensures that no goroutine did leak from inside the tested function. + + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.NoGoRoutineLeak(t T, inside func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NoGoRoutineLeak) | package-level function | +| [`assert.NoGoRoutineLeakf(t T, inside func(), msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NoGoRoutineLeakf) | formatted variant | +| [`assert.(*Assertions).NoGoRoutineLeak(inside func()) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NoGoRoutineLeak) | method variant | +| [`assert.(*Assertions).NoGoRoutineLeakf(inside func(), msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NoGoRoutineLeakf) | method formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.NoGoRoutineLeak(t T, inside func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NoGoRoutineLeak) | package-level function | +| [`require.NoGoRoutineLeakf(t T, inside func(), msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NoGoRoutineLeakf) | formatted variant | +| [`require.(*Assertions).NoGoRoutineLeak(inside func()) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NoGoRoutineLeak) | method variant | +| [`require.(*Assertions).NoGoRoutineLeakf(inside func(), msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NoGoRoutineLeakf) | method formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.NoGoRoutineLeak(t T, inside func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NoGoRoutineLeak) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NoGoRoutineLeak](https://github.com/go-openapi/testify/blob/master/internal/assertions/safety.go#L9) +{{% /tab %}} +{{< /tabs >}} + +--- + +--- + +Generated with github.com/go-openapi/testify/codegen/v2 + +[godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 +[godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 + + diff --git a/docs/doc-site/api/string.md b/docs/doc-site/api/string.md index ab4abdc26..8fbc3119e 100644 --- a/docs/doc-site/api/string.md +++ b/docs/doc-site/api/string.md @@ -1,8 +1,7 @@ --- title: "String" description: "Asserting Strings" -modified: 2026-01-27 -weight: 13 +weight: 14 domains: - "string" keywords: @@ -47,14 +46,44 @@ See [Regexp](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Regexp). ```go assertions.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") assertions.NotRegexp(t, "^start", "it's not starting") + success: "^start", "not starting" + failure: "^start", "starting" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: "^start", "not starting" - failure: "^start", "starting" +// real-world test would inject *testing.T from TestNotRegexp(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotRegexp(t, "^start", "not starting") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -97,14 +126,44 @@ See [RegexpT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#RegexpT ```go assertions.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") assertions.NotRegexp(t, "^start", "it's not starting") + success: "^start", "not starting" + failure: "^start", "starting" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: "^start", "not starting" - failure: "^start", "starting" +// real-world test would inject *testing.T from TestNotRegexpT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotRegexpT(t, "^start", "not starting") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -125,7 +184,7 @@ See [RegexpT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#RegexpT {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.NotRegexpT(t T, rx Rex, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotRegexpT) | internal implementation | +| [`assertions.NotRegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotRegexpT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotRegexpT](https://github.com/go-openapi/testify/blob/master/internal/assertions/string.go#L131) {{% /tab %}} @@ -145,14 +204,44 @@ The actual argument to be matched may be a string, []byte or anything that print ```go assertions.Regexp(t, regexp.MustCompile("start"), "it's starting") assertions.Regexp(t, "start...$", "it's not starting") + success: "^start", "starting" + failure: "^start", "not starting" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: "^start", "starting" - failure: "^start", "not starting" +// real-world test would inject *testing.T from TestRegexp(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Regexp(t, "^start", "starting") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -193,12 +282,42 @@ See [Regexp](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Regexp). {{% expand title="Examples" %}} {{< tabs >}} -{{% tab title="Examples" %}} -```go success: "^start", "starting" failure: "^start", "not starting" +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + +```go +// real-world test would inject *testing.T from TestRegexpT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.RegexpT(t, "^start", "starting") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -219,7 +338,7 @@ See [Regexp](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Regexp). {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.RegexpT(t T, rx Rex, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#RegexpT) | internal implementation | +| [`assertions.RegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#RegexpT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#RegexpT](https://github.com/go-openapi/testify/blob/master/internal/assertions/string.go#L63) {{% /tab %}} @@ -240,6 +359,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/testing.md b/docs/doc-site/api/testing.md index 52f5cd5ea..73ca5b955 100644 --- a/docs/doc-site/api/testing.md +++ b/docs/doc-site/api/testing.md @@ -1,8 +1,7 @@ --- title: "Testing" description: "Mimics Methods From The Testing Standard Library" -modified: 2026-01-27 -weight: 14 +weight: 15 domains: - "testing" keywords: @@ -135,6 +134,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/time.md b/docs/doc-site/api/time.md index 6206e6ead..d0f70b57c 100644 --- a/docs/doc-site/api/time.md +++ b/docs/doc-site/api/time.md @@ -1,8 +1,7 @@ --- title: "Time" description: "Asserting Times And Durations" -modified: 2026-01-27 -weight: 15 +weight: 16 domains: - "time" keywords: @@ -37,14 +36,45 @@ WithinDuration asserts that the two times are within duration delta of each othe {{% tab title="Usage" %}} ```go assertions.WithinDuration(t, time.Now(), 10*time.Second) + success: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second + failure: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 10, 0, time.UTC), 1*time.Second ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second - failure: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 10, 0, time.UTC), 1*time.Second +// real-world test would inject *testing.T from TestWithinDuration(t *testing.T) +package main + +import ( + "fmt" + "testing" + "time" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.WithinDuration(t, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -84,14 +114,45 @@ WithinRange asserts that a time is within a time range (inclusive). {{% tab title="Usage" %}} ```go assertions.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) + success: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC) + failure: time.Date(2024, 1, 1, 14, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC) ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC) - failure: time.Date(2024, 1, 1, 14, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC) +// real-world test would inject *testing.T from TestWithinRange(t *testing.T) +package main + +import ( + "fmt" + "testing" + "time" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.WithinRange(t, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC)) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -137,6 +198,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/type.md b/docs/doc-site/api/type.md index 62381299d..6ee1b229a 100644 --- a/docs/doc-site/api/type.md +++ b/docs/doc-site/api/type.md @@ -1,8 +1,7 @@ --- title: "Type" description: "Asserting Types Rather Than Values" -modified: 2026-01-27 -weight: 16 +weight: 17 domains: - "type" keywords: @@ -62,14 +61,56 @@ Implements asserts that an object is implemented by the specified interface. {{% tab title="Usage" %}} ```go assertions.Implements(t, (*MyInterface)(nil), new(MyObject)) + success: ptr(dummyInterface), new(testing.T) + failure: (*error)(nil), new(testing.T) ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: ptr(dummyInterface), new(testing.T) - failure: (*error)(nil), new(testing.T) +// real-world test would inject *testing.T from TestImplements(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Implements(t, ptr(dummyInterface), new(testing.T)) + fmt.Println("passed") + +} + +var ( + staticVar = "static string" + + dummyInterface require.T +) + +func ptr[T any](value T) *T { + p := value + + return &p +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -109,14 +150,46 @@ IsNotOfTypeT asserts that an object is not of a given type. {{% tab title="Usage" %}} ```go assertions.IsOfType[MyType](t,myVar) + success: 123.123 + failure: myType(123.123) ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 123.123 - failure: myType(123.123) +// real-world test would inject *testing.T from TestIsNotOfTypeT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.IsNotOfTypeT[myType](t, 123.123) + fmt.Println("passed") + +} + +type myType float64 + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -137,7 +210,7 @@ IsNotOfTypeT asserts that an object is not of a given type. {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.IsNotOfTypeT(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNotOfTypeT) | internal implementation | +| [`assertions.IsNotOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNotOfTypeT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNotOfTypeT](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L141) {{% /tab %}} @@ -152,14 +225,44 @@ IsNotType asserts that the specified objects are not of the same type. {{% tab title="Usage" %}} ```go assertions.IsNotType(t, &NotMyStruct{}, &MyStruct{}) + success: int32(123), int64(456) + failure: 123, 456 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: int32(123), int64(456) - failure: 123, 456 +// real-world test would inject *testing.T from TestIsNotType(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.IsNotType(t, int32(123), int64(456)) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -199,14 +302,46 @@ IsOfTypeT asserts that an object is of a given type. {{% tab title="Usage" %}} ```go assertions.IsOfTypeT[MyType](t,myVar) + success: myType(123.123) + failure: 123.123 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: myType(123.123) - failure: 123.123 +// real-world test would inject *testing.T from TestIsOfTypeT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.IsOfTypeT[myType](t, myType(123.123)) + fmt.Println("passed") + +} + +type myType float64 + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -227,7 +362,7 @@ IsOfTypeT asserts that an object is of a given type. {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.IsOfTypeT(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsOfTypeT) | internal implementation | +| [`assertions.IsOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsOfTypeT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsOfTypeT](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L96) {{% /tab %}} @@ -242,14 +377,44 @@ IsType asserts that the specified objects are of the same type. {{% tab title="Usage" %}} ```go assertions.IsType(t, &MyStruct{}, &MyStruct{}) + success: 123, 456 + failure: int32(123), int64(456) ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 123, 456 - failure: int32(123), int64(456) +// real-world test would inject *testing.T from TestIsType(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.IsType(t, 123, 456) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -292,14 +457,45 @@ are comparable to [reflect.Invalid](https://pkg.go.dev/reflect#Invalid). See als {{% tab title="Usage" %}} ```go assertions.Kind(t, reflect.String, "Hello World") + success: reflect.String, "hello" + failure: reflect.String, 0 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: reflect.String, "hello" - failure: reflect.String, 0 +// real-world test would inject *testing.T from TestKind(t *testing.T) +package main + +import ( + "fmt" + "reflect" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Kind(t, reflect.String, "hello") + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -339,14 +535,44 @@ NotImplements asserts that an object does not implement the specified interface. {{% tab title="Usage" %}} ```go assertions.NotImplements(t, (*MyInterface)(nil), new(MyObject)) + success: (*error)(nil), new(testing.T) + failure: ptr(dummyInterface), new(testing.T) ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: (*error)(nil), new(testing.T) - failure: ptr(dummyInterface), new(testing.T) +// real-world test would inject *testing.T from TestNotImplements(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotImplements(t, (*error)(nil), new(testing.T)) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -389,14 +615,45 @@ are comparable to [reflect.Invalid](https://pkg.go.dev/reflect#Invalid). See als {{% tab title="Usage" %}} ```go assertions.NotKind(t, reflect.Int, "Hello World") + success: reflect.String, 0 + failure: reflect.String, "hello" ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: reflect.String, 0 - failure: reflect.String, "hello" +// real-world test would inject *testing.T from TestNotKind(t *testing.T) +package main + +import ( + "fmt" + "reflect" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotKind(t, reflect.String, 0) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -436,14 +693,44 @@ NotZero asserts that i is not the zero value for its type. {{% tab title="Usage" %}} ```go assertions.NotZero(t, obj) + success: 1 + failure: 0 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 1 - failure: 0 +// real-world test would inject *testing.T from TestNotZero(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.NotZero(t, 1) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -483,14 +770,44 @@ Zero asserts that i is the zero value for its type. {{% tab title="Usage" %}} ```go assertions.Zero(t, obj) + success: 0 + failure: 1 ``` {{< /tab >}} -{{% tab title="Examples" %}} +{{% tab title="Testable Examples" %}} +{{% cards %}} +{{% card href="https://go.dev/play/" %}} + + +*Copy and click to open Go Playground* + + ```go - success: 0 - failure: 1 +// real-world test would inject *testing.T from TestZero(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) + require.Zero(t, 0) + fmt.Println("passed") + +} + ``` +{{% /card %}} + + +{{% /cards %}} {{< /tab >}} + + {{< /tabs >}} {{% /expand %}} @@ -536,6 +853,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/api/yaml.md b/docs/doc-site/api/yaml.md index 8bccbc7b5..573b5a261 100644 --- a/docs/doc-site/api/yaml.md +++ b/docs/doc-site/api/yaml.md @@ -1,8 +1,7 @@ --- title: "Yaml" description: "Asserting Yaml Documents" -modified: 2026-01-27 -weight: 17 +weight: 18 domains: - "yaml" keywords: @@ -12,6 +11,10 @@ keywords: - "YAMLEqBytesf" - "YAMLEqT" - "YAMLEqTf" + - "YAMLMarshalAsT" + - "YAMLMarshalAsTf" + - "YAMLUnmarshalAsT" + - "YAMLUnmarshalAsTf" --- Asserting Yaml Documents @@ -23,13 +26,15 @@ Asserting Yaml Documents _All links point to _ -This domain exposes 3 functionalities. +This domain exposes 5 functionalities. Generic assertions are marked with a {{% icon icon="star" color=orange %}}. ```tree - [YAMLEq](#yamleq) | angles-right - [YAMLEqBytes](#yamleqbytes) | angles-right - [YAMLEqT[EDoc, ADoc Text]](#yamleqtedoc-adoc-text) | star | orange +- [YAMLMarshalAsT[EDoc Text]](#yamlmarshalastedoc-text) | star | orange +- [YAMLUnmarshalAsT[Object any, ADoc Text]](#yamlunmarshalastobject-any-adoc-text) | star | orange ``` ### YAMLEq{#yamleq} @@ -181,12 +186,120 @@ See [YAMLEqBytes](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAM {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.YAMLEqT(t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#YAMLEqT) | internal implementation | +| [`assertions.YAMLEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#YAMLEqT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#YAMLEqT](https://github.com/go-openapi/testify/blob/master/internal/assertions/yaml.go#L96) {{% /tab %}} {{< /tabs >}} +### YAMLMarshalAsT[EDoc Text] {{% icon icon="star" color=orange %}}{#yamlmarshalastedoc-text} + +YAMLMarshalAsT wraps [YAMLEq](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEq) after [yaml.Marshal](https://pkg.go.dev/yaml#Marshal). + +The input YAML may be a string or []byte. + +It fails if the marshaling returns an error or if the expected YAML bytes differ semantically +from the expected ones. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + actual := struct { + A int `yaml:"a"` + }{ + A: 10, + } + assertions.YAMLUnmarshalAsT(t,expected, `{"a": 10}`) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + panic: "key: value", "key: value" + should panic without the yaml feature enabled. +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.YAMLMarshalAsT[EDoc Text](t T, expected EDoc, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLMarshalAsT) | package-level function | +| [`assert.YAMLMarshalAsTf[EDoc Text](t T, expected EDoc, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLMarshalAsTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.YAMLMarshalAsT[EDoc Text](t T, expected EDoc, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLMarshalAsT) | package-level function | +| [`require.YAMLMarshalAsTf[EDoc Text](t T, expected EDoc, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLMarshalAsTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.YAMLMarshalAsT[EDoc Text](t T, expected EDoc, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#YAMLMarshalAsT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#YAMLMarshalAsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/yaml.go#L163) +{{% /tab %}} +{{< /tabs >}} + +### YAMLUnmarshalAsT[Object any, ADoc Text] {{% icon icon="star" color=orange %}}{#yamlunmarshalastobject-any-adoc-text} + +YAMLUnmarshalAsT wraps [Equal](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Equal) after [yaml.Unmarshal](https://pkg.go.dev/yaml#Unmarshal). + +The input YAML may be a string or []byte. + +It fails if the unmarshaling returns an error or if the resulting object is not equal to the expected one. + +Be careful not to wrap the expected object into an "any" interface if this is not what you expected: +the unmarshaling would take this type to unmarshal as a map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)any. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + expected := struct { + A int `yaml:"a"` + }{ + A: 10, + } + assertions.YAMLUnmarshalAsT(t,expected, `{"a": 10}`) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + panic: "key: value", "key: value" + should panic without the yaml feature enabled. +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.YAMLUnmarshalAsT[Object any, ADoc Text](t T, expected Object, jazon ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLUnmarshalAsT) | package-level function | +| [`assert.YAMLUnmarshalAsTf[Object any, ADoc Text](t T, expected Object, jazon ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLUnmarshalAsTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.YAMLUnmarshalAsT[Object any, ADoc Text](t T, expected Object, jazon ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLUnmarshalAsT) | package-level function | +| [`require.YAMLUnmarshalAsTf[Object any, ADoc Text](t T, expected Object, jazon ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLUnmarshalAsTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.YAMLUnmarshalAsT[Object any, ADoc Text](t T, expected Object, jazon ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#YAMLUnmarshalAsT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#YAMLUnmarshalAsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/yaml.go#L128) +{{% /tab %}} +{{< /tabs >}} + --- --- @@ -202,6 +315,4 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. - -Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] --> diff --git a/docs/doc-site/project/APPROACH.md b/docs/doc-site/project/APPROACH.md index 51bfaa7ed..552afa586 100644 --- a/docs/doc-site/project/APPROACH.md +++ b/docs/doc-site/project/APPROACH.md @@ -391,7 +391,7 @@ Go's testing ecosystem reflects the broader assertion-vs-BDD divide: | **Integration** | Works with `go test` directly | Requires `ginkgo` CLI tool | | **Learning curve** | Immediate (standard Go) | Moderate (new DSL) | | **Dependencies** | Zero external packages | Multiple framework packages | -| **Type safety** | 38 generic assertions | Reflection-based matchers | +| **Type safety** | Generic assertions | Reflection-based matchers | | **Organization** | Standard Go subtests | Narrative hierarchy (Describe/Context/It) | | **Go philosophy** | Aligns with Go values | Different priorities | diff --git a/docs/doc-site/project/maintainers/ARCHITECTURE.md b/docs/doc-site/project/maintainers/ARCHITECTURE.md index 1986a25a7..7319eaf05 100644 --- a/docs/doc-site/project/maintainers/ARCHITECTURE.md +++ b/docs/doc-site/project/maintainers/ARCHITECTURE.md @@ -55,22 +55,22 @@ All these variants make up several hundreds functions, which poses a challenge f We have adopted code and documentation generation as a mean to mitigate this issue. -#### Current (v2.2.0) +#### Current (v2.3.0-unreleased) - 1. Generic assertions (with type parameters): 38 functions + 1. Generic assertions (with type parameters): 42 functions 2. Non-generic assertions (with t T parameter, no type parameters): 82 functions 3. Helper functions (no t T parameter): 4 functions - Total: 124 functions to _maintain_ + Total: 128 functions to _maintain_ **Generated Functions** - 1. Generic assertions: 152 + 1. Generic assertions: 168 2. Non-generic assertions: 656 3. Helper functions: 8 4. Constructors: 2 - Total: 818 functions + Total: 834 functions ## Architecture Overview diff --git a/docs/doc-site/project/maintainers/BENCHMARKS.md b/docs/doc-site/project/maintainers/BENCHMARKS.md index b8cacf395..312ced07b 100644 --- a/docs/doc-site/project/maintainers/BENCHMARKS.md +++ b/docs/doc-site/project/maintainers/BENCHMARKS.md @@ -158,7 +158,7 @@ go test -run=^$ -bench=. -benchmem ./internal/assertions ## Benchmark Coverage -**38 generic functions benchmarked across 10 domains:** +**38/42 generic functions benchmarked across 10 domains:** - Boolean (2): TrueT, FalseT - Collection (12): StringContainsT, SliceContainsT, MapContainsT, SeqContainsT, ElementsMatchT, SliceSubsetT, and negative variants - Comparison (6): GreaterT, LessT, GreaterOrEqualT, LessOrEqualT, PositiveT, NegativeT diff --git a/docs/doc-site/project/maintainers/DEPENDENCIES.md b/docs/doc-site/project/maintainers/DEPENDENCIES.md new file mode 100644 index 000000000..d381b2566 --- /dev/null +++ b/docs/doc-site/project/maintainers/DEPENDENCIES.md @@ -0,0 +1,387 @@ +--- +title: Optional Dependencies +description: Zero-dependency architecture with opt-in external features. +weight: 4 +--- + +{{% notice primary "TL;DR" "meteor" %}} +> The main module has zero external dependencies. Optional features (YAML, colorized output) are activated +> by importing separate `enable/` modules. Internal stubs panic with helpful messages when a feature is used +> without being enabled. +{{% /notice %}} + +## The Problem + +Testing libraries sit at the bottom of the dependency tree: every package in a project imports them. +Any dependency pulled in by the testing library propagates to all consumers. The original `stretchr/testify` +pulls in `gopkg.in/yaml.v3`, `github.com/davecgh/go-spew`, and `github.com/pmw/go-difflib` for *all* +users, even those who never call `YAMLEq`. + +Our goal: **zero external dependencies in the main module**, with opt-in features for users who need them. + +## Architecture Overview + +Three layers collaborate to deliver optional features without coupling: + +{{< mermaid align="center" zoom="true" >}} +graph TD + user["User Code"] + enablemod["enable/yaml or enable/colors
separate Go modules"] + stubs["enable/stubs/yaml or enable/stubs/colors
public delegation API"] + internal["internal/assertions/enable/yaml or .../colors
internal stubs with function pointers"] + assertions["internal/assertions/yaml.go or equal.go, diff.go
assertion implementations"] + + user -- "blank import" --> enablemod + enablemod -- "init(): wires real impl" --> stubs + stubs -- "delegates to" --> internal + assertions -- "calls" --> internal + + style enablemod fill:#4a9eff,color:#fff + style stubs fill:#90ee90,color:#000 + style internal fill:#ffb6c1,color:#000 + style assertions fill:#f0f0f0,color:#000 +{{< /mermaid >}} + +| Layer | Location | Has external deps? | Purpose | +|-------|----------|-------------------|---------| +| **Feature module** | `enable/yaml/`, `enable/colors/` | Yes (own `go.mod`) | Imports the real library, wires it in via `init()` | +| **Public stubs** | `enable/stubs/yaml/`, `enable/stubs/colors/` | No | Stable public API that delegates to internal package | +| **Internal stubs** | `internal/assertions/enable/yaml/`, `.../colors/` | No | Holds function pointers, panics when unset | +| **Assertions** | `internal/assertions/*.go` | No | Calls internal stubs; unaware of external libraries | + +--- + +## How It Works: YAML + +### The Wiring Chain + +``` +User imports: _ "github.com/go-openapi/testify/enable/yaml/v2" + │ + ▼ +enable/yaml/enable_yaml.go init() + ├─ calls yamlstub.EnableYAMLWithUnmarshal(yaml.Unmarshal) + └─ calls yamlstub.EnableYAMLWithMarshal(yaml.Marshal) + │ + ▼ +enable/stubs/yaml/enable_yaml.go + └─ delegates to internal/assertions/enable/yaml + │ + ▼ +internal/assertions/enable/yaml/enable_yaml.go + └─ stores function pointers in package-level vars + │ + ▼ +internal/assertions/yaml.go + └─ calls yaml.Unmarshal() / yaml.Marshal() via the stored pointers +``` + +### What Happens Without Enablement + +If a user calls `assert.YAMLEq(t, a, b)` without the blank import, the internal stub panics: + +``` +panic: YAML is not enabled. To enable YAML support, add a blank import: + + import _ "github.com/go-openapi/testify/enable/yaml/v2" +``` + +This is intentional: fail fast with a clear fix, rather than silently returning wrong results. + +### Internal Stub Pattern (YAML) + +The internal stub stores function pointers that start as `nil`: + +```go +// internal/assertions/enable/yaml/enable_yaml.go + +var ( + enableYAMLUnmarshal func([]byte, any) error + enableYAMLMarshal func(any) ([]byte, error) +) + +func Unmarshal(in []byte, out any) error { + if enableYAMLUnmarshal == nil { + panic("YAML is not enabled...") + } + return enableYAMLUnmarshal(in, out) +} +``` + +### Feature Module (YAML) + +The feature module is a separate Go module with its own `go.mod`: + +``` +// enable/yaml/go.mod +module github.com/go-openapi/testify/enable/yaml/v2 + +require go.yaml.in/yaml/v3 v3.0.4 +``` + +Its `init()` wires in the real implementation: + +```go +// enable/yaml/enable_yaml.go + +func init() { + yamlstub.EnableYAMLWithUnmarshal(yaml.Unmarshal) + yamlstub.EnableYAMLWithMarshal(yaml.Marshal) +} +``` + +### Custom YAML Library + +Users can bypass the default `enable/yaml` module and inject their own YAML library: + +```go +import ( + yaml "github.com/goccy/go-yaml" + yamlstub "github.com/go-openapi/testify/v2/enable/stubs/yaml" +) + +func init() { + yamlstub.EnableYAMLWithUnmarshal(yaml.Unmarshal) +} +``` + +This works because the public stubs API accepts any function matching the expected signature. + +--- + +## How It Works: Colorized Output + +### The Wiring Chain + +``` +User imports: _ "github.com/go-openapi/testify/enable/colors/v2" + │ + ▼ +enable/colors/enable.go init() + ├─ registers CLI flags: -testify.colorized, -testify.theme, -testify.colorized.notty + ├─ reads env vars: TESTIFY_COLORIZED, TESTIFY_THEME, TESTIFY_COLORIZED_NOTTY + ├─ detects TTY via golang.org/x/term + └─ calls colorstub.Enable(func() []Option { ... }) + │ + ▼ +enable/stubs/colors/enable_colors.go + └─ delegates Enable() to internal/assertions/enable/colors + │ + ▼ +internal/assertions/enable/colors/enable_colors.go + └─ stores enabler function, resolves lazily via sync.Once + │ + ▼ +internal/assertions/equal.go, diff.go + └─ calls colors.ExpectedColorizer(), colors.ActualColorizer(), colors.Options() +``` + +### Lazy Initialization + +Colors use a different pattern than YAML: lazy initialization with `sync.Once`. + +```go +// internal/assertions/enable/colors/enable_colors.go + +var ( + resolveOptionsOnce sync.Once + optionsEnabler func() []Option +) + +func Enable(enabler func() []Option) { + optionsEnabler = enabler +} + +func resolveOptions() { + resolveOptionsOnce.Do(func() { + if optionsEnabler == nil { + // Not enabled: use no-op colorizers + return + } + // Enabled: build ANSI colorizers from options + o := optionsWithDefaults(optionsEnabler()) + colorOptions = makeDiffOptions(o) + stringColorizers = setColorizers(o) + }) +} +``` + +**Why lazy?** The `init()` function in `enable/colors` registers CLI flags, but flag values +are only available after `flag.Parse()` runs (which happens when the test binary starts). +The `sync.Once` defers resolution until the first assertion call, when flags are ready. + +**Why not panic?** Colors are purely cosmetic. If not enabled, assertions work identically +with plain text output. No-op colorizers are used by default. + +### Feature Module (Colors) + +``` +// enable/colors/go.mod +module github.com/go-openapi/testify/enable/colors/v2 + +require golang.org/x/term v0.39.0 +``` + +The `golang.org/x/term` dependency is used solely for TTY detection (`term.IsTerminal`). + +--- + +## Design Decisions + +### Why Three Layers? + +A simpler two-layer design (feature module calls internal directly) would work, but three layers provide: + +1. **Stable public API** -- `enable/stubs/` is the contract users depend on for custom wiring. + Internal package paths can change without breaking user code. +2. **Testability** -- stubs can be tested independently of feature modules. +3. **Substitutability** -- users can wire alternative implementations through the stubs API + without importing the default feature module. + +### Panic vs. Silent Degradation + +| Feature | Strategy | Reason | +|---------|----------|--------| +| YAML | Panic | Incorrect results are worse than a crash. If `YAMLEq` silently fails, tests pass when they shouldn't. | +| Colors | No-op | Missing colors don't affect correctness. Assertions work fine without ANSI codes. | + +### Why Separate Go Modules? + +Each `enable/` feature is its own Go module (own `go.mod`). This means: + +- `go mod tidy` in the main module never pulls in `go.yaml.in/yaml/v3` or `golang.org/x/term` +- Users who `go get github.com/go-openapi/testify/v2` get zero transitive dependencies +- Feature dependencies are resolved only when the feature module is imported + +--- + +## Adding a New Optional Feature + +Follow these steps to add a new opt-in feature (e.g., a hypothetical JSON schema validator): + +### 1. Create internal stubs + +```go +// internal/assertions/enable/jsonschema/enable.go +package jsonschema + +var validateFunc func(schema, document []byte) error + +func Validate(schema, document []byte) error { + if validateFunc == nil { + panic(`JSON Schema validation is not enabled. Import: + _ "github.com/go-openapi/testify/enable/jsonschema/v2"`) + } + return validateFunc(schema, document) +} + +func EnableWithValidate(fn func([]byte, []byte) error) { + validateFunc = fn +} +``` + +### 2. Create public stubs + +```go +// enable/stubs/jsonschema/enable.go +package jsonschema + +import internal "github.com/go-openapi/testify/v2/internal/assertions/enable/jsonschema" + +func EnableWithValidate(fn func([]byte, []byte) error) { + internal.EnableWithValidate(fn) +} +``` + +### 3. Create feature module + +``` +// enable/jsonschema/go.mod +module github.com/go-openapi/testify/enable/jsonschema/v2 + +require github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 +``` + +```go +// enable/jsonschema/enable.go +package jsonschema + +import ( + "github.com/santhosh-tekuri/jsonschema/v6" + stub "github.com/go-openapi/testify/v2/enable/stubs/jsonschema" +) + +func init() { + stub.EnableWithValidate(func(schema, doc []byte) error { + // wire in the real validator + }) +} +``` + +### 4. Use in assertions + +```go +// internal/assertions/jsonschema.go +package assertions + +import jsonschema "github.com/go-openapi/testify/v2/internal/assertions/enable/jsonschema" + +func JSONSchemaValid(t T, schema, document []byte, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + if err := jsonschema.Validate(schema, document); err != nil { + return Fail(t, fmt.Sprintf("JSON Schema validation failed: %s", err), msgAndArgs...) + } + return true +} +``` + +### 5. Add integration tests + +Create `internal/testintegration/jsonschema/` with tests that import the feature module +and exercise the assertion end-to-end. Update `internal/testintegration/go.mod` to add +the new dependency. + +--- + +## Integration Testing + +Optional features require a separate Go module for end-to-end testing, since the main module +cannot import external dependencies. + +The `internal/testintegration/` module serves this purpose: + +``` +internal/testintegration/ +├── go.mod # Imports: yaml, colors, rapid +├── yaml/ # Tests YAMLEq with real YAML parser +├── colors/ # Tests colorized output with ANSI detection +└── spew/ # Property-based testing (unrelated to opt-in features) +``` + +See `internal/testintegration/README.md` for details on running these tests. + +--- + +## Module Map + +{{< mermaid align="center" zoom="true" >}} +graph LR + main["github.com/go-openapi/testify/v2
main module
zero dependencies"] + yaml_mod["enable/yaml/v2
go.yaml.in/yaml/v3"] + colors_mod["enable/colors/v2
golang.org/x/term"] + testint["internal/testintegration/v2
yaml + colors + rapid"] + + yaml_mod -- "replace =>" --> main + colors_mod -- "replace =>" --> main + testint -- "replace =>" --> main + + style main fill:#4a9eff,color:#fff + style yaml_mod fill:#90ee90,color:#000 + style colors_mod fill:#90ee90,color:#000 + style testint fill:#ffb6c1,color:#000 +{{< /mermaid >}} + +All feature modules use `replace` directives to point to the local main module during development. +In production, Go module resolution handles versioning automatically. diff --git a/docs/doc-site/project/maintainers/MAINTAINERS.md b/docs/doc-site/project/maintainers/MAINTAINERS.md index 0b59e5163..33cfe0acc 100644 --- a/docs/doc-site/project/maintainers/MAINTAINERS.md +++ b/docs/doc-site/project/maintainers/MAINTAINERS.md @@ -185,7 +185,7 @@ Reference documentation (released): [contributors-doc]: ../contributing/CONTRIBUTORS.md [contributing-doc]: ../contributing/CONTRIBUTING.md [dco-doc]: ../contributing/DCO.md -[style-doc]: ./STYLE.md +[style-doc]: ../contributing/STYLE.md [coc-doc]: ../contributing/CODE_OF_CONDUCT.md [security-doc]: ../SECURITY.md [license-doc]: ../LICENSE.md diff --git a/docs/doc-site/project/maintainers/ROADMAP.md b/docs/doc-site/project/maintainers/ROADMAP.md index 6d02c313f..80ea45ca9 100644 --- a/docs/doc-site/project/maintainers/ROADMAP.md +++ b/docs/doc-site/project/maintainers/ROADMAP.md @@ -1,7 +1,7 @@ --- title: "Roadmap" description: "Let's share our plans." -weight: 4 +weight: 5 --- ## What's next with this project? @@ -52,7 +52,7 @@ timeline 6. [x] Introduces colorization (opt-in) 7. [x] Introduces generics 8. [x] Realign behavior re quirks, bugs, unexpected logics ... (e.g. IsNonDecreasing, EventuallyWithT...) -10. [ ] Unclear assertions might be provided an alternative verb (e.g. `EventuallyWithT`) +10. [x] Unclear assertions might be provided an alternative verb (e.g. `EventuallyWithT`) ### Adoption timeline at go-openapi diff --git a/docs/doc-site/usage/CHANGES.md b/docs/doc-site/usage/CHANGES.md index 866327396..6d1535f85 100644 --- a/docs/doc-site/usage/CHANGES.md +++ b/docs/doc-site/usage/CHANGES.md @@ -8,7 +8,7 @@ weight: 15 **Key Changes:** - ✅ **Zero Dependencies**: Completely self-contained -- ✅ **New functions**: 51 additional assertions (38 generic + 13 reflection-based) +- ✅ **New functions**: 55 additional assertions (42 generic + 13 reflection-based) - ✅ **Performance**: ~10x for generic variants (from 1.2x to 81x, your mileage may vary) - ✅ **Breaking changes**: Requires go1.24, removed suites, mocks, http tooling, and deprecated functions. YAMLEq becomes optional (panics by default). @@ -16,7 +16,7 @@ weight: 15 **Testify v2 represents a comprehensive modernization** -- ✅ **Type Safety**: 38 generic assertions catch errors at compile time +- ✅ **Type Safety**: generic assertions catch errors at compile time - ✅ **Documentation**: compelling documentation site to search the API by use-case domain - ✅ **Maintainability**: 100% code generation from single source - ✅ **Quality**: 96% test coverage, use unified test scenarios, extensive fuzzing & benchmarking @@ -211,9 +211,10 @@ See also a quick [migration guide](./MIGRATION.md). | Change | Origin | Description | |--------|--------|-------------| -| Fixed goroutine leak | [#1611] | Consolidated `Eventually`, `Never`, and `EventuallyWithT` into single `pollCondition` function | +| Fixed goroutine leak | [#1611] | Consolidated `Eventually`, `Never`, and `EventuallyWith` into single `pollCondition` function | | Context-based polling | Internal refactoring | Reimplemented with context-based approach for better resource management | | Unified implementation | Internal refactoring | Single implementation eliminates code duplication and prevents resource leaks | +| **Renaming** | `EventuallyWithT` renamed into `EventuallyWith` (conflicted with the convention adopted for generics) | **Impact**: This fix eliminates goroutine leaks that could occur when using `Eventually` or `Never` assertions. The new implementation uses a context-based approach that properly manages resources and provides a cleaner shutdown mechanism. Callers should **NOT** assume that the call to `Eventually` or `Never` exits before the condition is evaluated. Callers should **NOT** assume that the call to `Eventually` or `Never` exits before the condition is evaluated. @@ -277,11 +278,13 @@ See also a quick [migration guide](./MIGRATION.md). {{% expand title="Generics" %}} -#### New Generic Function (1) +#### New Generic Functions (3) | Function | Type Parameters | Description | |----------|-----------------|-------------| | `JSONEqT[S Text]` | String or []byte | Type-safe JSON semantic equality | +| `JSONMarshalAsT[EDoc Text]` | String or []byte | Type-safe JSON marshal and equality check | +| `JSONUnmarshalAsT[ADoc Text, Object any]` | String or []byte | Type-safe JSON unmarshal and equality check | **Performance**: Comparable (JSON parsing dominates) {{% /expand %}} @@ -436,11 +439,13 @@ Removed extraneous type declaration `PanicTestFunc` (`func()`). {{% expand title="Generics" %}} -#### New Generic Function (1) +#### New Generic Functions (3) | Function | Type Parameters | Description | |----------|-----------------|-------------| | `YAMLEqT[S Text]` | String or []byte | Type-safe YAML semantic equality | +| `YAMLMarshalAsT[EDoc Text]` | String or []byte | Type-safe YAML marshal and equality check | +| `YAMLUnmarshalAsT[ADoc Text, Object any]` | String or []byte | Type-safe YAML unmarshal and equality check | **Performance**: Comparable (YAML parsing dominates) {{% /expand %}} @@ -458,6 +463,7 @@ Removed extraneous type declaration `PanicTestFunc` (`func()`). #### ⚠️ Behavior Changes **Architecture change**: YAML support is now opt-in via `import _ "github.com/go-openapi/testify/v2/enable/yaml"` + **Behavior changes**: None ## Other changes @@ -487,8 +493,8 @@ These affect the way the project is maintained, but not how it is used. #### Code Generation All assert and require packages are 100% generated from a single source: -- **Source**: `internal/assertions/` (~5,000 LOC) -- **Generated**: ~600+ functions across assert/require packages +- **Source**: `internal/assertions/` (~6,000 LOC) +- **Generated**: ~800+ functions across assert/require packages - **Variants**: 8 variants per assertion (assert/require x standard/format/forward/forward+format), 4 variants for generic assertions (assert/require x standard/format) @@ -527,13 +533,14 @@ github.com/go-openapi/testify/v2 # Core (zero deps) [go.mod] | Metric | Value | |--------|-------| -| **New functions** | 51 (38 generic + 13 reflection) | -| **Total assertions** | 76 base assertions | -| **Generated functions** | ~600 (76 × 8 variants - generics get 4 variants only) | -| **Generic coverage** | 10 domains | +| **New functions** | 55 (42 generic + 13 reflection) | +| **Total assertions** | 128 base assertions | +| **Generated functions** | ~800 (see [the +maths](../project/maintainers/ARCHITECTURE.md#the-maths-with-assertion-variants) +| **Generic coverage** | 10 domains (10/18) | | **Performance improvement** | 1.2x to 81x faster | | **Dependencies** | 0 external (was 2 required) | -| **Test coverage** | 96% overall, 100% on public APIs | +| **Test coverage** | 96% overall, 99% on public APIs | | **Documentation domains** | 18 logical categories | --- diff --git a/docs/doc-site/usage/EXAMPLES.md b/docs/doc-site/usage/EXAMPLES.md index b9ec83e0a..73124e0ad 100644 --- a/docs/doc-site/usage/EXAMPLES.md +++ b/docs/doc-site/usage/EXAMPLES.md @@ -524,7 +524,15 @@ func TestUserCompleteness(t *testing.T) { ### Asynchronous Testing -Testify provides three assertions for testing asynchronous code: `Eventually`, `Never`, and `EventuallyWithT`. +Testify provides three assertions for testing asynchronous code: `Eventually`, `Never`, and `EventuallyWith`. + +{{% notice warning %}} +> Asynchronous testing may sometimes be unavoidable. It should be avoided whenever possible. +> +> Async tests (with timeouts, ticks etc) may easily become flaky under heavy concurrence on small CI runners. +> +> When you've control over the code you test, always prefer sync tests, possibly with well-designed mocks. +{{% /notice %}} #### Eventually: Wait for a Condition to Become True @@ -645,9 +653,9 @@ func TestRateLimiter(t *testing.T) { } ``` -#### EventuallyWithT: Complex Async Assertions +#### EventuallyWith: Complex Async Assertions -Use `EventuallyWithT` when you need multiple assertions to pass together. +Use `EventuallyWith` when you need multiple assertions to pass together. The `CollectT` parameter lets you make regular assertions. ```go @@ -665,7 +673,7 @@ func TestAPIEventualConsistency(t *testing.T) { // Wait for the user to be fully replicated across all shards // All conditions must pass in the same tick - assert.EventuallyWithT(t, func(c *assert.CollectT) { + assert.EventuallyWith(t, func(c *assert.CollectT) { user, err := api.GetUser("alice") // All these assertions must pass together @@ -692,7 +700,7 @@ func TestDistributedCacheSync(t *testing.T) { primary.Set("key", "value", 5*time.Minute) // Verify value propagates to all replicas with correct TTL - assert.EventuallyWithT(t, func(c *assert.CollectT) { + assert.EventuallyWith(t, func(c *assert.CollectT) { val1, ttl1, ok1 := replica1.Get("key") val2, ttl2, ok2 := replica2.Get("key") @@ -713,11 +721,11 @@ func TestDistributedCacheSync(t *testing.T) { "cache value should replicate to all nodes with correct TTL") } -// Advanced: Using require in EventuallyWithT to fail fast +// Advanced: Using require in EventuallyWith to fail fast func TestEventuallyWithRequire(t *testing.T) { api := NewAPI() - assert.EventuallyWithT(t, func(c *assert.CollectT) { + assert.EventuallyWith(t, func(c *assert.CollectT) { resp, err := api.HealthCheck() // Use require to stop checking this tick if request fails @@ -739,15 +747,57 @@ func TestEventuallyWithRequire(t *testing.T) { **Key differences:** - **Eventually**: Simple boolean condition, use for single checks - **Never**: Opposite of Eventually, verifies condition stays false -- **EventuallyWithT**: Complex checks with multiple assertions, use when you need detailed failure messages +- **EventuallyWith**: Complex checks with multiple assertions, use when you need detailed failure messages **Best practices:** 1. Choose appropriate timeouts: long enough for async operations, short enough for fast test feedback 2. Choose appropriate tick intervals: frequent enough to catch state changes, infrequent enough to avoid overhead -3. Use `EventuallyWithT` when you need to understand *which* assertion failed +3. Use `EventuallyWith` when you need to understand *which* assertion failed 4. Use `Eventually` for simple boolean conditions (faster, simpler) 5. Use `Never` to verify invariants over time (no race conditions, no invalid state) +### Extensible assertions + +The `Assertions` type may be extended to fit your needs like so. + +```go + import ( + "fmt" + "strings" + + "github.com/go-openapi/testify/v2/assert" + ) + + // Assertions is a customized version of [assert.Assertions]. + type Assertions struct { + *assert.Assertions + } + + func New(t assert.T) *Assertions { + return &Assertions{ + Assertions: assert.New(t), + } + } + + // StartsWith asserts that the string starts with the given prefix. + // + // Examples: + // + // success: "hello world", "hello" + // failure: "hello world", "bye" + func (a *Assertions) StartsWith(str, prefix string, msgAndArgs ...any) bool { + if h, ok := a.T.(assert.H); ok { + h.Helper() // preserve the original failing location + } + + if !strings.HasPrefix(str, prefix) { + return a.Fail(fmt.Sprintf("Expected %q to start with %q", str, prefix), msgAndArgs...) + } + + return true + } +``` + --- ## YAML Support (Optional) diff --git a/docs/doc-site/usage/GENERICS.md b/docs/doc-site/usage/GENERICS.md index e59748af7..446f9e696 100644 --- a/docs/doc-site/usage/GENERICS.md +++ b/docs/doc-site/usage/GENERICS.md @@ -4,10 +4,13 @@ description: Using generic assertions. weight: 10 --- -Testify v2 provides **38 generic assertion functions** that offer compile-time type safety alongside the traditional reflection-based assertions. Generic variants are identified by the `T` suffix (e.g., `EqualT`, `GreaterT`, `ElementsMatchT`). +Testify v2 provides **42 generic assertion functions** that offer compile-time type safety alongside +the traditional reflection-based assertions. Generic variants are identified by the `T` suffix +(e.g., `EqualT`, `GreaterT`, `ElementsMatchT`). {{% notice style="success" title="Type Safety First" icon="check" %}} -Generic assertions catch type mismatches **when writing tests**, not when running them. The performance improvements (1.2x-81x faster) are a bonus on top of this primary benefit. +Generic assertions catch type mismatches **when writing tests**, not when running them. +The performance improvements (1.2x-81x faster) are a bonus on top of this primary benefit. {{% /notice %}} ## Quick Start diff --git a/docs/doc-site/usage/USAGE.md b/docs/doc-site/usage/USAGE.md index 9e39c948f..214fd83c7 100644 --- a/docs/doc-site/usage/USAGE.md +++ b/docs/doc-site/usage/USAGE.md @@ -123,8 +123,6 @@ The `f` suffix follows Go's standard library convention (like `Printf`, `Errorf` - **Generic (`T` suffix)**: Prefer for compile-time type safety and better performance {{% /notice %}} -One (historical) exception: `EventuallyWithT` is not generic... - ### Inverse Assertions Most assertions come with their opposite variant, typically formed by adding a `Not` prefix: @@ -269,10 +267,16 @@ When unsure about argument order: ### Forward Methods -Create an `Assertion` object to reduce repetition in tests with many assertions: +Create an `Assertions` object to reduce repetition in tests with many assertions. -{{< cards >}} -{{% card title="Package-Level Functions" %}} +**Both styles are equivalent** - choose based on your preference and test structure. + +**⚠️ Generic assertions are not directly available as forward methods** (this is a limitation of go generics). + +However, the forward-style assertion may use generic assertions like shown below. + +{{< tabs >}} +{{% tab title="Package-Level Functions" %}} ```go import ( "testing" @@ -289,9 +293,9 @@ func TestUser(t *testing.T) { assert.Greater(t, user.Age, 0) } ``` -{{% /card %}} +{{% /tab %}} -{{% card title="Forward Methods" %}} +{{% tab title="Forward Methods" %}} ```go import ( "testing" @@ -308,13 +312,30 @@ func TestUser(t *testing.T) { a.True(user.Active) } ``` -{{% /card %}} -{{< /cards >}} +{{% /tab %}} -**Both styles are equivalent** - choose based on your preference and test structure. +{{% tab title="Forward Methods with generics" %}} + +This pattern overcomes the go limitation so you may use a "forward-style" and still benefit from generics. + +```go +import ( + "testing" -**⚠️ Generic assertions are not available as forward methods** (this is a limitation of go generics). + "github.com/go-openapi/testify/v2/assert" +) + +func TestUser(t *testing.T) { + a := assert.New(t) // Create once + user := getUser() + a.NotNil(user) // No 't' needed + assert.EqualT(a.T, "Alice", user.Name) // use a.T as an escape hatch to call generics + a.True(user.Active) +} +``` +{{% /tab %}} +{{< /tabs >}} ## Common Usage Patterns diff --git a/enable/colors/disable_testintegration.go b/enable/colors/disable_testintegration.go new file mode 100644 index 000000000..1c605c0ae --- /dev/null +++ b/enable/colors/disable_testintegration.go @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +//go:build !testcolorized + +package colors + +// integrationTestHatch is specifically designed to force colorized settings in integration tests. +// When no build tag "testcolorized" is defined (the default), it returns false and is a noop. +func integrationTestHatch() bool { + return false +} diff --git a/enable/colors/enable.go b/enable/colors/enable.go index c034b7a6b..cd84c38ea 100644 --- a/enable/colors/enable.go +++ b/enable/colors/enable.go @@ -15,9 +15,10 @@ import ( ) const ( - envVarColorize = "TESTIFY_COLORIZED" - envVarTheme = "TESTIFY_THEME" - envVarNoTTY = "TESTIFY_COLORIZED_NOTTY" + envVarColorize = "TESTIFY_COLORIZED" + envVarTheme = "TESTIFY_THEME" + envVarNoTTY = "TESTIFY_COLORIZED_NOTTY" + envVarTestHatch = "TESTIFY_INTEGRATION_TEST" // escape hatch to enable init from CI ) var flags cliFlags //nolint:gochecknoglobals // it's okay to store the state CLI flags in a package global @@ -48,7 +49,7 @@ func colorizeFromEnv() bool { envColorize := os.Getenv(envVarColorize) isEnvConfigured, _ := strconv.ParseBool(envColorize) - return isEnvConfigured + return isEnvConfigured || integrationTestHatch() } func themeFromEnv() string { @@ -61,5 +62,5 @@ func nottyFromEnv() bool { envNoTTY := os.Getenv(envVarNoTTY) isEnvNoTTY, _ := strconv.ParseBool(envNoTTY) - return isEnvNoTTY + return isEnvNoTTY || integrationTestHatch() } diff --git a/enable/colors/enable_testintegration.go b/enable/colors/enable_testintegration.go new file mode 100644 index 000000000..5e1b91a1c --- /dev/null +++ b/enable/colors/enable_testintegration.go @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +//go:build testcolorized + +package colors + +// integrationTestHatch is specifically designed to force colorized settings in integration tests. +// When build tag "testcolorized" is defined, it returns true and colorization is enabled, regardless +// of command line arguments or environment. +func integrationTestHatch() bool { + return true +} diff --git a/enable/stubs/yaml/enable_yaml.go b/enable/stubs/yaml/enable_yaml.go index 55aec1921..cfc442c67 100644 --- a/enable/stubs/yaml/enable_yaml.go +++ b/enable/stubs/yaml/enable_yaml.go @@ -26,3 +26,10 @@ import ( func EnableYAMLWithUnmarshal(unmarshaller func([]byte, any) error) { yamlstub.EnableYAMLWithUnmarshal(unmarshaller) } + +// EnableYAMLWithMarshal registers a YAML-capable marshaler. +// +// See [EnableYAMLWithUnmarshal]. +func EnableYAMLWithMarshal(marshaller func(any) ([]byte, error)) { + yamlstub.EnableYAMLWithMarshal(marshaller) +} diff --git a/enable/stubs/yaml/enable_yaml_test.go b/enable/stubs/yaml/enable_yaml_test.go index 73d5af724..73abd3448 100644 --- a/enable/stubs/yaml/enable_yaml_test.go +++ b/enable/stubs/yaml/enable_yaml_test.go @@ -10,13 +10,26 @@ import ( target "github.com/go-openapi/testify/v2/assert" ) +// TestEnableYAML is merely a smoke test to validate that the chain of calls is resolved properly. func TestEnableYAML(t *testing.T) { t.Parallel() EnableYAMLWithUnmarshal(func(_ []byte, _ any) error { return fmt.Errorf("called: %w", target.ErrTest) }) + EnableYAMLWithMarshal(func(_ any) ([]byte, error) { + return nil, fmt.Errorf("called: %w", target.ErrTest) + }) + type dummy struct { + Hello string `yaml:"hello"` + Foo string `yaml:"foo"` + } + value := dummy{Hello: "world", Foo: "bar"} mock := new(testing.T) target.False(t, target.YAMLEq(mock, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`)) + + // the struct is correct, but we inject a serializer that always fails + target.False(t, target.YAMLUnmarshalAsT(mock, value, `{"hello": "world", "foo": "bar"}`)) + target.False(t, target.YAMLMarshalAsT(mock, `{"hello": "world", "foo": "bar"}`, value)) } diff --git a/enable/yaml/assertions_test.go b/enable/yaml/assertions_test.go index 24bd6f6d0..f36f5cff1 100644 --- a/enable/yaml/assertions_test.go +++ b/enable/yaml/assertions_test.go @@ -12,78 +12,78 @@ import ( func TestYAMLEq_EqualYAMLString(t *testing.T) { t.Parallel() - mockT := new(testing.T) - target.True(t, target.YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`)) + mock := new(testing.T) + target.True(t, target.YAMLEq(mock, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`)) } func TestYAMLEq_EquivalentButNotEqual(t *testing.T) { t.Parallel() - mockT := new(testing.T) - target.True(t, target.YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) + mock := new(testing.T) + target.True(t, target.YAMLEq(mock, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) } func TestYAMLEq_HashOfArraysAndHashes(t *testing.T) { t.Parallel() - mockT := new(testing.T) - target.True(t, target.YAMLEq(mockT, expectedYAML, actualYAML)) + mock := new(testing.T) + target.True(t, target.YAMLEq(mock, expectedYAML, actualYAML)) } func TestYAMLEq_Array(t *testing.T) { t.Parallel() - mockT := new(testing.T) - target.True(t, target.YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`)) + mock := new(testing.T) + target.True(t, target.YAMLEq(mock, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`)) } func TestYAMLEq_HashAndArrayNotEquivalent(t *testing.T) { t.Parallel() - mockT := new(testing.T) - target.False(t, target.YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`)) + mock := new(testing.T) + target.False(t, target.YAMLEq(mock, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`)) } func TestYAMLEq_HashesNotEquivalent(t *testing.T) { t.Parallel() - mockT := new(testing.T) - target.False(t, target.YAMLEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) + mock := new(testing.T) + target.False(t, target.YAMLEq(mock, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) } func TestYAMLEq_ActualIsSimpleString(t *testing.T) { t.Parallel() - mockT := new(testing.T) - target.False(t, target.YAMLEq(mockT, `{"foo": "bar"}`, "Simple String")) + mock := new(testing.T) + target.False(t, target.YAMLEq(mock, `{"foo": "bar"}`, "Simple String")) } func TestYAMLEq_ExpectedIsSimpleString(t *testing.T) { t.Parallel() - mockT := new(testing.T) - target.False(t, target.YAMLEq(mockT, "Simple String", `{"foo": "bar", "hello": "world"}`)) + mock := new(testing.T) + target.False(t, target.YAMLEq(mock, "Simple String", `{"foo": "bar", "hello": "world"}`)) } func TestYAMLEq_ExpectedAndActualSimpleString(t *testing.T) { t.Parallel() - mockT := new(testing.T) - target.True(t, target.YAMLEq(mockT, "Simple String", "Simple String")) + mock := new(testing.T) + target.True(t, target.YAMLEq(mock, "Simple String", "Simple String")) } func TestYAMLEq_ArraysOfDifferentOrder(t *testing.T) { t.Parallel() - mockT := new(testing.T) - target.False(t, target.YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`)) + mock := new(testing.T) + target.False(t, target.YAMLEq(mock, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`)) } func TestYAMLEq_OnlyFirstDocument(t *testing.T) { t.Parallel() - mockT := new(testing.T) - target.True(t, target.YAMLEq(mockT, + mock := new(testing.T) + target.True(t, target.YAMLEq(mock, `--- doc1: same --- @@ -100,6 +100,23 @@ doc2: notsame func TestYAMLEq_InvalidIdenticalYAML(t *testing.T) { t.Parallel() - mockT := new(testing.T) - target.False(t, target.YAMLEq(mockT, `}`, `}`)) + mock := new(testing.T) + target.False(t, target.YAMLEq(mock, `}`, `}`)) +} + +func TestYAMLUnmarshalAs(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + type dummy struct { + Hello string `yaml:"hello"` + Foo string `yaml:"foo"` + } + + value := dummy{Hello: "world", Foo: "bar"} + target.False(t, target.YAMLUnmarshalAsT(mock, value, `{"hello": "buzz", "foo": "lightyear"}`)) + target.False(t, target.YAMLMarshalAsT(mock, `{"hello": "buzz", "foo": "lightyear"}`, value)) + + target.True(t, target.YAMLUnmarshalAsT(mock, value, `{"hello": "world", "foo": "bar"}`)) + target.True(t, target.YAMLMarshalAsT(mock, `{"hello": "world", "foo": "bar"}`, value)) } diff --git a/enable/yaml/enable_yaml.go b/enable/yaml/enable_yaml.go index 0aa08960b..23fff61eb 100644 --- a/enable/yaml/enable_yaml.go +++ b/enable/yaml/enable_yaml.go @@ -11,4 +11,5 @@ import ( func init() { //nolint:gochecknoinits // we precisely want this init to run when importing the package yamlstub.EnableYAMLWithUnmarshal(yaml.Unmarshal) + yamlstub.EnableYAMLWithMarshal(yaml.Marshal) } diff --git a/enable/yaml/forward_requirements_test.go b/enable/yaml/forward_requirements_test.go index 4e3beccef..5d6144e7e 100644 --- a/enable/yaml/forward_requirements_test.go +++ b/enable/yaml/forward_requirements_test.go @@ -12,11 +12,11 @@ import ( func TestRequireYAMLEqWrapper_EqualYAMLString(t *testing.T) { t.Parallel() - mockT := new(MockT) - mockRequire := target.New(mockT) + mock := new(MockT) + mockRequire := target.New(mock) mockRequire.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) - if mockT.Failed { + if mock.Failed { t.Error("Check should pass") } } @@ -24,11 +24,11 @@ func TestRequireYAMLEqWrapper_EqualYAMLString(t *testing.T) { func TestRequireYAMLEqWrapper_EquivalentButNotEqual(t *testing.T) { t.Parallel() - mockT := new(MockT) - mockRequire := target.New(mockT) + mock := new(MockT) + mockRequire := target.New(mock) mockRequire.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) - if mockT.Failed { + if mock.Failed { t.Error("Check should pass") } } @@ -36,8 +36,8 @@ func TestRequireYAMLEqWrapper_EquivalentButNotEqual(t *testing.T) { func TestRequireYAMLEqWrapper_HashOfArraysAndHashes(t *testing.T) { t.Parallel() - mockT := new(MockT) - mockRequire := target.New(mockT) + mock := new(MockT) + mockRequire := target.New(mock) expected := ` numeric: 1.5 @@ -66,7 +66,7 @@ array: ` mockRequire.YAMLEq(expected, actual) - if mockT.Failed { + if mock.Failed { t.Error("Check should pass") } } @@ -74,11 +74,11 @@ array: func TestRequireYAMLEqWrapper_Array(t *testing.T) { t.Parallel() - mockT := new(MockT) - mockRequire := target.New(mockT) + mock := new(MockT) + mockRequire := target.New(mock) mockRequire.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) - if mockT.Failed { + if mock.Failed { t.Error("Check should pass") } } @@ -86,11 +86,11 @@ func TestRequireYAMLEqWrapper_Array(t *testing.T) { func TestRequireYAMLEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { t.Parallel() - mockT := new(MockT) - mockRequire := target.New(mockT) + mock := new(MockT) + mockRequire := target.New(mock) mockRequire.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) - if !mockT.Failed { + if !mock.Failed { t.Error("Check should fail") } } @@ -98,11 +98,11 @@ func TestRequireYAMLEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { func TestRequireYAMLEqWrapper_HashesNotEquivalent(t *testing.T) { t.Parallel() - mockT := new(MockT) - mockRequire := target.New(mockT) + mock := new(MockT) + mockRequire := target.New(mock) mockRequire.YAMLEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) - if !mockT.Failed { + if !mock.Failed { t.Error("Check should fail") } } @@ -110,11 +110,11 @@ func TestRequireYAMLEqWrapper_HashesNotEquivalent(t *testing.T) { func TestRequireYAMLEqWrapper_ActualIsSimpleString(t *testing.T) { t.Parallel() - mockT := new(MockT) - mockRequire := target.New(mockT) + mock := new(MockT) + mockRequire := target.New(mock) mockRequire.YAMLEq(`{"foo": "bar"}`, "Simple String") - if !mockT.Failed { + if !mock.Failed { t.Error("Check should fail") } } @@ -122,11 +122,11 @@ func TestRequireYAMLEqWrapper_ActualIsSimpleString(t *testing.T) { func TestRequireYAMLEqWrapper_ExpectedIsSimpleString(t *testing.T) { t.Parallel() - mockT := new(MockT) - mockRequire := target.New(mockT) + mock := new(MockT) + mockRequire := target.New(mock) mockRequire.YAMLEq("Simple String", `{"foo": "bar", "hello": "world"}`) - if !mockT.Failed { + if !mock.Failed { t.Error("Check should fail") } } @@ -134,11 +134,11 @@ func TestRequireYAMLEqWrapper_ExpectedIsSimpleString(t *testing.T) { func TestRequireYAMLEqWrapper_ExpectedAndActualSimpleString(t *testing.T) { t.Parallel() - mockT := new(MockT) - mockRequire := target.New(mockT) + mock := new(MockT) + mockRequire := target.New(mock) mockRequire.YAMLEq("Simple String", "Simple String") - if mockT.Failed { + if mock.Failed { t.Error("Check should pass") } } @@ -146,11 +146,11 @@ func TestRequireYAMLEqWrapper_ExpectedAndActualSimpleString(t *testing.T) { func TestRequireYAMLEqWrapper_ArraysOfDifferentOrder(t *testing.T) { t.Parallel() - mockT := new(MockT) - mockRequire := target.New(mockT) + mock := new(MockT) + mockRequire := target.New(mock) mockRequire.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) - if !mockT.Failed { + if !mock.Failed { t.Error("Check should fail") } } diff --git a/enable/yaml/requirements_test.go b/enable/yaml/requirements_test.go index c195cb698..e9680f0d5 100644 --- a/enable/yaml/requirements_test.go +++ b/enable/yaml/requirements_test.go @@ -137,6 +137,20 @@ func TestRequireYAMLEq_ArraysOfDifferentOrder(t *testing.T) { } } +func TestRequireYAMLUnmarshalAsWrapper(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + type dummy struct { + Hello string `yaml:"hello"` + Foo string `yaml:"foo"` + } + + value := dummy{Hello: "world", Foo: "bar"} + target.YAMLUnmarshalAsT(mock, value, `{"hello": "world", "foo": "bar"}`) + target.YAMLMarshalAsT(mock, `{"hello": "world", "foo": "bar"}`, value) +} + type MockT struct { Failed bool } diff --git a/internal/assertions/assertion.go b/internal/assertions/assertion.go deleted file mode 100644 index a2dbd34ac..000000000 --- a/internal/assertions/assertion.go +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -package assertions - -// Assertions provides assertion methods around the [T] interface. -type Assertions struct { - t T //nolint:unused // the internal version of this type doesn't use this field, but generated copies do. -} diff --git a/internal/assertions/boolean.go b/internal/assertions/boolean.go index defc25728..563d64e4c 100644 --- a/internal/assertions/boolean.go +++ b/internal/assertions/boolean.go @@ -95,7 +95,7 @@ func FalseT[B Boolean](t T, value B, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return Fail(t, "Should be true", msgAndArgs...) + return Fail(t, "Should be false", msgAndArgs...) } return true diff --git a/internal/assertions/boolean_test.go b/internal/assertions/boolean_test.go index f77f1170c..ccb48c2dc 100644 --- a/internal/assertions/boolean_test.go +++ b/internal/assertions/boolean_test.go @@ -3,12 +3,16 @@ package assertions -import "testing" +import ( + "iter" + "slices" + "testing" +) func TestBooleanTrue(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) if !True(mock, true) { t.Error("True should return true") @@ -22,7 +26,7 @@ func TestBooleanTrue(t *testing.T) { func TestBooleanFalse(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) if !False(mock, false) { t.Error("False should return true") @@ -65,3 +69,34 @@ func TestBooleanTrueTFalseT(t *testing.T) { } }) } + +func TestBooleanErrorMessages(t *testing.T) { + t.Parallel() + + runFailCases(t, booleanFailCases()) +} + +func booleanFailCases() iter.Seq[failCase] { + return slices.Values([]failCase{ + { + name: "True/false-value", + assertion: func(t T) bool { return True(t, false) }, + wantError: "Should be true", + }, + { + name: "False/true-value", + assertion: func(t T) bool { return False(t, true) }, + wantError: "Should be false", + }, + { + name: "TrueT/false-value", + assertion: func(t T) bool { return TrueT(t, false) }, + wantError: "Should be true", + }, + { + name: "FalseT/true-value", + assertion: func(t T) bool { return FalseT(t, true) }, + wantError: "Should be false", + }, + }) +} diff --git a/internal/assertions/collection.go b/internal/assertions/collection.go index 2d1e3682e..0570de5b7 100644 --- a/internal/assertions/collection.go +++ b/internal/assertions/collection.go @@ -33,7 +33,7 @@ import ( func Len(t T, object any, length int, msgAndArgs ...any) bool { // Domain: collection // - // Note: (proposals) this does not currently support iterators, or collection objects that have a Len() method. + // NOTE: (proposal for enhancement) this does not currently support iterators, or collection objects that have a Len() method. if h, ok := t.(H); ok { h.Helper() } diff --git a/internal/assertions/collection_impl_test.go b/internal/assertions/collection_impl_test.go index d594d6029..6f7191258 100644 --- a/internal/assertions/collection_impl_test.go +++ b/internal/assertions/collection_impl_test.go @@ -24,49 +24,28 @@ func testContainsElement() func(*testing.T) { list2 := []int{1, 2} simpleMap := map[any]any{"Foo": "Bar"} - ok, found := containsElement("Hello World", "World") - True(t, ok) - True(t, found) - - ok, found = containsElement(list1, "Foo") - True(t, ok) - True(t, found) - - ok, found = containsElement(list1, "Bar") - True(t, ok) - True(t, found) - - ok, found = containsElement(list2, 1) - True(t, ok) - True(t, found) - - ok, found = containsElement(list2, 2) - True(t, ok) - True(t, found) - - ok, found = containsElement(list1, "Foo!") - True(t, ok) - False(t, found) - - ok, found = containsElement(list2, 3) - True(t, ok) - False(t, found) - - ok, found = containsElement(list2, "1") - True(t, ok) - False(t, found) - - ok, found = containsElement(simpleMap, "Foo") - True(t, ok) - True(t, found) - - ok, found = containsElement(simpleMap, "Bar") - True(t, ok) - False(t, found) + checkContains := func(list any, elem any, expectOK, expectFound bool) { + t.Helper() + ok, found := containsElement(list, elem) + if ok != expectOK { + t.Errorf("containsElement(%v, %v): expected ok=%v, got %v", list, elem, expectOK, ok) + } + if found != expectFound { + t.Errorf("containsElement(%v, %v): expected found=%v, got %v", list, elem, expectFound, found) + } + } - ok, found = containsElement(1433, "1") - False(t, ok) - False(t, found) + checkContains("Hello World", "World", true, true) + checkContains(list1, "Foo", true, true) + checkContains(list1, "Bar", true, true) + checkContains(list2, 1, true, true) + checkContains(list2, 2, true, true) + checkContains(list1, "Foo!", true, false) + checkContains(list2, 3, true, false) + checkContains(list2, "1", true, false) + checkContains(simpleMap, "Foo", true, true) + checkContains(simpleMap, "Bar", true, false) + checkContains(1433, "1", false, false) } } @@ -76,14 +55,22 @@ func testGetLen() func(*testing.T) { for v := range collectionImplGetLenFalseCases() { l, ok := getLen(v) - False(t, ok, "Expected getLen fail to get length of %#v", v) - Equal(t, 0, l, "getLen should return 0 for %#v", v) + if ok { + t.Errorf("expected getLen to fail for %#v", v) + } + if l != 0 { + t.Errorf("expected getLen to return 0 for %#v, got %d", v, l) + } } for c := range collectionImplGetLenTrueCases() { l, ok := getLen(c.v) - True(t, ok, "Expected getLen success to get length of %#v", c.v) - Equal(t, c.l, l) + if !ok { + t.Errorf("expected getLen to succeed for %#v", c.v) + } + if c.l != l { + t.Errorf("expected length %d for %#v, got %d", c.l, c.v, l) + } } } } diff --git a/internal/assertions/collection_test.go b/internal/assertions/collection_test.go index d43718310..5ba8ff135 100644 --- a/internal/assertions/collection_test.go +++ b/internal/assertions/collection_test.go @@ -7,58 +7,12 @@ import ( "errors" "fmt" "iter" - "path/filepath" "reflect" - "runtime" "slices" "strings" "testing" ) -// TestCollectionErrorMessages tests error message formatting for collection assertions. -func TestCollectionErrorMessages(t *testing.T) { - t.Parallel() - - t.Run("should render when value is too long to print", testCollectionTooLongToPrint()) - - // Test specific error messages for Contains/NotContains failures - t.Run("should fail with error message", func(t *testing.T) { - t.Parallel() - - for c := range containsFailMessageCases() { - name := filepath.Base(runtime.FuncForPC(reflect.ValueOf(c.assertion).Pointer()).Name()) - t.Run(fmt.Sprintf("%v(%T, %T)", name, c.container, c.instance), func(t *testing.T) { - t.Parallel() - mock := new(mockT) - - c.assertion(mock, c.container, c.instance) - actualFail := mock.errorString() - if !strings.Contains(actualFail, c.expected) { - t.Errorf("failure should include %q but was %q", c.expected, actualFail) - } - }) - } - }) - - // Test nil container error message - t.Run("with Contains on nil value", func(t *testing.T) { - t.Parallel() - mock := new(mockT) - - Contains(mock, nil, "key") - expectedFail := " could not be applied builtin len()" - actualFail := mock.errorString() - if !strings.Contains(actualFail, expectedFail) { - t.Errorf("Contains failure should include %q but was %q", expectedFail, actualFail) - } - - NotContains(mock, nil, "key") - if !strings.Contains(actualFail, expectedFail) { - t.Errorf("NotContains failure should include %q but was %q", expectedFail, actualFail) - } - }) -} - // TestCollectionLen tests the Len assertion. func TestCollectionLen(t *testing.T) { t.Parallel() @@ -160,6 +114,13 @@ func TestCollectionElementsMatch(t *testing.T) { } } +// TestCollectionErrorMessages tests error message formatting for collection assertions. +func TestCollectionErrorMessages(t *testing.T) { + t.Parallel() + + runFailCases(t, collectionErrorMessageCases()) +} + // ============================================================================ // TestCollectionLen // ============================================================================ @@ -946,127 +907,115 @@ func unifiedElementsMatchCases() iter.Seq[elementsMatchTestCase] { // TestCollectionErrorMessages // ============================================================================ -func testCollectionTooLongToPrint() func(*testing.T) { - longSlice := make([]int, 1_000_000) - - return func(t *testing.T) { - t.Run("with Nil", func(t *testing.T) { - t.Parallel() - mock := new(mockT) - - Nil(mock, &longSlice) - Contains(t, mock.errorString(), `Expected nil, but got: &[]int{0, 0, 0,`) - Contains(t, mock.errorString(), `<... truncated>`) - }) - - t.Run("with Empty", func(t *testing.T) { - t.Parallel() - mock := new(mockT) - - Empty(mock, longSlice) - Contains(t, mock.errorString(), `Should be empty, but was [0 0 0`) - Contains(t, mock.errorString(), `<... truncated>`) - }) - - t.Run("with Contains", func(t *testing.T) { - t.Parallel() - mock := new(mockT) - - Contains(mock, longSlice, 1) - Contains(t, mock.errorString(), `[]int{0, 0, 0,`) - Contains(t, mock.errorString(), `<... truncated> does not contain 1`) - }) - - t.Run("with NotContains", func(t *testing.T) { - t.Parallel() - mock := new(mockT) - - NotContains(mock, longSlice, 0) - Contains(t, mock.errorString(), `[]int{0, 0, 0,`) - Contains(t, mock.errorString(), `<... truncated> should not contain 0`) - }) - - t.Run("with Subset/slice", func(t *testing.T) { - t.Parallel() - mock := new(mockT) - - Subset(mock, longSlice, []int{1}) - Contains(t, mock.errorString(), `[]int{0, 0, 0,`) - Contains(t, mock.errorString(), `<... truncated> does not contain 1`) - }) - - t.Run("with Subset/map", func(t *testing.T) { - t.Parallel() - mock := new(mockT) - - Subset(mock, map[bool][]int{true: longSlice}, map[bool][]int{false: longSlice}) - Contains(t, mock.errorString(), `map[bool][]int{true:[]int{0, 0, 0,`) - Contains(t, mock.errorString(), `<... truncated> does not contain map[bool][]int{false:[]int{0, 0, 0,`) - }) - - t.Run("with NotSubset/slice", func(t *testing.T) { - t.Parallel() - mock := new(mockT) - - NotSubset(mock, longSlice, longSlice) - Contains(t, mock.errorString(), `['\x00' '\x00' '\x00'`) - Contains(t, mock.errorString(), `<... truncated> is a subset of ['\x00' '\x00' '\x00'`) - }) - - t.Run("with NotSubset/map", func(t *testing.T) { - t.Parallel() - mock := new(mockT) - - NotSubset(mock, map[int][]int{1: longSlice}, map[int][]int{1: longSlice}) - Contains(t, mock.errorString(), `map['\x01':['\x00' '\x00' '\x00'`) - Contains(t, mock.errorString(), `<... truncated> is a subset of map['\x01':['\x00' '\x00' '\x00'`) - }) - } -} - -type containsFailMessageCase struct { - assertion func(t T, s, contains any, msgAndArgs ...any) bool - container any - instance any - expected string -} - -func containsFailMessageCases() iter.Seq[containsFailMessageCase] { +func collectionErrorMessageCases() iter.Seq[failCase] { const pkg = "assertions" type nonContainer struct { Value string } + longSlice := make([]int, 1_000_000) - return slices.Values([]containsFailMessageCase{ + return slices.Values([]failCase{ + // Contains/NotContains fail messages { - assertion: Contains, - container: "Hello World", - instance: errors.New("Hello"), - expected: `"Hello World" does not contain &errors.errorString{s:"Hello"}`, + name: "Contains(string, error)", + assertion: func(t T) bool { return Contains(t, "Hello World", errors.New("Hello")) }, + wantContains: []string{`"Hello World" does not contain &errors.errorString{s:"Hello"}`}, }, { - assertion: Contains, - container: map[string]int{"one": 1}, - instance: "two", - expected: `map[string]int{"one":1} does not contain "two"` + "\n", + name: "Contains(map, missing-key)", + assertion: func(t T) bool { return Contains(t, map[string]int{"one": 1}, "two") }, + wantContains: []string{`map[string]int{"one":1} does not contain "two"`}, }, { - assertion: NotContains, - container: map[string]int{"one": 1}, - instance: "one", - expected: `map[string]int{"one":1} should not contain "one"`, + name: "NotContains(map, present-key)", + assertion: func(t T) bool { return NotContains(t, map[string]int{"one": 1}, "one") }, + wantContains: []string{`map[string]int{"one":1} should not contain "one"`}, }, { - assertion: Contains, - container: nonContainer{Value: "Hello"}, - instance: "Hello", - expected: pkg + `.nonContainer{Value:"Hello"} could not be applied builtin len()` + "\n", + name: "Contains(nonContainer, string)", + assertion: func(t T) bool { + return Contains(t, nonContainer{Value: "Hello"}, "Hello") + }, + wantContains: []string{pkg + `.nonContainer{Value:"Hello"} could not be applied builtin len()`}, }, { - assertion: NotContains, - container: nonContainer{Value: "Hello"}, - instance: "Hello", - expected: pkg + `.nonContainer{Value:"Hello"} could not be applied builtin len()` + "\n", + name: "NotContains(nonContainer, string)", + assertion: func(t T) bool { + return NotContains(t, nonContainer{Value: "Hello"}, "Hello") + }, + wantContains: []string{pkg + `.nonContainer{Value:"Hello"} could not be applied builtin len()`}, + }, + + // nil container + { + name: "Contains(nil, key)", + assertion: func(t T) bool { return Contains(t, nil, "key") }, + wantContains: []string{" could not be applied builtin len()"}, + }, + { + name: "NotContains(nil, key)", + assertion: func(t T) bool { return NotContains(t, nil, "key") }, + wantContains: []string{" could not be applied builtin len()"}, + }, + + // truncation: too long to print + truncationCase("truncation/Nil(longSlice)", func(t T) bool { + return Nil(t, &longSlice) + }), + truncationCase("truncation/Empty(longSlice)", func(t T) bool { + return Empty(t, longSlice) + }), + { + name: "truncation/Contains(longSlice, 1)", + assertion: func(t T) bool { return Contains(t, longSlice, 1) }, + wantContains: []string{ + `[]int{0, 0, 0,`, + `<... truncated> does not contain 1`, + }, + }, + { + name: "truncation/NotContains(longSlice, 0)", + assertion: func(t T) bool { return NotContains(t, longSlice, 0) }, + wantContains: []string{ + `[]int{0, 0, 0,`, + `<... truncated> should not contain 0`, + }, + }, + { + name: "truncation/Subset(longSlice, [1])", + assertion: func(t T) bool { return Subset(t, longSlice, []int{1}) }, + wantContains: []string{ + `[]int{0, 0, 0,`, + `<... truncated> does not contain 1`, + }, + }, + { + name: "truncation/Subset(map-longSlice)", + assertion: func(t T) bool { + return Subset(t, map[bool][]int{true: longSlice}, map[bool][]int{false: longSlice}) + }, + wantContains: []string{ + `map[bool][]int{true:[]int{0, 0, 0,`, + `<... truncated> does not contain map[bool][]int{false:[]int{0, 0, 0,`, + }, + }, + { + name: "truncation/NotSubset(longSlice)", + assertion: func(t T) bool { return NotSubset(t, longSlice, longSlice) }, + wantContains: []string{ + `['\x00' '\x00' '\x00'`, + `<... truncated> is a subset of ['\x00' '\x00' '\x00'`, + }, + }, + { + name: "truncation/NotSubset(map-longSlice)", + assertion: func(t T) bool { + return NotSubset(t, map[int][]int{1: longSlice}, map[int][]int{1: longSlice}) + }, + wantContains: []string{ + `map['\x01':['\x00' '\x00' '\x00'`, + `<... truncated> is a subset of map['\x01':['\x00' '\x00' '\x00'`, + }, }, }) } diff --git a/internal/assertions/compare_impl_test.go b/internal/assertions/compare_impl_test.go index 453a4bcee..4d49cdb97 100644 --- a/internal/assertions/compare_impl_test.go +++ b/internal/assertions/compare_impl_test.go @@ -65,7 +65,9 @@ func testContainsValue() func(*testing.T) { for currCase := range compareContainsValueCases() { result := containsValue(currCase.values, currCase.value) - Equal(t, currCase.result, result) + if currCase.result != result { + t.Errorf("containsValue(%v, %v): expected %v, got %v", currCase.values, currCase.value, currCase.result, result) + } } } } @@ -75,10 +77,12 @@ func testCompareTwoValuesDifferentTypes() func(*testing.T) { for currCase := range compareTwoValuesDifferentTypesCases() { t.Run("different types should not be comparable", func(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) result := compareTwoValues(mock, currCase.v1, currCase.v2, []compareResult{compareLess, compareEqual, compareGreater}, "testFailMessage") - False(t, result) + if result { + t.Error("expected compareTwoValues to return false for different types") + } }) } } @@ -89,10 +93,12 @@ func testCompareTwoValuesNotComparable() func(*testing.T) { for currCase := range compareTwoValuesNotComparableCases() { t.Run("should not be comparable", func(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) result := compareTwoValues(mock, currCase.v1, currCase.v2, []compareResult{compareLess, compareEqual, compareGreater}, "testFailMessage") - False(t, result) + if result { + t.Error("expected compareTwoValues to return false for non-comparable types") + } }) } } @@ -103,10 +109,12 @@ func testCompareTwoValuesCorrectCompareResult() func(*testing.T) { for currCase := range compareTwoValuesCorrectResultCases() { t.Run("should be comparable", func(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) result := compareTwoValues(mock, currCase.v1, currCase.v2, currCase.allowedResults, "testFailMessage") - True(t, result) + if !result { + t.Errorf("expected compareTwoValues to return true for %v vs %v with allowed %v", currCase.v1, currCase.v2, currCase.allowedResults) + } }) } } diff --git a/internal/assertions/compare_test.go b/internal/assertions/compare_test.go index a09d72f29..cfe1445fe 100644 --- a/internal/assertions/compare_test.go +++ b/internal/assertions/compare_test.go @@ -4,132 +4,12 @@ package assertions import ( - "bytes" "iter" "slices" "testing" "time" ) -func TestCompareErrorMessages(t *testing.T) { - // Error message validation - t.Parallel() - - t.Run("with Greater", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - Greater(mock, 1, 2) - - if !mock.Failed() { - t.Error("Expected test to fail but it passed") - } - - errorMsg := mock.errorString() - if !Contains(t, errorMsg, `"1" is not greater than "2"`) { - t.Errorf("Error message should contain comparison details, got: %s", errorMsg) - } - }) - - t.Run("with GreaterOrEqual", func(t *testing.T) { - mock := new(mockT) - GreaterOrEqual(mock, 1, 2) - - if !mock.Failed() { - t.Error("Expected test to fail but it passed") - } - - errorMsg := mock.errorString() - if !Contains(t, errorMsg, `"1" is not greater than or equal to "2"`) { - t.Errorf("Error message should contain comparison details, got: %s", errorMsg) - } - }) - - t.Run("with Less", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - Less(mock, 2, 1) - - if !mock.Failed() { - t.Error("Expected test to fail but it passed") - } - - errorMsg := mock.errorString() - if !Contains(t, errorMsg, `"2" is not less than "1"`) { - t.Errorf("Error message should contain comparison details, got: %s", errorMsg) - } - }) - - t.Run("with LessOrEqual", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - LessOrEqual(mock, 2, 1) - - if !mock.Failed() { - t.Error("Expected test to fail but it passed") - } - - errorMsg := mock.errorString() - if !Contains(t, errorMsg, `"2" is not less than or equal to "1"`) { - t.Errorf("Error message should contain comparison details, got: %s", errorMsg) - } - }) - - t.Run("with Positive", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - Positive(mock, -1) - - if !mock.Failed() { - t.Error("Expected test to fail but it passed") - } - - errorMsg := mock.errorString() - if !Contains(t, errorMsg, `"-1" is not positive`) { - t.Errorf("Error message should contain sign check details, got: %s", errorMsg) - } - }) - - t.Run("with Negative", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - Negative(mock, 1) - - if !mock.Failed() { - t.Error("Expected test to fail but it passed") - } - - errorMsg := mock.errorString() - if !Contains(t, errorMsg, `"1" is not negative`) { - t.Errorf("Error message should contain sign check details, got: %s", errorMsg) - } - }) - - t.Run("with forwarded args", func(t *testing.T) { - msgAndArgs := []any{"format %s %x", "this", 0xc001} - const expectedOutput = "format this c001\n" - - funcs := []func(t T){ - func(t T) { Greater(t, 1, 2, msgAndArgs...) }, - func(t T) { GreaterOrEqual(t, 1, 2, msgAndArgs...) }, - func(t T) { Less(t, 2, 1, msgAndArgs...) }, - func(t T) { LessOrEqual(t, 2, 1, msgAndArgs...) }, - func(t T) { Positive(t, 0, msgAndArgs...) }, - func(t T) { Negative(t, 0, msgAndArgs...) }, - } - - for _, f := range funcs { - mock := &outputT{buf: bytes.NewBuffer(nil)} - f(mock) - Contains(t, mock.buf.String(), expectedOutput) - } - }) -} - func TestCompareGreaterAndLess(t *testing.T) { t.Parallel() @@ -226,6 +106,12 @@ func TestComparePositive(t *testing.T) { } } +func TestCompareErrorMessages(t *testing.T) { + t.Parallel() + + runFailCases(t, compareFailCases()) +} + // genericTestCase wraps a test function with its name for table-driven tests of generic functions. // Kept for compatibility with existing special-case tests. type genericTestCase struct { @@ -241,8 +127,12 @@ func testGreaterTCustomInt() func(*testing.T) { mock := new(mockT) type MyInt int - True(t, GreaterT(mock, MyInt(2), MyInt(1))) - False(t, GreaterT(mock, MyInt(1), MyInt(2))) + if !GreaterT(mock, MyInt(2), MyInt(1)) { + t.Error("expected GreaterT(2, 1) to pass") + } + if GreaterT(mock, MyInt(1), MyInt(2)) { + t.Error("expected GreaterT(1, 2) to fail") + } } } @@ -350,16 +240,7 @@ func testComparison(cmp func(T, any, any, ...any) bool, e1, e2 any, shouldPass b mock := new(mockT) result := cmp(mock, e1, e2) - - if shouldPass { - True(t, result) - False(t, mock.Failed()) - - return - } - - False(t, result) - True(t, mock.Failed()) + shouldPassOrFail(t, mock, result, shouldPass) } } @@ -421,16 +302,7 @@ func testComparisonT[V Ordered](cmp func(T, V, V, ...any) bool, e1, e2 V, should mock := new(mockT) result := cmp(mock, e1, e2) - - if shouldPass { - True(t, result) - False(t, mock.Failed()) - - return - } - - False(t, result) - True(t, mock.Failed()) + shouldPassOrFail(t, mock, result, shouldPass) } } @@ -497,16 +369,7 @@ func testSign(sign func(T, any, ...any) bool, e any, shouldPass bool) func(*test mock := new(mockT) result := sign(mock, e) - - if shouldPass { - True(t, result) - False(t, mock.Failed()) - - return - } - - False(t, result) - True(t, mock.Failed()) + shouldPassOrFail(t, mock, result, shouldPass) } } @@ -555,15 +418,45 @@ func testSignT[V SignedNumeric](sign func(T, V, ...any) bool, e V, shouldPass bo mock := new(mockT) result := sign(mock, e) - - if shouldPass { - True(t, result) - False(t, mock.Failed()) - - return - } - - False(t, result) - True(t, mock.Failed()) + shouldPassOrFail(t, mock, result, shouldPass) } } + +// ============================================================================ +// TestCompareErrorMessages +// ============================================================================ + +func compareFailCases() iter.Seq[failCase] { + return slices.Values([]failCase{ + { + name: "Greater/int", + assertion: func(t T) bool { return Greater(t, 1, 2) }, + wantContains: []string{`"1" is not greater than "2"`}, + }, + { + name: "GreaterOrEqual/int", + assertion: func(t T) bool { return GreaterOrEqual(t, 1, 2) }, + wantContains: []string{`"1" is not greater than or equal to "2"`}, + }, + { + name: "Less/int", + assertion: func(t T) bool { return Less(t, 2, 1) }, + wantContains: []string{`"2" is not less than "1"`}, + }, + { + name: "LessOrEqual/int", + assertion: func(t T) bool { return LessOrEqual(t, 2, 1) }, + wantContains: []string{`"2" is not less than or equal to "1"`}, + }, + { + name: "Positive/negative-value", + assertion: func(t T) bool { return Positive(t, -1) }, + wantContains: []string{`"-1" is not positive`}, + }, + { + name: "Negative/positive-value", + assertion: func(t T) bool { return Negative(t, 1) }, + wantContains: []string{`"1" is not negative`}, + }, + }) +} diff --git a/internal/assertions/condition.go b/internal/assertions/condition.go index d1b942b75..844398b19 100644 --- a/internal/assertions/condition.go +++ b/internal/assertions/condition.go @@ -105,7 +105,7 @@ func Never(t T, condition func() bool, waitFor time.Duration, tick time.Duration return never(t, condition, waitFor, tick, msgAndArgs...) } -// EventuallyWithT asserts that the given condition will be met in waitFor time, +// EventuallyWith asserts that the given condition will be met in waitFor time, // periodically checking the target function at each tick. // // In contrast to [Eventually], the condition function is supplied with a [CollectT] @@ -127,7 +127,7 @@ func Never(t T, condition func() bool, waitFor time.Duration, tick time.Duration // externalValue = true // }() // -// assertions.EventuallyWithT(t, func(c *assertions.CollectT) { +// assertions.EventuallyWith(t, func(c *assertions.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assertions.True(c, externalValue, "expected 'externalValue' to be true") // }, @@ -145,7 +145,7 @@ func Never(t T, condition func() bool, waitFor time.Duration, tick time.Duration // // success: func(c *CollectT) { True(c,true) }, 100*time.Millisecond, 20*time.Millisecond // failure: func(c *CollectT) { False(c,true) }, 100*time.Millisecond, 20*time.Millisecond -func EventuallyWithT(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { +func EventuallyWith(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { // Domain: condition if h, ok := t.(H); ok { h.Helper() @@ -480,7 +480,7 @@ func (p *conditionPoller) cancellableContext(parentCtx context.Context, waitFor // CollectT implements the [T] interface and collects all errors. // -// [CollectT] is specifically intended to be used with [EventuallyWithT] and +// [CollectT] is specifically intended to be used with [EventuallyWith] and // should not be used outside of that context. type CollectT struct { // Domain: condition @@ -507,7 +507,7 @@ func (c *CollectT) Errorf(format string, args ...any) { c.errors = append(c.errors, fmt.Errorf(format, args...)) } -// FailNow records a failure and cancels the parent [EventuallyWithT] context, +// FailNow records a failure and cancels the parent [EventuallyWith] context, // before exiting the current go routine with [runtime.Goexit]. // // This causes the assertion to fail immediately without waiting for a timeout. diff --git a/internal/assertions/condition_test.go b/internal/assertions/condition_test.go index 6ab66d6c6..651a1b331 100644 --- a/internal/assertions/condition_test.go +++ b/internal/assertions/condition_test.go @@ -5,6 +5,8 @@ package assertions import ( "context" + "iter" + "slices" "sort" "strings" "sync" @@ -23,7 +25,7 @@ func TestCondition(t *testing.T) { t.Run("condition should be true", func(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) if !Condition(mock, func() bool { return true }, "Truth") { t.Error("condition should return true") } @@ -32,7 +34,7 @@ func TestCondition(t *testing.T) { t.Run("condition should be false", func(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) if Condition(mock, func() bool { return false }, "Lie") { t.Error("condition should return false") } @@ -50,7 +52,9 @@ func TestConditionEventually(t *testing.T) { return false } - False(t, Eventually(mock, condition, testTimeout, testTick)) + if Eventually(mock, condition, testTimeout, testTick) { + t.Error("expected Eventually to return false") + } }) t.Run("condition should Eventually be true", func(t *testing.T) { @@ -64,7 +68,9 @@ func TestConditionEventually(t *testing.T) { return state == 2 } - True(t, Eventually(t, condition, testTimeout, testTick)) + if !Eventually(t, condition, testTimeout, testTick) { + t.Error("expected Eventually to return true") + } }) } @@ -84,7 +90,9 @@ func TestConditionEventuallyTimeout(t *testing.T) { return true } - False(t, Eventually(mock, condition, time.Millisecond, time.Microsecond)) + if Eventually(mock, condition, time.Millisecond, time.Microsecond) { + t.Error("expected Eventually to return false on timeout") + } }) t.Run("should fail on parent test failed", func(t *testing.T) { @@ -101,11 +109,15 @@ func TestConditionEventuallyTimeout(t *testing.T) { return true } - False(t, Eventually(mock, condition, testTimeout, testTick)) + if Eventually(mock, condition, testTimeout, testTick) { + t.Error("expected Eventually to return false when parent test fails") + } t.Run("reported errors should include the context cancellation", func(t *testing.T) { // assert how this failure is reported - Len(t, mock.errors, 2, "expected to have 2 error messages: 1 for the context canceled, 1 for the never met condition") + if len(mock.errors) != 2 { + t.Errorf("expected 2 error messages (1 for context canceled, 1 for never met condition), got %d", len(mock.errors)) + } var hasContextCancelled, hasFailedCondition bool for _, err := range mock.errors { @@ -117,8 +129,12 @@ func TestConditionEventuallyTimeout(t *testing.T) { hasFailedCondition = true } } - True(t, hasContextCancelled, "expected a context cancelled error") - True(t, hasFailedCondition, "expected a condition never satisfied error") + if !hasContextCancelled { + t.Error("expected a context cancelled error") + } + if !hasFailedCondition { + t.Error("expected a condition never satisfied error") + } }) }) } @@ -132,7 +148,9 @@ func TestConditionEventuallySucceedQuickly(t *testing.T) { // By making the tick longer than the total duration, we expect that this test would fail if // we didn't check the condition before the first tick elapses. - True(t, Eventually(mock, condition, testTimeout, 1*time.Second)) + if !Eventually(mock, condition, testTimeout, 1*time.Second) { + t.Error("expected Eventually to return true before first tick") + } }) } @@ -188,8 +206,12 @@ func TestConditionEventuallyNoLeak(t *testing.T) { record(5) const expectedActions = 6 - Len(t, recordedActions, expectedActions, "expected 6 actions to be recorded during this execution", "got:", len(recordedActions)) - True(t, sort.IntsAreSorted(recordedActions), "expected recorded actions to be ordered") + if len(recordedActions) != expectedActions { + t.Errorf("expected %d actions to be recorded, got %d", expectedActions, len(recordedActions)) + } + if !sort.IntsAreSorted(recordedActions) { + t.Errorf("expected recorded actions to be ordered, got %v", recordedActions) + } }) t.Run("should not leak a go routine for condition execution", func(t *testing.T) { @@ -214,11 +236,13 @@ func TestConditionEventuallyNoLeak(t *testing.T) { inEventually = false result := <-done - True(t, result, "Condition should end while Eventually still runs.") + if !result { + t.Error("Condition should end while Eventually still runs.") + } }) } -func TestConditionEventuallyWithT(t *testing.T) { +func TestConditionEventuallyWith(t *testing.T) { t.Parallel() t.Run("should complete with false", func(t *testing.T) { @@ -232,10 +256,14 @@ func TestConditionEventuallyWithT(t *testing.T) { Fail(collect, "another condition fixed failure") } - False(t, EventuallyWithT(mock, condition, testTimeout, testTick)) + if EventuallyWith(mock, condition, testTimeout, testTick) { + t.Error("expected EventuallyWith to return false") + } const expectedErrors = 4 - Len(t, mock.errors, expectedErrors, "expected 2 errors from the condition, and 2 additional errors from Eventually") + if len(mock.errors) != expectedErrors { + t.Errorf("expected %d errors (2 from condition, 2 from Eventually), got %d", expectedErrors, len(mock.errors)) + } expectedCalls := int(testTimeout / testTick) if counter < expectedCalls-1 || counter > expectedCalls+1 { // it may be 4, 5 or 6 depending on how the test schedules @@ -253,10 +281,16 @@ func TestConditionEventuallyWithT(t *testing.T) { True(collect, counter == 2) } - True(t, EventuallyWithT(mock, condition, testTimeout, testTick)) - Len(t, mock.errors, 0) + if !EventuallyWith(mock, condition, testTimeout, testTick) { + t.Error("expected EventuallyWith to return true") + } + if len(mock.errors) != 0 { + t.Errorf("expected 0 errors, got %d", len(mock.errors)) + } const expectedCalls = 2 - Equal(t, expectedCalls, counter, "Condition is expected to have been called 2 times") + if expectedCalls != counter { + t.Errorf("expected condition to be called %d times, got %d", expectedCalls, counter) + } }) t.Run("should complete with fail, on a nanosecond tick", func(t *testing.T) { @@ -267,10 +301,14 @@ func TestConditionEventuallyWithT(t *testing.T) { Fail(collect, "condition fixed failure") } - // To trigger race conditions, we run EventuallyWithT with a nanosecond tick. - False(t, EventuallyWithT(mock, condition, testTimeout, time.Nanosecond)) + // To trigger race conditions, we run EventuallyWith with a nanosecond tick. + if EventuallyWith(mock, condition, testTimeout, time.Nanosecond) { + t.Error("expected EventuallyWith to return false") + } const expectedErrors = 3 - Len(t, mock.errors, expectedErrors, "expected 1 errors from the condition, and 2 additional errors from Eventually") + if len(mock.errors) != expectedErrors { + t.Errorf("expected %d errors (1 from condition, 2 from Eventually), got %d", expectedErrors, len(mock.errors)) + } }) t.Run("should complete with fail, with latest failed condition", func(t *testing.T) { @@ -294,9 +332,13 @@ func TestConditionEventuallyWithT(t *testing.T) { Fail(collect, "condition fixed failure") } - False(t, EventuallyWithT(mock, condition, testTimeout, testTick)) + if EventuallyWith(mock, condition, testTimeout, testTick) { + t.Error("expected EventuallyWith to return false") + } const expectedErrors = 3 - Len(t, mock.errors, expectedErrors, "expected 1 errors from the condition, and 2 additional errors from Eventually") + if len(mock.errors) != expectedErrors { + t.Errorf("expected %d errors (1 from condition, 2 from Eventually), got %d", expectedErrors, len(mock.errors)) + } }) t.Run("should complete with success, with the ticker never used", func(t *testing.T) { @@ -307,7 +349,9 @@ func TestConditionEventuallyWithT(t *testing.T) { // By making the tick longer than the total duration, we expect that this test would fail if // we didn't check the condition before the first tick elapses. - True(t, EventuallyWithT(mock, condition, testTimeout, time.Second)) + if !EventuallyWith(mock, condition, testTimeout, time.Second) { + t.Error("expected EventuallyWith to return true") + } }) t.Run("should fail with a call to collect.FailNow", func(t *testing.T) { @@ -316,16 +360,20 @@ func TestConditionEventuallyWithT(t *testing.T) { mock := new(errorsCapturingT) counter := 0 - // The call to FailNow cancels the execution context of EventuallyWithT. + // The call to FailNow cancels the execution context of EventuallyWith. // so we don't have to wait for the timeout. condition := func(collect *CollectT) { counter++ collect.FailNow() } - False(t, EventuallyWithT(mock, condition, 30*time.Minute, testTick)) + if EventuallyWith(mock, condition, 30*time.Minute, testTick) { + t.Error("expected EventuallyWith to return false") + } const expectedErrors = 2 - Len(t, mock.errors, expectedErrors) // we have 0 accumulated error + 2 errors from EventuallyWithT (includes the timeout) + if len(mock.errors) != expectedErrors { + t.Errorf("expected %d errors (0 accumulated + 2 from EventuallyWith), got %d", expectedErrors, len(mock.errors)) + } if counter != 1 { t.Errorf("expected the condition function to have been called only once, but got: %d", counter) } @@ -343,7 +391,9 @@ func TestConditionNever(t *testing.T) { return false } - True(t, Never(mock, condition, testTimeout, testTick)) + if !Never(mock, condition, testTimeout, testTick) { + t.Error("expected Never to return true") + } }) t.Run("should never be true, on timeout", func(t *testing.T) { @@ -356,7 +406,9 @@ func TestConditionNever(t *testing.T) { return true } - True(t, Never(mock, condition, testTick, 1*time.Millisecond)) + if !Never(mock, condition, testTick, 1*time.Millisecond) { + t.Error("expected Never to return true on timeout") + } }) t.Run("should never be true fails", func(t *testing.T) { @@ -376,7 +428,9 @@ func TestConditionNever(t *testing.T) { return <-returns } - False(t, Never(mock, condition, testTimeout, testTick)) + if Never(mock, condition, testTimeout, testTick) { + t.Error("expected Never to return false") + } }) t.Run("should never be true fails, with ticker never triggered", func(t *testing.T) { @@ -386,7 +440,9 @@ func TestConditionNever(t *testing.T) { // By making the tick longer than the total duration, we expect that this test would fail if // we didn't check the condition before the first tick elapses. condition := func() bool { return true } - False(t, Never(mock, condition, testTimeout, time.Second)) + if Never(mock, condition, testTimeout, time.Second) { + t.Error("expected Never to return false") + } }) t.Run("should never be true fails, with parent test failing", func(t *testing.T) { @@ -398,6 +454,24 @@ func TestConditionNever(t *testing.T) { failParent() // cancels the parent context, which results in Never to fail return false } - False(t, Never(mock, condition, testTimeout, time.Second)) + if Never(mock, condition, testTimeout, time.Second) { + t.Error("expected Never to return false when parent test fails") + } + }) +} + +func TestConditionErrorMessages(t *testing.T) { + t.Parallel() + + runFailCases(t, conditionFailCases()) +} + +func conditionFailCases() iter.Seq[failCase] { + return slices.Values([]failCase{ + { + name: "Condition/false", + assertion: func(t T) bool { return Condition(t, func() bool { return false }) }, + wantError: "condition failed", + }, }) } diff --git a/internal/assertions/diff_test.go b/internal/assertions/diff_test.go index f4779632d..478f11ae2 100644 --- a/internal/assertions/diff_test.go +++ b/internal/assertions/diff_test.go @@ -84,3 +84,39 @@ func TestDiffTypeAndKind(t *testing.T) { } }) } + +// Ensure there are no data races with diff. +func TestTypeDiffRace(t *testing.T) { + t.Parallel() + + expected := map[string]string{ + "a": "A", + "b": "B", + "c": "C", + } + + actual := map[string]string{ + "d": "D", + "e": "E", + "f": "F", + } + + // run diffs in parallel simulating tests with t.Parallel() + numRoutines := 10 + rChans := make([]chan string, numRoutines) + for idx := range rChans { + rChans[idx] = make(chan string) + go func(ch chan string) { + defer close(ch) + ch <- diff(expected, actual) + }(rChans[idx]) + } + + for _, ch := range rChans { + for msg := range ch { + if msg == "" { + t.Error("expected non-empty diff result") + } + } + } +} diff --git a/internal/assertions/doc.go b/internal/assertions/doc.go index 42e15e84b..4be64e0e5 100644 --- a/internal/assertions/doc.go +++ b/internal/assertions/doc.go @@ -26,7 +26,7 @@ // - ordering: asserting how collections are ordered // - panic: asserting a panic behavior // - string: asserting strings -// - testing: mimicks methods from the testing standard library +// - testing: mimics methods from the testing standard library // - time: asserting times and durations // - type: asserting types rather than values // - yaml: asserting yaml documents diff --git a/internal/assertions/enable/yaml/enable_yaml.go b/internal/assertions/enable/yaml/enable_yaml.go index 0a66aa736..c411f23e6 100644 --- a/internal/assertions/enable/yaml/enable_yaml.go +++ b/internal/assertions/enable/yaml/enable_yaml.go @@ -1,13 +1,17 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Package yaml is an indirection to handle YAML deserialization. +// Package yaml is an indirection to handle YAML serialization/deserialization. // // This package allows the builder to override the indirection with an alternative implementation -// of YAML deserialization. +// of YAML serialization. package yaml -var enableYAMLUnmarshal func([]byte, any) error //nolint:gochecknoglobals // in this particular case, we need a global to enable the feature from another module +//nolint:gochecknoglobals // in this particular case, we need a global to enable the feature from another module +var ( + enableYAMLUnmarshal func([]byte, any) error + enableYAMLMarshal func(any) ([]byte, error) +) // EnableYAMLWithUnmarshal registers a YAML-capable unmarshaler. // @@ -16,6 +20,10 @@ func EnableYAMLWithUnmarshal(unmarshaler func([]byte, any) error) { enableYAMLUnmarshal = unmarshaler } +func EnableYAMLWithMarshal(marshaler func(any) ([]byte, error)) { + enableYAMLMarshal = marshaler +} + // Unmarshal is a wrapper to some exernal library to unmarshal YAML documents. func Unmarshal(in []byte, out any) error { if enableYAMLUnmarshal == nil { @@ -34,3 +42,22 @@ import ( } return enableYAMLUnmarshal(in, out) } + +// Marshal is a wrapper to some exernal library to marshal YAML documents. +func Marshal(in any) ([]byte, error) { + if enableYAMLMarshal == nil { + // fail early and loud + panic(` +YAML is not enabled yet! + +You should enable a YAML library before running this test, +e.g. by adding the following to your imports: + +import ( + _ "github.com/go-openapi/testify/enable/yaml/v2" +) +`, + ) + } + return enableYAMLMarshal(in) +} diff --git a/internal/assertions/equal_impl_test.go b/internal/assertions/equal_impl_test.go index 00953756e..8268745c4 100644 --- a/internal/assertions/equal_impl_test.go +++ b/internal/assertions/equal_impl_test.go @@ -28,8 +28,12 @@ func testFormatUnequalValues() func(*testing.T) { t.Parallel() expected, actual := formatUnequalValues(tt.unequalExpected, tt.unequalActual) - Equal(t, tt.expectedExpected, expected, tt.testName) - Equal(t, tt.expectedActual, actual, tt.testName) + if tt.expectedExpected != expected { + t.Errorf("%s: expected formatted expected %q, got %q", tt.testName, tt.expectedExpected, expected) + } + if tt.expectedActual != actual { + t.Errorf("%s: expected formatted actual %q, got %q", tt.testName, tt.expectedActual, actual) + } }) } } diff --git a/internal/assertions/equal_pointer_test.go b/internal/assertions/equal_pointer_test.go index b6e7ed539..2bfb72abb 100644 --- a/internal/assertions/equal_pointer_test.go +++ b/internal/assertions/equal_pointer_test.go @@ -37,6 +37,16 @@ func TestEqualPointers(t *testing.T) { } } +func TestEqualPointerErrorMessages(t *testing.T) { + t.Parallel() + + runFailCases(t, equalPointerFailCases()) +} + +// ============================================================================ +// TestEqualPointers +// ============================================================================ + type pointerPairTestCase struct { name string makeValues func() (expected, actual any) @@ -181,3 +191,58 @@ func testPointerGenericAssertion[P any](mock T, kind pointerAssertionKind, expec panic(fmt.Errorf("test case configuration error: invalid pointerAssertionKind: %d", kind)) } } + +// ============================================================================ +// TestEqualPointerErrorMessages +// ============================================================================ + +func equalPointerFailCases() iter.Seq[failCase] { + return slices.Values([]failCase{ + { + name: "Same/different-pointers", + assertion: func(t T) bool { + v1, v2 := 42, 42 + return Same(t, &v1, &v2) + }, + wantContains: []string{"Not same"}, + }, + { + name: "Same/not-pointers", + assertion: func(t T) bool { + return Same(t, 1, 2) + }, + wantError: "Both arguments must be pointers", + }, + { + name: "NotSame/same-pointer", + assertion: func(t T) bool { + v := 42 + return NotSame(t, &v, &v) + }, + wantContains: []string{"Expected and actual point to the same object"}, + }, + { + name: "NotSame/not-pointers", + assertion: func(t T) bool { + return NotSame(t, 1, 2) + }, + wantError: "Both arguments must be pointers", + }, + { + name: "SameT/different-pointers", + assertion: func(t T) bool { + v1, v2 := 42, 42 + return SameT(t, &v1, &v2) + }, + wantContains: []string{"Not same"}, + }, + { + name: "NotSameT/same-pointer", + assertion: func(t T) bool { + v := 42 + return NotSameT(t, &v, &v) + }, + wantContains: []string{"Expected and actual point to the same object"}, + }, + }) +} diff --git a/internal/assertions/equal_test.go b/internal/assertions/equal_test.go index c640434ec..b8049932e 100644 --- a/internal/assertions/equal_test.go +++ b/internal/assertions/equal_test.go @@ -6,40 +6,10 @@ package assertions import ( "fmt" "iter" - "regexp" "slices" - "strings" "testing" ) -func TestEqualErrorMessages(t *testing.T) { - t.Parallel() - - t.Run("should render when value is too long to print", testEqualTooLongToPrint()) - - t.Run("error message should match expression", func(t *testing.T) { - // checking error messsages on Equal with a regexp. The object of the test is Equal, not Regexp - for tc := range stringEqualFormattingCases() { - t.Run(tc.name, func(t *testing.T) { - mock := &bufferT{} - - isEqual := Equal(mock, tc.equalWant, tc.equalGot, tc.msgAndArgs...) - if isEqual { - t.Errorf("expected %q to be different than %q", tc.equalGot, tc.equalWant) - - return - } - - rex := regexp.MustCompile(tc.want) - match := rex.MatchString(mock.buf.String()) - if !match { - t.Errorf("expected message to match %q, but got:\n%s", tc.want, mock.buf.String()) - } - }) - } - }) -} - // Test EqualValues and NotEqualValues. func TestEqualValues(t *testing.T) { t.Parallel() @@ -49,7 +19,7 @@ func TestEqualValues(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) res := NotEqualValues(mock, tc.expected, tc.actual) if res != tc.notEqualValue { @@ -61,7 +31,7 @@ func TestEqualValues(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) res := EqualValues(mock, tc.expected, tc.actual) if res != tc.equalValue { @@ -71,6 +41,12 @@ func TestEqualValues(t *testing.T) { } } +func TestEqualErrorMessages(t *testing.T) { + t.Parallel() + + runFailCases(t, equalErrorMessageCases()) +} + // Test EqualExportedValues. func TestEqualExportedValues(t *testing.T) { t.Parallel() @@ -84,20 +60,18 @@ func TestEqualExportedValues(t *testing.T) { if actual != tc.expectedEqual { t.Errorf("Expected EqualExportedValues to be %t, but was %t", tc.expectedEqual, actual) } - - if tc.expectedFailMsg == "" { - // skip error message check - return - } - - actualFail := mockT.errorString() - if !strings.Contains(actualFail, tc.expectedFailMsg) { - t.Errorf("Contains failure should include %q but was %q", tc.expectedFailMsg, actualFail) - } }) } } +// TestEqualExportedValuesErrorMessages tests the semantic content of error messages +// produced by EqualExportedValues for failing cases. +func TestEqualExportedValuesErrorMessages(t *testing.T) { + t.Parallel() + + runFailCases(t, equalExportedValuesFailCases()) +} + // Deep equality tests (Equal, EqualT, NotEqual, NotEqualT, Exactly). func TestEqualDeepEqual(t *testing.T) { t.Parallel() @@ -510,191 +484,58 @@ func testEqualityGenericAssertion[V comparable](mock T, kind equalityAssertionKi } // ============================================================================ -// TestEqualErrorMessages: too long to print checks that error -// messages are properly truncated when the values to display are too large. +// equalErrorMessageCases: error message tests for Equal-family assertions. // ============================================================================ -func testEqualTooLongToPrint() func(*testing.T) { - const ( - expected = `&[]int{0, 0, 0,` - message = ` - Error Trace: - Error: Should not be: []int{0, 0, 0,` - trailer = `<... truncated>` - ) - - return func(t *testing.T) { - t.Run("with Same", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - +func equalErrorMessageCases() iter.Seq[failCase] { + return slices.Values([]failCase{ + // Truncation cases (values too long to print) + truncationCase("truncation/Same", func(t T) bool { longSlice := make([]int, 1_000_000) - result := Same(mock, &[]int{}, &longSlice) - if result { - t.Errorf("expected Same to fail") - return - } - - if !strings.Contains(mock.errorString(), expected) { - t.Errorf("expected message to contain %q but got: %q", expected, mock.errorString()) - } - }) - - t.Run("with NotSame", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - + return Same(t, &[]int{}, &longSlice) + }), + truncationCase("truncation/NotSame", func(t T) bool { longSlice := make([]int, 1_000_000) - result := NotSame(mock, &longSlice, &longSlice) - if result { - t.Errorf("expected NotSame to fail") - return - } - - if !strings.Contains(mock.errorString(), expected) { - t.Errorf("expected message to contain %q but got: %q", expected, mock.errorString()) - } - }) - - t.Run("with NotEqual", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - + return NotSame(t, &longSlice, &longSlice) + }), + truncationCase("truncation/NotEqual", func(t T) bool { longSlice := make([]int, 1_000_000) - result := NotEqual(mock, longSlice, longSlice) - if result { - t.Errorf("expected NotEqual to fail") - return - } - - if !strings.Contains(mock.errorString(), message) { - t.Errorf("expected message to contain %q but got: %q", message, mock.errorString()) - } - - if !strings.Contains(mock.errorString(), trailer) { - t.Errorf("expected message to contain %q but got: %q", trailer, mock.errorString()) - } - }) - - t.Run("with NotEqualValues", func(t *testing.T) { - t.Parallel() - mock := new(mockT) - + return NotEqual(t, longSlice, longSlice) + }), + truncationCase("truncation/NotEqualValues", func(t T) bool { longSlice := make([]int, 1_000_000) - result := NotEqualValues(mock, longSlice, longSlice) - if result { - t.Errorf("expected NotEqualValues to fail") - return - } - - if !strings.Contains(mock.errorString(), message) { - t.Errorf("expected message to contain %q but got: %q", message, mock.errorString()) - } - const trailer = `<... truncated>` - if !strings.Contains(mock.errorString(), trailer) { - t.Errorf("expected message to contain %q but got: %q", trailer, mock.errorString()) - } - }) - } -} - -type equalStringCase struct { - name string - equalWant string - equalGot string - msgAndArgs []any - want string -} + return NotEqualValues(t, longSlice, longSlice) + }), -func stringEqualFormattingCases() iter.Seq[equalStringCase] { - return slices.Values([]equalStringCase{ - { - name: "multiline diff message", - equalWant: "hi, \nmy name is", - equalGot: "what,\nmy name is", - want: "\t[a-z]+.go:\\d+: \n" + - "\t+Error Trace:\t\n+" + - "\t+Error:\\s+Not equal:\\s+\n" + - "\\s+expected: \"hi, \\\\nmy name is\"\n" + - "\\s+actual\\s+: " + "\"what,\\\\nmy name is\"\n" + - "\\s+Diff:\n" + - "\\s+-+ Expected\n\\s+\\++ " + - "Actual\n" + - "\\s+@@ -1,2 \\+1,2 @@\n" + - "\\s+-hi, \n\\s+\\+what,\n" + - "\\s+my name is", - }, - { - name: "single line diff message", - equalWant: "want", - equalGot: "got", - want: "\t[a-z]+.go:\\d+: \n" + - "\t+Error Trace:\t\n" + - "\t+Error:\\s+Not equal:\\s+\n" + - "\\s+expected: \"want\"\n" + - "\\s+actual\\s+: \"got\"\n" + - "\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ " + - "Actual\n" + - "\\s+@@ -1 \\+1 @@\n" + - "\\s+-want\n" + - "\\s+\\+got\n", - }, - { - name: "diff message with args", - equalWant: "want", - equalGot: "got", - msgAndArgs: []any{"hello, %v!", "world"}, - want: "\t[a-z]+.go:[0-9]+: \n" + - "\t+Error Trace:\t\n" + - "\t+Error:\\s+Not equal:\\s+\n" + - "\\s+expected: \"want\"\n" + - "\\s+actual\\s+: \"got\"\n" + - "\\s+Diff:\n" + - "\\s+-+ Expected\n" + - "\\s+\\++ Actual\n" + - "\\s+@@ -1 \\+1 @@\n" + - "\\s+-want\n" + - "\\s+\\+got\n" + - "\\s+Messages:\\s+hello, world!\n", - }, - { - name: "diff message with integer arg", - equalWant: "want", - equalGot: "got", - msgAndArgs: []any{123}, - want: "\t[a-z]+.go:[0-9]+: \n" + - "\t+Error Trace:\t\n" + - "\t+Error:\\s+Not equal:\\s+\n" + - "\\s+expected: \"want\"\n" + - "\\s+actual\\s+: \"got\"\n" + - "\\s+Diff:\n" + - "\\s+-+ Expected\n" + - "\\s+\\++ Actual\n" + - "\\s+@@ -1 \\+1 @@\n" + - "\\s+-want\n" + - "\\s+\\+got\n" + - "\\s+Messages:\\s+123\n", - }, - { - name: "diff message with struct arg", - equalWant: "want", - equalGot: "got", - msgAndArgs: []any{struct{ a string }{"hello"}}, - want: "\t[a-z]+.go:[0-9]+: \n" + - "\t+Error Trace:\t\n" + - "\t+Error:\\s+Not equal:\\s+\n" + - "\\s+expected: \"want\"\n" + - "\\s+actual\\s+: \"got\"\n" + - "\\s+Diff:\n" + - "\\s+-+ Expected\n" + - "\\s+\\++ Actual\n" + - "\\s+@@ -1 \\+1 @@\n" + - "\\s+-want\n" + - "\\s+\\+got\n" + - "\\s+Messages:\\s+{a:hello}\n", + // Semantic content of Equal diff messages + { + name: "Equal/multiline-diff", + assertion: func(t T) bool { + return Equal(t, "hi, \nmy name is", "what,\nmy name is") + }, + wantContains: []string{ + "Not equal:", + `expected: "hi, \nmy name is"`, + `actual : "what,\nmy name is"`, + "--- Expected", + "+++ Actual", + "-hi,", + "+what,", + "my name is", + }, + }, + { + name: "Equal/single-line-diff", + assertion: func(t T) bool { + return Equal(t, "want", "got") + }, + wantContains: []string{ + "Not equal:", + `expected: "want"`, + `actual : "got"`, + "-want", + "+got", + }, }, }) } @@ -742,11 +583,10 @@ func equalValuesCases() iter.Seq[equalValuesCase] { // ============================================================================ type objectEqualExportedValuesCase struct { - name string - expected any - actual any - expectedEqual bool - expectedFailMsg string + name string + expected any + actual any + expectedEqual bool } func objectEqualExportedValuesCases() iter.Seq[objectEqualExportedValuesCase] { @@ -810,32 +650,12 @@ func objectEqualExportedValuesCases() iter.Seq[objectEqualExportedValuesCase] { expected: S{1, Nested{2, 3}, 4, Nested{5, 6}}, actual: S{1, Nested{1, nil}, nil, Nested{}}, expectedEqual: false, - expectedFailMsg: fmt.Sprintf(` - Diff: - --- Expected - +++ Actual - @@ -3,3 +3,3 @@ - Exported2: (%s.Nested) { - - Exported: (int) 2, - + Exported: (int) 1, - notExported: (interface {}) `, - shortpkg), }, { name: "diff-values/nested-struct(2)", expected: S3{&Nested{1, 2}, &Nested{3, 4}}, actual: S3{&Nested{"a", 2}, &Nested{3, 4}}, expectedEqual: false, - expectedFailMsg: fmt.Sprintf(` - Diff: - --- Expected - +++ Actual - @@ -2,3 +2,3 @@ - Exported1: (*%s.Nested)({ - - Exported: (int) 1, - + Exported: (string) (len=1) "a", - notExported: (interface {}) `, - shortpkg), }, { name: "diff-values/inner-slice", @@ -848,16 +668,6 @@ func objectEqualExportedValuesCases() iter.Seq[objectEqualExportedValuesCase] { {2, "b"}, }}, expectedEqual: false, - expectedFailMsg: fmt.Sprintf(` - Diff: - --- Expected - +++ Actual - @@ -7,3 +7,3 @@ - (*%s.Nested)({ - - Exported: (int) 3, - + Exported: (int) 2, - notExported: (interface {}) `, - shortpkg), }, { name: "equal-values/inner-array-unexported-diff", @@ -876,16 +686,6 @@ func objectEqualExportedValuesCases() iter.Seq[objectEqualExportedValuesCase] { expected: &S{1, Nested{2, 3}, 4, Nested{5, 6}}, actual: &S{1, Nested{1, nil}, nil, Nested{}}, expectedEqual: false, - expectedFailMsg: fmt.Sprintf(` - Diff: - --- Expected - +++ Actual - @@ -3,3 +3,3 @@ - Exported2: (%s.Nested) { - - Exported: (int) 2, - + Exported: (int) 1, - notExported: (interface {}) `, - shortpkg), }, { name: "equal-values/slice", @@ -898,15 +698,6 @@ func objectEqualExportedValuesCases() iter.Seq[objectEqualExportedValuesCase] { expected: []int{1, 2}, actual: []int{1, 3}, expectedEqual: false, - expectedFailMsg: ` - Diff: - --- Expected - +++ Actual - @@ -2,3 +2,3 @@ - (int) 1, - - (int) 2 - + (int) 3 - }`, }, { name: "equal-values/slice-of-pointers", @@ -937,16 +728,114 @@ func objectEqualExportedValuesCases() iter.Seq[objectEqualExportedValuesCase] { {2, "b"}, }, expectedEqual: false, - expectedFailMsg: fmt.Sprintf(` - Diff: - --- Expected - +++ Actual - @@ -6,3 +6,3 @@ - (*%s.Nested)({ - - Exported: (int) 3, - + Exported: (int) 2, - notExported: (interface {}) `, - shortpkg), + }, + }) +} + +// ============================================================================ +// TestEqualExportedValuesErrorMessages +// ============================================================================ + +func equalExportedValuesFailCases() iter.Seq[failCase] { + return slices.Values([]failCase{ + { + name: "nested-struct(1)/diff-in-exported-field", + assertion: func(t T) bool { + return EqualExportedValues(t, + S{1, Nested{2, 3}, 4, Nested{5, 6}}, + S{1, Nested{1, nil}, nil, Nested{}}, + ) + }, + wantContains: []string{ + "Not equal (comparing only exported fields):", + "--- Expected", + "+++ Actual", + fmt.Sprintf("Exported2: (%s.Nested) {", shortpkg), + "- Exported: (int) 2,", + "+ Exported: (int) 1,", + }, + }, + { + name: "nested-struct(2)/int-vs-string", + assertion: func(t T) bool { + return EqualExportedValues(t, + S3{&Nested{1, 2}, &Nested{3, 4}}, + S3{&Nested{"a", 2}, &Nested{3, 4}}, + ) + }, + wantContains: []string{ + "Not equal (comparing only exported fields):", + "--- Expected", + "+++ Actual", + fmt.Sprintf("Exported1: (*%s.Nested)({", shortpkg), + "- Exported: (int) 1,", + `+ Exported: (string) (len=1) "a",`, + }, + }, + { + name: "inner-slice/diff-in-nested-exported", + assertion: func(t T) bool { + return EqualExportedValues(t, + S4{[]*Nested{{1, 2}, {3, 4}}}, + S4{[]*Nested{{1, "a"}, {2, "b"}}}, + ) + }, + wantContains: []string{ + "Not equal (comparing only exported fields):", + "--- Expected", + "+++ Actual", + fmt.Sprintf("(*%s.Nested)({", shortpkg), + "- Exported: (int) 3,", + "+ Exported: (int) 2,", + }, + }, + { + name: "inner-slice-exported-diff/ptr-receiver", + assertion: func(t T) bool { + return EqualExportedValues(t, + &S{1, Nested{2, 3}, 4, Nested{5, 6}}, + &S{1, Nested{1, nil}, nil, Nested{}}, + ) + }, + wantContains: []string{ + "Not equal (comparing only exported fields):", + "--- Expected", + "+++ Actual", + fmt.Sprintf("Exported2: (%s.Nested) {", shortpkg), + "- Exported: (int) 2,", + "+ Exported: (int) 1,", + }, + }, + { + name: "slice/int-diff", + assertion: func(t T) bool { + return EqualExportedValues(t, []int{1, 2}, []int{1, 3}) + }, + wantContains: []string{ + "Not equal (comparing only exported fields):", + "--- Expected", + "+++ Actual", + "(int) 1,", + "- (int) 2", + "+ (int) 3", + }, + }, + { + name: "slice-of-struct/diff-in-exported", + assertion: func(t T) bool { + return EqualExportedValues(t, + []*Nested{{1, 2}, {3, 4}}, + []*Nested{{1, "a"}, {2, "b"}}, + ) + }, + wantContains: []string{ + "Not equal (comparing only exported fields):", + "--- Expected", + "+++ Actual", + fmt.Sprintf("(*%s.Nested)({", shortpkg), + "- Exported: (int) 3,", + "+ Exported: (int) 2,", + }, }, }) } diff --git a/internal/assertions/equal_unary_test.go b/internal/assertions/equal_unary_test.go index 15b29a74f..e3e2a1097 100644 --- a/internal/assertions/equal_unary_test.go +++ b/internal/assertions/equal_unary_test.go @@ -11,20 +11,6 @@ import ( "testing" ) -func TestEqualUnaryErrorMessages(t *testing.T) { - // error messages validation - for tc := range equalEmptyCases() { - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - - mock := new(captureT) - - res := Empty(mock, tc.value) - mock.checkResultAndErrMsg(t, res, tc.expectedResult, tc.expectedErrMsg) - }) - } -} - // Unary assertion tests (Nil, NotNil, Empty, NotEmpty). func TestEqualUnaryAssertions(t *testing.T) { t.Parallel() @@ -41,6 +27,16 @@ func TestEqualUnaryAssertions(t *testing.T) { } } +func TestEqualUnaryErrorMessages(t *testing.T) { + t.Parallel() + + runFailCases(t, equalUnaryFailCases()) +} + +// ============================================================================ +// TestEqualUnaryAssertions +// ============================================================================ + type unaryTestCase struct { name string object any @@ -156,27 +152,13 @@ func testUnaryAssertion(tc unaryTestCase, kind unaryAssertionKind, unaryAssertio } } -type equalEmptyCase struct { - name string - value any - expectedResult bool - expectedErrMsg string -} +// ============================================================================ +// TestEqualUnaryErrorMessages +// ============================================================================ -// Proposal for enhancement: should improve the overall readability and maintainability of the error message -// checks. Expectations are too specific to maintain, down to the tab/CR character. -// The line feeds and tab are not helping to spot what is expected. -// -// See [captureT] to figure -// a test refactoring plan. -func equalEmptyCases() iter.Seq[equalEmptyCase] { +func equalUnaryFailCases() iter.Seq[failCase] { chWithValue := make(chan struct{}, 1) chWithValue <- struct{}{} - // var tiP *time.Time - // var tiNP time.Time - // var s *string - // var f *os.File - // sP := &s x := 1 xP := &x @@ -185,117 +167,71 @@ func equalEmptyCases() iter.Seq[equalEmptyCase] { x int } - return slices.Values([]equalEmptyCase{ - { - name: "Non Empty string is not empty", - value: "something", - expectedResult: false, - expectedErrMsg: "Should be empty, but was something\n", - }, - { - name: "Non nil object is not empty", - value: errors.New("something"), - expectedResult: false, - expectedErrMsg: "Should be empty, but was something\n", - }, - { - name: "Non empty string array is not empty", - value: []string{"something"}, - expectedResult: false, - expectedErrMsg: "Should be empty, but was [something]\n", - }, + return slices.Values([]failCase{ { - name: "Non-zero int value is not empty", - value: 1, - expectedResult: false, - expectedErrMsg: "Should be empty, but was 1\n", + name: "Empty/non-empty-string", + assertion: func(t T) bool { return Empty(t, "something") }, + wantError: "Should be empty, but was something", }, { - name: "True value is not empty", - value: true, - expectedResult: false, - expectedErrMsg: "Should be empty, but was true\n", + name: "Empty/non-nil-error", + assertion: func(t T) bool { return Empty(t, errors.New("something")) }, + wantError: "Should be empty, but was something", }, { - name: "Channel with values is not empty", - value: chWithValue, - expectedResult: false, - expectedErrMsg: fmt.Sprintf("Should be empty, but was %v\n", chWithValue), + name: "Empty/non-empty-string-array", + assertion: func(t T) bool { return Empty(t, []string{"something"}) }, + wantError: "Should be empty, but was [something]", }, { - name: "struct with initialized values is empty", - value: TStruct{x: 1}, - expectedResult: false, - expectedErrMsg: "Should be empty, but was {1}\n", + name: "Empty/non-zero-int", + assertion: func(t T) bool { return Empty(t, 1) }, + wantError: "Should be empty, but was 1", }, { - name: "non-empty aliased string is empty", - value: TString("abc"), - expectedResult: false, - expectedErrMsg: "Should be empty, but was abc\n", + name: "Empty/true-value", + assertion: func(t T) bool { return Empty(t, true) }, + wantError: "Should be empty, but was true", }, { - name: "ptr to non-nil value is not empty", - value: xP, - expectedResult: false, - expectedErrMsg: fmt.Sprintf("Should be empty, but was %p\n", xP), + name: "Empty/channel-with-values", + assertion: func(t T) bool { return Empty(t, chWithValue) }, + wantContains: []string{"Should be empty, but was"}, }, { - name: "array is not state", - value: [1]int{42}, - expectedResult: false, - expectedErrMsg: "Should be empty, but was [42]\n", - }, - - // Here are some edge cases - { - name: "string with only spaces is not empty", - value: " ", - expectedResult: false, - expectedErrMsg: "Should be empty, but was \n", + name: "Empty/struct-with-values", + assertion: func(t T) bool { return Empty(t, TStruct{x: 1}) }, + wantError: "Should be empty, but was {1}", }, { - name: "string with a line feed is not empty", - value: "\n", - expectedResult: false, - expectedErrMsg: "Should be empty, but was \n", + name: "Empty/aliased-string", + assertion: func(t T) bool { return Empty(t, TString("abc")) }, + wantError: "Should be empty, but was abc", }, { - name: "string with only tabulation and lines feed is not empty", - value: "\n\t\n", - expectedResult: false, - expectedErrMsg: "" + // this syntax is used to show how errors are reported. - "Should be empty, but was \n" + - "\t\n", + name: "Empty/ptr-to-non-nil", + assertion: func(t T) bool { return Empty(t, xP) }, + wantContains: []string{"Should be empty, but was"}, }, { - name: "string with trailing lines feed is not empty", - value: "foo\n\n", - expectedResult: false, - expectedErrMsg: "Should be empty, but was foo\n\n", + name: "Empty/non-zero-array", + assertion: func(t T) bool { return Empty(t, [1]int{42}) }, + wantError: "Should be empty, but was [42]", }, { - name: "string with leading and trailing tabulation and lines feed is not empty", - value: "\n\nfoo\t\n\t\n", - expectedResult: false, - expectedErrMsg: "" + - "Should be empty, but was \n" + - "\n" + - "foo\t\n" + - "\t\n", + name: "Empty/whitespace-string", + assertion: func(t T) bool { return Empty(t, " ") }, + wantContains: []string{"Should be empty, but was"}, }, { - name: "non-printable character is not empty", - value: "\u00a0", // NO-BREAK SPACE UNICODE CHARACTER - expectedResult: false, - expectedErrMsg: "Should be empty, but was \u00a0\n", + name: "Empty/newline-string", + assertion: func(t T) bool { return Empty(t, "\n") }, + wantContains: []string{"Should be empty, but was"}, }, { - // check that there is no error message on success - name: "Empty string is empty", - value: "", - expectedResult: true, - expectedErrMsg: "", + name: "Empty/non-printable-char", + assertion: func(t T) bool { return Empty(t, "\u00a0") }, + wantContains: []string{"Should be empty, but was"}, }, }) } diff --git a/internal/assertions/error.go b/internal/assertions/error.go index c2a5d0e13..45f14d29d 100644 --- a/internal/assertions/error.go +++ b/internal/assertions/error.go @@ -10,6 +10,8 @@ import ( "strings" ) +var _ error = TestExampleError("") + // TestExampleError is a sentinel error type that may be used for testing. type TestExampleError string diff --git a/internal/assertions/error_test.go b/internal/assertions/error_test.go index 91ea008b8..be2973ed4 100644 --- a/internal/assertions/error_test.go +++ b/internal/assertions/error_test.go @@ -12,133 +12,37 @@ import ( "testing" ) -func TestErrorNoErrorWithErrorTooLongToPrint(t *testing.T) { - t.Parallel() - mock := new(mockT) - - longSlice := make([]int, 1_000_000) - NoError(mock, fmt.Errorf("long: %v", longSlice)) - Contains(t, mock.errorString(), ` - Error Trace: - Error: Received unexpected error: - long: [0 0 0`) - Contains(t, mock.errorString(), `<... truncated>`) -} - -func TestErrorEqualErrorWithErrorTooLongToPrint(t *testing.T) { - t.Parallel() - mock := new(mockT) - - longSlice := make([]int, 1_000_000) - EqualError(mock, fmt.Errorf("long: %v", longSlice), "EOF") - Contains(t, mock.errorString(), ` - Error Trace: - Error: Error message not equal: - expected: "EOF" - actual : "long: [0 0 0`) - Contains(t, mock.errorString(), `<... truncated>`) -} - -func TestErrorContainsWithErrorTooLongToPrint(t *testing.T) { - t.Parallel() - mock := new(mockT) - - longSlice := make([]int, 1_000_000) - ErrorContains(mock, fmt.Errorf("long: %v", longSlice), "EOF") - Contains(t, mock.errorString(), ` - Error Trace: - Error: Error "long: [0 0 0`) - Contains(t, mock.errorString(), `<... truncated> does not contain "EOF"`) -} - -func TestErrorIsWithErrorTooLongToPrint(t *testing.T) { - t.Parallel() - mock := new(mockT) - - longSlice := make([]int, 1_000_000) - ErrorIs(mock, fmt.Errorf("long: %v", longSlice), fmt.Errorf("also: %v", longSlice)) - Contains(t, mock.errorString(), ` - Error Trace: - Error: Target error should be in err chain: - expected: "also: [0 0 0`) - Contains(t, mock.errorString(), `<... truncated> - in chain: "long: [0 0 0`) -} - -func TestErrorNotErrorIsWithErrorTooLongToPrint(t *testing.T) { - t.Parallel() - mock := new(mockT) - - longSlice := make([]int, 1_000_000) - err := fmt.Errorf("long: %v", longSlice) - NotErrorIs(mock, err, err) - Contains(t, mock.errorString(), ` - Error Trace: - Error: Target error should not be in err chain: - found: "long: [0 0 0`) - Contains(t, mock.errorString(), `<... truncated> - in chain: "long: [0 0 0`) -} - -func TestErrorAsWithErrorTooLongToPrint(t *testing.T) { - t.Parallel() - mock := new(mockT) - - longSlice := make([]int, 1_000_000) - var target *customError - ErrorAs(mock, fmt.Errorf("long: %v", longSlice), &target) - Contains(t, mock.errorString(), fmt.Sprintf(` - Error Trace: - Error: Should be in error chain: - expected: *%s.customError`, - shortpkg)) - Contains(t, mock.errorString(), ` - in chain: "long: [0 0 0`) - Contains(t, mock.errorString(), "<... truncated>") -} - -func TestErrorNotErrorAsWithErrorTooLongToPrint(t *testing.T) { - t.Parallel() - mock := new(mockT) - - longSlice := make([]int, 1_000_000) - var target *customError - NotErrorAs(mock, fmt.Errorf("long: %v %w", longSlice, &customError{}), &target) - Contains(t, mock.errorString(), fmt.Sprintf(` - Error Trace: - Error: Target error should not be in err chain: - found: *%s.customError`, - shortpkg)) - Contains(t, mock.errorString(), ` - in chain: "long: [0 0 0`) - Contains(t, mock.errorString(), "<... truncated>") -} - func TestErrorNotErrorAs(t *testing.T) { t.Parallel() for tt := range errorNotErrorAsCases() { t.Run(fmt.Sprintf("NotErrorAs(%#v,%#v)", tt.err, &customError{}), func(t *testing.T) { t.Parallel() - mock := new(captureT) + mock := new(mockT) var target *customError res := NotErrorAs(mock, tt.err, &target) - mock.checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg) + shouldPassOrFail(t, mock, res, tt.result) }) } } +func TestErrorErrorMessages(t *testing.T) { + t.Parallel() + + runFailCases(t, errorFailCases()) +} + func TestErrorIs(t *testing.T) { t.Parallel() for tt := range errorIsCases() { t.Run(fmt.Sprintf("ErrorIs(%#v,%#v)", tt.err, tt.target), func(t *testing.T) { t.Parallel() - mock := new(captureT) + mock := new(mockT) res := ErrorIs(mock, tt.err, tt.target) - mock.checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg) + shouldPassOrFail(t, mock, res, tt.result) }) } } @@ -149,10 +53,10 @@ func TestErrorNotErrorIs(t *testing.T) { for tt := range errorNotErrorIsCases() { t.Run(fmt.Sprintf("NotErrorIs(%#v,%#v)", tt.err, tt.target), func(t *testing.T) { t.Parallel() - mock := new(captureT) + mock := new(mockT) res := NotErrorIs(mock, tt.err, tt.target) - mock.checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg) + shouldPassOrFail(t, mock, res, tt.result) }) } } @@ -163,28 +67,32 @@ func TestErrorAs(t *testing.T) { for tt := range errorAsCases() { t.Run(fmt.Sprintf("ErrorAs(%#v,%#v)", tt.err, &customError{}), func(t *testing.T) { t.Parallel() - mock := new(captureT) + mock := new(mockT) var target *customError res := ErrorAs(mock, tt.err, &target) - mock.checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg) + shouldPassOrFail(t, mock, res, tt.result) }) } } func TestErrorNoError(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) // start with a nil error var err error - True(t, NoError(mock, err), "NoError should return True for nil arg") + if !NoError(mock, err) { + t.Error("NoError should return true for nil arg") + } // now set an error err = errors.New("some error") - False(t, NoError(mock, err), "NoError with error should return False") + if NoError(mock, err) { + t.Error("NoError with error should return false") + } // returning an empty error interface err = func() error { @@ -196,22 +104,28 @@ func TestErrorNoError(t *testing.T) { t.Errorf("Error should be nil due to empty interface: %s", err) } - False(t, NoError(mock, err), "NoError should fail with empty error interface") + if NoError(mock, err) { + t.Error("NoError should fail with empty error interface") + } } func TestError(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) // start with a nil error var err error - False(t, Error(mock, err), "Error should return False for nil arg") + if Error(mock, err) { + t.Error("Error should return false for nil arg") + } // now set an error err = errors.New("some error") - True(t, Error(mock, err), "Error with error should return True") + if !Error(mock, err) { + t.Error("Error with error should return true") + } // returning an empty error interface err = func() error { @@ -223,49 +137,61 @@ func TestError(t *testing.T) { t.Errorf("Error should be nil due to empty interface: %s", err) } - True(t, Error(mock, err), "Error should pass with empty error interface") + if !Error(mock, err) { + t.Error("Error should pass with empty error interface") + } } func TestErrorEqualError(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) // start with a nil error var err error - False(t, EqualError(mock, err, ""), - "EqualError should return false for nil arg") + if EqualError(mock, err, "") { + t.Error("EqualError should return false for nil arg") + } // now set an error err = errors.New("some error") - False(t, EqualError(mock, err, "Not some error"), - "EqualError should return false for different error string") - True(t, EqualError(mock, err, "some error"), - "EqualError should return true") + if EqualError(mock, err, "Not some error") { + t.Error("EqualError should return false for different error string") + } + if !EqualError(mock, err, "some error") { + t.Error("EqualError should return true") + } } func TestErrorContains(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) // start with a nil error var err error - False(t, ErrorContains(mock, err, ""), - "ErrorContains should return false for nil arg") + if ErrorContains(mock, err, "") { + t.Error("ErrorContains should return false for nil arg") + } // now set an error err = errors.New("some error: another error") - False(t, ErrorContains(mock, err, "bad error"), - "ErrorContains should return false for different error string") - True(t, ErrorContains(mock, err, "some error"), - "ErrorContains should return true") - True(t, ErrorContains(mock, err, "another error"), - "ErrorContains should return true") + if ErrorContains(mock, err, "bad error") { + t.Error("ErrorContains should return false for different error string") + } + if !ErrorContains(mock, err, "some error") { + t.Error("ErrorContains should return true for 'some error'") + } + if !ErrorContains(mock, err, "another error") { + t.Error("ErrorContains should return true for 'another error'") + } } +// ============================================================================ +// TestNotErrorAs +// ============================================================================ + type errorNotErrorAsCase struct { - err error - result bool - resultErrMsg string + err error + result bool } func errorNotErrorAsCases() iter.Seq[errorNotErrorAsCase] { @@ -273,11 +199,6 @@ func errorNotErrorAsCases() iter.Seq[errorNotErrorAsCase] { { err: fmt.Errorf("wrap: %w", &customError{}), result: false, - resultErrMsg: "" + - "Target error should not be in err chain:\n" + - fmt.Sprintf("found: *%[1]s.customError\n", shortpkg) + - "in chain: \"wrap: fail\" (*fmt.wrapError)\n" + - fmt.Sprintf("\t\"fail\" (*%[1]s.customError)\n", shortpkg), }, { err: io.EOF, @@ -291,165 +212,222 @@ func errorNotErrorAsCases() iter.Seq[errorNotErrorAsCase] { } type errorIsCase struct { - err error - target error - result bool - resultErrMsg string + err error + target error + result bool } func errorIsCases() iter.Seq[errorIsCase] { return slices.Values([]errorIsCase{ + {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}, + {err: nil, target: io.EOF, result: false}, + {err: io.EOF, target: nil, result: false}, + {err: nil, target: nil, result: true}, + {err: fmt.Errorf("abc: %w", errors.New("def")), target: io.EOF, result: false}, + }) +} + +type errorNotErrorIsCase struct { + err error + target error + result bool +} + +func errorNotErrorIsCases() iter.Seq[errorNotErrorIsCase] { + return slices.Values([]errorNotErrorIsCase{ + {err: io.EOF, target: io.EOF, result: false}, + {err: fmt.Errorf("wrap: %w", io.EOF), target: io.EOF, result: false}, + {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}, + {err: fmt.Errorf("abc: %w", errors.New("def")), target: io.EOF, result: true}, + }) +} + +type errorAsCase struct { + err error + result bool +} + +func errorAsCases() iter.Seq[errorAsCase] { + return slices.Values([]errorAsCase{ + {err: fmt.Errorf("wrap: %w", &customError{}), result: true}, + {err: io.EOF, result: false}, + {err: nil, result: false}, + {err: fmt.Errorf("abc: %w", errors.New("def")), result: false}, + }) +} + +type customError struct{} + +func (*customError) Error() string { return "fail" } + +// ============================================================================ +// TestErrorErrorMessages +// ============================================================================ + +func errorFailCases() iter.Seq[failCase] { + longSlice := make([]int, 1_000_000) + + return slices.Values([]failCase{ + // --- truncation cases --- + truncationCase("NoError/truncation", func(t T) bool { + return NoError(t, fmt.Errorf("long: %v", longSlice)) + }), + truncationCase("EqualError/truncation", func(t T) bool { + return EqualError(t, fmt.Errorf("long: %v", longSlice), "EOF") + }), + truncationCase("ErrorContains/truncation", func(t T) bool { + return ErrorContains(t, fmt.Errorf("long: %v", longSlice), "EOF") + }), + truncationCase("ErrorIs/truncation", func(t T) bool { + return ErrorIs(t, fmt.Errorf("long: %v", longSlice), fmt.Errorf("also: %v", longSlice)) + }), + truncationCase("NotErrorIs/truncation", func(t T) bool { + err := fmt.Errorf("long: %v", longSlice) + return NotErrorIs(t, err, err) + }), + truncationCase("ErrorAs/truncation", func(t T) bool { + var target *customError + return ErrorAs(t, fmt.Errorf("long: %v", longSlice), &target) + }), + truncationCase("NotErrorAs/truncation", func(t T) bool { + var target *customError + return NotErrorAs(t, fmt.Errorf("long: %v %w", longSlice, &customError{}), &target) + }), + + // --- ErrorIs message cases --- { - 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: "" + + name: "ErrorIs/not_in_chain", + assertion: func(t T) bool { + return ErrorIs(t, io.EOF, io.ErrClosedPipe) + }, + wantError: "" + "Target error should be in err chain:\n" + "expected: \"io: read/write on closed pipe\"\n" + - "in chain: \"EOF\"\n", + "in chain: \"EOF\"", }, { - err: nil, - target: io.EOF, - result: false, - resultErrMsg: "Expected error with \"EOF\" in chain but got nil.\n", + name: "ErrorIs/nil_err", + assertion: func(t T) bool { + return ErrorIs(t, nil, io.EOF) + }, + wantError: "Expected error with \"EOF\" in chain but got nil.", }, { - err: io.EOF, - target: nil, - result: false, - resultErrMsg: "" + + name: "ErrorIs/nil_target", + assertion: func(t T) bool { + return ErrorIs(t, io.EOF, nil) + }, + wantError: "" + "Target error should be in err chain:\n" + "expected: \"\"\n" + - "in chain: \"EOF\"\n", - }, - { - err: nil, - target: nil, - result: true, + "in chain: \"EOF\"", }, { - err: fmt.Errorf("abc: %w", errors.New("def")), - target: io.EOF, - result: false, - resultErrMsg: "" + + name: "ErrorIs/wrapped_not_in_chain", + assertion: func(t T) bool { + return ErrorIs(t, fmt.Errorf("abc: %w", errors.New("def")), io.EOF) + }, + wantError: "" + "Target error should be in err chain:\n" + "expected: \"EOF\"\n" + "in chain: \"abc: def\"\n" + - "\t\"def\"\n", + "\t\"def\"", }, - }) -} - -type errorNotErrorIsCase struct { - err error - target error - result bool - resultErrMsg string -} -func errorNotErrorIsCases() iter.Seq[errorNotErrorIsCase] { - return slices.Values([]errorNotErrorIsCase{ + // --- NotErrorIs message cases --- { - err: io.EOF, - target: io.EOF, - result: false, - resultErrMsg: "" + + name: "NotErrorIs/same_error", + assertion: func(t T) bool { + return NotErrorIs(t, io.EOF, io.EOF) + }, + wantError: "" + "Target error should not be in err chain:\n" + "found: \"EOF\"\n" + - "in chain: \"EOF\"\n", + "in chain: \"EOF\"", }, { - err: fmt.Errorf("wrap: %w", io.EOF), - target: io.EOF, - result: false, - resultErrMsg: "" + + name: "NotErrorIs/wrapped_in_chain", + assertion: func(t T) bool { + return NotErrorIs(t, fmt.Errorf("wrap: %w", io.EOF), io.EOF) + }, + wantError: "" + "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, + "\t\"EOF\"", }, { - err: io.EOF, - target: nil, - result: true, - }, - { - err: nil, - target: nil, - result: false, - resultErrMsg: "" + + name: "NotErrorIs/both_nil", + assertion: func(t T) bool { + return NotErrorIs(t, nil, nil) + }, + wantError: "" + "Target error should not be in err chain:\n" + "found: \"\"\n" + - "in chain: \n", + "in chain: ", }, - { - err: fmt.Errorf("abc: %w", errors.New("def")), - target: io.EOF, - result: true, - }, - }) -} -type errorAsCase struct { - err error - result bool - resultErrMsg string -} - -func errorAsCases() iter.Seq[errorAsCase] { - return slices.Values([]errorAsCase{ + // --- ErrorAs message cases --- { - err: fmt.Errorf("wrap: %w", &customError{}), - result: true, - }, - { - err: io.EOF, - result: false, - resultErrMsg: "" + + name: "ErrorAs/not_in_chain", + assertion: func(t T) bool { + var target *customError + return ErrorAs(t, io.EOF, &target) + }, + wantError: "" + "Should be in error chain:\n" + - fmt.Sprintf("expected: *%[1]s.customError\n", shortpkg) + - "in chain: \"EOF\" (*errors.errorString)\n", + fmt.Sprintf("expected: *%s.customError\n", shortpkg) + + "in chain: \"EOF\" (*errors.errorString)", }, { - err: nil, - result: false, - resultErrMsg: "" + + name: "ErrorAs/nil_err", + assertion: func(t T) bool { + var target *customError + return ErrorAs(t, nil, &target) + }, + wantError: "" + "An error is expected but got nil.\n" + - fmt.Sprintf(`expected: *%s.customError`, shortpkg) + "\n", + fmt.Sprintf("expected: *%s.customError", shortpkg), }, { - err: fmt.Errorf("abc: %w", errors.New("def")), - result: false, - resultErrMsg: "" + + name: "ErrorAs/wrapped_not_in_chain", + assertion: func(t T) bool { + var target *customError + return ErrorAs(t, fmt.Errorf("abc: %w", errors.New("def")), &target) + }, + wantError: "" + "Should be in error chain:\n" + - fmt.Sprintf("expected: *%[1]s.customError\n", shortpkg) + + fmt.Sprintf("expected: *%s.customError\n", shortpkg) + "in chain: \"abc: def\" (*fmt.wrapError)\n" + - "\t\"def\" (*errors.errorString)\n", + "\t\"def\" (*errors.errorString)", + }, + + // --- NotErrorAs message cases --- + { + name: "NotErrorAs/found_in_chain", + assertion: func(t T) bool { + var target *customError + return NotErrorAs(t, fmt.Errorf("wrap: %w", &customError{}), &target) + }, + wantError: "" + + "Target error should not be in err chain:\n" + + fmt.Sprintf("found: *%s.customError\n", shortpkg) + + "in chain: \"wrap: fail\" (*fmt.wrapError)\n" + + fmt.Sprintf("\t\"fail\" (*%s.customError)", shortpkg), + }, + // -- TestExample error + { + name: "NotError/TestExampleError", + assertion: func(t T) bool { + return NoError(t, ErrTest) + }, + wantError: "Received unexpected error:\n" + + "assert.ErrTest general error for testing", }, }) } - -type customError struct{} - -func (*customError) Error() string { return "fail" } diff --git a/internal/assertions/file.go b/internal/assertions/file.go index 4efe7ad08..5bc678594 100644 --- a/internal/assertions/file.go +++ b/internal/assertions/file.go @@ -25,16 +25,16 @@ func FileExists(t T, path string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } - info, err := os.Lstat(path) + + info, err := lstat(path, "file") if err != nil { - if os.IsNotExist(err) { - return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...) - } - return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...) + return Fail(t, err.Error(), msgAndArgs...) } + if info.IsDir() { return Fail(t, fmt.Sprintf("%q is a directory", path), msgAndArgs...) } + return true } @@ -54,10 +54,12 @@ func FileNotExists(t T, path string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } + info, err := os.Lstat(path) if err != nil { return true } + if info.IsDir() { return true } @@ -80,16 +82,16 @@ func DirExists(t T, path string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } - info, err := os.Lstat(path) + + info, err := lstat(path, "directory") if err != nil { - if os.IsNotExist(err) { - return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...) - } - return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...) + return Fail(t, err.Error(), msgAndArgs...) } + if !info.IsDir() { return Fail(t, fmt.Sprintf("%q is a file", path), msgAndArgs...) } + return true } @@ -109,16 +111,16 @@ func DirNotExists(t T, path string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } + info, err := os.Lstat(path) if err != nil { - if os.IsNotExist(err) { - return true - } return true } + if !info.IsDir() { return true } + return Fail(t, fmt.Sprintf("directory %q exists", path), msgAndArgs...) } @@ -138,16 +140,16 @@ func FileEmpty(t T, path string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } - info, err := os.Lstat(path) + + info, err := lstat(path, "file") if err != nil { - if os.IsNotExist(err) { - return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...) - } - return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...) + return Fail(t, err.Error(), msgAndArgs...) } + if info.IsDir() { return Fail(t, fmt.Sprintf("%q is a directory", path), msgAndArgs...) } + if info.Mode()&fs.ModeSymlink > 0 { target, err := os.Readlink(path) if err != nil { @@ -179,16 +181,16 @@ func FileNotEmpty(t T, path string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } - info, err := os.Lstat(path) + + info, err := lstat(path, "file") if err != nil { - if os.IsNotExist(err) { - return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...) - } - return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...) + return Fail(t, err.Error(), msgAndArgs...) } + if info.IsDir() { return Fail(t, fmt.Sprintf("%q is a directory", path), msgAndArgs...) } + if info.Mode()&fs.ModeSymlink > 0 { target, err := os.Readlink(path) if err != nil { @@ -203,3 +205,16 @@ func FileNotEmpty(t T, path string, msgAndArgs ...any) bool { return true } + +func lstat(path, kind string) (info os.FileInfo, err error) { + info, err = os.Lstat(path) + if err != nil { + if os.IsNotExist(err) { + return info, fmt.Errorf("unable to find %s %q: %w", kind, path, err) + } + + return info, fmt.Errorf("error when running os.Lstat(%q): %w", path, err) + } + + return info, nil +} diff --git a/internal/assertions/file_test.go b/internal/assertions/file_test.go index 1067c055c..ca3da9ef7 100644 --- a/internal/assertions/file_test.go +++ b/internal/assertions/file_test.go @@ -4,149 +4,225 @@ package assertions import ( + "iter" "os" "path/filepath" + "slices" "testing" ) func TestFileExists(t *testing.T) { t.Parallel() - mock := new(testing.T) - True(t, FileExists(mock, filepath.Join("testdata", "existing_file"))) + mock := new(mockT) + if !FileExists(mock, filepath.Join("testdata", "existing_file")) { + t.Error("expected FileExists to return true for existing file") + } - mock = new(testing.T) - False(t, FileExists(mock, "random_file")) + mock = new(mockT) + if FileExists(mock, "random_file") { + t.Error("expected FileExists to return false for random_file") + } - mock = new(testing.T) - False(t, FileExists(mock, filepath.Join("testdata", "existing_dir"))) + mock = new(mockT) + if FileExists(mock, filepath.Join("testdata", "existing_dir")) { + t.Error("expected FileExists to return false for directory") + } link := getTempSymlinkPath(t, filepath.Join("testdata", "existing_file")) - mock = new(testing.T) - True(t, FileExists(mock, link)) + mock = new(mockT) + if !FileExists(mock, link) { + t.Error("expected FileExists to return true for symlink to existing file") + } link = getTempSymlinkPath(t, "non_existent_file") - mock = new(testing.T) - True(t, FileExists(mock, link)) + mock = new(mockT) + if !FileExists(mock, link) { + t.Error("expected FileExists to return true for symlink (broken symlink is still a file)") + } } func TestFileFileNotExists(t *testing.T) { t.Parallel() - mock := new(testing.T) - False(t, FileNotExists(mock, filepath.Join("testdata", "existing_file"))) + mock := new(mockT) + if FileNotExists(mock, filepath.Join("testdata", "existing_file")) { + t.Error("expected FileNotExists to return false for existing file") + } - mock = new(testing.T) - True(t, FileNotExists(mock, "non_existent_file")) + mock = new(mockT) + if !FileNotExists(mock, "non_existent_file") { + t.Error("expected FileNotExists to return true for non-existent file") + } - mock = new(testing.T) - True(t, FileNotExists(mock, filepath.Join("testdata", "existing_dir"))) + mock = new(mockT) + if !FileNotExists(mock, filepath.Join("testdata", "existing_dir")) { + t.Error("expected FileNotExists to return true for directory") + } link := getTempSymlinkPath(t, filepath.Join("testdata", "existing_file")) - mock = new(testing.T) - False(t, FileNotExists(mock, link)) + mock = new(mockT) + if FileNotExists(mock, link) { + t.Error("expected FileNotExists to return false for symlink to existing file") + } link = getTempSymlinkPath(t, "non_existent_file") - mock = new(testing.T) - False(t, FileNotExists(mock, link)) + mock = new(mockT) + if FileNotExists(mock, link) { + t.Error("expected FileNotExists to return false for symlink") + } } func TestFileDirExists(t *testing.T) { t.Parallel() - mock := new(testing.T) - False(t, DirExists(mock, filepath.Join("testdata", "existing_file"))) + mock := new(mockT) + if DirExists(mock, filepath.Join("testdata", "existing_file")) { + t.Error("expected DirExists to return false for file") + } - mock = new(testing.T) - False(t, DirExists(mock, "non_existent_dir")) + mock = new(mockT) + if DirExists(mock, "non_existent_dir") { + t.Error("expected DirExists to return false for non-existent dir") + } - mock = new(testing.T) - True(t, DirExists(mock, filepath.Join("testdata", "existing_dir"))) + mock = new(mockT) + if !DirExists(mock, filepath.Join("testdata", "existing_dir")) { + t.Error("expected DirExists to return true for existing dir") + } link := getTempSymlinkPath(t, filepath.Join("testdata", "existing_file")) - mock = new(testing.T) - False(t, DirExists(mock, link)) + mock = new(mockT) + if DirExists(mock, link) { + t.Error("expected DirExists to return false for symlink to file") + } link = getTempSymlinkPath(t, "non_existent_dir") - mock = new(testing.T) - False(t, DirExists(mock, link)) + mock = new(mockT) + if DirExists(mock, link) { + t.Error("expected DirExists to return false for symlink to non-existent dir") + } } func TestFileDirNotExists(t *testing.T) { t.Parallel() - mock := new(testing.T) - True(t, DirNotExists(mock, filepath.Join("testdata", "existing_file"))) + mock := new(mockT) + if !DirNotExists(mock, filepath.Join("testdata", "existing_file")) { + t.Error("expected DirNotExists to return true for file") + } - mock = new(testing.T) - True(t, DirNotExists(mock, "non_existent_dir")) + mock = new(mockT) + if !DirNotExists(mock, "non_existent_dir") { + t.Error("expected DirNotExists to return true for non-existent dir") + } - mock = new(testing.T) - False(t, DirNotExists(mock, filepath.Join("testdata", "existing_dir"))) + mock = new(mockT) + if DirNotExists(mock, filepath.Join("testdata", "existing_dir")) { + t.Error("expected DirNotExists to return false for existing dir") + } link := getTempSymlinkPath(t, filepath.Join("testdata", "existing_file")) - mock = new(testing.T) - True(t, DirNotExists(mock, link)) + mock = new(mockT) + if !DirNotExists(mock, link) { + t.Error("expected DirNotExists to return true for symlink to file") + } link = getTempSymlinkPath(t, "non_existent_dir") - mock = new(testing.T) - True(t, DirNotExists(mock, link)) + mock = new(mockT) + if !DirNotExists(mock, link) { + t.Error("expected DirNotExists to return true for symlink to non-existent dir") + } } func TestFileEmpty(t *testing.T) { t.Parallel() - mock := new(testing.T) - True(t, FileEmpty(mock, filepath.Join("testdata", "empty_file"))) + mock := new(mockT) + if !FileEmpty(mock, filepath.Join("testdata", "empty_file")) { + t.Error("expected FileEmpty to return true for empty file") + } - mock = new(testing.T) - False(t, FileEmpty(mock, filepath.Join("testdata", "existing_file"))) + mock = new(mockT) + if FileEmpty(mock, filepath.Join("testdata", "existing_file")) { + t.Error("expected FileEmpty to return false for non-empty file") + } - mock = new(testing.T) - False(t, FileEmpty(mock, "random_file")) + mock = new(mockT) + if FileEmpty(mock, "random_file") { + t.Error("expected FileEmpty to return false for non-existent file") + } - mock = new(testing.T) - False(t, FileEmpty(mock, filepath.Join("testdata", "existing_dir"))) + mock = new(mockT) + if FileEmpty(mock, filepath.Join("testdata", "existing_dir")) { + t.Error("expected FileEmpty to return false for directory") + } link := getTempSymlinkPath(t, filepath.Join("testdata", "empty_file")) - mock = new(testing.T) - True(t, FileEmpty(mock, link)) + mock = new(mockT) + if !FileEmpty(mock, link) { + t.Error("expected FileEmpty to return true for symlink to empty file") + } link = getTempSymlinkPath(t, filepath.Join("testdata", "existing_file")) - mock = new(testing.T) - False(t, FileEmpty(mock, link)) + mock = new(mockT) + if FileEmpty(mock, link) { + t.Error("expected FileEmpty to return false for symlink to non-empty file") + } link = getTempSymlinkPath(t, "non_existent_file") - mock = new(testing.T) - False(t, FileEmpty(mock, link)) + mock = new(mockT) + if FileEmpty(mock, link) { + t.Error("expected FileEmpty to return false for symlink to non-existent file") + } } func TestFileNotEmpty(t *testing.T) { t.Parallel() - mock := new(testing.T) - True(t, FileNotEmpty(mock, filepath.Join("testdata", "existing_file"))) + mock := new(mockT) + if !FileNotEmpty(mock, filepath.Join("testdata", "existing_file")) { + t.Error("expected FileNotEmpty to return true for non-empty file") + } - mock = new(testing.T) - False(t, FileNotEmpty(mock, filepath.Join("testdata", "empty_file"))) + mock = new(mockT) + if FileNotEmpty(mock, filepath.Join("testdata", "empty_file")) { + t.Error("expected FileNotEmpty to return false for empty file") + } - mock = new(testing.T) - False(t, FileNotEmpty(mock, "non_existent_file")) + mock = new(mockT) + if FileNotEmpty(mock, "non_existent_file") { + t.Error("expected FileNotEmpty to return false for non-existent file") + } - mock = new(testing.T) - False(t, FileNotEmpty(mock, filepath.Join("testdata", "existing_dir"))) + mock = new(mockT) + if FileNotEmpty(mock, filepath.Join("testdata", "existing_dir")) { + t.Error("expected FileNotEmpty to return false for directory") + } link := getTempSymlinkPath(t, filepath.Join("testdata", "empty_file")) - mock = new(testing.T) - False(t, FileNotEmpty(mock, link)) + mock = new(mockT) + if FileNotEmpty(mock, link) { + t.Error("expected FileNotEmpty to return false for symlink to empty file") + } link = getTempSymlinkPath(t, filepath.Join("testdata", "existing_file")) - mock = new(testing.T) - True(t, FileNotEmpty(mock, link)) + mock = new(mockT) + if !FileNotEmpty(mock, link) { + t.Error("expected FileNotEmpty to return true for symlink to non-empty file") + } link = getTempSymlinkPath(t, "non_existent_file") - mock = new(testing.T) - False(t, FileNotExists(mock, link)) + mock = new(mockT) + if FileNotExists(mock, link) { + t.Error("expected FileNotExists to return false for symlink") + } +} + +func TestFileErrorMessages(t *testing.T) { + t.Parallel() + + runFailCases(t, fileFailCases()) } func getTempSymlinkPath(t *testing.T, file string) string { @@ -159,3 +235,62 @@ func getTempSymlinkPath(t *testing.T, file string) string { } return link } + +// ============================================================================ +// TestFileErrorMessages +// ============================================================================ + +func fileFailCases() iter.Seq[failCase] { + return slices.Values([]failCase{ + { + name: "FileExists/nonexistent", + assertion: func(t T) bool { return FileExists(t, "nonexistent_file") }, + wantContains: []string{"unable to find file"}, + }, + { + name: "FileExists/is-directory", + assertion: func(t T) bool { return FileExists(t, filepath.Join("testdata", "existing_dir")) }, + wantContains: []string{"is a directory"}, + }, + { + name: "FileNotExists/existing-file", + assertion: func(t T) bool { return FileNotExists(t, filepath.Join("testdata", "existing_file")) }, + wantContains: []string{"file", "exists"}, + }, + { + name: "DirExists/nonexistent", + assertion: func(t T) bool { return DirExists(t, "nonexistent_dir") }, + wantContains: []string{"unable to find directory"}, + }, + { + name: "DirExists/is-file", + assertion: func(t T) bool { return DirExists(t, filepath.Join("testdata", "existing_file")) }, + wantContains: []string{"is a file"}, + }, + { + name: "DirNotExists/existing-dir", + assertion: func(t T) bool { return DirNotExists(t, filepath.Join("testdata", "existing_dir")) }, + wantContains: []string{"directory", "exists"}, + }, + { + name: "FileEmpty/non-empty-file", + assertion: func(t T) bool { return FileEmpty(t, filepath.Join("testdata", "existing_file")) }, + wantContains: []string{"is not empty"}, + }, + { + name: "FileEmpty/nonexistent", + assertion: func(t T) bool { return FileEmpty(t, "nonexistent_file") }, + wantContains: []string{"unable to find file"}, + }, + { + name: "FileNotEmpty/empty-file", + assertion: func(t T) bool { return FileNotEmpty(t, filepath.Join("testdata", "empty_file")) }, + wantContains: []string{"is empty"}, + }, + { + name: "FileNotEmpty/nonexistent", + assertion: func(t T) bool { return FileNotEmpty(t, "nonexistent_file") }, + wantContains: []string{"unable to find file"}, + }, + }) +} diff --git a/internal/assertions/helpers_impl_test.go b/internal/assertions/helpers_impl_test.go index 3aad18942..cbb2a5d75 100644 --- a/internal/assertions/helpers_impl_test.go +++ b/internal/assertions/helpers_impl_test.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "iter" + "reflect" "slices" "strings" "testing" @@ -29,13 +30,19 @@ func testTruncatingFormat() func(*testing.T) { t.Run("should not truncate rendered value", func(t *testing.T) { result := truncatingFormat("%#v", original) - Equal(t, fmt.Sprintf("%#v", original), result, "string should not be truncated") + expected := fmt.Sprintf("%#v", original) + if expected != result { + t.Errorf("string should not be truncated: expected %q, got %q", expected, result) + } }) t.Run("should truncate rendered value", func(t *testing.T) { original += strings.Repeat("x", 100) result := truncatingFormat("%#v", original) - NotEqual(t, fmt.Sprintf("%#v", original), result, "string should have been truncated.") + full := fmt.Sprintf("%#v", original) + if full == result { + t.Error("string should have been truncated") + } if !strings.HasSuffix(result, "<... truncated>") { t.Error("truncated string should have <... truncated> suffix") @@ -57,7 +64,9 @@ func testDiff() func(*testing.T) { tt.valueA, tt.valueB, ) - Equal(t, tt.expected, actual) + if tt.expected != actual { + t.Errorf("expected diff:\n%s\ngot:\n%s", tt.expected, actual) + } } }) } @@ -73,10 +82,14 @@ func testDiffList() func(*testing.T) { t.Parallel() actualExtraA, actualExtraB := diffLists(test.listA, test.listB) - Equal(t, test.extraA, actualExtraA, "extra A does not match for listA=%v listB=%v", - test.listA, test.listB) - Equal(t, test.extraB, actualExtraB, "extra B does not match for listA=%v listB=%v", - test.listA, test.listB) + if !reflect.DeepEqual(test.extraA, actualExtraA) { + t.Errorf("extra A does not match for listA=%v listB=%v: expected %v, got %v", + test.listA, test.listB, test.extraA, actualExtraA) + } + if !reflect.DeepEqual(test.extraB, actualExtraB) { + t.Errorf("extra B does not match for listA=%v listB=%v: expected %v, got %v", + test.listA, test.listB, test.extraB, actualExtraB) + } }) } } diff --git a/internal/assertions/http_test.go b/internal/assertions/http_test.go index 6a2156578..8bd7e4998 100644 --- a/internal/assertions/http_test.go +++ b/internal/assertions/http_test.go @@ -6,126 +6,211 @@ package assertions import ( "fmt" "io" + "iter" "net/http" "net/url" + "reflect" + "slices" "testing" ) func TestHTTPSuccess(t *testing.T) { t.Parallel() - mock1 := new(testing.T) - Equal(t, HTTPSuccess(mock1, httpOK, "GET", "/", nil), true) - False(t, mock1.Failed()) + mock1 := new(mockT) + if result := HTTPSuccess(mock1, httpOK, "GET", "/", nil); !result { + t.Error("expected HTTPSuccess to return true for httpOK") + } + if mock1.Failed() { + t.Error("expected mock not to have failed") + } - mock2 := new(testing.T) - Equal(t, HTTPSuccess(mock2, httpRedirect, "GET", "/", nil), false) - True(t, mock2.Failed()) + mock2 := new(mockT) + if result := HTTPSuccess(mock2, httpRedirect, "GET", "/", nil); result { + t.Error("expected HTTPSuccess to return false for httpRedirect") + } + if !mock2.Failed() { + t.Error("expected mock to have failed") + } mock3 := new(mockT) - Equal(t, HTTPSuccess( + if result := HTTPSuccess( mock3, httpError, "GET", "/", nil, "was not expecting a failure here", - ), false) - True(t, mock3.Failed()) - Contains(t, mock3.errorString(), "was not expecting a failure here") + ); result { + t.Error("expected HTTPSuccess to return false for httpError") + } + if !mock3.Failed() { + t.Error("expected mock to have failed") + } - mock4 := new(testing.T) - Equal(t, HTTPSuccess(mock4, httpStatusCode, "GET", "/", nil), false) - True(t, mock4.Failed()) + mock4 := new(mockT) + if result := HTTPSuccess(mock4, httpStatusCode, "GET", "/", nil); result { + t.Error("expected HTTPSuccess to return false for httpStatusCode") + } + if !mock4.Failed() { + t.Error("expected mock to have failed") + } - mock5 := new(testing.T) - Equal(t, HTTPSuccess(mock5, httpReadBody, "POST", "/", nil), true) - False(t, mock5.Failed()) + mock5 := new(mockT) + if result := HTTPSuccess(mock5, httpReadBody, "POST", "/", nil); !result { + t.Error("expected HTTPSuccess to return true for httpReadBody") + } + if mock5.Failed() { + t.Error("expected mock not to have failed") + } } func TestHTTPRedirect(t *testing.T) { t.Parallel() - mock1 := new(mockT) - Equal(t, HTTPRedirect( + mock1 := new(mockT) + if result := HTTPRedirect( mock1, httpOK, "GET", "/", nil, "was expecting a 3xx status code. Got 200.", - ), false) - True(t, mock1.Failed()) - Contains(t, mock1.errorString(), "was expecting a 3xx status code. Got 200.") + ); result { + t.Error("expected HTTPRedirect to return false for httpOK") + } + if !mock1.Failed() { + t.Error("expected mock to have failed") + } - mock2 := new(testing.T) - Equal(t, HTTPRedirect(mock2, httpRedirect, "GET", "/", nil), true) - False(t, mock2.Failed()) + mock2 := new(mockT) + if result := HTTPRedirect(mock2, httpRedirect, "GET", "/", nil); !result { + t.Error("expected HTTPRedirect to return true for httpRedirect") + } + if mock2.Failed() { + t.Error("expected mock not to have failed") + } - mock3 := new(testing.T) - Equal(t, HTTPRedirect(mock3, httpError, "GET", "/", nil), false) - True(t, mock3.Failed()) + mock3 := new(mockT) + if result := HTTPRedirect(mock3, httpError, "GET", "/", nil); result { + t.Error("expected HTTPRedirect to return false for httpError") + } + if !mock3.Failed() { + t.Error("expected mock to have failed") + } - mock4 := new(testing.T) - Equal(t, HTTPRedirect(mock4, httpStatusCode, "GET", "/", nil), false) - True(t, mock4.Failed()) + mock4 := new(mockT) + if result := HTTPRedirect(mock4, httpStatusCode, "GET", "/", nil); result { + t.Error("expected HTTPRedirect to return false for httpStatusCode") + } + if !mock4.Failed() { + t.Error("expected mock to have failed") + } } func TestHTTPError(t *testing.T) { t.Parallel() - mock1 := new(testing.T) - Equal(t, HTTPError(mock1, httpOK, "GET", "/", nil), false) - True(t, mock1.Failed()) + mock1 := new(mockT) + if result := HTTPError(mock1, httpOK, "GET", "/", nil); result { + t.Error("expected HTTPError to return false for httpOK") + } + if !mock1.Failed() { + t.Error("expected mock to have failed") + } mock2 := new(mockT) - Equal(t, HTTPError( + if result := HTTPError( mock2, httpRedirect, "GET", "/", nil, "Expected this request to error out. But it didn't", - ), false) - True(t, mock2.Failed()) - Contains(t, mock2.errorString(), "Expected this request to error out. But it didn't") + ); result { + t.Error("expected HTTPError to return false for httpRedirect") + } + if !mock2.Failed() { + t.Error("expected mock to have failed") + } - mock3 := new(testing.T) - Equal(t, HTTPError(mock3, httpError, "GET", "/", nil), true) - False(t, mock3.Failed()) + mock3 := new(mockT) + if result := HTTPError(mock3, httpError, "GET", "/", nil); !result { + t.Error("expected HTTPError to return true for httpError") + } + if mock3.Failed() { + t.Error("expected mock not to have failed") + } - mock4 := new(testing.T) - Equal(t, HTTPError(mock4, httpStatusCode, "GET", "/", nil), false) - True(t, mock4.Failed()) + mock4 := new(mockT) + if result := HTTPError(mock4, httpStatusCode, "GET", "/", nil); result { + t.Error("expected HTTPError to return false for httpStatusCode") + } + if !mock4.Failed() { + t.Error("expected mock to have failed") + } } func TestHTTPStatusCode(t *testing.T) { t.Parallel() - mock1 := new(testing.T) - Equal(t, HTTPStatusCode(mock1, httpOK, "GET", "/", nil, http.StatusSwitchingProtocols), false) - True(t, mock1.Failed()) + mock1 := new(mockT) + if result := HTTPStatusCode(mock1, httpOK, "GET", "/", nil, http.StatusSwitchingProtocols); result { + t.Error("expected HTTPStatusCode to return false for httpOK") + } + if !mock1.Failed() { + t.Error("expected mock to have failed") + } - mock2 := new(testing.T) - Equal(t, HTTPStatusCode(mock2, httpRedirect, "GET", "/", nil, http.StatusSwitchingProtocols), false) - True(t, mock2.Failed()) + mock2 := new(mockT) + if result := HTTPStatusCode(mock2, httpRedirect, "GET", "/", nil, http.StatusSwitchingProtocols); result { + t.Error("expected HTTPStatusCode to return false for httpRedirect") + } + if !mock2.Failed() { + t.Error("expected mock to have failed") + } mock3 := new(mockT) - Equal(t, HTTPStatusCode( + if result := HTTPStatusCode( mock3, httpError, "GET", "/", nil, http.StatusSwitchingProtocols, "Expected the status code to be %d", http.StatusSwitchingProtocols, - ), false) - True(t, mock3.Failed()) - Contains(t, mock3.errorString(), "Expected the status code to be 101") + ); result { + t.Error("expected HTTPStatusCode to return false for httpError") + } + if !mock3.Failed() { + t.Error("expected mock to have failed") + } - mock4 := new(testing.T) - Equal(t, HTTPStatusCode(mock4, httpStatusCode, "GET", "/", nil, http.StatusSwitchingProtocols), true) - False(t, mock4.Failed()) + mock4 := new(mockT) + if result := HTTPStatusCode(mock4, httpStatusCode, "GET", "/", nil, http.StatusSwitchingProtocols); !result { + t.Error("expected HTTPStatusCode to return true for httpStatusCode") + } + if mock4.Failed() { + t.Error("expected mock not to have failed") + } } func TestHTTPStatusWrapper(t *testing.T) { t.Parallel() mock := new(mockT) - Equal(t, HTTPSuccess(mock, httpOK, "GET", "/", nil), true) - Equal(t, HTTPSuccess(mock, httpRedirect, "GET", "/", nil), false) - Equal(t, HTTPSuccess(mock, httpError, "GET", "/", nil), false) + if !HTTPSuccess(mock, httpOK, "GET", "/", nil) { + t.Error("expected HTTPSuccess(httpOK) to return true") + } + if HTTPSuccess(mock, httpRedirect, "GET", "/", nil) { + t.Error("expected HTTPSuccess(httpRedirect) to return false") + } + if HTTPSuccess(mock, httpError, "GET", "/", nil) { + t.Error("expected HTTPSuccess(httpError) to return false") + } - Equal(t, HTTPRedirect(mock, httpOK, "GET", "/", nil), false) - Equal(t, HTTPRedirect(mock, httpRedirect, "GET", "/", nil), true) - Equal(t, HTTPRedirect(mock, httpError, "GET", "/", nil), false) + if HTTPRedirect(mock, httpOK, "GET", "/", nil) { + t.Error("expected HTTPRedirect(httpOK) to return false") + } + if !HTTPRedirect(mock, httpRedirect, "GET", "/", nil) { + t.Error("expected HTTPRedirect(httpRedirect) to return true") + } + if HTTPRedirect(mock, httpError, "GET", "/", nil) { + t.Error("expected HTTPRedirect(httpError) to return false") + } - Equal(t, HTTPError(mock, httpOK, "GET", "/", nil), false) - Equal(t, HTTPError(mock, httpRedirect, "GET", "/", nil), false) - Equal(t, HTTPError(mock, httpError, "GET", "/", nil), true) + if HTTPError(mock, httpOK, "GET", "/", nil) { + t.Error("expected HTTPError(httpOK) to return false") + } + if HTTPError(mock, httpRedirect, "GET", "/", nil) { + t.Error("expected HTTPError(httpRedirect) to return false") + } + if !HTTPError(mock, httpError, "GET", "/", nil) { + t.Error("expected HTTPError(httpError) to return true") + } } func TestHTTPRequestWithNoParams(t *testing.T) { @@ -137,10 +222,16 @@ func TestHTTPRequestWithNoParams(t *testing.T) { w.WriteHeader(http.StatusOK) } - True(t, HTTPSuccess(t, handler, "GET", "/url", nil)) + if !HTTPSuccess(t, handler, "GET", "/url", nil) { + t.Error("expected HTTPSuccess to return true") + } - Empty(t, got.URL.Query()) - Equal(t, "/url", got.URL.RequestURI()) + if len(got.URL.Query()) != 0 { + t.Errorf("expected empty query, got %v", got.URL.Query()) + } + if got.URL.RequestURI() != "/url" { + t.Errorf("expected RequestURI %q, got %q", "/url", got.URL.RequestURI()) + } } func TestHTTPRequestWithParams(t *testing.T) { @@ -154,43 +245,105 @@ func TestHTTPRequestWithParams(t *testing.T) { params := url.Values{} params.Add("id", "12345") - True(t, HTTPSuccess(t, handler, "GET", "/url", params)) + if !HTTPSuccess(t, handler, "GET", "/url", params) { + t.Error("expected HTTPSuccess to return true") + } - Equal(t, url.Values{"id": []string{"12345"}}, got.URL.Query()) - Equal(t, "/url?id=12345", got.URL.String()) - Equal(t, "/url?id=12345", got.URL.RequestURI()) + expectedQuery := url.Values{"id": []string{"12345"}} + if !reflect.DeepEqual(expectedQuery, got.URL.Query()) { + t.Errorf("expected query %v, got %v", expectedQuery, got.URL.Query()) + } + if got.URL.String() != "/url?id=12345" { + t.Errorf("expected URL string %q, got %q", "/url?id=12345", got.URL.String()) + } + if got.URL.RequestURI() != "/url?id=12345" { + t.Errorf("expected RequestURI %q, got %q", "/url?id=12345", got.URL.RequestURI()) + } } func TestHttpBody(t *testing.T) { t.Parallel() mock := new(mockT) - True(t, HTTPBodyContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) - True(t, HTTPBodyContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) - False(t, HTTPBodyContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) + if !HTTPBodyContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!") { + t.Error("expected HTTPBodyContains to return true for 'Hello, World!'") + } + if !HTTPBodyContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World") { + t.Error("expected HTTPBodyContains to return true for 'World'") + } + if HTTPBodyContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world") { + t.Error("expected HTTPBodyContains to return false for 'world' (case sensitive)") + } - False(t, HTTPBodyNotContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) - False(t, HTTPBodyNotContains( + if HTTPBodyNotContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!") { + t.Error("expected HTTPBodyNotContains to return false for 'Hello, World!'") + } + if HTTPBodyNotContains( mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World", "Expected the request body to not contain 'World'. But it did.", - )) - True(t, HTTPBodyNotContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) - Contains(t, mock.errorString(), "Expected the request body to not contain 'World'. But it did.") + ) { + t.Error("expected HTTPBodyNotContains to return false for 'World'") + } + if !HTTPBodyNotContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world") { + t.Error("expected HTTPBodyNotContains to return true for 'world' (case sensitive)") + } + if !HTTPBodyContains(mock, httpReadBody, "GET", "/", nil, "hello") { + t.Error("expected HTTPBodyContains to return true for httpReadBody 'hello'") + } +} - True(t, HTTPBodyContains(mock, httpReadBody, "GET", "/", nil, "hello")) +func TestHTTPErrorMessages(t *testing.T) { + t.Parallel() + + runFailCases(t, httpFailCases()) +} + +// ============================================================================ +// TestHTTPErrorMessages +// ============================================================================. +func httpFailCases() iter.Seq[failCase] { + return slices.Values([]failCase{ + { + name: "HTTPSuccess/error-handler", + assertion: func(t T) bool { return HTTPSuccess(t, httpError, "GET", "/", nil) }, + wantContains: []string{"Expected HTTP success status code"}, + }, + { + name: "HTTPRedirect/ok-handler", + assertion: func(t T) bool { return HTTPRedirect(t, httpOK, "GET", "/", nil) }, + wantContains: []string{"Expected HTTP redirect status code"}, + }, + { + name: "HTTPError/redirect-handler", + assertion: func(t T) bool { return HTTPError(t, httpRedirect, "GET", "/", nil) }, + wantContains: []string{"Expected HTTP error status code"}, + }, + }) } func TestHTTPBodyWrappers(t *testing.T) { t.Parallel() mock := new(mockT) - True(t, HTTPBodyContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) - True(t, HTTPBodyContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) - False(t, HTTPBodyContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) + if !HTTPBodyContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!") { + t.Error("expected HTTPBodyContains to return true for 'Hello, World!'") + } + if !HTTPBodyContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World") { + t.Error("expected HTTPBodyContains to return true for 'World'") + } + if HTTPBodyContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world") { + t.Error("expected HTTPBodyContains to return false for 'world'") + } - False(t, HTTPBodyNotContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) - False(t, HTTPBodyNotContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) - True(t, HTTPBodyNotContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) + if HTTPBodyNotContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!") { + t.Error("expected HTTPBodyNotContains to return false for 'Hello, World!'") + } + if HTTPBodyNotContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World") { + t.Error("expected HTTPBodyNotContains to return false for 'World'") + } + if !HTTPBodyNotContains(mock, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world") { + t.Error("expected HTTPBodyNotContains to return true for 'world'") + } } func httpHelloName(w http.ResponseWriter, r *http.Request) { diff --git a/internal/assertions/json.go b/internal/assertions/json.go index 4f95478a7..4d6377d29 100644 --- a/internal/assertions/json.go +++ b/internal/assertions/json.go @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // we need to duplicate at least some godoc. +//nolint:dupl // YAML is actually very similar to JSON but we can't easily factorize this. package assertions import ( @@ -33,7 +33,7 @@ func JSONEqBytes(t T, expected, actual []byte, msgAndArgs ...any) bool { var expectedJSONAsInterface, actualJSONAsInterface any if err := json.Unmarshal(expected, &expectedJSONAsInterface); err != nil { - return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...) + return Fail(t, fmt.Sprintf("Expected value (%q) is not valid json.\nJSON parsing error: %v", expected, err), msgAndArgs...) } // Shortcut if same bytes @@ -42,7 +42,7 @@ func JSONEqBytes(t T, expected, actual []byte, msgAndArgs ...any) bool { } if err := json.Unmarshal(actual, &actualJSONAsInterface); err != nil { - return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...) + return Fail(t, fmt.Sprintf("Input (%q) needs to be valid json.\nJSON parsing error: %v", actual, err), msgAndArgs...) } return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...) @@ -91,3 +91,75 @@ func JSONEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any return JSONEqBytes(t, []byte(expected), []byte(actual), msgAndArgs) } + +// JSONUnmarshalAsT wraps [Equal] after [json.Unmarshal]. +// +// The input JSON may be a string or []byte. +// +// It fails if the unmarshaling returns an error or if the resulting object is not equal to the expected one. +// +// Be careful not to wrap the expected object into an "any" interface if this is not what you expected: +// the unmarshaling would take this type to unmarshal as a map[string]any. +// +// # Usage +// +// expected := struct { +// A int `json:"a"` +// }{ +// A: 10, +// } +// +// assertions.JSONUnmarshalAsT(t,expected, `{"a": 10}`) +// +// # Examples +// +// success: dummyStruct{A: "a"} , []byte(`{"A": "a"}`) +// failure: 1, `[{"foo": "bar"}, {"hello": "world"}]` +func JSONUnmarshalAsT[Object any, ADoc Text](t T, expected Object, jazon ADoc, msgAndArgs ...any) bool { + // Domain: json + if h, ok := t.(H); ok { + h.Helper() + } + + var actual Object + if err := json.Unmarshal([]byte(jazon), &actual); err != nil { + return Fail(t, fmt.Sprintf("JSON unmarshal failed: %v", err), msgAndArgs...) + } + + return Equal(t, expected, actual, msgAndArgs...) +} + +// JSONMarshalAsT wraps [JSONEq] after [json.Marshal]. +// +// The input JSON may be a string or []byte. +// +// It fails if the marshaling returns an error or if the expected JSON bytes differ semantically +// from the expected ones. +// +// # Usage +// +// actual := struct { +// A int `json:"a"` +// }{ +// A: 10, +// } +// +// assertions.JSONUnmarshalAsT(t,expected, `{"a": 10}`) +// +// # Examples +// +// success: []byte(`{"A": "a"}`), dummyStruct{A: "a"} +// failure: `[{"foo": "bar"}, {"hello": "world"}]`, 1 +func JSONMarshalAsT[EDoc Text](t T, expected EDoc, object any, msgAndArgs ...any) bool { + // Domain: json + if h, ok := t.(H); ok { + h.Helper() + } + + actual, err := json.Marshal(object) + if err != nil { + return Fail(t, fmt.Sprintf("JSON marshal failed: %v", err), msgAndArgs...) + } + + return JSONEqBytes(t, []byte(expected), actual, msgAndArgs...) +} diff --git a/internal/assertions/json_test.go b/internal/assertions/json_test.go index e01a7035f..7caf91bbe 100644 --- a/internal/assertions/json_test.go +++ b/internal/assertions/json_test.go @@ -17,6 +17,24 @@ func TestJSONEq(t *testing.T) { } } +func TestJSONMarshalUnmarshalAs(t *testing.T) { + t.Parallel() + + for tc := range jsonMarshalCases() { + t.Run(tc.name, tc.test) + } +} + +func TestJSONErrorMessages(t *testing.T) { + t.Parallel() + + runFailCases(t, jsonFailCases()) +} + +// ======================================= +// Test JSONEq variants +// ======================================= + // test all JSONEq variants with the same input (possibly converted). func testAllJSONEq(expected, actual string, success bool) func(*testing.T) { //nolint:thelper // linter false positive: this is not a helper @@ -34,7 +52,7 @@ func testJSONEq(expected, actual string, success bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) res := JSONEq(mock, expected, actual) if res != success { if success { @@ -50,7 +68,7 @@ func testJSONEqBytes(expected, actual string, success bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) res := JSONEqBytes(mock, []byte(expected), []byte(actual)) if res != success { if success { @@ -67,7 +85,7 @@ func testJSONEqT[EDoc, ADoc Text](expected, actual string, success bool) func(*t return func(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) res := JSONEqT(mock, EDoc(expected), ADoc(actual)) if res != success { if success { @@ -145,3 +163,119 @@ func jsonCases() iter.Seq[genericTestCase] { )}, }) } + +// ======================================= +// Test JSONMarshalAsT / JSONUnmarshalAsT +// ======================================= + +func jsonMarshalCases() iter.Seq[genericTestCase] { + type canMarshalJSON struct { + A string `json:"a"` + B int `json:"b"` + C int `json:"c,omitempty"` + } + type cantMarshalJSON struct { + A string `json:"a"` + B int `json:"b"` + C func() `json:"c,omitempty"` // this fails when marshaling + } + + const jazon = `{"a":"x","b":1}` + valueCanDo := canMarshalJSON{A: "x", B: 1} + valueCantDo := cantMarshalJSON{A: "x", B: 1, C: func() {}} + + return slices.Values([]genericTestCase{ + {"can JSON", testAllMarshalAs(valueCanDo, jazon, true)}, + {"can't JSON/marshal-fails", testAllMarshalAs(valueCantDo, jazon, false)}, + {"can JSON/different-values", testAllMarshalAs(valueCanDo, `{"a": 1,"b":"x"}`, false)}, + }) +} + +//nolint:thelper // false positive: this is not a helper +func testAllMarshalAs[Doc Text, Object any](value Object, jazon Doc, success bool) func(*testing.T) { + return func(t *testing.T) { + t.Run("with JSONMarshalAsT", testJSONMarshalAsT(value, jazon, success)) + + t.Run("with JSONUnmarshalAsT", testJSONUnmarshalAsT(value, jazon, success)) + } +} + +func testJSONMarshalAsT[Doc Text, Object any](value Object, jazon Doc, success bool) func(*testing.T) { + if success { + return func(t *testing.T) { + t.Run("should marshal JSON", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + res := JSONMarshalAsT(mock, jazon, value) + if !res { + t.Errorf("expected struct to marshal correctly as JSON: %#v <=> %s.Got %s", value, jazon, mock.errorString()) + } + }) + } + } + + return func(t *testing.T) { + t.Run("should not marshal JSON", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + res := JSONMarshalAsT(mock, jazon, value) + if res { + t.Errorf("expected struct NOT to marshal correctly as JSON: %#v != %s", value, jazon) + } + }) + } +} + +func testJSONUnmarshalAsT[Doc Text, Object any](value Object, jazon Doc, success bool) func(*testing.T) { + if success { + return func(t *testing.T) { + t.Run("should unmarshal JSON", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + res := JSONUnmarshalAsT(mock, value, jazon) + if !res { + t.Errorf("expected json string to unmarshal correctly from JSON: %#v <=> %s. Got %s", value, jazon, mock.errorString()) + } + }) + } + } + + return func(t *testing.T) { + t.Run("should not unmarshal JSON", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + res := JSONUnmarshalAsT(mock, value, jazon) + if res { + t.Errorf("expected json string NOT to unmarshal correctly from JSON: %#v != %s", value, jazon) + } + }) + } +} + +// ======================================= +// Test JSONErrorMessages +// ======================================= + +func jsonFailCases() iter.Seq[failCase] { + return slices.Values([]failCase{ + { + name: "JSONEq/not-equal", + assertion: func(t T) bool { return JSONEq(t, `{"a":1}`, `{"a":2}`) }, + wantContains: []string{"Not equal"}, + }, + { + name: "JSONEq/invalid-expected", + assertion: func(t T) bool { return JSONEq(t, "not json", `{"a":1}`) }, + wantContains: []string{"is not valid json"}, + }, + { + name: "JSONEq/invalid-actual", + assertion: func(t T) bool { return JSONEq(t, `{"a":1}`, "not json") }, + wantContains: []string{"needs to be valid json"}, + }, + }) +} diff --git a/internal/assertions/mock_test.go b/internal/assertions/mock_test.go index 9d21fcf7e..32c39e9ae 100644 --- a/internal/assertions/mock_test.go +++ b/internal/assertions/mock_test.go @@ -4,11 +4,10 @@ package assertions import ( - "bytes" "context" "fmt" + "iter" "regexp" - "runtime" "strings" "testing" ) @@ -18,10 +17,7 @@ var ( _ T = &mockFailNowT{} _ failNower = &mockFailNowT{} _ T = &captureT{} - _ T = &bufferT{} - _ T = &dummyT{} _ T = &errorsCapturingT{} - _ T = &outputT{} ) type mockT struct { @@ -29,6 +25,11 @@ type mockT struct { args []any } +const ( + errString = "Error" + errTrace = "Error Trace" +) + // Helper is like [testing.T.Helper] but does nothing. func (mockT) Helper() {} @@ -61,14 +62,6 @@ func (m *mockFailNowT) FailNow() { m.failed = true } -type dummyT struct{} - -func (dummyT) Errorf(string, ...any) {} - -func (dummyT) Context() context.Context { - return context.Background() -} - // errorsCapturingT is a mock implementation of TestingT that captures errors reported with Errorf. type errorsCapturingT struct { errors []error @@ -96,28 +89,6 @@ func (t *errorsCapturingT) Errorf(format string, args ...any) { t.errors = append(t.errors, fmt.Errorf(format, args...)) } -type outputT struct { - buf *bytes.Buffer - helpers map[string]struct{} -} - -func newOutputMock() *outputT { - return &outputT{buf: bytes.NewBuffer(nil)} -} - -// Implements T. -func (t *outputT) Errorf(format string, args ...any) { - s := fmt.Sprintf(format, args...) - t.buf.WriteString(s) -} - -func (t *outputT) Helper() { - if t.helpers == nil { - t.helpers = make(map[string]struct{}) - } - t.helpers[callerName(1)] = struct{}{} -} - type captureT struct { failed bool msg string @@ -131,86 +102,6 @@ func (ctt *captureT) Errorf(format string, args ...any) { ctt.failed = true } -func (ctt *captureT) checkResultAndErrMsg(t *testing.T, expectedRes, res bool, expectedErrMsg string) { - t.Helper() - - if res != expectedRes { - t.Errorf("Should return %t", expectedRes) - return - } - if res == ctt.failed { - t.Errorf("The test result (%t) should be reflected in the testing.T type (%t)", res, !ctt.failed) - return - } - contents := parseLabeledOutput(ctt.msg) - if res == true { - if contents != nil { - t.Errorf("Should not log an error. Log output: %q", ctt.msg) - } - return - } - if contents == nil { - t.Errorf("Should log an error. Log output: %q", ctt.msg) - return - } - - for _, content := range contents { - if content.label == "Error" { - if expectedErrMsg == content.content { - return - } - t.Errorf("Recorded Error: %q", content.content) - } - } - - t.Errorf("Expected Error: %q", expectedErrMsg) -} - -// bufferT implements TestingT. Its implementation of Errorf writes the output that would be produced by -// testing.T.Errorf to an internal bytes.Buffer. -type bufferT struct { - buf bytes.Buffer -} - -// Helper is like [testing.T.Helper] but does nothing. -func (bufferT) Helper() {} - -func (t *bufferT) Errorf(format string, args ...any) { - // implementation of decorate is copied from testing.T - decorate := func(s string) string { - _, file, line, ok := runtime.Caller(3) // decorate + log + public function. - if ok { - // Truncate file name at last file name separator. - if index := strings.LastIndex(file, "/"); index >= 0 { - file = file[index+1:] - } else if index = strings.LastIndex(file, "\\"); index >= 0 { - file = file[index+1:] - } - } else { - file = "???" - line = 1 - } - buf := new(bytes.Buffer) - // Every line is indented at least one tab. - buf.WriteByte('\t') - fmt.Fprintf(buf, "%s:%d: ", file, line) - lines := strings.Split(s, "\n") - if l := len(lines); l > 1 && lines[l-1] == "" { - lines = lines[:l-1] - } - for i, line := range lines { - if i > 0 { - // Second and subsequent lines are indented an extra tab. - buf.WriteString("\n\t\t") - } - buf.WriteString(line) - } - buf.WriteByte('\n') - return buf.String() - } - t.buf.WriteString(decorate(fmt.Sprintf(format, args...))) -} - // 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 { @@ -248,40 +139,124 @@ func parseLabeledOutput(output string) []labeledContent { return contents } -// callerName gives the function name (qualified with a package path) -// for the caller after skip frames (where 0 means the current function). -func callerName(skip int) string { - // Make room for the skip PC. - var pc [1]uintptr - n := runtime.Callers(skip+2, pc[:]) // skip + runtime.Callers + callerName - if n == 0 { - panic("testing: zero callers found") - } - frames := runtime.CallersFrames(pc[:n]) - frame, _ := frames.Next() - return frame.Function -} - func shouldPassOrFail(t *testing.T, mock *mockT, result, shouldPass bool) { t.Helper() if shouldPass { - t.Run("should pass", func(t *testing.T) { - if !result || mock.Failed() { - t.Errorf("expected to pass") - } - }) + if !result || mock.Failed() { + t.Error("expected to pass") + } return } - t.Run("should fail", func(t *testing.T) { - if result || !mock.Failed() { - t.Errorf("expected to fail") - } - }) + if result || !mock.Failed() { + t.Error("expected to fail") + } } func ptr(i int) *int { return &i } + +// failCase defines a test case for verifying assertion error messages. +// +// Only one of wantError, wantMatch, or wantContains should be set per case. +type failCase struct { + name string + assertion func(t T) bool // assertion call with bad inputs baked in + wantError string // exact match on errString label content + wantMatch string // regexp match on errString label content + wantContains []string // substring checks on errString label content +} + +// runFailCases runs a set of failCase tests using the standard harness. +func runFailCases(t *testing.T, cases iter.Seq[failCase]) { + t.Helper() + + for tc := range cases { + t.Run(tc.name, runFailCase(tc)) + } +} + +func runFailCase(tc failCase) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + mock := new(captureT) + result := tc.assertion(mock) + + // 1. Verify assertion returned false + if result { + t.Error("expected assertion to return false") + return + } + + // 2. Verify mock recorded a failure + if !mock.failed { + t.Error("expected mock to record a failure") + return + } + + // 3. Parse envelope + parsed := parseLabeledOutput(mock.msg) + if parsed == nil { + t.Errorf("could not parse labeled output: %q", mock.msg) + return + } + + // 4. Validate envelope structure + var hasErrorTrace, hasError bool + var errorContent string + for _, lc := range parsed { + switch lc.label { + case errTrace: + hasErrorTrace = true + case errString: + hasError = true + errorContent = strings.TrimRight(lc.content, "\n") + } + } + if !hasErrorTrace { + t.Error("envelope missing Error Trace label") + } + if !hasError { + t.Error("envelope missing Error label") + return + } + + // 5. Match based on which want* field is set + switch { + case tc.wantError != "": + if errorContent != tc.wantError { + t.Errorf("error content mismatch:\n want: %q\n got: %q", tc.wantError, errorContent) + } + case tc.wantMatch != "": + matched, err := regexp.MatchString(tc.wantMatch, errorContent) + if err != nil { + t.Errorf("invalid regexp %q: %v", tc.wantMatch, err) + + return + } + + if !matched { + t.Errorf("error content does not match pattern %q:\n got: %q", tc.wantMatch, errorContent) + } + case len(tc.wantContains) > 0: + for _, sub := range tc.wantContains { + if !strings.Contains(errorContent, sub) { + t.Errorf("error content missing substring %q:\n got: %q", sub, errorContent) + } + } + } + } +} + +// truncationCase is a convenience constructor for a failCase that checks for truncation. +func truncationCase(name string, assertion func(t T) bool) failCase { + return failCase{ + name: name, + assertion: assertion, + wantContains: []string{"<... truncated>"}, + } +} diff --git a/internal/assertions/number_test.go b/internal/assertions/number_test.go index 6a471417d..467b7046c 100644 --- a/internal/assertions/number_test.go +++ b/internal/assertions/number_test.go @@ -7,85 +7,27 @@ import ( "iter" "math" "slices" - "strings" "testing" ) -func TestNumberInDeltaTErrorMessage(t *testing.T) { +func TestNumberEdgeCases(t *testing.T) { t.Parallel() - mock := new(mockT) - - // Test that error message shows correct difference - result := InDeltaT(mock, 10, 1, 5) - if result || !mock.Failed() { - t.Error("Expected test to fail but it passed") - } - - // Verify the error message contains the actual difference (9) - errorMsg := mock.errorString() - if !strings.Contains(errorMsg, "difference was 9") { - t.Errorf("Error message should contain 'difference was 9', got: %s", errorMsg) - } -} - -func TestNumberInEpsilonTErrorMessage(t *testing.T) { - t.Parallel() - - t.Run("relative error message", func(t *testing.T) { + t.Run("InDelta reflect-specific (type conversion)", func(t *testing.T) { t.Parallel() mock := new(mockT) - - // Test relative error: 100 vs 110 has 10% error, exceeds 5% epsilon - result := InEpsilonT(mock, 100.0, 110.0, 0.05) - if result || !mock.Failed() { - t.Error("Expected test to fail but it passed") - } - - // Verify the error message contains relative error - errorMsg := mock.errorString() - if !strings.Contains(errorMsg, "Relative error is too high") { - t.Errorf("Error message should contain 'Relative error is too high', got: %s", errorMsg) - } - - // Should show actual relative error of 0.1 (10%) - if !Contains(t, errorMsg, "0.1") { - t.Errorf("Error message should contain '0.1' (10%% relative error), got: %s", errorMsg) + result := InDelta(mock, "", nil, 1) + if result { + t.Errorf("Expected non numerals to fail") } }) - t.Run("absolute error message for zero expected", func(t *testing.T) { + t.Run("InEpsilon reflect-specific (type conversion)", func(t *testing.T) { t.Parallel() mock := new(mockT) - - // Test absolute error: expected=0, actual=0.5, epsilon=0.1 - result := InEpsilonT(mock, 0.0, 0.5, 0.1) - if result || !mock.Failed() { - t.Error("Expected test to fail but it passed") - } - - // Verify the error message mentions absolute error - errorMsg := mock.errorString() - if !strings.Contains(errorMsg, "Expected value is zero, using absolute error comparison") { - t.Errorf("Error message should mention absolute error comparison, got: %s", errorMsg) - } - // Should show actual absolute difference of 0.5 - if !strings.Contains(errorMsg, "0.5") { - t.Errorf("Error message should contain '0.5' (absolute difference), got: %s", errorMsg) - } - }) -} - -func TestNumberInDeltaEdgeCases(t *testing.T) { - t.Parallel() - - t.Run("InDelta specific (type conversion)", func(t *testing.T) { - t.Parallel() - - mock := new(testing.T) - result := InDelta(mock, "", nil, 1) + result := InEpsilon(mock, "", nil, 1) if result { t.Errorf("Expected non numerals to fail") } @@ -119,11 +61,14 @@ func TestNumberInDeltaSlice(t *testing.T) { func TestNumberInDeltaMapValues(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) // only have a reflection-based assertion here for tc := range numberInDeltaMapCases() { - tc.f(t, InDeltaMapValues(mock, tc.expect, tc.actual, tc.delta), tc.name+"\n"+diff(tc.expect, tc.actual)) + result := InDeltaMapValues(mock, tc.expect, tc.actual, tc.delta) + if result != tc.shouldPass { + t.Errorf("%s: expected result=%v, got %v\n%s", tc.name, tc.shouldPass, result, diff(tc.expect, tc.actual)) + } } } @@ -144,7 +89,15 @@ func TestNumberInEpsilonSlice(t *testing.T) { } } -// Helper functions and test data for InDelta/InDeltaT +func TestNumberErrorMessages(t *testing.T) { + t.Parallel() + + runFailCases(t, numberFailCases()) +} + +// ======================================= +// Test NumberInDelta variants +// ======================================= func deltaCases() iter.Seq[genericTestCase] { return slices.Values([]genericTestCase{ @@ -312,14 +265,7 @@ func testDelta[Number Measurable](expected, actual, delta Number, shouldPass boo mock := new(mockT) // InDelta requires delta as float64, so convert it result := InDelta(mock, expected, actual, float64(delta)) - - if shouldPass { - True(t, result) - False(t, mock.Failed()) - } else { - False(t, result) - True(t, mock.Failed()) - } + shouldPassOrFail(t, mock, result, shouldPass) } } @@ -329,25 +275,18 @@ func testDeltaT[Number Measurable](expected, actual, delta Number, shouldPass bo mock := new(mockT) result := InDeltaT(mock, expected, actual, delta) - - if shouldPass { - True(t, result) - False(t, mock.Failed()) - } else { - False(t, result) - True(t, mock.Failed()) - } + shouldPassOrFail(t, mock, result, shouldPass) } } // Helper functions and test data for InDeltaMapValues type numberInDeltaMapCase struct { - name string - expect any - actual any - f func(T, bool, ...any) bool - delta float64 + name string + expect any + actual any + shouldPass bool + delta float64 } func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { @@ -367,8 +306,8 @@ func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { "bar": 1.99, "baz": math.NaN(), }, - delta: 0.1, - f: True, + delta: 0.1, + shouldPass: true, }, { name: "Within delta", @@ -380,8 +319,8 @@ func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { 1: 1.0, 2: 1.99, }, - delta: 0.1, - f: True, + delta: 0.1, + shouldPass: true, }, { name: "Different number of keys", @@ -392,8 +331,8 @@ func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { actual: map[int]float64{ 1: 1.0, }, - delta: 0.1, - f: False, + delta: 0.1, + shouldPass: false, }, { name: "Within delta with zero value", @@ -403,8 +342,8 @@ func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { actual: map[string]float64{ "zero": 0, }, - delta: 0.1, - f: True, + delta: 0.1, + shouldPass: true, }, { name: "With missing key with zero value", @@ -416,25 +355,25 @@ func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { "zero": 0, "bar": 0, }, - f: False, + shouldPass: false, }, { - name: "With nil maps", - expect: map[string]float64(nil), - actual: map[string]float64(nil), - f: True, + name: "With nil maps", + expect: map[string]float64(nil), + actual: map[string]float64(nil), + shouldPass: true, }, { - name: "With nil values (not a map)", - expect: map[string]float64(nil), - actual: []float64(nil), - f: False, + name: "With nil values (not a map)", + expect: map[string]float64(nil), + actual: []float64(nil), + shouldPass: false, }, { - name: "With nil values (not a map)", - expect: []float64(nil), - actual: map[string]float64(nil), - f: False, + name: "With nil values (not a map)", + expect: []float64(nil), + actual: map[string]float64(nil), + shouldPass: false, }, { name: "With expected nil keys", @@ -446,7 +385,7 @@ func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { &keyA: 1.00, (*string)(nil): 2.00, }, - f: True, + shouldPass: true, }, { name: "With expected invalid value", @@ -456,12 +395,14 @@ func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { actual: map[string]any{ keyA: &iface, }, - f: False, + shouldPass: false, }, }) } -// Helper functions and test data for InEpsilon/InEpsilonT +// ======================================= +// Test NumberInEpsilon variants +// ======================================= func epsilonCases() iter.Seq[genericTestCase] { return slices.Values([]genericTestCase{ @@ -615,14 +556,7 @@ func testEpsilon[Number Measurable](expected, actual Number, epsilon float64, sh mock := new(mockT) result := InEpsilon(mock, expected, actual, epsilon) - - if shouldPass { - True(t, result) - False(t, mock.Failed()) - } else { - False(t, result) - True(t, mock.Failed()) - } + shouldPassOrFail(t, mock, result, shouldPass) } } @@ -632,14 +566,7 @@ func testEpsilonT[Number Measurable](expected, actual Number, epsilon float64, s mock := new(mockT) result := InEpsilonT(mock, expected, actual, epsilon) - - if shouldPass { - True(t, result) - False(t, mock.Failed()) - } else { - False(t, result) - True(t, mock.Failed()) - } + shouldPassOrFail(t, mock, result, shouldPass) } } @@ -693,14 +620,7 @@ func testDeltaSlice(expected, actual any, delta float64, shouldPass bool) func(* mock := new(mockT) result := InDeltaSlice(mock, expected, actual, delta) - - if shouldPass { - True(t, result) - False(t, mock.Failed()) - } else { - False(t, result) - True(t, mock.Failed()) - } + shouldPassOrFail(t, mock, result, shouldPass) } } @@ -765,13 +685,40 @@ func testEpsilonSlice(expected, actual any, epsilon float64, shouldPass bool) fu mock := new(mockT) result := InEpsilonSlice(mock, expected, actual, epsilon) - - if shouldPass { - True(t, result) - False(t, mock.Failed()) - } else { - False(t, result) - True(t, mock.Failed()) - } + shouldPassOrFail(t, mock, result, shouldPass) } } + +// ======================================= +// Test NumberErrorMessages +// ======================================= + +func numberFailCases() iter.Seq[failCase] { + return slices.Values([]failCase{ + { + name: "InDeltaT/shows-difference", + assertion: func(t T) bool { return InDeltaT(t, 10, 1, 5) }, + wantContains: []string{"difference was 9"}, + }, + { + name: "InEpsilonT/relative-error", + assertion: func(t T) bool { return InEpsilonT(t, 100.0, 110.0, 0.05) }, + wantContains: []string{"Relative error is too high", "0.1"}, + }, + { + name: "InEpsilonT/absolute-error-for-zero", + assertion: func(t T) bool { return InEpsilonT(t, 0.0, 0.5, 0.1) }, + wantContains: []string{"Expected value is zero, using absolute error comparison", "0.5"}, + }, + { + name: "InDelta/non-numeric-types", + assertion: func(t T) bool { return InDelta(t, "", 0.5, 0.1) }, + wantContains: []string{"Parameters must be numerical"}, + }, + { + name: "InEpsilon/non-numeric-types", + assertion: func(t T) bool { return InEpsilon(t, "", 0.5, 0.1) }, + wantContains: []string{"Parameters must be numerical"}, + }, + }) +} diff --git a/internal/assertions/object_test.go b/internal/assertions/object_test.go index 88217263d..94d442064 100644 --- a/internal/assertions/object_test.go +++ b/internal/assertions/object_test.go @@ -26,30 +26,6 @@ func TestObjectsAreEqual(t *testing.T) { } } -/* redundant with Equal -func TestEqualBytes(t *testing.T) { - t.Parallel() - - i := 0 - for c := range equalBytesCases() { - Equal(t, reflect.DeepEqual(c.a, c.b), ObjectsAreEqual(c.a, c.b), "case %d failed", i) - i++ - } -} - -type equalBytesCase struct { - a, b []byte -} - -func equalBytesCases() iter.Seq[equalBytesCase] { - return slices.Values([]equalBytesCase{ - {make([]byte, 2), make([]byte, 2)}, - {make([]byte, 2), make([]byte, 2, 3)}, - {nil, make([]byte, 0)}, - }) -} -*/ - func TestObjectsAreEqualValues(t *testing.T) { t.Parallel() diff --git a/internal/assertions/order_test.go b/internal/assertions/order_test.go index 11c4b1c9d..85bc80ab7 100644 --- a/internal/assertions/order_test.go +++ b/internal/assertions/order_test.go @@ -7,33 +7,10 @@ import ( "fmt" "iter" "slices" - "strings" "testing" "time" ) -func TestOrderErrorMessages(t *testing.T) { - t.Parallel() - - for tc := range orderErrorMessageCases() { - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - - mock := newOutputMock() - result := tc.fn(mock, tc.collection, tc.msgAndArgs...) - if result { - t.Errorf("expected ordering assertion %q to fail on %v", tc.name, tc.collection) - - return - } - - if !strings.Contains(mock.buf.String(), tc.expectedInMsg) { - t.Errorf("expected error message to contain: %s but got %q", tc.expectedInMsg, mock.buf.String()) - } - }) - } -} - // Test functions for reflection-based and generic assertions // Unified test for all order assertions, with different input types @@ -54,6 +31,16 @@ func TestOrder(t *testing.T) { } } +func TestOrderErrorMessages(t *testing.T) { + t.Parallel() + + runFailCases(t, orderFailCases()) +} + +// ======================================= +// TestOrder +// ======================================= + func testAllOrdersWithTypes(tc orderTestCase) func(*testing.T) { return func(t *testing.T) { t.Run("with IsIncreasing", func(t *testing.T) { @@ -379,48 +366,81 @@ func testGenericAssertion[Collection ~[]E, E Ordered](mock T, assertionKind orde } } -type errorMessageTestCase struct { - name string - fn func(T, any, ...any) bool - collection any - msgAndArgs []any - expectedInMsg string -} - -func orderErrorMessageCases() iter.Seq[errorMessageTestCase] { - const ( - format = "format %s %x" - arg1 = "this" - arg2 = 0xc001 - expectedOutput = "format this c001\n" - ) - - msgAndArgs := []any{format, arg1, arg2} - - return slices.Values([]errorMessageTestCase{ - // Test msgAndArgs formatting - {"IsIncreasing/with-msgAndArgs", IsIncreasing, []int{1, 2, 1}, msgAndArgs, expectedOutput}, - {"IsNonIncreasing/with-msgAndArgs", IsNonIncreasing, []int{1, 2, 3}, msgAndArgs, expectedOutput}, - {"IsDecreasing/with-msgAndArgs", IsDecreasing, []int{1, 2, 1}, msgAndArgs, expectedOutput}, - {"IsNonDecreasing/with-msgAndArgs", IsNonDecreasing, []int{3, 2, 1}, msgAndArgs, expectedOutput}, - - // Test specific error messages - {"IsIncreasing/string", IsIncreasing, []string{"b", "a"}, nil, `"b" is not less than "a"`}, - {"IsIncreasing/int", IsIncreasing, []int{2, 1}, nil, `"2" is not less than "1"`}, - {"IsIncreasing/int8", IsIncreasing, []int8{2, 1}, nil, `"2" is not less than "1"`}, - {"IsIncreasing/float32", IsIncreasing, []float32{2.34, 1.23}, nil, `"2.34" is not less than "1.23"`}, - {"IsIncreasing/invalid-type", IsIncreasing, struct{}{}, nil, `object struct {} is not an ordered collection`}, - - {"IsNonIncreasing/string", IsNonIncreasing, []string{"a", "b"}, nil, `should not be increasing`}, - {"IsNonIncreasing/int", IsNonIncreasing, []int{1, 2}, nil, `should not be increasing`}, - {"IsNonIncreasing/float64", IsNonIncreasing, []float64{1.23, 2.34}, nil, `should not be increasing`}, - - {"IsDecreasing/string", IsDecreasing, []string{"a", "b"}, nil, `"a" is not greater than "b"`}, - {"IsDecreasing/int", IsDecreasing, []int{1, 2}, nil, `"1" is not greater than "2"`}, - {"IsDecreasing/uint64", IsDecreasing, []uint64{1, 2}, nil, `"1" is not greater than "2"`}, - - {"IsNonDecreasing/string", IsNonDecreasing, []string{"b", "a"}, nil, `should not be decreasing`}, - {"IsNonDecreasing/int", IsNonDecreasing, []int{2, 1}, nil, `should not be decreasing`}, - {"IsNonDecreasing/float32", IsNonDecreasing, []float32{2.34, 1.23}, nil, `should not be decreasing`}, +// ======================================= +// TestOrderErrorMessages +// ======================================= + +func orderFailCases() iter.Seq[failCase] { + return slices.Values([]failCase{ + { + name: "IsIncreasing/string", + assertion: func(t T) bool { return IsIncreasing(t, []string{"b", "a"}) }, + wantContains: []string{`"b" is not less than "a"`}, + }, + { + name: "IsIncreasing/int", + assertion: func(t T) bool { return IsIncreasing(t, []int{2, 1}) }, + wantContains: []string{`"2" is not less than "1"`}, + }, + { + name: "IsIncreasing/int8", + assertion: func(t T) bool { return IsIncreasing(t, []int8{2, 1}) }, + wantContains: []string{`"2" is not less than "1"`}, + }, + { + name: "IsIncreasing/float32", + assertion: func(t T) bool { return IsIncreasing(t, []float32{2.34, 1.23}) }, + wantContains: []string{`"2.34" is not less than "1.23"`}, + }, + { + name: "IsIncreasing/invalid-type", + assertion: func(t T) bool { return IsIncreasing(t, struct{}{}) }, + wantContains: []string{`object struct {} is not an ordered collection`}, + }, + { + name: "IsNonIncreasing/string", + assertion: func(t T) bool { return IsNonIncreasing(t, []string{"a", "b"}) }, + wantContains: []string{`should not be increasing`}, + }, + { + name: "IsNonIncreasing/int", + assertion: func(t T) bool { return IsNonIncreasing(t, []int{1, 2}) }, + wantContains: []string{`should not be increasing`}, + }, + { + name: "IsNonIncreasing/float64", + assertion: func(t T) bool { return IsNonIncreasing(t, []float64{1.23, 2.34}) }, + wantContains: []string{`should not be increasing`}, + }, + { + name: "IsDecreasing/string", + assertion: func(t T) bool { return IsDecreasing(t, []string{"a", "b"}) }, + wantContains: []string{`"a" is not greater than "b"`}, + }, + { + name: "IsDecreasing/int", + assertion: func(t T) bool { return IsDecreasing(t, []int{1, 2}) }, + wantContains: []string{`"1" is not greater than "2"`}, + }, + { + name: "IsDecreasing/uint64", + assertion: func(t T) bool { return IsDecreasing(t, []uint64{1, 2}) }, + wantContains: []string{`"1" is not greater than "2"`}, + }, + { + name: "IsNonDecreasing/string", + assertion: func(t T) bool { return IsNonDecreasing(t, []string{"b", "a"}) }, + wantContains: []string{`should not be decreasing`}, + }, + { + name: "IsNonDecreasing/int", + assertion: func(t T) bool { return IsNonDecreasing(t, []int{2, 1}) }, + wantContains: []string{`should not be decreasing`}, + }, + { + name: "IsNonDecreasing/float32", + assertion: func(t T) bool { return IsNonDecreasing(t, []float32{2.34, 1.23}) }, + wantContains: []string{`should not be decreasing`}, + }, }) } diff --git a/internal/assertions/panic_test.go b/internal/assertions/panic_test.go index 07218314f..a6c65992f 100644 --- a/internal/assertions/panic_test.go +++ b/internal/assertions/panic_test.go @@ -6,6 +6,9 @@ package assertions import ( "errors" "io" + "iter" + "slices" + "strings" "testing" ) @@ -41,7 +44,7 @@ func TestPanicDidPanic(t *testing.T) { func TestPanics(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) if !Panics(mock, func() { panic("Panic!") @@ -57,7 +60,7 @@ func TestPanics(t *testing.T) { func TestPanicsWithValue(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) if !PanicsWithValue(mock, "Panic!", func() { panic("Panic!") @@ -89,39 +92,16 @@ func TestPanicsWithValue(t *testing.T) { func TestPanicsWithError(t *testing.T) { t.Parallel() - mock := new(captureT) + mock := new(mockT) succeeded := PanicsWithError(mock, "panic", func() { panic(errors.New("panic")) }) - mock.checkResultAndErrMsg(t, true, succeeded, "") - - succeeded = PanicsWithError(mock, "Panic!", func() {}) - Equal(t, false, succeeded, "PanicsWithError should return false") - Contains(t, mock.msg, "Panic value:\t") - - succeeded = PanicsWithError(mock, "expected panic err msg", func() { - panic(errors.New("actual panic err msg")) - }) - Equal(t, false, succeeded, "PanicsWithError should return false") - Contains(t, mock.msg, `Error message: "actual panic err msg"`) - - succeeded = PanicsWithError(mock, "expected panic err msg", func() { - panic(&PanicsWrapperError{"wrapped", errors.New("actual panic err msg")}) - }) - Equal(t, false, succeeded, "PanicsWithError should return false") - Contains(t, mock.msg, `Error message: "wrapped: actual panic err msg"`) - - succeeded = PanicsWithError(mock, "expected panic msg", func() { - panic("actual panic msg") - }) - Equal(t, false, succeeded, "PanicsWithError should return false") - Contains(t, mock.msg, `Panic value: "actual panic msg"`) - NotContains(t, mock.msg, "Error message:", "PanicsWithError should not report error message if not due an error") + shouldPassOrFail(t, mock, succeeded, true) } func TestPanicNotPanics(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) if !NotPanics(mock, func() { }) { @@ -138,8 +118,72 @@ func TestPanicNotPanics(t *testing.T) { func TestPanicCallerInfoWithAutogeneratedFunctions(t *testing.T) { t.Parallel() - NotPanics(t, func() { + func() { + defer func() { + if r := recover(); r != nil { + t.Errorf("testAutogeneratedFunction should not panic: %v", r) + } + }() testAutogeneratedFunction() + }() +} + +func TestPanicsWithErrorMessages(t *testing.T) { + t.Parallel() + + runFailCases(t, panicsWithErrorFailCases()) + + t.Run("string-panic-has-no-error-message-label", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + PanicsWithError(mock, "expected panic msg", func() { + panic("actual panic msg") + }) + if strings.Contains(mock.errorString(), "Error message:") { + t.Error("PanicsWithError should not report error message if not due an error") + } + }) +} + +// ======================================= +// TestPanicsWithErrorMessages +// ======================================= + +func panicsWithErrorFailCases() iter.Seq[failCase] { + return slices.Values([]failCase{ + { + name: "PanicsWithError/no-panic", + assertion: func(t T) bool { return PanicsWithError(t, "Panic!", func() {}) }, + wantContains: []string{"Panic value:"}, + }, + { + name: "PanicsWithError/wrong-error", + assertion: func(t T) bool { + return PanicsWithError(t, "expected panic err msg", func() { + panic(errors.New("actual panic err msg")) + }) + }, + wantContains: []string{"Error message:", "actual panic err msg"}, + }, + { + name: "PanicsWithError/wrapped-error", + assertion: func(t T) bool { + return PanicsWithError(t, "expected panic err msg", func() { + panic(&PanicsWrapperError{"wrapped", errors.New("actual panic err msg")}) + }) + }, + wantContains: []string{"Error message:", "wrapped: actual panic err msg"}, + }, + { + name: "PanicsWithError/string-panic", + assertion: func(t T) bool { + return PanicsWithError(t, "expected panic msg", func() { + panic("actual panic msg") + }) + }, + wantContains: []string{"Panic value:", "actual panic msg"}, + }, }) } diff --git a/internal/assertions/safety.go b/internal/assertions/safety.go new file mode 100644 index 000000000..926d6ff41 --- /dev/null +++ b/internal/assertions/safety.go @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package assertions + +import "github.com/go-openapi/testify/v2/internal/leak" + +// NoGoRoutineLeak ensures that no goroutine did leak from inside the tested function. +func NoGoRoutineLeak(t T, inside func(), msgAndArgs ...any) bool { + // Domain: safety + _ = inside // TODO + if h, ok := t.(H); ok { + h.Helper() + } + + err := leak.Find() + if err == nil { + return true + } + + return Fail(t, err.Error(), msgAndArgs...) +} diff --git a/internal/assertions/string_test.go b/internal/assertions/string_test.go index eb1f1a90d..9f8089400 100644 --- a/internal/assertions/string_test.go +++ b/internal/assertions/string_test.go @@ -29,6 +29,16 @@ func TestStringRegexp(t *testing.T) { } } +func TestStringErrorMessages(t *testing.T) { + t.Parallel() + + runFailCases(t, stringFailCases()) +} + +// ======================================= +// TestStringRegexp +// ======================================= + // Values to populate the test harness: // // - valid and invalid patterns @@ -217,7 +227,7 @@ func testRegexp(rx any, str any, success bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) res := Regexp(mock, rx, str) if res != success { if success { @@ -233,7 +243,7 @@ func testNotRegexp(rx any, str any, success bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) res := NotRegexp(mock, rx, str) if res != success { if success { @@ -249,7 +259,7 @@ func testRegexpT[Rex RegExp, ADoc Text](rx Rex, str ADoc, success bool) func(*te return func(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) res := RegexpT(mock, rx, str) if res != success { if success { @@ -265,7 +275,7 @@ func testNotRegexpT[Rex RegExp, ADoc Text](rx Rex, str ADoc, success bool) func( return func(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) res := NotRegexpT(mock, rx, str) if res != success { if success { @@ -291,3 +301,32 @@ func testRex(rex string) *regexp.Regexp { rx, _ := compileRegex(rex) return rx } + +// ======================================= +// TestStringErrorMessages +// ======================================= + +func stringFailCases() iter.Seq[failCase] { + return slices.Values([]failCase{ + { + name: "Regexp/no-match", + assertion: func(t T) bool { return Regexp(t, "^start", "no match") }, + wantContains: []string{"Expect", "to match"}, + }, + { + name: "NotRegexp/unexpected-match", + assertion: func(t T) bool { return NotRegexp(t, "^start", "starting") }, + wantContains: []string{"Expect", "to NOT match"}, + }, + { + name: "Regexp/invalid-regexp", + assertion: func(t T) bool { return Regexp(t, "\\C", "whatever") }, + wantContains: []string{"invalid error expression"}, + }, + { + name: "Regexp/invalid-type", + assertion: func(t T) bool { return Regexp(t, struct{ a string }{a: "x"}, "whatever") }, + wantContains: []string{"type for regexp is not supported"}, + }, + }) +} diff --git a/internal/assertions/testing_test.go b/internal/assertions/testing_test.go index a27e686a0..3724c1174 100644 --- a/internal/assertions/testing_test.go +++ b/internal/assertions/testing_test.go @@ -3,22 +3,328 @@ package assertions -import "testing" +import ( + "iter" + "slices" + "strings" + "testing" +) func TestTestingFailNowWithPlainT(t *testing.T) { t.Parallel() mock := &mockT{} - Panics(t, func() { + func() { + defer func() { + if recover() == nil { + t.Error("should panic since mockT is missing FailNow()") + } + }() FailNow(mock, "failed") - }, "should panic since mockT is missing FailNow()") + }() } func TestTestingFailNowWithFullT(t *testing.T) { t.Parallel() mock := &mockFailNowT{} - NotPanics(t, func() { + func() { + defer func() { + if r := recover(); r != nil { + t.Errorf("should call mockT.FailNow() rather than panicking: %v", r) + } + }() FailNow(mock, "failed") - }, "should call mockT.FailNow() rather than panicking") + }() +} + +func TestIndentMessageLines(t *testing.T) { + t.Parallel() + + for tc := range indentMessageLinesCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + result := indentMessageLines(tc.message, tc.longestLabel) + if tc.expected != result { + t.Errorf("expected %q, got %q", tc.expected, result) + } + }) + } +} + +func TestParseLabeledOutput(t *testing.T) { + t.Parallel() + + t.Run("round-trip single label", func(t *testing.T) { + t.Parallel() + + input := []labeledContent{{errString, "test message"}} + output := labeledOutput(input...) + parsed := parseLabeledOutput(output) + + if parsed == nil { + t.Fatal("expected non-nil parsed output") + } + if len(parsed) != 1 { + t.Fatalf("expected 1 label, got %d", len(parsed)) + } + if parsed[0].label != errString { + t.Errorf("expected label %q, got %q", errString, parsed[0].label) + } + if parsed[0].content != "test message\n" { + t.Errorf("expected content %q, got %q", "test message\n", parsed[0].content) + } + }) + + t.Run("round-trip multiple labels", func(t *testing.T) { + t.Parallel() + + input := []labeledContent{ + {"Error Trace", "file.go:42"}, + {errString, "not equal"}, + {"Test", "TestFoo"}, + {"Messages", "extra info"}, + } + output := labeledOutput(input...) + parsed := parseLabeledOutput(output) + + if parsed == nil { + t.Fatal("expected non-nil parsed output") + } + if len(parsed) != 4 { + t.Fatalf("expected 4 labels, got %d", len(parsed)) + } + if parsed[0].label != "Error Trace" { + t.Errorf("expected label %q, got %q", "Error Trace", parsed[0].label) + } + if parsed[1].label != errString { + t.Errorf("expected label %q, got %q", errString, parsed[1].label) + } + if parsed[2].label != "Test" { + t.Errorf("expected label %q, got %q", "Test", parsed[2].label) + } + if parsed[3].label != "Messages" { + t.Errorf("expected label %q, got %q", "Messages", parsed[3].label) + } + }) + + t.Run("blank line skipping", func(t *testing.T) { + t.Parallel() + + // Build output with blank lines injected + output := "\n\tError:\ttest message\n\n" + parsed := parseLabeledOutput(output) + + if parsed == nil { + t.Fatal("expected non-nil parsed output") + } + if len(parsed) != 1 { + t.Fatalf("expected 1 label, got %d", len(parsed)) + } + if parsed[0].label != errString { + t.Errorf("expected label %q, got %q", errString, parsed[0].label) + } + }) + + t.Run("malformed input returns nil", func(t *testing.T) { + t.Parallel() + + parsed := parseLabeledOutput("this is not labeled output") + if parsed != nil { + t.Errorf("expected nil, got %v", parsed) + } + }) +} + +func TestMessageFromMsgAndArgs(t *testing.T) { + t.Parallel() + + for tc := range messageFromMsgAndArgsCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + result := messageFromMsgAndArgs(tc.args...) + if tc.expected != result { + t.Errorf("expected %q, got %q", tc.expected, result) + } + }) + } +} + +// Tests for envelope infrastructure functions. +func TestLabeledOutput(t *testing.T) { + t.Parallel() + + for tc := range labeledOutputCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + result := labeledOutput(tc.input...) + if tc.expected != result { + t.Errorf("expected %q, got %q", tc.expected, result) + } + }) + } +} + +func TestErrorEnvelopeIntegration(t *testing.T) { + t.Parallel() + + mock := new(captureT) + Fail(mock, "test message") + + if !mock.failed { + t.Fatal("Fail should mark test as failed") + } + + parsed := parseLabeledOutput(mock.msg) + if parsed == nil { + t.Fatal("Fail output should be parseable by parseLabeledOutput") + } + + var hasErrorTrace, hasError bool + var errorContent string + for _, lc := range parsed { + switch lc.label { + case "Error Trace": + hasErrorTrace = true + case errString: + hasError = true + errorContent = strings.TrimRight(lc.content, "\n") + } + } + + if !hasErrorTrace { + t.Error("envelope should contain Error Trace label") + } + if !hasError { + t.Error("envelope should contain Error label") + } + if errorContent != "test message" { + t.Errorf("expected error content %q, got %q", "test message", errorContent) + } +} + +// ======================================= +// TestLabeledOutput +// ======================================= + +type labeledOutputCase struct { + name string + input []labeledContent + expected string +} + +func labeledOutputCases() iter.Seq[labeledOutputCase] { + return slices.Values([]labeledOutputCase{ + { + name: "single label", + input: []labeledContent{{errString, "something failed"}}, + expected: "\tError:\tsomething failed\n", + }, + { + name: "multiple labels aligned", + input: []labeledContent{ + {"Error Trace", "file.go:42"}, + {errString, "not equal"}, + {"Test", "TestFoo"}, + }, + expected: "\tError Trace:\tfile.go:42\n" + + "\tError: \tnot equal\n" + + "\tTest: \tTestFoo\n", + }, + { + name: "multi-line content indented", + input: []labeledContent{{errString, "line1\nline2\nline3"}}, + expected: "\tError:\tline1\n" + + "\t \tline2\n" + + "\t \tline3\n", + }, + { + name: "empty content", + input: []labeledContent{{"Label", ""}}, + expected: "\tLabel:\t\n", + }, + }) +} + +// ======================================= +// TestIndentMessageLines +// ======================================= + +type indentMessageLinesCase struct { + name string + message string + longestLabel int + expected string +} + +func indentMessageLinesCases() iter.Seq[indentMessageLinesCase] { + return slices.Values([]indentMessageLinesCase{ + { + name: "single line unchanged", + message: "hello world", + longestLabel: 5, + expected: "hello world", + }, + { + name: "multi-line indented", + message: "line1\nline2\nline3", + longestLabel: 5, + expected: "line1\n\t \tline2\n\t \tline3", + }, + { + name: "empty string", + message: "", + longestLabel: 5, + expected: "", + }, + { + name: "trailing newline", + message: "hello\n", + longestLabel: 3, + expected: "hello", + }, + }) +} + +// ======================================= +// TestMessageFromMsgAndArgs +// ======================================= + +type messageFromMsgAndArgsCase struct { + name string + args []any + expected string +} + +func messageFromMsgAndArgsCases() iter.Seq[messageFromMsgAndArgsCase] { + return slices.Values([]messageFromMsgAndArgsCase{ + { + name: "no args", + args: nil, + expected: "", + }, + { + name: "single string", + args: []any{"hello"}, + expected: "hello", + }, + { + name: "single non-string", + args: []any{42}, + expected: "42", + }, + { + name: "format string with args", + args: []any{"value is %d", 42}, + expected: "value is 42", + }, + { + name: "format string with multiple args", + args: []any{"%s=%d", "x", 5}, + expected: "x=5", + }, + }) } diff --git a/internal/assertions/time_test.go b/internal/assertions/time_test.go index 4531afd2e..e8803cd96 100644 --- a/internal/assertions/time_test.go +++ b/internal/assertions/time_test.go @@ -4,7 +4,8 @@ package assertions import ( - "regexp" + "iter" + "slices" "testing" "time" ) @@ -12,49 +13,89 @@ import ( func TestTimeWithinDuration(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) a := time.Now() b := a.Add(10 * time.Second) - True(t, WithinDuration(mock, a, b, 10*time.Second), "A 10s difference is within a 10s time difference") - True(t, WithinDuration(mock, b, a, 10*time.Second), "A 10s difference is within a 10s time difference") - - False(t, WithinDuration(mock, a, b, 9*time.Second), "A 10s difference is not within a 9s time difference") - False(t, WithinDuration(mock, b, a, 9*time.Second), "A 10s difference is not within a 9s time difference") - - False(t, WithinDuration(mock, a, b, -9*time.Second), "A 10s difference is not within a 9s time difference") - False(t, WithinDuration(mock, b, a, -9*time.Second), "A 10s difference is not within a 9s time difference") - - False(t, WithinDuration(mock, a, b, -11*time.Second), "A 10s difference is not within a 9s time difference") - False(t, WithinDuration(mock, b, a, -11*time.Second), "A 10s difference is not within a 9s time difference") + if !WithinDuration(mock, a, b, 10*time.Second) { + t.Error("A 10s difference is within a 10s time difference") + } + if !WithinDuration(mock, b, a, 10*time.Second) { + t.Error("A 10s difference is within a 10s time difference (reversed)") + } + + if WithinDuration(mock, a, b, 9*time.Second) { + t.Error("A 10s difference is not within a 9s time difference") + } + if WithinDuration(mock, b, a, 9*time.Second) { + t.Error("A 10s difference is not within a 9s time difference (reversed)") + } + + if WithinDuration(mock, a, b, -9*time.Second) { + t.Error("A 10s difference is not within a -9s time difference") + } + if WithinDuration(mock, b, a, -9*time.Second) { + t.Error("A 10s difference is not within a -9s time difference (reversed)") + } + + if WithinDuration(mock, a, b, -11*time.Second) { + t.Error("A 10s difference is not within a -11s time difference") + } + if WithinDuration(mock, b, a, -11*time.Second) { + t.Error("A 10s difference is not within a -11s time difference (reversed)") + } } func TestTimeWithinRange(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) n := time.Now() s := n.Add(-time.Second) e := n.Add(time.Second) - True(t, WithinRange(mock, n, n, n), "Exact same actual, start, and end values return true") - - True(t, WithinRange(mock, n, s, e), "Time in range is within the time range") - True(t, WithinRange(mock, s, s, e), "The start time is within the time range") - True(t, WithinRange(mock, e, s, e), "The end time is within the time range") - - False(t, WithinRange(mock, s.Add(-time.Nanosecond), s, e, "Just before the start time is not within the time range")) - False(t, WithinRange(mock, e.Add(time.Nanosecond), s, e, "Just after the end time is not within the time range")) - - False(t, WithinRange(mock, n, e, s, "Just after the end time is not within the time range")) + if !WithinRange(mock, n, n, n) { + t.Error("Exact same actual, start, and end values should return true") + } + + if !WithinRange(mock, n, s, e) { + t.Error("Time in range should be within the time range") + } + if !WithinRange(mock, s, s, e) { + t.Error("The start time should be within the time range") + } + if !WithinRange(mock, e, s, e) { + t.Error("The end time should be within the time range") + } + + if WithinRange(mock, s.Add(-time.Nanosecond), s, e) { + t.Error("Just before the start time should not be within the time range") + } + if WithinRange(mock, e.Add(time.Nanosecond), s, e) { + t.Error("Just after the end time should not be within the time range") + } + + if WithinRange(mock, n, e, s) { + t.Error("Reversed range (start > end) should return false") + } } -func TestTimeEqualityErrorFormatting(t *testing.T) { +func TestTimeErrorMessages(t *testing.T) { t.Parallel() - mock := new(mockT) - Equal(mock, time.Second*2, time.Millisecond) + runFailCases(t, timeFailCases()) +} - expectedErr := "\\s+Error Trace:\\s+Error:\\s+Not equal:\\s+\n\\s+expected: 2s\n\\s+actual\\s+: 1ms\n" - Regexp(t, regexp.MustCompile(expectedErr), mock.errorString()) +// ======================================= +// TestTimeErrorMessages +// ======================================= + +func timeFailCases() iter.Seq[failCase] { + return slices.Values([]failCase{ + { + name: "Equal/time-formatting", + assertion: func(t T) bool { return Equal(t, time.Second*2, time.Millisecond) }, + wantContains: []string{"Not equal:", "2s", "1ms"}, + }, + }) } diff --git a/internal/assertions/type_test.go b/internal/assertions/type_test.go index 1f247f1e8..74fce7f76 100644 --- a/internal/assertions/type_test.go +++ b/internal/assertions/type_test.go @@ -13,7 +13,7 @@ import ( func TestTypeImplements(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) if !Implements(mock, (*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) { t.Error("Implements method should return true: AssertionTesterConformingObject implements AssertionTesterInterface") @@ -28,7 +28,7 @@ func TestTypeImplements(t *testing.T) { func TestTypeNotImplements(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) if !NotImplements(mock, (*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) { t.Error("NotImplements method should return true: AssertionTesterNonConformingObject does not implement AssertionTesterInterface") @@ -43,7 +43,7 @@ func TestTypeNotImplements(t *testing.T) { func TestTypeIsType(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) if !IsType(mock, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) { t.Error("IsType should return true: AssertionTesterConformingObject is the same type as AssertionTesterConformingObject") @@ -55,7 +55,7 @@ func TestTypeIsType(t *testing.T) { func TestTypeNotIsType(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) if !IsNotType(mock, new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) { t.Error("NotIsType should return true: AssertionTesterConformingObject is not the same type as AssertionTesterNonConformingObject") @@ -73,47 +73,51 @@ func TestTypeIsOfTypeT(t *testing.T) { var myVar myType = 1.2 f := 1.2 - True(t, IsOfTypeT[myType](mock, myVar), "expected myVar to be of type %T", myVar) - False(t, IsNotOfTypeT[myType](mock, myVar), "expected myVar to be of type %T", myVar) - False(t, IsOfTypeT[myType](mock, f), "expected f (%T) not to be of type %T", f, myVar) - True(t, IsNotOfTypeT[myType](mock, f), "expected f (%T) not to be of type %T", f, myVar) -} - -func TestTypeZeroWithSliceTooLongToPrint(t *testing.T) { - t.Parallel() - mock := new(mockT) - - longSlice := make([]int, 1_000_000) - Zero(mock, longSlice) - Contains(t, mock.errorString(), ` - Error Trace: - Error: Should be zero, but was [0 0 0`) - Contains(t, mock.errorString(), `<... truncated>`) + if !IsOfTypeT[myType](mock, myVar) { + t.Errorf("expected myVar to be of type %T", myVar) + } + if IsNotOfTypeT[myType](mock, myVar) { + t.Errorf("expected IsNotOfTypeT to return false for myVar of type %T", myVar) + } + if IsOfTypeT[myType](mock, f) { + t.Errorf("expected f (%T) not to be of type %T", f, myVar) + } + if !IsNotOfTypeT[myType](mock, f) { + t.Errorf("expected IsNotOfTypeT to return true for f (%T) vs %T", f, myVar) + } } func TestTypeZero(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) for test := range typeZeros() { - True(t, Zero(mock, test, "%#v is not the %T zero value", test, test)) + if !Zero(mock, test) { + t.Errorf("expected %#v to be the zero value for %T", test, test) + } } for test := range typeNonZeros() { - False(t, Zero(mock, test, "%#v is not the %T zero value", test, test)) + if Zero(mock, test) { + t.Errorf("expected %#v to NOT be the zero value for %T", test, test) + } } } func TestTypeNotZero(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) for test := range typeZeros() { - False(t, NotZero(mock, test, "%#v is not the %T zero value", test, test)) + if NotZero(mock, test) { + t.Errorf("expected NotZero to return false for zero value %#v (%T)", test, test) + } } for test := range typeNonZeros() { - True(t, NotZero(mock, test, "%#v is not the %T zero value", test, test)) + if !NotZero(mock, test) { + t.Errorf("expected NotZero to return true for non-zero value %#v (%T)", test, test) + } } } @@ -153,48 +157,47 @@ func TestTypeKind(t *testing.T) { func TestTypeDiffEmptyCases(t *testing.T) { t.Parallel() - Equal(t, "", diff(nil, nil)) - Equal(t, "", diff(struct{ foo string }{}, nil)) - Equal(t, "", diff(nil, struct{ foo string }{})) - Equal(t, "", diff(1, 2)) - Equal(t, "", diff(1, 2)) - Equal(t, "", diff([]int{1}, []bool{true})) + cases := []struct { + a, b any + }{ + {nil, nil}, + {struct{ foo string }{}, nil}, + {nil, struct{ foo string }{}}, + {1, 2}, + {1, 2}, + {[]int{1}, []bool{true}}, + } + for _, tc := range cases { + if result := diff(tc.a, tc.b); result != "" { + t.Errorf("expected empty diff for (%v, %v), got %q", tc.a, tc.b, result) + } + } } -// Ensure there are no data races. -func TestTypeDiffRace(t *testing.T) { +func TestTypeErrorMessages(t *testing.T) { t.Parallel() - expected := map[string]string{ - "a": "A", - "b": "B", - "c": "C", - } - - actual := map[string]string{ - "d": "D", - "e": "E", - "f": "F", - } - - // run diffs in parallel simulating tests with t.Parallel() - numRoutines := 10 - rChans := make([]chan string, numRoutines) - for idx := range rChans { - rChans[idx] = make(chan string) - go func(ch chan string) { - defer close(ch) - ch <- diff(expected, actual) - }(rChans[idx]) - } + runFailCases(t, typeFailCases()) +} - for _, ch := range rChans { - for msg := range ch { - NotZero(t, msg) // dummy assert - } - } +// ======================================= +// TestTypeErrorMessages +// ======================================= + +func typeFailCases() iter.Seq[failCase] { + return slices.Values([]failCase{ + { + name: "Zero/large-slice-truncated", + assertion: func(t T) bool { return Zero(t, make([]int, 1_000_000)) }, + wantContains: []string{"Should be zero, but was", "<... truncated>"}, + }, + }) } +// ======================================= +// TestTypeIsZero +// ======================================= + func typeZeros() iter.Seq[any] { return slices.Values([]any{ false, diff --git a/internal/assertions/yaml.go b/internal/assertions/yaml.go index 1299abfe1..2b5c90cbd 100644 --- a/internal/assertions/yaml.go +++ b/internal/assertions/yaml.go @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // we need to duplicate at least some godoc. +//nolint:dupl // YAML is actually very similar to JSON but we can't easily factorize this. package assertions import ( @@ -51,7 +51,7 @@ func YAMLEqBytes(t T, expected, actual []byte, msgAndArgs ...any) bool { var expectedYAMLAsInterface, actualYAMLAsInterface any if err := yaml.Unmarshal(expected, &expectedYAMLAsInterface); err != nil { - return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid yaml.\nYAML parsing error: '%s'", expected, err.Error()), msgAndArgs...) + return Fail(t, fmt.Sprintf("Expected value (%q) is not valid yaml.\nYAML parsing error: %v", expected, err), msgAndArgs...) } // Shortcut if same bytes @@ -60,7 +60,7 @@ func YAMLEqBytes(t T, expected, actual []byte, msgAndArgs ...any) bool { } if err := yaml.Unmarshal(actual, &actualYAMLAsInterface); err != nil { - return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid yaml.\nYAML error: '%s'", actual, err.Error()), msgAndArgs...) + return Fail(t, fmt.Sprintf("Input (%q) needs to be valid yaml.\nYAML error: %v", actual, err), msgAndArgs...) } return Equal(t, expectedYAMLAsInterface, actualYAMLAsInterface, msgAndArgs...) @@ -101,3 +101,75 @@ func YAMLEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any return YAMLEqBytes(t, []byte(expected), []byte(actual), msgAndArgs) } + +// YAMLUnmarshalAsT wraps [Equal] after [yaml.Unmarshal]. +// +// The input YAML may be a string or []byte. +// +// It fails if the unmarshaling returns an error or if the resulting object is not equal to the expected one. +// +// Be careful not to wrap the expected object into an "any" interface if this is not what you expected: +// the unmarshaling would take this type to unmarshal as a map[string]any. +// +// # Usage +// +// expected := struct { +// A int `yaml:"a"` +// }{ +// A: 10, +// } +// +// assertions.YAMLUnmarshalAsT(t,expected, `{"a": 10}`) +// +// # Examples +// +// panic: "key: value", "key: value" +// should panic without the yaml feature enabled. +func YAMLUnmarshalAsT[Object any, ADoc Text](t T, expected Object, jazon ADoc, msgAndArgs ...any) bool { + // Domain: yaml + if h, ok := t.(H); ok { + h.Helper() + } + + var actual Object + if err := yaml.Unmarshal([]byte(jazon), &actual); err != nil { + return Fail(t, fmt.Sprintf("YAML unmarshal failed: %v", err), msgAndArgs...) + } + + return Equal(t, expected, actual, msgAndArgs...) +} + +// YAMLMarshalAsT wraps [YAMLEq] after [yaml.Marshal]. +// +// The input YAML may be a string or []byte. +// +// It fails if the marshaling returns an error or if the expected YAML bytes differ semantically +// from the expected ones. +// +// # Usage +// +// actual := struct { +// A int `yaml:"a"` +// }{ +// A: 10, +// } +// +// assertions.YAMLUnmarshalAsT(t,expected, `{"a": 10}`) +// +// # Examples +// +// panic: "key: value", "key: value" +// should panic without the yaml feature enabled. +func YAMLMarshalAsT[EDoc Text](t T, expected EDoc, object any, msgAndArgs ...any) bool { + // Domain: yaml + if h, ok := t.(H); ok { + h.Helper() + } + + actual, err := yaml.Marshal(object) + if err != nil { + return Fail(t, fmt.Sprintf("YAML marshal failed: %v", err), msgAndArgs...) + } + + return YAMLEqBytes(t, []byte(expected), actual, msgAndArgs...) +} diff --git a/internal/assertions/yaml_test.go b/internal/assertions/yaml_test.go index 8ef460b8a..976d98027 100644 --- a/internal/assertions/yaml_test.go +++ b/internal/assertions/yaml_test.go @@ -11,6 +11,10 @@ func TestYAML(t *testing.T) { t.Run("should panic", testAllYAMLEq()) } +// ======================================= +// TestYAML: all YAML assertions +// ======================================= + func testAllYAMLEq() func(*testing.T) { return func(t *testing.T) { const ( @@ -21,6 +25,11 @@ a: 1 expected = "" success = false ) + a := struct { + A string `json:"a"` + }{ + A: "x", + } t.Run("with YAMLEq", testYAMLEq(expected, actual, success)) t.Run("with YAMLEqBytes", testYAMLEqBytes(expected, actual, success)) @@ -28,6 +37,8 @@ a: 1 t.Run("with YAMLEqT[[]byte,string]", testYAMLEqT[[]byte, string](expected, actual, success)) t.Run("with YAMLEqT[string,[]byte]", testYAMLEqT[string, []byte](expected, actual, success)) t.Run("with YAMLEqT[[]byte,[]byte]", testYAMLEqT[[]byte, []byte](expected, actual, success)) + t.Run("with YAMLMarshalAsT[[]byte,struct{}]", testYAMLMarshalAsT(expected, a, success)) + t.Run("with YAMLUnmarshalAsT[struct{},[]byte]", testYAMLUnmarshalAsT(a, actual, success)) } } @@ -37,9 +48,16 @@ func testYAMLEq(expected, actual string, success bool) func(*testing.T) { t.Parallel() mock := new(mockT) - if !Panics(t, func() { + panicked := func() (didPanic bool) { + defer func() { + if recover() != nil { + didPanic = true + } + }() _ = YAMLEq(mock, expected, actual) - }) { + return false + }() + if !panicked { croakWantPanic(t, "YAMLEq") } } @@ -51,9 +69,16 @@ func testYAMLEqBytes(expected, actual string, success bool) func(*testing.T) { t.Parallel() mock := new(mockT) - if !Panics(t, func() { + panicked := func() (didPanic bool) { + defer func() { + if recover() != nil { + didPanic = true + } + }() _ = YAMLEqBytes(mock, []byte(expected), []byte(actual)) - }) { + return false + }() + if !panicked { croakWantPanic(t, "YAMLEqBytes") } } @@ -66,14 +91,63 @@ func testYAMLEqT[ADoc, EDoc Text](expected, actual string, success bool) func(*t t.Parallel() mock := new(mockT) - if !Panics(t, func() { + panicked := func() (didPanic bool) { + defer func() { + if recover() != nil { + didPanic = true + } + }() _ = YAMLEqT(mock, EDoc(expected), ADoc(actual)) - }) { + return false + }() + if !panicked { croakWantPanic(t, "YAMLEqT") } } } +func testYAMLUnmarshalAsT[ADoc Text, Object any](expected Object, actual ADoc, success bool) func(*testing.T) { + _ = success + return func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + panicked := func() (didPanic bool) { + defer func() { + if recover() != nil { + didPanic = true + } + }() + _ = YAMLUnmarshalAsT(mock, expected, actual) + return false + }() + if !panicked { + croakWantPanic(t, "YAMLUnmarshalAsT") + } + } +} + +func testYAMLMarshalAsT[EDoc Text](expected EDoc, actual any, success bool) func(*testing.T) { + _ = success + return func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + panicked := func() (didPanic bool) { + defer func() { + if recover() != nil { + didPanic = true + } + }() + _ = YAMLMarshalAsT(mock, expected, actual) + return false + }() + if !panicked { + croakWantPanic(t, "YAMLMarshalAsT") + } + } +} + func croakWantPanic(t *testing.T, fn string) { t.Helper() t.Errorf("expected %q to panic with default settings", fn) diff --git a/internal/leak/leaks.go b/internal/leak/leaks.go new file mode 100644 index 000000000..a8434f305 --- /dev/null +++ b/internal/leak/leaks.go @@ -0,0 +1,92 @@ +//nolint:inamedparam // temporary setup +package leak + +import ( + "errors" + "fmt" +) + +// TestingT is the minimal subset of testing.TB that we use. +type TestingT interface { + Error(...any) +} + +// filterStacks will filter any stacks excluded by the given opts. +// filterStacks modifies the passed in stacks slice. +func filterStacks(stacks []Stack, skipID int, opts *opts) []Stack { + filtered := stacks[:0] + for _, stack := range stacks { + // Always skip the running goroutine. + if stack.ID() == skipID { + continue + } + // Run any default or user-specified filters. + if opts.filter(stack) { + continue + } + filtered = append(filtered, stack) + } + return filtered +} + +// Find looks for extra goroutines, and returns a descriptive error if +// any are found. +func Find(options ...Option) error { + cur := Current().ID() + + opts := buildOpts(options...) + if opts.cleanup != nil { + return errors.New("Cleanup can only be passed to VerifyNone or VerifyTestMain") + } + if opts.runOnFailure { + return errors.New("RunOnFailure can only be passed to VerifyTestMain") + } + var stacks []Stack + retry := true + for i := 0; retry; i++ { + stacks = filterStacks(All(), cur, opts) + + if len(stacks) == 0 { + return nil + } + retry = opts.retry(i) + } + + return fmt.Errorf("found unexpected goroutines:\n%s", stacks) +} + +/* +type testHelper interface { + Helper() +} + +// VerifyNone marks the given TestingT as failed if any extra goroutines are +// found by Find. This is a helper method to make it easier to integrate in +// tests by doing: +// +// defer VerifyNone(t) +// +// VerifyNone is currently incompatible with t.Parallel because it cannot +// associate specific goroutines with specific tests. Thus, non-leaking +// goroutines from other tests running in parallel could fail this check. +// If you need to run tests in parallel, use [VerifyTestMain] instead, +// which will verify that no leaking goroutines exist after ALL tests finish. +func VerifyNone(t TestingT, options ...Option) { + opts := buildOpts(options...) + var cleanup func(int) + cleanup, opts.cleanup = opts.cleanup, nil + + if h, ok := t.(testHelper); ok { + // Mark this function as a test helper, if available. + h.Helper() + } + + if err := Find(opts); err != nil { + t.Error(err) + } + + if cleanup != nil { + cleanup(0) + } +} +*/ diff --git a/internal/leak/leaks_test.go b/internal/leak/leaks_test.go new file mode 100644 index 000000000..9b01d6bdc --- /dev/null +++ b/internal/leak/leaks_test.go @@ -0,0 +1,175 @@ +package leak + +/* +// Ensure that testingT is a subset of testing.TB. +var _ = TestingT(testing.TB(nil)) + +// testOptions passes a shorter max sleep time, used so tests don't wait +// ~1 second in cases where we expect Find to error out. +func testOptions() Option { + return maxSleep(time.Millisecond) +} + +func TestFind(t *testing.T) { + t.Run("Should find no leaks by default", func(t *testing.T) { + require.NoError(t, Find()) + }) + + t.Run("Find leaks with leaked goroutine", func(t *testing.T) { + bg := startBlockedG() + err := Find(testOptions()) + require.Error(t, err, "Should find leaks with leaked goroutine") + assert.ErrorContains(t, err, "blockedG") + assert.ErrorContains(t, err, "created by go.uber.org/goleak.startBlockedG") + + // Once we unblock the goroutine, we shouldn't have leaks. + bg.unblock() + require.NoError(t, Find(), "Should find no leaks by default") + }) + + t.Run("Find can't take in Cleanup option", func(t *testing.T) { + err := Find(Cleanup(func(int) { assert.Fail(t, "this should not be called") })) + require.Error(t, err, "Should exit with invalid option") + }) +} + +func TestFindRetry(t *testing.T) { + // for i := 0; i < 10; i++ { + bg := startBlockedG() + require.Error(t, Find(testOptions()), "Should find leaks with leaked goroutine") + + go func() { + time.Sleep(time.Millisecond) + bg.unblock() + }() + require.NoError(t, Find(), "Find should retry while background goroutine ends") +} + +type fakeT struct { + errors []string +} + +func (ft *fakeT) Error(args ...interface{}) { + ft.errors = append(ft.errors, fmt.Sprint(args...)) +} + +func TestVerifyNone(t *testing.T) { + t.Run("VerifyNone finds leaks", func(t *testing.T) { + ft := &fakeT{} + VerifyNone(ft) + require.Empty(t, ft.errors, "Expect no errors from VerifyNone") + + bg := startBlockedG() + VerifyNone(ft, testOptions()) + require.NotEmpty(t, ft.errors, "Expect errors from VerifyNone on leaked goroutine") + bg.unblock() + }) + + t.Run("cleanup registered callback should be called", func(t *testing.T) { + ft := &fakeT{} + cleanupCalled := false + VerifyNone(ft, Cleanup(func(c int) { + assert.Equal(t, 0, c) + cleanupCalled = true + })) + require.True(t, cleanupCalled, "expect cleanup registered callback to be called") + }) +} + +func TestIgnoreCurrent(t *testing.T) { + t.Run("Should ignore current", func(t *testing.T) { + defer VerifyNone(t) + + done := make(chan struct{}) + go func() { + <-done + }() + + // We expect the above goroutine to be ignored. + VerifyNone(t, IgnoreCurrent()) + close(done) + }) + + t.Run("Should detect new leaks", func(t *testing.T) { + defer VerifyNone(t) + + // There are no leaks currently. + VerifyNone(t) + + done1 := make(chan struct{}) + done2 := make(chan struct{}) + + go func() { + <-done1 + }() + + err := Find() + require.Error(t, err, "Expected to find background goroutine as leak") + + opt := IgnoreCurrent() + VerifyNone(t, opt) + + // A second goroutine started after IgnoreCurrent is a leak + go func() { + <-done2 + }() + + err = Find(opt) + require.Error(t, err, "Expect second goroutine to be flagged as a leak") + + close(done1) + close(done2) + }) + + t.Run("Should not ignore false positive", func(t *testing.T) { + defer VerifyNone(t) + + const goroutinesCount = 5 + var wg sync.WaitGroup + done := make(chan struct{}) + + // Spawn few goroutines before checking leaks + for i := 0; i < goroutinesCount; i++ { + wg.Add(1) + go func() { + <-done + wg.Done() + }() + } + + // Store all goroutines + option := IgnoreCurrent() + + // Free goroutines + close(done) + wg.Wait() + + // We expect the below goroutines to be founded. + for i := 0; i < goroutinesCount; i++ { + ch := make(chan struct{}) + + go func() { + <-ch + }() + + require.Error(t, Find(option), "Expect spawned goroutine to be flagged as a leak") + + // Free spawned goroutine + close(ch) + + // Make sure that there are no leaks + VerifyNone(t) + } + }) +} + +func TestVerifyParallel(t *testing.T) { + t.Run("parallel", func(t *testing.T) { + t.Parallel() + }) + + t.Run("serial", func(t *testing.T) { + VerifyNone(t) + }) +} +*/ diff --git a/internal/leak/options.go b/internal/leak/options.go new file mode 100644 index 000000000..57753baa9 --- /dev/null +++ b/internal/leak/options.go @@ -0,0 +1,201 @@ +//nolint:ireturn,inamedparam // temporary setup +package leak + +import ( + "strings" + "time" +) + +// Option lets users specify custom verifications. +type Option interface { + apply(*opts) +} + +// We retry up to 20 times if we can't find the goroutine that +// we are looking for. In between each attempt, we will sleep for +// a short while to let any running goroutines complete. +const _defaultRetries = 20 + +const defaultMaxSleep = 100 * time.Millisecond + +type opts struct { + filters []func(Stack) bool + maxRetries int + maxSleep time.Duration + cleanup func(int) + runOnFailure bool +} + +// implement apply so that opts struct itself can be used as +// an Option. +func (o *opts) apply(opts *opts) { + opts.filters = o.filters + opts.maxRetries = o.maxRetries + opts.maxSleep = o.maxSleep + opts.cleanup = o.cleanup + opts.runOnFailure = o.runOnFailure +} + +// optionFunc lets us easily write options without a custom type. +type optionFunc func(*opts) + +func (f optionFunc) apply(opts *opts) { f(opts) } + +// IgnoreTopFunction ignores any goroutines where the specified function +// is at the top of the The function name should be fully qualified, +// e.g., go.uber.org/goleak.IgnoreTopFunction. +func IgnoreTopFunction(f string) Option { + return addFilter(func(s Stack) bool { + return s.FirstFunction() == f + }) +} + +// IgnoreAnyFunction ignores goroutines where the specified function +// is present anywhere in the +// +// The function name must be fully qualified, e.g., +// +// go.uber.org/goleak.IgnoreAnyFunction +// +// For methods, the fully qualified form looks like: +// +// go.uber.org/goleak.(*MyType).MyMethod +func IgnoreAnyFunction(f string) Option { + return addFilter(func(s Stack) bool { + return s.HasFunction(f) + }) +} + +// IgnoreCreatedBy ignores any goroutines that where spawned from the +// specified function. The function name should be fully qualified, e.g. +// go.uber.org/goleak.IgnoreCreatedBy. +func IgnoreCreatedBy(f string) Option { + return addFilter(func(s Stack) bool { + return s.CreatedBy() == f + }) +} + +// Cleanup sets up a cleanup function that will be executed at the +// end of the leak check. +// When passed to [VerifyTestMain], the exit code passed to cleanupFunc +// will be set to the exit code of TestMain. +// When passed to [VerifyNone], the exit code will be set to 0. +// This cannot be passed to [Find]. +func Cleanup(cleanupFunc func(exitCode int)) Option { + return optionFunc(func(opts *opts) { + opts.cleanup = cleanupFunc + }) +} + +// IgnoreCurrent records all current goroutines when the option is created, and ignores +// them in any future Find/Verify calls. +func IgnoreCurrent() Option { + excludeIDSet := map[int]bool{} + for _, s := range All() { + excludeIDSet[s.ID()] = true + } + return addFilter(func(s Stack) bool { + return excludeIDSet[s.ID()] + }) +} + +// RunOnFailure makes goleak look for leaking goroutines upon test failures. +// By default goleak only looks for leaking goroutines when tests succeed. +func RunOnFailure() Option { + return optionFunc(func(opts *opts) { + opts.runOnFailure = true + }) +} + +/* +func maxSleep(d time.Duration) Option { + return optionFunc(func(opts *opts) { + opts.maxSleep = d + }) +} +*/ + +func addFilter(f func(Stack) bool) Option { + return optionFunc(func(opts *opts) { + opts.filters = append(opts.filters, f) + }) +} + +func buildOpts(options ...Option) *opts { + opts := &opts{ + maxRetries: _defaultRetries, + maxSleep: defaultMaxSleep, + } + opts.filters = append(opts.filters, + isTestStack, + isSyscallStack, + isStdLibStack, + isTraceStack, + ) + for _, option := range options { + option.apply(opts) + } + return opts +} + +func (o *opts) filter(s Stack) bool { + for _, filter := range o.filters { + if filter(s) { + return true + } + } + return false +} + +func (o *opts) retry(i int) bool { + if i >= o.maxRetries { + return false + } + + d := min(time.Duration(int(time.Microsecond)<example.com/path/to/package/file.go:123 +0x123 + // + // We don't care about the position so we can skip this line. + if p.scan.Scan() { + // Be defensive: + // Skip the line only if it starts with a tab. + bs := p.scan.Bytes() + if len(bs) > 0 && bs[0] == '\t' { + fullStack.Write(bs) + fullStack.WriteByte('\n') + } else { + // Put it back and let the next iteration handle it + // if it doesn't start with a tab. + p.scan.Unscan() + } + } + + if creator { + // The "created by" line is the last line of the stack. + // We can stop parsing now. + // + // Note that if tracebackancestors=N is set, + // there may be more a traceback of the creator function + // following the "created by" line, + // but it should not be considered part of this stack. + // e.g., + // + // created by testing.(*T).Run in goroutine 1 + // /usr/lib/go/src/testing/testing.go:1648 +0x3ad + // [originating from goroutine 1]: + // testing.(*T).Run(...) + // /usr/lib/go/src/testing/testing.go:1649 +0x3ad + // + createdBy = funcName + break + } + } + + return Stack{ + id: id, + state: state, + createdBy: createdBy, + firstFunction: firstFunction, + allFunctions: funcs, + fullStack: fullStack.String(), + }, nil +} + +// All returns the stacks for all running goroutines. +func All() []Stack { + return getStacks(true) +} + +// Current returns the stack for the current goroutine. +func Current() Stack { + return getStacks(false)[0] +} + +func getStackBuffer(all bool) []byte { + for i := _defaultBufferSize; ; i *= 2 { + buf := make([]byte, i) + if n := runtime.Stack(buf, all); n < i { + return buf[:n] + } + } +} + +// Parses a single function from the given line. +// The line is in one of these formats: +// +// example.com/path/to/package.funcName(args...) +// example.com/path/to/package.(*typeName).funcName(args...) +// created by example.com/path/to/package.funcName +// created by example.com/path/to/package.funcName in goroutine [...] +// +// Also reports whether the line was a "created by" line. +func parseFuncName(line string) (name string, creator bool, err error) { + if after, ok := strings.CutPrefix(line, "created by "); ok { + // The function name is the part after "created by " + // and before " in goroutine [...]". + idx := strings.Index(after, " in goroutine") + if idx >= 0 { + after = after[:idx] + } + name = after + creator = true + } else if idx := strings.LastIndexByte(line, '('); idx >= 0 { + // The function name is the part before the last '('. + name = line[:idx] + } + + if name == "" { + return "", false, fmt.Errorf("no function found: %q", line) + } + + return name, creator, nil +} + +// parseGoStackHeader parses a stack header that looks like: +// goroutine 643 [runnable]:\n +// And returns the goroutine ID, and the state. +func parseGoStackHeader(line string) (goroutineID int, state string, err error) { + // The scanner will have already trimmed the "\n", + // but we'll guard against it just in case. + // + // Trimming them separately makes them both optional. + const partsPerLine = 3 + line = strings.TrimSuffix(strings.TrimSuffix(line, ":"), "\n") + parts := strings.SplitN(line, " ", partsPerLine) + if len(parts) != partsPerLine { + return 0, "", fmt.Errorf("unexpected format: %q", line) + } + + id, err := strconv.Atoi(parts[1]) + if err != nil { + return 0, "", fmt.Errorf("bad goroutine ID %q in line %q", parts[1], line) + } + + state = strings.TrimSuffix(strings.TrimPrefix(parts[2], "["), "]") + return id, state, nil +} diff --git a/internal/leak/stacks_test.go b/internal/leak/stacks_test.go new file mode 100644 index 000000000..221a9b42b --- /dev/null +++ b/internal/leak/stacks_test.go @@ -0,0 +1,24 @@ +package leak + +import ( + "sync" + "testing" + + "github.com/go-openapi/testify/v2/internal/spew" +) + +func TestStackCurrent(_ *testing.T) { + var wg sync.WaitGroup + + var s Stack + wg.Add(1) + go func() { + defer wg.Done() + s = Current() + }() + + wg.Wait() + + // spew.Config = spew.ConfigState{DisableMethods: true} + spew.Dump(s) +} diff --git a/internal/spew/bypass.go b/internal/spew/bypass.go index e566ab6cb..708d40b3c 100644 --- a/internal/spew/bypass.go +++ b/internal/spew/bypass.go @@ -39,6 +39,7 @@ const ( type flag uintptr +//nolint:gochecknoglobals // reflect internals, set once during init var ( // flagRO indicates whether the value field of a reflect.Value // is read-only. @@ -54,21 +55,33 @@ var ( // it is in the lower 5 bits. const flagKindMask = flag(0x1f) +// Bit positions for the reflect.Value flags field. +// Different Go versions use different layouts. +const ( + flagROBit = 5 // read-only bit position + flagROBit2 = 6 // secondary read-only bit (Go 1.5+) + flagAddrBit14 = 7 // addressable bit position (Go 1.4) + flagAddrBit15 = 8 // addressable bit position (Go 1.5+) +) + // Different versions of Go have used different // bit layouts for the flags type. This table // records the known combinations. +// +//nolint:gochecknoglobals // immutable reflect flag lookup table var okFlags = []struct { ro, addr flag }{{ // From Go 1.4 to 1.5 - ro: 1 << 5, - addr: 1 << 7, + ro: 1 << flagROBit, + addr: 1 << flagAddrBit14, }, { // Up to Go tip. - ro: 1<<5 | 1<<6, - addr: 1 << 8, + ro: 1<= 0 { - w.Write(plusBytes) + _, _ = w.Write(plusBytes) } - w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision))) - w.Write(iBytes) - w.Write(closeParenBytes) + _, _ = w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision))) + _, _ = w.Write(iBytes) + _, _ = w.Write(closeParenBytes) } // printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x' @@ -241,15 +254,15 @@ func printHexPtr(w io.Writer, p uintptr) { // Null pointer. num := uint64(p) if num == 0 { - w.Write(nilAngleBytes) + _, _ = w.Write(nilAngleBytes) return } // Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix - buf := make([]byte, 18) + buf := make([]byte, hexBufSize) // It's simpler to construct the hex string right to left. - base := uint64(16) + base := uint64(hexBase) i := len(buf) - 1 for num >= base { buf[i] = hexDigits[num%base] @@ -266,7 +279,88 @@ func printHexPtr(w io.Writer, p uintptr) { // Strip unused leading bytes. buf = buf[i:] - w.Write(buf) + _, _ = w.Write(buf) +} + +// tryHandleMethods checks whether the value should be handled by its +// Stringer/error methods and invokes them if so. Returns true if the value +// was handled. +func tryHandleMethods(cs *ConfigState, w io.Writer, v reflect.Value, kind reflect.Kind) bool { + if cs.DisableMethods && (!cs.EnableTimeStringer || !isTime(v)) { + return false + } + if kind == reflect.Invalid || kind == reflect.Interface { + return false + } + return handleMethods(cs, w, v) +} + +// ptrResolution holds the result of walking a pointer chain. +type ptrResolution struct { + value reflect.Value + pointerChain []uintptr + indirects int + nilFound bool + cycleFound bool +} + +// resolvePtr walks a pointer chain, dereferencing pointers and unpacking +// interfaces while detecting circular references. It returns the final +// resolved value along with metadata about the chain. +func resolvePtr(v reflect.Value, depth int, pointers map[uintptr]int) ptrResolution { + r := ptrResolution{value: v} + + for r.value.Kind() == reflect.Pointer { + if r.value.IsNil() { + r.nilFound = true + return r + } + + r.indirects++ + addr := r.value.Pointer() + r.pointerChain = append(r.pointerChain, addr) + if pd, ok := pointers[addr]; ok && pd < depth { + r.cycleFound = true + r.indirects-- + return r + } + pointers[addr] = depth + + r.value = r.value.Elem() + if r.value.Kind() == reflect.Interface { + if done := r.unpackInterface(depth, pointers); done { + return r + } + } + } + + return r +} + +// unpackInterface unpacks an interface value found during pointer resolution. +// It returns true if the resolution is complete (nil or cycle found). +func (r *ptrResolution) unpackInterface(depth int, pointers map[uintptr]int) bool { + if r.value.IsNil() { + r.nilFound = true + return true + } + r.value = r.value.Elem() + + if r.value.Kind() != reflect.Pointer { + return false + } + if r.value.IsNil() { + r.nilFound = true + return true + } + addr := r.value.Pointer() + if pd, ok := pointers[addr]; ok && pd <= depth { + r.cycleFound = true + r.indirects-- + return true + } + pointers[addr] = depth + return false } // valuesSorter implements sort.Interface to allow a slice of reflect.Value diff --git a/internal/spew/common_test.go b/internal/spew/common_test.go index 03b3b8f7b..19dbd4c85 100644 --- a/internal/spew/common_test.go +++ b/internal/spew/common_test.go @@ -27,136 +27,6 @@ import ( "github.com/go-openapi/testify/v2/internal/spew" ) -// custom type to test Stinger interface on non-pointer receiver. -type stringer string - -// String implements the Stringer interface for testing invocation of custom -// stringers on types with non-pointer receivers. -func (s stringer) String() string { - return "stringer " + string(s) -} - -// custom type to test Stinger interface on pointer receiver. -type pstringer string - -// String implements the Stringer interface for testing invocation of custom -// stringers on types with only pointer receivers. -func (s *pstringer) String() string { - return "stringer " + string(*s) -} - -// xref1 and xref2 are cross referencing structs for testing circular reference -// detection. -type xref1 struct { - ps2 *xref2 -} -type xref2 struct { - ps1 *xref1 -} - -// indirCir1, indirCir2, and indirCir3 are used to generate an indirect circular -// reference for testing detection. -type indirCir1 struct { - ps2 *indirCir2 -} -type indirCir2 struct { - ps3 *indirCir3 -} -type indirCir3 struct { - ps1 *indirCir1 -} - -// embed is used to test embedded structures. -type embed struct { - a string -} - -// embedwrap is used to test embedded structures. -type embedwrap struct { - *embed - - e *embed -} - -// panicer is used to intentionally cause a panic for testing spew properly -// handles them. -type panicer int - -func (p panicer) String() string { - panic("test panic") -} - -// customError is used to test custom error interface invocation. -type customError int - -func (e customError) Error() string { - return fmt.Sprintf("error: %d", int(e)) -} - -// stringizeWants verts a slice of wanted test output into a format suitable -// for a test error message. -func stringizeWants(wants []string) string { - s := "" - var sSb97 strings.Builder - for i, want := range wants { - if i > 0 { - sSb97.WriteString(fmt.Sprintf("want%d: %s", i+1, want)) - } else { - sSb97.WriteString("want: " + want) - } - } - s += sSb97.String() - return s -} - -// testFailed returns whether or not a test failed by checking if the result -// of the test is in the slice of wanted strings. -func testFailed(result string, wants []string) bool { - return !slices.Contains(wants, result) -} - -type sortableStruct struct { - x int -} - -func (ss sortableStruct) String() string { - return fmt.Sprintf("ss.%d", ss.x) -} - -type unsortableStruct struct { - x int -} - -type sortTestCase struct { - input []reflect.Value - expected []reflect.Value -} - -func helpTestSortValues(t *testing.T, tests []sortTestCase, cs *spew.ConfigState) { - t.Helper() - - getInterfaces := func(values []reflect.Value) []any { - interfaces := []any{} - for _, v := range values { - interfaces = append(interfaces, v.Interface()) - } - return interfaces - } - - for _, test := range tests { - spew.SortValues(test.input, cs) - // reflect.DeepEqual cannot really make sense of reflect.Value, - // probably because of all the pointer tricks. For instance, - // v(2.0) != v(2.0) on a 32-bits system. Turn them into interface{} - // instead. - input := getInterfaces(test.input) - expected := getInterfaces(test.expected) - if !reflect.DeepEqual(input, expected) { - t.Errorf("Sort mismatch:\n %v != %v\n\n%#v != %#v", input, expected, input, expected) - } - } -} - // TestSortValues ensures the sort functionality for relect.Value based sorting // works as intended. func TestSortValues(t *testing.T) { @@ -273,6 +143,7 @@ func TestSortValues(t *testing.T) { }, } cs := spew.ConfigState{DisableMethods: true, SpewKeys: false} + helpTestSortValues(t, tests, &cs) } @@ -382,6 +253,136 @@ func TestSortTimeValuesWithString(t *testing.T) { helpTestSortValues(t, tests, &cs) } +// custom type to test Stinger interface on non-pointer receiver. +type stringer string + +// String implements the Stringer interface for testing invocation of custom +// stringers on types with non-pointer receivers. +func (s stringer) String() string { + return "stringer " + string(s) +} + +// custom type to test Stinger interface on pointer receiver. +type pstringer string + +// String implements the Stringer interface for testing invocation of custom +// stringers on types with only pointer receivers. +func (s *pstringer) String() string { + return "stringer " + string(*s) +} + +// xref1 and xref2 are cross referencing structs for testing circular reference +// detection. +type xref1 struct { + ps2 *xref2 +} +type xref2 struct { + ps1 *xref1 +} + +// indirCir1, indirCir2, and indirCir3 are used to generate an indirect circular +// reference for testing detection. +type indirCir1 struct { + ps2 *indirCir2 +} +type indirCir2 struct { + ps3 *indirCir3 +} +type indirCir3 struct { + ps1 *indirCir1 +} + +// embed is used to test embedded structures. +type embed struct { + a string +} + +// embedwrap is used to test embedded structures. +type embedwrap struct { + *embed + + e *embed +} + +// panicer is used to intentionally cause a panic for testing spew properly +// handles them. +type panicer int + +func (p panicer) String() string { + panic("test panic") +} + +// customError is used to test custom error interface invocation. +type customError int + +func (e customError) Error() string { + return fmt.Sprintf("error: %d", int(e)) +} + +// stringizeWants verts a slice of wanted test output into a format suitable +// for a test error message. +func stringizeWants(wants []string) string { + s := "" + var sSb97 strings.Builder + for i, want := range wants { + if i > 0 { + sSb97.WriteString(fmt.Sprintf("want%d: %s", i+1, want)) + } else { + sSb97.WriteString("want: " + want) + } + } + s += sSb97.String() + return s +} + +// testFailed returns whether or not a test failed by checking if the result +// of the test is in the slice of wanted strings. +func testFailed(result string, wants []string) bool { + return !slices.Contains(wants, result) +} + +type sortableStruct struct { + x int +} + +func (ss sortableStruct) String() string { + return fmt.Sprintf("ss.%d", ss.x) +} + +type unsortableStruct struct { + x int +} + +type sortTestCase struct { + input []reflect.Value + expected []reflect.Value +} + +func helpTestSortValues(t *testing.T, tests []sortTestCase, cs *spew.ConfigState) { + t.Helper() + + getInterfaces := func(values []reflect.Value) []any { + interfaces := make([]any, 0, len(values)) + for _, v := range values { + interfaces = append(interfaces, v.Interface()) + } + return interfaces + } + + for _, test := range tests { + spew.SortValues(test.input, cs) + // reflect.DeepEqual cannot really make sense of reflect.Value, + // probably because of all the pointer tricks. For instance, + // v(2.0) != v(2.0) on a 32-bits system. Turn them into interface{} + // instead. + input := getInterfaces(test.input) + expected := getInterfaces(test.expected) + if !reflect.DeepEqual(input, expected) { + t.Errorf("Sort mismatch:\n %v != %v\n\n%#v != %#v", input, expected, input, expected) + } + } +} + func testTimings() (t0, t1, t2 time.Time) { t0 = time.Now() t1 = t0.Add(time.Hour) diff --git a/internal/spew/config.go b/internal/spew/config.go index aa57909b6..60e4bf83a 100644 --- a/internal/spew/config.go +++ b/internal/spew/config.go @@ -113,6 +113,18 @@ var Config = ConfigState{ //nolint:gochecknoglobals // this is global configurat EnableTimeStringer: true, } +// NewDefaultConfig returns a ConfigState with the following default settings. +// +// Indent: " " +// MaxDepth: 0 +// DisableMethods: false +// DisablePointerMethods: false +// ContinueOnMethod: false +// SortKeys: false +func NewDefaultConfig() *ConfigState { + return &ConfigState{Indent: " "} +} + // Errorf is a wrapper for fmt.Errorf that treats each argument as if it were // passed with a Formatter interface returned by c.NewFormatter. It returns // the formatted string as a value that satisfies error. See NewFormatter @@ -169,7 +181,7 @@ func (c *ConfigState) Fprintln(w io.Writer, a ...any) (n int, err error) { // // fmt.Print(c.NewFormatter(a), c.NewFormatter(b)) func (c *ConfigState) Print(a ...any) (n int, err error) { - return fmt.Print(c.convertArgs(a)...) + return fmt.Print(c.convertArgs(a)...) //nolint:forbidigo // public API wrapping fmt.Print } // Printf is a wrapper for fmt.Printf that treats each argument as if it were @@ -181,7 +193,7 @@ func (c *ConfigState) Print(a ...any) (n int, err error) { // // fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b)) func (c *ConfigState) Printf(format string, a ...any) (n int, err error) { - return fmt.Printf(format, c.convertArgs(a)...) + return fmt.Printf(format, c.convertArgs(a)...) //nolint:forbidigo // public API wrapping fmt.Printf } // Println is a wrapper for fmt.Println that treats each argument as if it were @@ -193,7 +205,7 @@ func (c *ConfigState) Printf(format string, a ...any) (n int, err error) { // // fmt.Println(c.NewFormatter(a), c.NewFormatter(b)) func (c *ConfigState) Println(a ...any) (n int, err error) { - return fmt.Println(c.convertArgs(a)...) + return fmt.Println(c.convertArgs(a)...) //nolint:forbidigo // public API wrapping fmt.Println } // Sprint is a wrapper for fmt.Sprint that treats each argument as if it were @@ -302,15 +314,3 @@ func (c *ConfigState) convertArgs(args []any) (formatters []any) { } return formatters } - -// NewDefaultConfig returns a ConfigState with the following default settings. -// -// Indent: " " -// MaxDepth: 0 -// DisableMethods: false -// DisablePointerMethods: false -// ContinueOnMethod: false -// SortKeys: false -func NewDefaultConfig() *ConfigState { - return &ConfigState{Indent: " "} -} diff --git a/internal/spew/dump.go b/internal/spew/dump.go index bff4a7155..83eaf01f4 100644 --- a/internal/spew/dump.go +++ b/internal/spew/dump.go @@ -28,10 +28,11 @@ import ( "strings" ) +//nolint:gochecknoglobals // immutable reflect type and compiled regexps var ( // uint8Type is a reflect.Type representing a uint8. It is used to // convert cgo types to uint8 slices for hexdumping. - uint8Type = reflect.TypeFor[uint8]() //nolint:gochecknoglobals // ok to store reflect stuff as global private immutable vars + uint8Type = reflect.TypeFor[uint8]() // cCharRE is a regular expression that matches a cgo char. // It is used to detect character arrays to hexdump them. @@ -64,7 +65,7 @@ func (d *dumpState) indent() { d.ignoreNextIndent = false return } - d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth)) + _, _ = d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth)) } // unpackValue returns values inside of non-nil interfaces when possible. @@ -87,90 +88,38 @@ func (d *dumpState) dumpPtr(v reflect.Value) { } } - // Keep list of all dereferenced pointers to show later. - pointerChain := make([]uintptr, 0) - - // Figure out how many levels of indirection there are by dereferencing - // pointers and unpacking interfaces down the chain while detecting circular - // references. - nilFound := false - cycleFound := false - indirects := 0 - ve := v - for ve.Kind() == reflect.Pointer { - if ve.IsNil() { - nilFound = true - break - } - - indirects++ - addr := ve.Pointer() - pointerChain = append(pointerChain, addr) - if pd, ok := d.pointers[addr]; ok && pd < d.depth { - cycleFound = true - indirects-- - break - } - d.pointers[addr] = d.depth - - ve = ve.Elem() - if ve.Kind() == reflect.Interface { - if ve.IsNil() { // interface with nil value - nilFound = true - break - } - ve = ve.Elem() - if ve.Kind() == reflect.Pointer { - if ve.IsNil() { - nilFound = true - break - } - - // case of interface containing a pointer that cycles to the same depth level. - // If we have a cycle at the same level, we should break the loop now. - addr = ve.Pointer() - if pd, ok := d.pointers[addr]; ok && pd <= d.depth { - cycleFound = true - indirects-- - break - } - d.pointers[addr] = d.depth - } - } - } + r := resolvePtr(v, d.depth, d.pointers) // Display type information. - d.w.Write(openParenBytes) - d.w.Write(bytes.Repeat(asteriskBytes, indirects)) - d.w.Write([]byte(ve.Type().String())) - d.w.Write(closeParenBytes) + _, _ = d.w.Write(openParenBytes) + _, _ = d.w.Write(bytes.Repeat(asteriskBytes, r.indirects)) + _, _ = d.w.Write([]byte(r.value.Type().String())) + _, _ = d.w.Write(closeParenBytes) // Display pointer information. - if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 { - d.w.Write(openParenBytes) - for i, addr := range pointerChain { + if !d.cs.DisablePointerAddresses && len(r.pointerChain) > 0 { + _, _ = d.w.Write(openParenBytes) + for i, addr := range r.pointerChain { if i > 0 { - d.w.Write(pointerChainBytes) + _, _ = d.w.Write(pointerChainBytes) } printHexPtr(d.w, addr) } - d.w.Write(closeParenBytes) + _, _ = d.w.Write(closeParenBytes) } // Display dereferenced value. - d.w.Write(openParenBytes) + _, _ = d.w.Write(openParenBytes) switch { - case nilFound: - d.w.Write(nilAngleBytes) - - case cycleFound: - d.w.Write(circularBytes) - + case r.nilFound: + _, _ = d.w.Write(nilAngleBytes) + case r.cycleFound: + _, _ = d.w.Write(circularBytes) default: d.ignoreNextType = true - d.dump(ve) + d.dump(r.value) } - d.w.Write(closeParenBytes) + _, _ = d.w.Write(closeParenBytes) } func (d *dumpState) dumpMap(v reflect.Value) { @@ -231,72 +180,73 @@ func (d *dumpState) dumpMap(v reflect.Value) { _, _ = d.w.Write(closeBraceBytes) } +// resolveHexDump determines whether a slice should be hex-dumped and returns +// the byte buffer if so. Returns (nil, false) if the slice should not be +// hex-dumped. +func (d *dumpState) resolveHexDump(v reflect.Value) ([]uint8, bool) { + numEntries := v.Len() + if numEntries == 0 { + return nil, false + } + + vt := v.Index(0).Type() + vts := vt.String() + + doConvert := false + switch { + // C types that need to be converted. + case cCharRE.MatchString(vts), + cUnsignedCharRE.MatchString(vts), + cUint8tCharRE.MatchString(vts): + doConvert = true + + // Try to use existing uint8 slices and fall back to converting + // and copying if that fails. + case vt.Kind() == reflect.Uint8: + if buf, ok := d.tryUint8Slice(v, numEntries); ok { + return buf, true + } + doConvert = true + } + + if doConvert && vt.ConvertibleTo(uint8Type) { + buf := make([]uint8, numEntries) + for i := range numEntries { + vv := v.Index(i) + buf[i] = uint8(vv.Convert(uint8Type).Uint()) //nolint:gosec // conversion is fine: the original type is uint8 + } + return buf, true + } + + return nil, false +} + +// tryUint8Slice attempts to directly extract a []uint8 from a reflect.Value +// whose elements are uint8. Returns the slice and true if successful. +func (d *dumpState) tryUint8Slice(v reflect.Value, numEntries int) ([]uint8, bool) { + vs := v + if !vs.CanInterface() || !vs.CanAddr() { + vs = unsafeReflectValue(vs) + } + if UnsafeDisabled { + return nil, false + } + vs = vs.Slice(0, numEntries) + iface := vs.Interface() + if slice, ok := iface.([]uint8); ok { + return slice, true + } + return nil, false +} + // dumpSlice handles formatting of arrays and slices. Byte (uint8 under // reflection) arrays and slices are dumped in hexdump -C fashion. func (d *dumpState) dumpSlice(v reflect.Value) { // Determine whether this type should be hex dumped or not. Also, // for types which should be hexdumped, try to use the underlying data // first, then fall back to trying to convert them to a uint8 slice. - var buf []uint8 - doConvert := false - doHexDump := false numEntries := v.Len() - if numEntries > 0 { - vt := v.Index(0).Type() - vts := vt.String() - switch { - // C types that need to be converted. - case cCharRE.MatchString(vts): - fallthrough - case cUnsignedCharRE.MatchString(vts): - fallthrough - case cUint8tCharRE.MatchString(vts): - doConvert = true - - // Try to use existing uint8 slices and fall back to converting - // and copying if that fails. - case vt.Kind() == reflect.Uint8: - // We need an addressable interface to convert the type - // to a byte slice. However, the reflect package won't - // give us an interface on certain things like - // unexported struct fields in order to enforce - // visibility rules. We use unsafe, when available, to - // bypass these restrictions since this package does not - // mutate the values. - vs := v - if !vs.CanInterface() || !vs.CanAddr() { - vs = unsafeReflectValue(vs) - } - if !UnsafeDisabled { - vs = vs.Slice(0, numEntries) - - // Use the existing uint8 slice if it can be - // type asserted. - iface := vs.Interface() - if slice, ok := iface.([]uint8); ok { - buf = slice - doHexDump = true - break - } - } - - // The underlying data needs to be converted if it can't - // be type asserted to a uint8 slice. - doConvert = true - } - - // Copy and convert the underlying type if needed. - if doConvert && vt.ConvertibleTo(uint8Type) { - // Convert and copy each element into a uint8 byte - // slice. - buf = make([]uint8, numEntries) - for i := range numEntries { - vv := v.Index(i) - buf[i] = uint8(vv.Convert(uint8Type).Uint()) //nolint:gosec // conversion is fine: the original type is uint8 - } - doHexDump = true - } - } + buf, doHexDump := d.resolveHexDump(v) // Hexdump the entire slice as needed. if doHexDump { @@ -304,7 +254,7 @@ func (d *dumpState) dumpSlice(v reflect.Value) { str := indent + hex.Dump(buf) str = strings.ReplaceAll(str, "\n", "\n"+indent) str = strings.TrimRight(str, d.cs.Indent) - d.w.Write([]byte(str)) + _, _ = d.w.Write([]byte(str)) return } @@ -312,11 +262,43 @@ func (d *dumpState) dumpSlice(v reflect.Value) { for i := range numEntries { d.dump(d.unpackValue(v.Index(i))) if i < (numEntries - 1) { - d.w.Write(commaNewlineBytes) + _, _ = d.w.Write(commaNewlineBytes) } else { - d.w.Write(newlineBytes) + _, _ = d.w.Write(newlineBytes) + } + } +} + +// dumpLenCap displays length and capacity if the built-in len and cap +// functions work with the value's kind and the values are non-zero. +func (d *dumpState) dumpLenCap(v reflect.Value) { + valueLen, valueCap := 0, 0 + switch v.Kind() { + case reflect.Array, reflect.Slice, reflect.Chan: + valueLen, valueCap = v.Len(), v.Cap() + case reflect.Map, reflect.String: + valueLen = v.Len() + default: + } + + if valueLen == 0 && (d.cs.DisableCapacities || valueCap == 0) { + return + } + + _, _ = d.w.Write(openParenBytes) + if valueLen != 0 { + _, _ = d.w.Write(lenEqualsBytes) + printInt(d.w, int64(valueLen), decimalBase) + } + if !d.cs.DisableCapacities && valueCap != 0 { + if valueLen != 0 { + _, _ = d.w.Write(spaceBytes) } + _, _ = d.w.Write(capEqualsBytes) + printInt(d.w, int64(valueCap), decimalBase) } + _, _ = d.w.Write(closeParenBytes) + _, _ = d.w.Write(spaceBytes) } // dump is the main workhorse for dumping a value. It uses the passed reflect @@ -327,7 +309,7 @@ func (d *dumpState) dump(v reflect.Value) { // Handle invalid reflect values immediately. kind := v.Kind() if kind == reflect.Invalid { - d.w.Write(invalidAngleBytes) + _, _ = d.w.Write(invalidAngleBytes) return } @@ -341,51 +323,28 @@ func (d *dumpState) dump(v reflect.Value) { // Print type information unless already handled elsewhere. if !d.ignoreNextType { d.indent() - d.w.Write(openParenBytes) - d.w.Write([]byte(v.Type().String())) - d.w.Write(closeParenBytes) - d.w.Write(spaceBytes) + _, _ = d.w.Write(openParenBytes) + _, _ = d.w.Write([]byte(v.Type().String())) + _, _ = d.w.Write(closeParenBytes) + _, _ = d.w.Write(spaceBytes) } d.ignoreNextType = false // Display length and capacity if the built-in len and cap functions // work with the value's kind and the len/cap itself is non-zero. - valueLen, valueCap := 0, 0 - switch v.Kind() { - case reflect.Array, reflect.Slice, reflect.Chan: - valueLen, valueCap = v.Len(), v.Cap() - case reflect.Map, reflect.String: - valueLen = v.Len() - default: - } - - if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 { - d.w.Write(openParenBytes) - if valueLen != 0 { - d.w.Write(lenEqualsBytes) - printInt(d.w, int64(valueLen), 10) - } - if !d.cs.DisableCapacities && valueCap != 0 { - if valueLen != 0 { - d.w.Write(spaceBytes) - } - d.w.Write(capEqualsBytes) - printInt(d.w, int64(valueCap), 10) - } - d.w.Write(closeParenBytes) - d.w.Write(spaceBytes) - } + d.dumpLenCap(v) // Call Stringer/error interfaces if they exist and the handle methods flag // is enabled - if !d.cs.DisableMethods || (d.cs.EnableTimeStringer && isTime(v)) { - if (kind != reflect.Invalid) && (kind != reflect.Interface) { - if handled := handleMethods(d.cs, d.w, v); handled { - return - } - } + if tryHandleMethods(d.cs, d.w, v, kind) { + return } + d.dumpValue(v, kind) +} + +// dumpValue handles the type-specific formatting for the dump method. +func (d *dumpState) dumpValue(v reflect.Value, kind reflect.Kind) { switch kind { case reflect.Invalid: // Do nothing. We should never get here since invalid has already @@ -395,51 +354,41 @@ func (d *dumpState) dump(v reflect.Value) { printBool(d.w, v.Bool()) case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: - printInt(d.w, v.Int(), 10) + printInt(d.w, v.Int(), decimalBase) case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - printUint(d.w, v.Uint(), 10) + printUint(d.w, v.Uint(), decimalBase) case reflect.Float32: - printFloat(d.w, v.Float(), 32) + printFloat(d.w, v.Float(), float32Precision) case reflect.Float64: - printFloat(d.w, v.Float(), 64) + printFloat(d.w, v.Float(), float64Precision) case reflect.Complex64: - printComplex(d.w, v.Complex(), 32) + printComplex(d.w, v.Complex(), complex64Precision) case reflect.Complex128: - printComplex(d.w, v.Complex(), 64) + printComplex(d.w, v.Complex(), complex128Precision) case reflect.Slice: if v.IsNil() { - d.w.Write(nilAngleBytes) + _, _ = d.w.Write(nilAngleBytes) break } - fallthrough + d.dumpArray(v) case reflect.Array: - d.w.Write(openBraceNewlineBytes) - d.depth++ - if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { - d.indent() - d.w.Write(maxNewlineBytes) - } else { - d.dumpSlice(v) - } - d.depth-- - d.indent() - d.w.Write(closeBraceBytes) + d.dumpArray(v) case reflect.String: - d.w.Write([]byte(strconv.Quote(v.String()))) + _, _ = d.w.Write([]byte(strconv.Quote(v.String()))) case reflect.Interface: // The only time we should get here is for nil interfaces due to // unpackValue calls. if v.IsNil() { - d.w.Write(nilAngleBytes) + _, _ = d.w.Write(nilAngleBytes) } case reflect.Pointer: @@ -450,31 +399,7 @@ func (d *dumpState) dump(v reflect.Value) { d.dumpMap(v) case reflect.Struct: - d.w.Write(openBraceNewlineBytes) - d.depth++ - if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { - d.indent() - d.w.Write(maxNewlineBytes) - } else { - vt := v.Type() - numFields := v.NumField() - for i := range numFields { - d.indent() - vtf := vt.Field(i) - d.w.Write([]byte(vtf.Name)) - d.w.Write(colonSpaceBytes) - d.ignoreNextIndent = true - d.dump(d.unpackValue(v.Field(i))) - if i < (numFields - 1) { - d.w.Write(commaNewlineBytes) - } else { - d.w.Write(newlineBytes) - } - } - } - d.depth-- - d.indent() - d.w.Write(closeBraceBytes) + d.dumpStruct(v) case reflect.Uintptr: printHexPtr(d.w, uintptr(v.Uint())) @@ -487,11 +412,55 @@ func (d *dumpState) dump(v reflect.Value) { // types are added. default: if v.CanInterface() { - fmt.Fprintf(d.w, "%v", v.Interface()) + _, _ = fmt.Fprintf(d.w, "%v", v.Interface()) } else { - fmt.Fprintf(d.w, "%v", v.String()) + _, _ = fmt.Fprintf(d.w, "%v", v.String()) + } + } +} + +// dumpArray handles formatting of array and non-nil slice values. +func (d *dumpState) dumpArray(v reflect.Value) { + _, _ = d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + _, _ = d.w.Write(maxNewlineBytes) + } else { + d.dumpSlice(v) + } + d.depth-- + d.indent() + _, _ = d.w.Write(closeBraceBytes) +} + +// dumpStruct handles formatting of struct values. +func (d *dumpState) dumpStruct(v reflect.Value) { + _, _ = d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + _, _ = d.w.Write(maxNewlineBytes) + } else { + vt := v.Type() + numFields := v.NumField() + for i := range numFields { + d.indent() + vtf := vt.Field(i) + _, _ = d.w.Write([]byte(vtf.Name)) + _, _ = d.w.Write(colonSpaceBytes) + d.ignoreNextIndent = true + d.dump(d.unpackValue(v.Field(i))) + if i < (numFields - 1) { + _, _ = d.w.Write(commaNewlineBytes) + } else { + _, _ = d.w.Write(newlineBytes) + } } } + d.depth-- + d.indent() + _, _ = d.w.Write(closeBraceBytes) } // fdump is a helper function to consolidate the logic from the various public @@ -499,17 +468,17 @@ func (d *dumpState) dump(v reflect.Value) { func fdump(cs *ConfigState, w io.Writer, a ...any) { for _, arg := range a { if arg == nil { - w.Write(interfaceBytes) - w.Write(spaceBytes) - w.Write(nilAngleBytes) - w.Write(newlineBytes) + _, _ = w.Write(interfaceBytes) + _, _ = w.Write(spaceBytes) + _, _ = w.Write(nilAngleBytes) + _, _ = w.Write(newlineBytes) continue } d := dumpState{w: w, cs: cs} d.pointers = make(map[uintptr]int) d.dump(reflect.ValueOf(arg)) - d.w.Write(newlineBytes) + _, _ = d.w.Write(newlineBytes) } } diff --git a/internal/spew/dump_test.go b/internal/spew/dump_test.go index 53c9e49b3..c3514ca67 100644 --- a/internal/spew/dump_test.go +++ b/internal/spew/dump_test.go @@ -64,6 +64,8 @@ package spew_test import ( "bytes" "fmt" + "iter" + "slices" "strconv" "testing" "time" @@ -72,35 +74,163 @@ import ( "github.com/go-openapi/testify/v2/internal/spew" ) +// Type name constants used in test expectations. +const ( + typeInt8 = "int8" + typeInt16 = "int16" + typeInt32 = "int32" + typeInt64 = "int64" + typeInt = "int" + typeUint8 = "uint8" + typeUint16 = "uint16" + typeUint32 = "uint32" + typeUint64 = "uint64" + typeUint = "uint" + typeBool = "bool" + typeFloat32 = "float32" + typeFloat64 = "float64" + typeComplex64 = "complex64" + typeComplex128 = "complex128" + typeString = "string" + typeInterface = "interface {}" + typeUintptr = "uintptr" + typeUnsafePointer = "unsafe.Pointer" + typeChanInt = "chan int" + typePstringer = "spew_test.pstringer" + typeS1 = "spew_test.s1" + typeS2 = "spew_test.s2" + typeNil = "" + valOne = "one" + valTest = "test" +) + +// TestDump executes all of the tests described by dumpTestCases. +func TestDump(t *testing.T) { + t.Parallel() + + i := 0 + for tc := range dumpTestCases() { + buf := new(bytes.Buffer) + spew.Fdump(buf, tc.in) + s := buf.String() + if testFailed(s, tc.wants) { + t.Errorf("Dump #%d\n got: %s %s", i, s, stringizeWants(tc.wants)) + } + i++ + } +} + +func TestDumpSortedKeys(t *testing.T) { + t.Parallel() + + cfg := spew.ConfigState{SortKeys: true} + s := cfg.Sdump(map[int]string{1: "1", 3: "3", 2: "2"}) + expected := "(map[int]string) (len=3) {\n(int) 1: (string) (len=1) " + + "\"1\",\n(int) 2: (string) (len=1) \"2\",\n(int) 3: (string) " + + "(len=1) \"3\"\n" + + "}\n" + if s != expected { + t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) + } + + s = cfg.Sdump(map[stringer]int{"1": 1, "3": 3, "2": 2}) + expected = "(map[spew_test.stringer]int) (len=3) {\n" + + "(spew_test.stringer) (len=1) stringer 1: (int) 1,\n" + + "(spew_test.stringer) (len=1) stringer 2: (int) 2,\n" + + "(spew_test.stringer) (len=1) stringer 3: (int) 3\n" + + "}\n" + if s != expected { + t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) + } + + s = cfg.Sdump(map[pstringer]int{pstringer("1"): 1, pstringer("3"): 3, pstringer("2"): 2}) + expected = "(map[spew_test.pstringer]int) (len=3) {\n" + + "(spew_test.pstringer) (len=1) stringer 1: (int) 1,\n" + + "(spew_test.pstringer) (len=1) stringer 2: (int) 2,\n" + + "(spew_test.pstringer) (len=1) stringer 3: (int) 3\n" + + "}\n" + if spew.UnsafeDisabled { + expected = "(map[spew_test.pstringer]int) (len=3) {\n" + + "(spew_test.pstringer) (len=1) \"1\": (int) 1,\n" + + "(spew_test.pstringer) (len=1) \"2\": (int) 2,\n" + + "(spew_test.pstringer) (len=1) \"3\": (int) 3\n" + + "}\n" + } + if s != expected { + t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) + } + + s = cfg.Sdump(map[customError]int{customError(1): 1, customError(3): 3, customError(2): 2}) + expected = "(map[spew_test.customError]int) (len=3) {\n" + + "(spew_test.customError) error: 1: (int) 1,\n" + + "(spew_test.customError) error: 2: (int) 2,\n" + + "(spew_test.customError) error: 3: (int) 3\n" + + "}\n" + if s != expected { + t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) + } +} + +// ======================= +// TestDump test cases +// ======================= + // dumpTest is used to describe a test to be performed against the Dump method. type dumpTest struct { in any wants []string } -// dumpTests houses all of the tests to be performed against the Dump method. -var dumpTests = make([]dumpTest, 0) +// dumpTestCases returns an iterator over all dump test cases. +func dumpTestCases() iter.Seq[dumpTest] { + return slices.Values(slices.Concat( + intDumpTests(), + uintDumpTests(), + boolDumpTests(), + floatDumpTests(), + complexDumpTests(), + arrayDumpTests(), + sliceDumpTests(), + stringDumpTests(), + interfaceDumpTests(), + mapDumpTests(), + structDumpTests(), + uintptrDumpTests(), + unsafePointerDumpTests(), + chanDumpTests(), + funcDumpTests(), + circularDumpTests(), + panicDumpTests(), + errorDumpTests(), + cgoDumpTests(), + timeDumpTests(), + )) +} -// addDumpTest is a helper method to append the passed input and desired result -// to dumpTests. -func addDumpTest(in any, wants ...string) { - test := dumpTest{in, wants} - dumpTests = append(dumpTests, test) +func ptr[T any](value T) *T { + v := value + return &v } -func addIntDumpTests() { +//nolint:dupl // int/uint test data follows the same pattern by design +func intDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Max int8. v := int8(127) nv := (*int8)(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "int8" + vt := typeInt8 vs := "127" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") + add(v, "("+vt+") "+vs+"\n") + add(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + add(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + add(nv, "(*"+vt+")()\n") // Max int16. v2 := int16(32767) @@ -108,12 +238,12 @@ func addIntDumpTests() { pv2 := &v2 v2Addr := fmt.Sprintf("%p", pv2) pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "int16" + v2t := typeInt16 v2s := "32767" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv2, "(*"+v2t+")()\n") + add(v2, "("+v2t+") "+v2s+"\n") + add(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + add(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + add(nv2, "(*"+v2t+")()\n") // Max int32. v3 := int32(2147483647) @@ -121,12 +251,12 @@ func addIntDumpTests() { pv3 := &v3 v3Addr := fmt.Sprintf("%p", pv3) pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "int32" + v3t := typeInt32 v3s := "2147483647" - addDumpTest(v3, "("+v3t+") "+v3s+"\n") - addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") - addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") - addDumpTest(nv3, "(*"+v3t+")()\n") + add(v3, "("+v3t+") "+v3s+"\n") + add(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") + add(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") + add(nv3, "(*"+v3t+")()\n") // Max int64. v4 := int64(9223372036854775807) @@ -134,12 +264,12 @@ func addIntDumpTests() { pv4 := &v4 v4Addr := fmt.Sprintf("%p", pv4) pv4Addr := fmt.Sprintf("%p", &pv4) - v4t := "int64" + v4t := typeInt64 v4s := "9223372036854775807" - addDumpTest(v4, "("+v4t+") "+v4s+"\n") - addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") - addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") - addDumpTest(nv4, "(*"+v4t+")()\n") + add(v4, "("+v4t+") "+v4s+"\n") + add(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") + add(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") + add(nv4, "(*"+v4t+")()\n") // Max int. v5 := int(2147483647) @@ -147,27 +277,35 @@ func addIntDumpTests() { pv5 := &v5 v5Addr := fmt.Sprintf("%p", pv5) pv5Addr := fmt.Sprintf("%p", &pv5) - v5t := "int" + v5t := typeInt v5s := "2147483647" - addDumpTest(v5, "("+v5t+") "+v5s+"\n") - addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n") - addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n") - addDumpTest(nv5, "(*"+v5t+")()\n") + add(v5, "("+v5t+") "+v5s+"\n") + add(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n") + add(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n") + add(nv5, "(*"+v5t+")()\n") + + return tests } -func addUintDumpTests() { +//nolint:dupl // int/uint test data follows the same pattern by design +func uintDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Max uint8. v := uint8(255) nv := (*uint8)(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "uint8" + vt := typeUint8 vs := "255" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") + add(v, "("+vt+") "+vs+"\n") + add(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + add(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + add(nv, "(*"+vt+")()\n") // Max uint16. v2 := uint16(65535) @@ -175,12 +313,12 @@ func addUintDumpTests() { pv2 := &v2 v2Addr := fmt.Sprintf("%p", pv2) pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "uint16" + v2t := typeUint16 v2s := "65535" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv2, "(*"+v2t+")()\n") + add(v2, "("+v2t+") "+v2s+"\n") + add(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + add(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + add(nv2, "(*"+v2t+")()\n") // Max uint32. v3 := uint32(4294967295) @@ -188,12 +326,12 @@ func addUintDumpTests() { pv3 := &v3 v3Addr := fmt.Sprintf("%p", pv3) pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "uint32" + v3t := typeUint32 v3s := "4294967295" - addDumpTest(v3, "("+v3t+") "+v3s+"\n") - addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") - addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") - addDumpTest(nv3, "(*"+v3t+")()\n") + add(v3, "("+v3t+") "+v3s+"\n") + add(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") + add(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") + add(nv3, "(*"+v3t+")()\n") // Max uint64. v4 := uint64(18446744073709551615) @@ -201,12 +339,12 @@ func addUintDumpTests() { pv4 := &v4 v4Addr := fmt.Sprintf("%p", pv4) pv4Addr := fmt.Sprintf("%p", &pv4) - v4t := "uint64" + v4t := typeUint64 v4s := "18446744073709551615" - addDumpTest(v4, "("+v4t+") "+v4s+"\n") - addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") - addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") - addDumpTest(nv4, "(*"+v4t+")()\n") + add(v4, "("+v4t+") "+v4s+"\n") + add(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") + add(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") + add(nv4, "(*"+v4t+")()\n") // Max uint. v5 := uint(4294967295) @@ -214,53 +352,67 @@ func addUintDumpTests() { pv5 := &v5 v5Addr := fmt.Sprintf("%p", pv5) pv5Addr := fmt.Sprintf("%p", &pv5) - v5t := "uint" + v5t := typeUint v5s := "4294967295" - addDumpTest(v5, "("+v5t+") "+v5s+"\n") - addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n") - addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n") - addDumpTest(nv5, "(*"+v5t+")()\n") + add(v5, "("+v5t+") "+v5s+"\n") + add(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n") + add(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n") + add(nv5, "(*"+v5t+")()\n") + + return tests } -func addBoolDumpTests() { +func boolDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Boolean true. v := bool(true) nv := (*bool)(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "bool" + vt := typeBool vs := "true" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") + add(v, "("+vt+") "+vs+"\n") + add(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + add(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + add(nv, "(*"+vt+")()\n") // Boolean false. v2 := bool(false) pv2 := &v2 v2Addr := fmt.Sprintf("%p", pv2) pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "bool" + v2t := typeBool v2s := "false" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + add(v2, "("+v2t+") "+v2s+"\n") + add(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + add(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + + return tests } -func addFloatDumpTests() { +func floatDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Standard float32. v := float32(3.1415) nv := (*float32)(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "float32" + vt := typeFloat32 vs := "3.1415" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") + add(v, "("+vt+") "+vs+"\n") + add(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + add(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + add(nv, "(*"+vt+")()\n") // Standard float64. v2 := float64(3.1415926) @@ -268,27 +420,34 @@ func addFloatDumpTests() { pv2 := &v2 v2Addr := fmt.Sprintf("%p", pv2) pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "float64" + v2t := typeFloat64 v2s := "3.1415926" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv2, "(*"+v2t+")()\n") + add(v2, "("+v2t+") "+v2s+"\n") + add(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + add(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + add(nv2, "(*"+v2t+")()\n") + + return tests } -func addComplexDumpTests() { +func complexDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Standard complex64. v := complex(float32(6), -2) nv := (*complex64)(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "complex64" + vt := typeComplex64 vs := "(6-2i)" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") + add(v, "("+vt+") "+vs+"\n") + add(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + add(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + add(nv, "(*"+vt+")()\n") // Standard complex128. v2 := complex(float64(-6), 2) @@ -296,15 +455,22 @@ func addComplexDumpTests() { pv2 := &v2 v2Addr := fmt.Sprintf("%p", pv2) pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "complex128" + v2t := typeComplex128 v2s := "(-6+2i)" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv2, "(*"+v2t+")()\n") + add(v2, "("+v2t+") "+v2s+"\n") + add(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + add(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + add(nv2, "(*"+v2t+")()\n") + + return tests } -func addArrayDumpTests() { +func arrayDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Array containing standard ints. v := [3]int{1, 2, 3} vLen := strconv.Itoa(len(v)) @@ -313,13 +479,13 @@ func addArrayDumpTests() { pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "int" + vt := typeInt vs := "(len=" + vLen + " cap=" + vCap + ") {\n (" + vt + ") 1,\n (" + vt + ") 2,\n (" + vt + ") 3\n}" - addDumpTest(v, "([3]"+vt+") "+vs+"\n") - addDumpTest(pv, "(*[3]"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**[3]"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*[3]"+vt+")()\n") + add(v, "([3]"+vt+") "+vs+"\n") + add(pv, "(*[3]"+vt+")("+vAddr+")("+vs+")\n") + add(&pv, "(**[3]"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + add(nv, "(*[3]"+vt+")()\n") // Array containing type with custom formatter on pointer receiver only. v2i0 := pstringer("1") @@ -335,7 +501,7 @@ func addArrayDumpTests() { pv2 := &v2 v2Addr := fmt.Sprintf("%p", pv2) pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "spew_test.pstringer" + v2t := typePstringer v2sp := "(len=" + v2Len + " cap=" + v2Cap + ") {\n (" + v2t + ") (len=" + v2i0Len + ") stringer 1,\n (" + v2t + ") (len=" + v2i1Len + ") stringer 2,\n (" + v2t + @@ -347,13 +513,13 @@ func addArrayDumpTests() { v2i1Len + ") \"2\",\n (" + v2t + ") (len=" + v2i2Len + ") " + "\"3\"\n}" } - addDumpTest(v2, "([3]"+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*[3]"+v2t+")("+v2Addr+")("+v2sp+")\n") - addDumpTest(&pv2, "(**[3]"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2sp+")\n") - addDumpTest(nv2, "(*[3]"+v2t+")()\n") + add(v2, "([3]"+v2t+") "+v2s+"\n") + add(pv2, "(*[3]"+v2t+")("+v2Addr+")("+v2sp+")\n") + add(&pv2, "(**[3]"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2sp+")\n") + add(nv2, "(*[3]"+v2t+")()\n") // Array containing interfaces. - v3i0 := "one" + v3i0 := valOne v3 := [3]any{v3i0, int(2), uint(3)} v3i0Len := strconv.Itoa(len(v3i0)) v3Len := strconv.Itoa(len(v3)) @@ -362,17 +528,17 @@ func addArrayDumpTests() { pv3 := &v3 v3Addr := fmt.Sprintf("%p", pv3) pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "[3]interface {}" - v3t2 := "string" - v3t3 := "int" - v3t4 := "uint" + v3t := "[3]" + typeInterface + v3t2 := typeString + v3t3 := typeInt + v3t4 := typeUint v3s := "(len=" + v3Len + " cap=" + v3Cap + ") {\n (" + v3t2 + ") " + "(len=" + v3i0Len + ") \"one\",\n (" + v3t3 + ") 2,\n (" + v3t4 + ") 3\n}" - addDumpTest(v3, "("+v3t+") "+v3s+"\n") - addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") - addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") - addDumpTest(nv3, "(*"+v3t+")()\n") + add(v3, "("+v3t+") "+v3s+"\n") + add(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") + add(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") + add(nv3, "(*"+v3t+")()\n") // Array containing bytes. v4 := [34]byte{ @@ -388,7 +554,7 @@ func addArrayDumpTests() { pv4 := &v4 v4Addr := fmt.Sprintf("%p", pv4) pv4Addr := fmt.Sprintf("%p", &pv4) - v4t := "[34]uint8" + v4t := "[34]" + typeUint8 v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " + "{\n 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20" + " |............... |\n" + @@ -396,13 +562,20 @@ func addArrayDumpTests() { " |!\"#$%&'()*+,-./0|\n" + " 00000020 31 32 " + " |12|\n}" - addDumpTest(v4, "("+v4t+") "+v4s+"\n") - addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") - addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") - addDumpTest(nv4, "(*"+v4t+")()\n") + add(v4, "("+v4t+") "+v4s+"\n") + add(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") + add(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") + add(nv4, "(*"+v4t+")()\n") + + return tests } -func addSliceDumpTests() { +func sliceDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Slice containing standard float32 values. v := []float32{3.14, 6.28, 12.56} vLen := strconv.Itoa(len(v)) @@ -411,13 +584,13 @@ func addSliceDumpTests() { pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "float32" + vt := typeFloat32 vs := "(len=" + vLen + " cap=" + vCap + ") {\n (" + vt + ") 3.14,\n (" + vt + ") 6.28,\n (" + vt + ") 12.56\n}" - addDumpTest(v, "([]"+vt+") "+vs+"\n") - addDumpTest(pv, "(*[]"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**[]"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*[]"+vt+")()\n") + add(v, "([]"+vt+") "+vs+"\n") + add(pv, "(*[]"+vt+")("+vAddr+")("+vs+")\n") + add(&pv, "(**[]"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + add(nv, "(*[]"+vt+")()\n") // Slice containing type with custom formatter on pointer receiver only. v2i0 := pstringer("1") @@ -433,18 +606,18 @@ func addSliceDumpTests() { pv2 := &v2 v2Addr := fmt.Sprintf("%p", pv2) pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "spew_test.pstringer" + v2t := typePstringer v2s := "(len=" + v2Len + " cap=" + v2Cap + ") {\n (" + v2t + ") (len=" + v2i0Len + ") stringer 1,\n (" + v2t + ") (len=" + v2i1Len + ") stringer 2,\n (" + v2t + ") (len=" + v2i2Len + ") " + "stringer 3\n}" - addDumpTest(v2, "([]"+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*[]"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**[]"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv2, "(*[]"+v2t+")()\n") + add(v2, "([]"+v2t+") "+v2s+"\n") + add(pv2, "(*[]"+v2t+")("+v2Addr+")("+v2s+")\n") + add(&pv2, "(**[]"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + add(nv2, "(*[]"+v2t+")()\n") // Slice containing interfaces. - v3i0 := "one" + v3i0 := valOne v3 := []any{v3i0, int(2), uint(3), nil} v3i0Len := strconv.Itoa(len(v3i0)) v3Len := strconv.Itoa(len(v3)) @@ -453,18 +626,18 @@ func addSliceDumpTests() { pv3 := &v3 v3Addr := fmt.Sprintf("%p", pv3) pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "[]interface {}" - v3t2 := "string" - v3t3 := "int" - v3t4 := "uint" - v3t5 := "interface {}" + v3t := "[]" + typeInterface + v3t2 := typeString + v3t3 := typeInt + v3t4 := typeUint + v3t5 := typeInterface v3s := "(len=" + v3Len + " cap=" + v3Cap + ") {\n (" + v3t2 + ") " + "(len=" + v3i0Len + ") \"one\",\n (" + v3t3 + ") 2,\n (" + v3t4 + ") 3,\n (" + v3t5 + ") \n}" - addDumpTest(v3, "("+v3t+") "+v3s+"\n") - addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") - addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") - addDumpTest(nv3, "(*"+v3t+")()\n") + add(v3, "("+v3t+") "+v3s+"\n") + add(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") + add(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") + add(nv3, "(*"+v3t+")()\n") // Slice containing bytes. v4 := []byte{ @@ -480,7 +653,7 @@ func addSliceDumpTests() { pv4 := &v4 v4Addr := fmt.Sprintf("%p", pv4) pv4Addr := fmt.Sprintf("%p", &pv4) - v4t := "[]uint8" + v4t := "[]" + typeUint8 v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " + "{\n 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20" + " |............... |\n" + @@ -488,10 +661,10 @@ func addSliceDumpTests() { " |!\"#$%&'()*+,-./0|\n" + " 00000020 31 32 " + " |12|\n}" - addDumpTest(v4, "("+v4t+") "+v4s+"\n") - addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") - addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") - addDumpTest(nv4, "(*"+v4t+")()\n") + add(v4, "("+v4t+") "+v4s+"\n") + add(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") + add(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") + add(nv4, "(*"+v4t+")()\n") // Nil slice. v5 := []int(nil) @@ -499,59 +672,80 @@ func addSliceDumpTests() { pv5 := &v5 v5Addr := fmt.Sprintf("%p", pv5) pv5Addr := fmt.Sprintf("%p", &pv5) - v5t := "[]int" - v5s := "" - addDumpTest(v5, "("+v5t+") "+v5s+"\n") - addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n") - addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n") - addDumpTest(nv5, "(*"+v5t+")()\n") + v5t := "[]" + typeInt + v5s := typeNil + add(v5, "("+v5t+") "+v5s+"\n") + add(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n") + add(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n") + add(nv5, "(*"+v5t+")()\n") + + return tests } -func addStringDumpTests() { +func stringDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Standard string. - v := "test" + v := valTest vLen := strconv.Itoa(len(v)) nv := (*string)(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "string" + vt := typeString vs := "(len=" + vLen + ") \"test\"" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") + add(v, "("+vt+") "+vs+"\n") + add(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + add(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + add(nv, "(*"+vt+")()\n") + + return tests } -func addInterfaceDumpTests() { +func interfaceDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Nil interface. var v any nv := (*any)(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "interface {}" - vs := "" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") + vt := typeInterface + vs := typeNil + add(v, "("+vt+") "+vs+"\n") + add(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + add(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + add(nv, "(*"+vt+")()\n") // Sub-interface. v2 := any(uint16(65535)) pv2 := &v2 v2Addr := fmt.Sprintf("%p", pv2) pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "uint16" + v2t := typeUint16 v2s := "65535" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + add(v2, "("+v2t+") "+v2s+"\n") + add(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + add(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + + return tests } -func addMapDumpTests() { +func mapDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Map with string keys and int vals. - k := "one" + k := valOne kk := "two" m := map[string]int{k: 1, kk: 2} klen := strconv.Itoa(len(k)) // not kLen to shut golint up @@ -563,24 +757,24 @@ func addMapDumpTests() { mAddr := fmt.Sprintf("%p", pm) pmAddr := fmt.Sprintf("%p", &pm) mt := "map[string]int" - mt1 := "string" - mt2 := "int" + mt1 := typeString + mt2 := typeInt ms := "(len=" + mLen + ") {\n (" + mt1 + ") (len=" + klen + ") " + "\"one\": (" + mt2 + ") 1,\n (" + mt1 + ") (len=" + kkLen + ") \"two\": (" + mt2 + ") 2\n}" ms2 := "(len=" + mLen + ") {\n (" + mt1 + ") (len=" + kkLen + ") " + "\"two\": (" + mt2 + ") 2,\n (" + mt1 + ") (len=" + klen + ") \"one\": (" + mt2 + ") 1\n}" - addDumpTest(m, "("+mt+") "+ms+"\n", "("+mt+") "+ms2+"\n") - addDumpTest(pm, "(*"+mt+")("+mAddr+")("+ms+")\n", + add(m, "("+mt+") "+ms+"\n", "("+mt+") "+ms2+"\n") + add(pm, "(*"+mt+")("+mAddr+")("+ms+")\n", "(*"+mt+")("+mAddr+")("+ms2+")\n") - addDumpTest(&pm, "(**"+mt+")("+pmAddr+"->"+mAddr+")("+ms+")\n", + add(&pm, "(**"+mt+")("+pmAddr+"->"+mAddr+")("+ms+")\n", "(**"+mt+")("+pmAddr+"->"+mAddr+")("+ms2+")\n") - addDumpTest(nm, "(*"+mt+")()\n") - addDumpTest(nilMap, "("+mt+") \n") + add(nm, "(*"+mt+")()\n") + add(nilMap, "("+mt+") \n") // Map with custom formatter type on pointer receiver only keys and vals. - k2 := pstringer("one") + k2 := pstringer(valOne) v2 := pstringer("1") m2 := map[pstringer]pstringer{k2: v2} k2Len := strconv.Itoa(len(k2)) @@ -592,8 +786,8 @@ func addMapDumpTests() { m2Addr := fmt.Sprintf("%p", pm2) pm2Addr := fmt.Sprintf("%p", &pm2) m2t := "map[spew_test.pstringer]spew_test.pstringer" - m2t1 := "spew_test.pstringer" - m2t2 := "spew_test.pstringer" + m2t1 := typePstringer + m2t2 := typePstringer m2s := "(len=" + m2Len + ") {\n (" + m2t1 + ") (len=" + k2Len + ") " + "stringer one: (" + m2t2 + ") (len=" + v2Len + ") stringer 1\n}" if spew.UnsafeDisabled { @@ -601,14 +795,14 @@ func addMapDumpTests() { ") " + "\"one\": (" + m2t2 + ") (len=" + v2Len + ") \"1\"\n}" } - addDumpTest(m2, "("+m2t+") "+m2s+"\n") - addDumpTest(pm2, "(*"+m2t+")("+m2Addr+")("+m2s+")\n") - addDumpTest(&pm2, "(**"+m2t+")("+pm2Addr+"->"+m2Addr+")("+m2s+")\n") - addDumpTest(nm2, "(*"+m2t+")()\n") - addDumpTest(nilMap2, "("+m2t+") \n") + add(m2, "("+m2t+") "+m2s+"\n") + add(pm2, "(*"+m2t+")("+m2Addr+")("+m2s+")\n") + add(&pm2, "(**"+m2t+")("+pm2Addr+"->"+m2Addr+")("+m2s+")\n") + add(nm2, "(*"+m2t+")()\n") + add(nilMap2, "("+m2t+") \n") // Map with interface keys and values. - k3 := "one" + k3 := valOne k3Len := strconv.Itoa(len(k3)) m3 := map[any]any{k3: 1} m3Len := strconv.Itoa(len(m3)) @@ -617,16 +811,16 @@ func addMapDumpTests() { pm3 := &m3 m3Addr := fmt.Sprintf("%p", pm3) pm3Addr := fmt.Sprintf("%p", &pm3) - m3t := "map[interface {}]interface {}" - m3t1 := "string" - m3t2 := "int" + m3t := "map[" + typeInterface + "]" + typeInterface + m3t1 := typeString + m3t2 := typeInt m3s := "(len=" + m3Len + ") {\n (" + m3t1 + ") (len=" + k3Len + ") " + "\"one\": (" + m3t2 + ") 1\n}" - addDumpTest(m3, "("+m3t+") "+m3s+"\n") - addDumpTest(pm3, "(*"+m3t+")("+m3Addr+")("+m3s+")\n") - addDumpTest(&pm3, "(**"+m3t+")("+pm3Addr+"->"+m3Addr+")("+m3s+")\n") - addDumpTest(nm3, "(*"+m3t+")()\n") - addDumpTest(nilMap3, "("+m3t+") \n") + add(m3, "("+m3t+") "+m3s+"\n") + add(pm3, "(*"+m3t+")("+m3Addr+")("+m3s+")\n") + add(&pm3, "(**"+m3t+")("+pm3Addr+"->"+m3Addr+")("+m3s+")\n") + add(nm3, "(*"+m3t+")()\n") + add(nilMap3, "("+m3t+") \n") // Map with nil interface value. k4 := "nil" @@ -638,19 +832,26 @@ func addMapDumpTests() { pm4 := &m4 m4Addr := fmt.Sprintf("%p", pm4) pm4Addr := fmt.Sprintf("%p", &pm4) - m4t := "map[string]interface {}" - m4t1 := "string" - m4t2 := "interface {}" + m4t := "map[string]" + typeInterface + m4t1 := typeString + m4t2 := typeInterface m4s := "(len=" + m4Len + ") {\n (" + m4t1 + ") (len=" + k4Len + ")" + " \"nil\": (" + m4t2 + ") \n}" - addDumpTest(m4, "("+m4t+") "+m4s+"\n") - addDumpTest(pm4, "(*"+m4t+")("+m4Addr+")("+m4s+")\n") - addDumpTest(&pm4, "(**"+m4t+")("+pm4Addr+"->"+m4Addr+")("+m4s+")\n") - addDumpTest(nm4, "(*"+m4t+")()\n") - addDumpTest(nilMap4, "("+m4t+") \n") + add(m4, "("+m4t+") "+m4s+"\n") + add(pm4, "(*"+m4t+")("+m4Addr+")("+m4s+")\n") + add(&pm4, "(**"+m4t+")("+pm4Addr+"->"+m4Addr+")("+m4s+")\n") + add(nm4, "(*"+m4t+")()\n") + add(nilMap4, "("+m4t+") \n") + + return tests } -func addStructDumpTests() { +func structDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Struct with primitives. type s1 struct { a int8 @@ -661,14 +862,14 @@ func addStructDumpTests() { pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "spew_test.s1" - vt2 := "int8" - vt3 := "uint8" + vt := typeS1 + vt2 := typeInt8 + vt3 := typeUint8 vs := "{\n a: (" + vt2 + ") 127,\n b: (" + vt3 + ") 255\n}" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") + add(v, "("+vt+") "+vs+"\n") + add(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + add(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + add(nv, "(*"+vt+")()\n") // Struct that contains another struct. type s2 struct { @@ -680,17 +881,17 @@ func addStructDumpTests() { pv2 := &v2 v2Addr := fmt.Sprintf("%p", pv2) pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "spew_test.s2" - v2t2 := "spew_test.s1" - v2t3 := "int8" - v2t4 := "uint8" - v2t5 := "bool" + v2t := typeS2 + v2t2 := typeS1 + v2t3 := typeInt8 + v2t4 := typeUint8 + v2t5 := typeBool v2s := "{\n s1: (" + v2t2 + ") {\n a: (" + v2t3 + ") 127,\n b: (" + v2t4 + ") 255\n },\n b: (" + v2t5 + ") true\n}" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv2, "(*"+v2t+")()\n") + add(v2, "("+v2t+") "+v2s+"\n") + add(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + add(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + add(nv2, "(*"+v2t+")()\n") // Struct that contains custom type with Stringer pointer interface via both // exported and unexported fields. @@ -698,13 +899,13 @@ func addStructDumpTests() { s pstringer S pstringer } - v3 := s3{"test", "test2"} + v3 := s3{valTest, "test2"} nv3 := (*s3)(nil) pv3 := &v3 v3Addr := fmt.Sprintf("%p", pv3) pv3Addr := fmt.Sprintf("%p", &pv3) v3t := "spew_test.s3" - v3t2 := "spew_test.pstringer" + v3t2 := typePstringer v3s := "{\n s: (" + v3t2 + ") (len=4) stringer test,\n S: (" + v3t2 + ") (len=5) stringer test2\n}" v3sp := v3s @@ -714,10 +915,10 @@ func addStructDumpTests() { v3sp = "{\n s: (" + v3t2 + ") (len=4) \"test\",\n S: (" + v3t2 + ") (len=5) stringer test2\n}" } - addDumpTest(v3, "("+v3t+") "+v3s+"\n") - addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3sp+")\n") - addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3sp+")\n") - addDumpTest(nv3, "(*"+v3t+")()\n") + add(v3, "("+v3t+") "+v3s+"\n") + add(pv3, "(*"+v3t+")("+v3Addr+")("+v3sp+")\n") + add(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3sp+")\n") + add(nv3, "(*"+v3t+")()\n") // Struct that contains embedded struct and field to same struct. e := embed{"embedstr"} @@ -730,28 +931,35 @@ func addStructDumpTests() { pv4Addr := fmt.Sprintf("%p", &pv4) v4t := "spew_test.embedwrap" v4t2 := "spew_test.embed" - v4t3 := "string" + v4t3 := typeString v4s := "{\n embed: (*" + v4t2 + ")(" + eAddr + ")({\n a: (" + v4t3 + ") (len=" + eLen + ") \"embedstr\"\n }),\n e: (*" + v4t2 + ")(" + eAddr + ")({\n a: (" + v4t3 + ") (len=" + eLen + ")" + " \"embedstr\"\n })\n}" - addDumpTest(v4, "("+v4t+") "+v4s+"\n") - addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") - addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") - addDumpTest(nv4, "(*"+v4t+")()\n") + add(v4, "("+v4t+") "+v4s+"\n") + add(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") + add(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") + add(nv4, "(*"+v4t+")()\n") + + return tests } -func addUintptrDumpTests() { +func uintptrDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Null pointer. v := uintptr(0) pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "uintptr" - vs := "" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + vt := typeUintptr + vs := typeNil + add(v, "("+vt+") "+vs+"\n") + add(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + add(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") // Address of real variable. i := 1 @@ -760,27 +968,34 @@ func addUintptrDumpTests() { pv2 := &v2 v2Addr := fmt.Sprintf("%p", pv2) pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "uintptr" + v2t := typeUintptr v2s := fmt.Sprintf("%p", &i) - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv2, "(*"+v2t+")()\n") + add(v2, "("+v2t+") "+v2s+"\n") + add(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + add(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + add(nv2, "(*"+v2t+")()\n") + + return tests } -func addUnsafePointerDumpTests() { +func unsafePointerDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Null pointer. v := unsafe.Pointer(nil) nv := (*unsafe.Pointer)(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "unsafe.Pointer" - vs := "" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") + vt := typeUnsafePointer + vs := typeNil + add(v, "("+vt+") "+vs+"\n") + add(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + add(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + add(nv, "(*"+vt+")()\n") // Address of real variable. i := 1 @@ -788,53 +1003,67 @@ func addUnsafePointerDumpTests() { pv2 := &v2 v2Addr := fmt.Sprintf("%p", pv2) pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "unsafe.Pointer" + v2t := typeUnsafePointer v2s := fmt.Sprintf("%p", &i) - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv, "(*"+vt+")()\n") + add(v2, "("+v2t+") "+v2s+"\n") + add(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + add(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + add(nv, "(*"+vt+")()\n") + + return tests } -func addChanDumpTests() { +func chanDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Nil channel. var v chan int pv := &v nv := (*chan int)(nil) vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "chan int" - vs := "" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") + vt := typeChanInt + vs := typeNil + add(v, "("+vt+") "+vs+"\n") + add(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + add(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + add(nv, "(*"+vt+")()\n") // Real channel. v2 := make(chan int) pv2 := &v2 v2Addr := fmt.Sprintf("%p", pv2) pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "chan int" + v2t := typeChanInt v2s := fmt.Sprintf("%p", v2) - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + add(v2, "("+v2t+") "+v2s+"\n") + add(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + add(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + + return tests } -func addFuncDumpTests() { +func funcDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Function with no params and no returns. - v := addIntDumpTests + v := func() {} nv := (*func())(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) vt := "func()" vs := fmt.Sprintf("%p", v) - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") + add(v, "("+vt+") "+vs+"\n") + add(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + add(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + add(nv, "(*"+vt+")()\n") // Function with param and no returns. v2 := TestDump @@ -844,13 +1073,13 @@ func addFuncDumpTests() { pv2Addr := fmt.Sprintf("%p", &pv2) v2t := "func(*testing.T)" v2s := fmt.Sprintf("%p", v2) - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv2, "(*"+v2t+")()\n") + add(v2, "("+v2t+") "+v2s+"\n") + add(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") + add(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") + add(nv2, "(*"+v2t+")()\n") // Function with multiple params and multiple returns. - var v3 = func(i int, s string) (b bool, err error) { + v3 := func(_ int, _ string) (b bool, err error) { return true, nil } nv3 := (*func(int, string) (bool, error))(nil) @@ -859,13 +1088,20 @@ func addFuncDumpTests() { pv3Addr := fmt.Sprintf("%p", &pv3) v3t := "func(int, string) (bool, error)" v3s := fmt.Sprintf("%p", v3) - addDumpTest(v3, "("+v3t+") "+v3s+"\n") - addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") - addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") - addDumpTest(nv3, "(*"+v3t+")()\n") + add(v3, "("+v3t+") "+v3s+"\n") + add(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") + add(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") + add(nv3, "(*"+v3t+")()\n") + + return tests } -func addCircularDumpTests() { +func circularDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Struct that is circular through self referencing. type circular struct { c *circular @@ -879,9 +1115,9 @@ func addCircularDumpTests() { vs := "{\n c: (*" + vt + ")(" + vAddr + ")({\n c: (*" + vt + ")(" + vAddr + ")()\n })\n}" vs2 := "{\n c: (*" + vt + ")(" + vAddr + ")()\n}" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs2+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs2+")\n") + add(v, "("+vt+") "+vs+"\n") + add(pv, "(*"+vt+")("+vAddr+")("+vs2+")\n") + add(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs2+")\n") // Structs that are circular through cross referencing. v2 := xref1{nil} @@ -898,9 +1134,9 @@ func addCircularDumpTests() { ")()\n })\n })\n}" v2s2 := "{\n ps2: (*" + v2t2 + ")(" + ts2Addr + ")({\n ps1: (*" + v2t + ")(" + v2Addr + ")()\n })\n}" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s2+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s2+")\n") + add(v2, "("+v2t+") "+v2s+"\n") + add(pv2, "(*"+v2t+")("+v2Addr+")("+v2s2+")\n") + add(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s2+")\n") // Structs that are indirectly circular. v3 := indirCir1{nil} @@ -923,12 +1159,19 @@ func addCircularDumpTests() { v3s2 := "{\n ps2: (*" + v3t2 + ")(" + tic2Addr + ")({\n ps3: (*" + v3t3 + ")(" + tic3Addr + ")({\n ps1: (*" + v3t + ")(" + v3Addr + ")()\n })\n })\n}" - addDumpTest(v3, "("+v3t+") "+v3s+"\n") - addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s2+")\n") - addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s2+")\n") + add(v3, "("+v3t+") "+v3s+"\n") + add(pv3, "(*"+v3t+")("+v3Addr+")("+v3s2+")\n") + add(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s2+")\n") + + return tests } -func addPanicDumpTests() { +func panicDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Type that panics in its Stringer interface. v := panicer(127) nv := (*panicer)(nil) @@ -937,13 +1180,20 @@ func addPanicDumpTests() { pvAddr := fmt.Sprintf("%p", &pv) vt := "spew_test.panicer" vs := "(PANIC=test panic)127" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") + add(v, "("+vt+") "+vs+"\n") + add(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + add(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + add(nv, "(*"+vt+")()\n") + + return tests } -func addErrorDumpTests() { +func errorDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // Type that has a custom Error interface. v := customError(127) nv := (*customError)(nil) @@ -952,10 +1202,12 @@ func addErrorDumpTests() { pvAddr := fmt.Sprintf("%p", &pv) vt := "spew_test.customError" vs := "error: 127" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") + add(v, "("+vt+") "+vs+"\n") + add(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") + add(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") + add(nv, "(*"+vt+")()\n") + + return tests } type ( @@ -973,7 +1225,12 @@ type ( } ) -func addTimeDumpTests() { +func timeDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + ts := time.Date(2006, time.January, 2, 15, 4, 5, 999999999, time.UTC) alias := aliasedTime(ts) //nolint:unconvert // we want to prove here that aliased types don't matter tsAddr := fmt.Sprintf("%p", &ts) @@ -995,51 +1252,51 @@ func addTimeDumpTests() { redeclaredTime: redeclaredTime(ts), } - addDumpTest( + add( // simple time.Time ts, "(time.Time) 2006-01-02 15:04:05.999999999 +0000 UTC\n", ) - addDumpTest( + add( // aliases are ignored at runtime alias, "(time.Time) 2006-01-02 15:04:05.999999999 +0000 UTC\n", ) - addDumpTest( + add( // pointer to time.Time &ts, "(*time.Time)("+tsAddr+")(2006-01-02 15:04:05.999999999 +0000 UTC)\n", ) - addDumpTest( + add( // struct with embedded time.Time es, "(spew_test.embeddedTime) 2006-01-02 15:04:05.999999999 +0000 UTC\n", ) - addDumpTest( + add( // struct with embedded pointer to time.Time ps, "(spew_test.embeddedTimePtr) 2006-01-02 15:04:05.999999999 +0000 UTC\n", ) - addDumpTest( + add( // nil time.Time tptr, "(*time.Time)()\n", ) - addDumpTest( + add( // **time.Time ppts, "(**time.Time)("+ppAddr+"->"+ppIAddr+")(2006-01-02 15:04:05.999999999 +0000 UTC)\n", ) - addDumpTest( + add( panick, // this is a stringer, but the inner member that implements String() string is nil "(spew_test.embeddedTimePtr) (PANIC=runtime error: invalid memory address or nil pointer dereference){\n Time: (*time.Time)()\n}\n", ) - addDumpTest( + add( // redeclared type convertible to time.Time redeclaredTime(ts), "(spew_test.redeclaredTime) 2006-01-02 15:04:05.999999999 +0000 UTC\n", ) - addDumpTest( + add( // redeclared type convertible to *time.Time // // NOTE: the information about the original (redeclared) type is lost. This is due to @@ -1047,101 +1304,12 @@ func addTimeDumpTests() { rtptr, "(*time.Time)("+tsAddr+")(2006-01-02 15:04:05.999999999 +0000 UTC)\n", ) - addDumpTest( + add( // embedded redeclared type convertible to time.Time er, "(spew_test.embeddedRedeclaredTime) {\n"+ " redeclaredTime: (spew_test.redeclaredTime) 2006-01-02 15:04:05.999999999 +0000 UTC\n}\n", ) -} - -// TestDump executes all of the tests described by dumpTests. -func TestDump(t *testing.T) { - // Setup tests. - addIntDumpTests() - addUintDumpTests() - addBoolDumpTests() - addFloatDumpTests() - addComplexDumpTests() - addArrayDumpTests() - addSliceDumpTests() - addStringDumpTests() - addInterfaceDumpTests() - addMapDumpTests() - addStructDumpTests() - addUintptrDumpTests() - addUnsafePointerDumpTests() - addChanDumpTests() - addFuncDumpTests() - addCircularDumpTests() - addPanicDumpTests() - addErrorDumpTests() - addCgoDumpTests() - addTimeDumpTests() - - t.Logf("Running %d tests", len(dumpTests)) - for i, test := range dumpTests { - buf := new(bytes.Buffer) - spew.Fdump(buf, test.in) - s := buf.String() - if testFailed(s, test.wants) { - t.Errorf("Dump #%d\n got: %s %s", i, s, stringizeWants(test.wants)) - continue - } - } -} - -func TestDumpSortedKeys(t *testing.T) { - cfg := spew.ConfigState{SortKeys: true} - s := cfg.Sdump(map[int]string{1: "1", 3: "3", 2: "2"}) - expected := "(map[int]string) (len=3) {\n(int) 1: (string) (len=1) " + - "\"1\",\n(int) 2: (string) (len=1) \"2\",\n(int) 3: (string) " + - "(len=1) \"3\"\n" + - "}\n" - if s != expected { - t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) - } - s = cfg.Sdump(map[stringer]int{"1": 1, "3": 3, "2": 2}) - expected = "(map[spew_test.stringer]int) (len=3) {\n" + - "(spew_test.stringer) (len=1) stringer 1: (int) 1,\n" + - "(spew_test.stringer) (len=1) stringer 2: (int) 2,\n" + - "(spew_test.stringer) (len=1) stringer 3: (int) 3\n" + - "}\n" - if s != expected { - t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) - } - - s = cfg.Sdump(map[pstringer]int{pstringer("1"): 1, pstringer("3"): 3, pstringer("2"): 2}) - expected = "(map[spew_test.pstringer]int) (len=3) {\n" + - "(spew_test.pstringer) (len=1) stringer 1: (int) 1,\n" + - "(spew_test.pstringer) (len=1) stringer 2: (int) 2,\n" + - "(spew_test.pstringer) (len=1) stringer 3: (int) 3\n" + - "}\n" - if spew.UnsafeDisabled { - expected = "(map[spew_test.pstringer]int) (len=3) {\n" + - "(spew_test.pstringer) (len=1) \"1\": (int) 1,\n" + - "(spew_test.pstringer) (len=1) \"2\": (int) 2,\n" + - "(spew_test.pstringer) (len=1) \"3\": (int) 3\n" + - "}\n" - } - if s != expected { - t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) - } - - s = cfg.Sdump(map[customError]int{customError(1): 1, customError(3): 3, customError(2): 2}) - expected = "(map[spew_test.customError]int) (len=3) {\n" + - "(spew_test.customError) error: 1: (int) 1,\n" + - "(spew_test.customError) error: 2: (int) 2,\n" + - "(spew_test.customError) error: 3: (int) 3\n" + - "}\n" - if s != expected { - t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) - } - -} - -func ptr[T any](value T) *T { - v := value - return &v + return tests } diff --git a/internal/spew/dumpcgo_test.go b/internal/spew/dumpcgo_test.go index c2bf5ae8a..a20fe5329 100644 --- a/internal/spew/dumpcgo_test.go +++ b/internal/spew/dumpcgo_test.go @@ -30,7 +30,12 @@ import ( "github.com/go-openapi/testify/v2/internal/spew/testsrc" ) -func addCgoDumpTests() { +func cgoDumpTests() []dumpTest { + var tests []dumpTest + add := func(in any, wants ...string) { + tests = append(tests, dumpTest{in, wants}) + } + // C char pointer. v := testsrc.GetCgoCharPointer() nv := testsrc.GetCgoNullCharPointer() @@ -40,10 +45,10 @@ func addCgoDumpTests() { pvAddr := fmt.Sprintf("%p", &pv) vt := "*testsrc._Ctype_char" vs := "116" - addDumpTest(v, "("+vt+")("+vcAddr+")("+vs+")\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+"->"+vcAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+"->"+vcAddr+")("+vs+")\n") - addDumpTest(nv, "("+vt+")()\n") + add(v, "("+vt+")("+vcAddr+")("+vs+")\n") + add(pv, "(*"+vt+")("+vAddr+"->"+vcAddr+")("+vs+")\n") + add(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+"->"+vcAddr+")("+vs+")\n") + add(nv, "("+vt+")()\n") // C char array. v2, v2l, v2c := testsrc.GetCgoCharArray() @@ -53,7 +58,7 @@ func addCgoDumpTests() { v2s := "(len=" + v2Len + " cap=" + v2Cap + ") " + "{\n 00000000 74 65 73 74 32 00 " + " |test2.|\n}" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") + add(v2, "("+v2t+") "+v2s+"\n") // C unsigned char array. v3, v3l, v3c := testsrc.GetCgoUnsignedCharArray() @@ -64,7 +69,7 @@ func addCgoDumpTests() { v3s := "(len=" + v3Len + " cap=" + v3Cap + ") " + "{\n 00000000 74 65 73 74 33 00 " + " |test3.|\n}" - addDumpTest(v3, "("+v3t+") "+v3s+"\n", "("+v3t2+") "+v3s+"\n") + add(v3, "("+v3t+") "+v3s+"\n", "("+v3t2+") "+v3s+"\n") // C signed char array. v4, v4l, v4c := testsrc.GetCgoSignedCharArray() @@ -76,7 +81,7 @@ func addCgoDumpTests() { "{\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 101,\n (" + v4t2 + ") 115,\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 52,\n (" + v4t2 + ") 0\n}" - addDumpTest(v4, "("+v4t+") "+v4s+"\n") + add(v4, "("+v4t+") "+v4s+"\n") // C uint8_t array. v5, v5l, v5c := testsrc.GetCgoUint8tArray() @@ -87,7 +92,7 @@ func addCgoDumpTests() { v5s := "(len=" + v5Len + " cap=" + v5Cap + ") " + "{\n 00000000 74 65 73 74 35 00 " + " |test5.|\n}" - addDumpTest(v5, "("+v5t+") "+v5s+"\n", "("+v5t2+") "+v5s+"\n") + add(v5, "("+v5t+") "+v5s+"\n", "("+v5t2+") "+v5s+"\n") // C typedefed unsigned char array. v6, v6l, v6c := testsrc.GetCgoTypdefedUnsignedCharArray() @@ -98,5 +103,7 @@ func addCgoDumpTests() { v6s := "(len=" + v6Len + " cap=" + v6Cap + ") " + "{\n 00000000 74 65 73 74 36 00 " + " |test6.|\n}" - addDumpTest(v6, "("+v6t+") "+v6s+"\n", "("+v6t2+") "+v6s+"\n") + add(v6, "("+v6t+") "+v6s+"\n", "("+v6t2+") "+v6s+"\n") + + return tests } diff --git a/internal/spew/dumpnocgo_test.go b/internal/spew/dumpnocgo_test.go index e026d3102..e4d5d11b9 100644 --- a/internal/spew/dumpnocgo_test.go +++ b/internal/spew/dumpnocgo_test.go @@ -21,7 +21,8 @@ package spew_test -func addCgoDumpTests() { +func cgoDumpTests() []dumpTest { // Don't add any tests for cgo since this file is only compiled when // there should not be any cgo tests. + return nil } diff --git a/internal/spew/edgecases_test.go b/internal/spew/edgecases_test.go index 380ed38b6..a7ca8f7af 100644 --- a/internal/spew/edgecases_test.go +++ b/internal/spew/edgecases_test.go @@ -10,6 +10,7 @@ import ( func TestEdgeCases(t *testing.T) { t.Parallel() + cfg := Config output := io.Discard diff --git a/internal/spew/example_test.go b/internal/spew/example_test.go index dc46124ca..19a65138c 100644 --- a/internal/spew/example_test.go +++ b/internal/spew/example_test.go @@ -24,21 +24,24 @@ import ( type Flag int +func (f Flag) String() string { + if s, ok := flagStrings()[f]; ok { + return s + } + + return fmt.Sprintf("Unknown flag (%d)", int(f)) +} + const ( flagOne Flag = iota flagTwo ) -var flagStrings = map[Flag]string{ - flagOne: "flagOne", - flagTwo: "flagTwo", -} - -func (f Flag) String() string { - if s, ok := flagStrings[f]; ok { - return s +func flagStrings() map[Flag]string { + return map[Flag]string{ + flagOne: "flagOne", + flagTwo: "flagTwo", } - return fmt.Sprintf("Unknown flag (%d)", int(f)) } type Bar struct { @@ -61,18 +64,20 @@ func ExampleDump() { flagTwo ) - var flagStrings = map[Flag]string{ - flagOne: "flagOne", - flagTwo: "flagTwo", - } - func (f Flag) String() string { - if s, ok := flagStrings[f]; ok { + if s, ok := flagStrings()[f]; ok { return s } return fmt.Sprintf("Unknown flag (%d)", int(f)) } + func flagStrings() map[Flag]string { + return map[Flag]string{ + flagOne: "flagOne", + flagTwo: "flagTwo", + } + } + type Bar struct { data uintptr } diff --git a/internal/spew/format.go b/internal/spew/format.go index fd22d1d91..bd04a1725 100644 --- a/internal/spew/format.go +++ b/internal/spew/format.go @@ -40,6 +40,29 @@ type formatState struct { cs *ConfigState } +// Format satisfies the fmt.Formatter interface. See NewFormatter for usage +// details. +func (f *formatState) Format(fs fmt.State, verb rune) { + f.fs = fs + + // Use standard formatting for verbs that are not v. + if verb != 'v' { + format := f.constructOrigFormat(verb) + _, _ = fmt.Fprintf(fs, format, f.value) + return + } + + if f.value == nil { + if fs.Flag('#') { + _, _ = fs.Write(interfaceBytes) + } + _, _ = fs.Write(nilAngleBytes) + return + } + + f.format(reflect.ValueOf(f.value)) +} + // buildDefaultFormat recreates the original format string without precision // and width information to pass in to fmt.Sprintf in the case of an // unrecognized type. Unless new types are added to the language, this @@ -106,7 +129,7 @@ func (f *formatState) formatPtr(v reflect.Value) { // Display nil if top level pointer is nil. showTypes := f.fs.Flag('#') if v.IsNil() && (!showTypes || f.ignoreNextType) { - f.fs.Write(nilAngleBytes) + _, _ = f.fs.Write(nilAngleBytes) return } @@ -118,79 +141,45 @@ func (f *formatState) formatPtr(v reflect.Value) { } } - // Keep list of all dereferenced pointers to possibly show later. - pointerChain := make([]uintptr, 0) - - // Figure out how many levels of indirection there are by derferencing - // pointers and unpacking interfaces down the chain while detecting circular - // references. - nilFound := false - cycleFound := false - indirects := 0 - ve := v - for ve.Kind() == reflect.Ptr { - if ve.IsNil() { - nilFound = true - break - } - indirects++ - addr := ve.Pointer() - pointerChain = append(pointerChain, addr) - if pd, ok := f.pointers[addr]; ok && pd < f.depth { - cycleFound = true - indirects-- - break - } - f.pointers[addr] = f.depth - - ve = ve.Elem() - if ve.Kind() == reflect.Interface { - if ve.IsNil() { - nilFound = true - break - } - ve = ve.Elem() - } - } + r := resolvePtr(v, f.depth, f.pointers) // Display type or indirection level depending on flags. if showTypes && !f.ignoreNextType { - f.fs.Write(openParenBytes) - f.fs.Write(bytes.Repeat(asteriskBytes, indirects)) - f.fs.Write([]byte(ve.Type().String())) - f.fs.Write(closeParenBytes) + _, _ = f.fs.Write(openParenBytes) + _, _ = f.fs.Write(bytes.Repeat(asteriskBytes, r.indirects)) + _, _ = f.fs.Write([]byte(r.value.Type().String())) + _, _ = f.fs.Write(closeParenBytes) } else { - if nilFound || cycleFound { - indirects += strings.Count(ve.Type().String(), "*") + indirects := r.indirects + if r.nilFound || r.cycleFound { + indirects += strings.Count(r.value.Type().String(), "*") } - f.fs.Write(openAngleBytes) - f.fs.Write([]byte(strings.Repeat("*", indirects))) - f.fs.Write(closeAngleBytes) + _, _ = f.fs.Write(openAngleBytes) + _, _ = f.fs.Write([]byte(strings.Repeat("*", indirects))) + _, _ = f.fs.Write(closeAngleBytes) } // Display pointer information depending on flags. - if f.fs.Flag('+') && (len(pointerChain) > 0) { - f.fs.Write(openParenBytes) - for i, addr := range pointerChain { + if f.fs.Flag('+') && (len(r.pointerChain) > 0) { + _, _ = f.fs.Write(openParenBytes) + for i, addr := range r.pointerChain { if i > 0 { - f.fs.Write(pointerChainBytes) + _, _ = f.fs.Write(pointerChainBytes) } printHexPtr(f.fs, addr) } - f.fs.Write(closeParenBytes) + _, _ = f.fs.Write(closeParenBytes) } // Display dereferenced value. switch { - case nilFound: - f.fs.Write(nilAngleBytes) - - case cycleFound: - f.fs.Write(circularShortBytes) - + case r.nilFound: + _, _ = f.fs.Write(nilAngleBytes) + case r.cycleFound: + _, _ = f.fs.Write(circularShortBytes) default: f.ignoreNextType = true - f.format(ve) + f.format(r.value) } } @@ -202,34 +191,35 @@ func (f *formatState) format(v reflect.Value) { // Handle invalid reflect values immediately. kind := v.Kind() if kind == reflect.Invalid { - f.fs.Write(invalidAngleBytes) + _, _ = f.fs.Write(invalidAngleBytes) return } // Handle pointers specially. - if kind == reflect.Ptr { + if kind == reflect.Pointer { f.formatPtr(v) return } // Print type information unless already handled elsewhere. if !f.ignoreNextType && f.fs.Flag('#') { - f.fs.Write(openParenBytes) - f.fs.Write([]byte(v.Type().String())) - f.fs.Write(closeParenBytes) + _, _ = f.fs.Write(openParenBytes) + _, _ = f.fs.Write([]byte(v.Type().String())) + _, _ = f.fs.Write(closeParenBytes) } f.ignoreNextType = false // Call Stringer/error interfaces if they exist and the handle methods // flag is enabled. - if !f.cs.DisableMethods || (f.cs.EnableTimeStringer && isTime(v)) { // we consider the case when we want times printed out - if (kind != reflect.Invalid) && (kind != reflect.Interface) { - if handled := handleMethods(f.cs, f.fs, v); handled { - return - } - } + if tryHandleMethods(f.cs, f.fs, v, kind) { + return } + f.formatValue(v, kind) +} + +// formatValue handles the type-specific formatting for the format method. +func (f *formatState) formatValue(v reflect.Value, kind reflect.Kind) { switch kind { case reflect.Invalid: // Do nothing. We should never get here since invalid has already @@ -239,114 +229,52 @@ func (f *formatState) format(v reflect.Value) { printBool(f.fs, v.Bool()) case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: - printInt(f.fs, v.Int(), 10) + printInt(f.fs, v.Int(), decimalBase) case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - printUint(f.fs, v.Uint(), 10) + printUint(f.fs, v.Uint(), decimalBase) case reflect.Float32: - printFloat(f.fs, v.Float(), 32) + printFloat(f.fs, v.Float(), float32Precision) case reflect.Float64: - printFloat(f.fs, v.Float(), 64) + printFloat(f.fs, v.Float(), float64Precision) case reflect.Complex64: - printComplex(f.fs, v.Complex(), 32) + printComplex(f.fs, v.Complex(), complex64Precision) case reflect.Complex128: - printComplex(f.fs, v.Complex(), 64) + printComplex(f.fs, v.Complex(), complex128Precision) case reflect.Slice: if v.IsNil() { - f.fs.Write(nilAngleBytes) + _, _ = f.fs.Write(nilAngleBytes) break } fallthrough case reflect.Array: - f.fs.Write(openBracketBytes) - f.depth++ - if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { - f.fs.Write(maxShortBytes) - } else { - numEntries := v.Len() - for i := range numEntries { - if i > 0 { - f.fs.Write(spaceBytes) - } - f.ignoreNextType = true - f.format(f.unpackValue(v.Index(i))) - } - } - f.depth-- - f.fs.Write(closeBracketBytes) + f.formatArray(v) case reflect.String: - f.fs.Write([]byte(v.String())) + _, _ = f.fs.Write([]byte(v.String())) case reflect.Interface: // The only time we should get here is for nil interfaces due to // unpackValue calls. if v.IsNil() { - f.fs.Write(nilAngleBytes) + _, _ = f.fs.Write(nilAngleBytes) } - case reflect.Ptr: + case reflect.Pointer: // Do nothing. We should never get here since pointers have already // been handled above. case reflect.Map: - // nil maps should be indicated as different than empty maps - if v.IsNil() { - f.fs.Write(nilAngleBytes) - break - } - - f.fs.Write(openMapBytes) - f.depth++ - if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { - f.fs.Write(maxShortBytes) - } else { - keys := v.MapKeys() - if f.cs.SortKeys { - sortValues(keys, f.cs) - } - for i, key := range keys { - if i > 0 { - f.fs.Write(spaceBytes) - } - f.ignoreNextType = true - f.format(f.unpackValue(key)) - f.fs.Write(colonBytes) - f.ignoreNextType = true - f.format(f.unpackValue(v.MapIndex(key))) - } - } - f.depth-- - f.fs.Write(closeMapBytes) + f.formatMap(v) case reflect.Struct: - numFields := v.NumField() - f.fs.Write(openBraceBytes) - f.depth++ - if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { - f.fs.Write(maxShortBytes) - } else { - vt := v.Type() - for i := range numFields { - if i > 0 { - f.fs.Write(spaceBytes) - } - vtf := vt.Field(i) - if f.fs.Flag('+') || f.fs.Flag('#') { - f.fs.Write([]byte(vtf.Name)) - f.fs.Write(colonBytes) - } - f.format(f.unpackValue(v.Field(i))) - } - } - f.depth-- - f.fs.Write(closeBraceBytes) + f.formatStruct(v) case reflect.Uintptr: printHexPtr(f.fs, uintptr(v.Uint())) @@ -359,34 +287,88 @@ func (f *formatState) format(v reflect.Value) { default: format := f.buildDefaultFormat() if v.CanInterface() { - fmt.Fprintf(f.fs, format, v.Interface()) + _, _ = fmt.Fprintf(f.fs, format, v.Interface()) } else { - fmt.Fprintf(f.fs, format, v.String()) + _, _ = fmt.Fprintf(f.fs, format, v.String()) } } } -// Format satisfies the fmt.Formatter interface. See NewFormatter for usage -// details. -func (f *formatState) Format(fs fmt.State, verb rune) { - f.fs = fs +// formatArray handles formatting of array and slice values. +func (f *formatState) formatArray(v reflect.Value) { + _, _ = f.fs.Write(openBracketBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + _, _ = f.fs.Write(maxShortBytes) + } else { + numEntries := v.Len() + for i := range numEntries { + if i > 0 { + _, _ = f.fs.Write(spaceBytes) + } + f.ignoreNextType = true + f.format(f.unpackValue(v.Index(i))) + } + } + f.depth-- + _, _ = f.fs.Write(closeBracketBytes) +} - // Use standard formatting for verbs that are not v. - if verb != 'v' { - format := f.constructOrigFormat(verb) - fmt.Fprintf(fs, format, f.value) +// formatMap handles formatting of map values. +func (f *formatState) formatMap(v reflect.Value) { + // nil maps should be indicated as different than empty maps + if v.IsNil() { + _, _ = f.fs.Write(nilAngleBytes) return } - if f.value == nil { - if fs.Flag('#') { - fs.Write(interfaceBytes) + _, _ = f.fs.Write(openMapBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + _, _ = f.fs.Write(maxShortBytes) + } else { + keys := v.MapKeys() + if f.cs.SortKeys { + sortValues(keys, f.cs) + } + for i, key := range keys { + if i > 0 { + _, _ = f.fs.Write(spaceBytes) + } + f.ignoreNextType = true + f.format(f.unpackValue(key)) + _, _ = f.fs.Write(colonBytes) + f.ignoreNextType = true + f.format(f.unpackValue(v.MapIndex(key))) } - fs.Write(nilAngleBytes) - return } + f.depth-- + _, _ = f.fs.Write(closeMapBytes) +} - f.format(reflect.ValueOf(f.value)) +// formatStruct handles formatting of struct values. +func (f *formatState) formatStruct(v reflect.Value) { + numFields := v.NumField() + _, _ = f.fs.Write(openBraceBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + _, _ = f.fs.Write(maxShortBytes) + } else { + vt := v.Type() + for i := range numFields { + if i > 0 { + _, _ = f.fs.Write(spaceBytes) + } + vtf := vt.Field(i) + if f.fs.Flag('+') || f.fs.Flag('#') { + _, _ = f.fs.Write([]byte(vtf.Name)) + _, _ = f.fs.Write(colonBytes) + } + f.format(f.unpackValue(v.Field(i))) + } + } + f.depth-- + _, _ = f.fs.Write(closeBraceBytes) } // newFormatter is a helper function to consolidate the logic from the various diff --git a/internal/spew/format_test.go b/internal/spew/format_test.go index 98da74afc..7e7d89cc0 100644 --- a/internal/spew/format_test.go +++ b/internal/spew/format_test.go @@ -69,12 +69,77 @@ package spew_test import ( "bytes" "fmt" + "iter" + "slices" "testing" "unsafe" "github.com/go-openapi/testify/v2/internal/spew" ) +// TestFormatter executes all of the tests described by formatterTestCases. +func TestFormatter(t *testing.T) { + t.Parallel() + + i := 0 + for tc := range formatterTestCases() { + buf := new(bytes.Buffer) + spew.Fprintf(buf, tc.format, tc.in) + s := buf.String() + if testFailed(s, tc.wants) { + t.Errorf("Formatter #%d format: %s got: %s %s", i, tc.format, s, + stringizeWants(tc.wants)) + } + i++ + } +} + +func TestPrintSortedKeys(t *testing.T) { + t.Parallel() + + cfg := spew.ConfigState{SortKeys: true} + s := cfg.Sprint(map[int]string{1: "1", 3: "3", 2: "2"}) + expected := "map[1:1 2:2 3:3]" + if s != expected { + t.Errorf("Sorted keys mismatch 1:\n %v %v", s, expected) + } + + s = cfg.Sprint(map[stringer]int{"1": 1, "3": 3, "2": 2}) + expected = "map[stringer 1:1 stringer 2:2 stringer 3:3]" + if s != expected { + t.Errorf("Sorted keys mismatch 2:\n %v %v", s, expected) + } + + s = cfg.Sprint(map[pstringer]int{pstringer("1"): 1, pstringer("3"): 3, pstringer("2"): 2}) + expected = "map[stringer 1:1 stringer 2:2 stringer 3:3]" + if spew.UnsafeDisabled { + expected = "map[1:1 2:2 3:3]" + } + if s != expected { + t.Errorf("Sorted keys mismatch 3:\n %v %v", s, expected) + } + + s = cfg.Sprint(map[testStruct]int{{1}: 1, {3}: 3, {2}: 2}) + expected = "map[ts.1:1 ts.2:2 ts.3:3]" + if s != expected { + t.Errorf("Sorted keys mismatch 4:\n %v %v", s, expected) + } + + if !spew.UnsafeDisabled { + s = cfg.Sprint(map[testStructP]int{{1}: 1, {3}: 3, {2}: 2}) + expected = "map[ts.1:1 ts.2:2 ts.3:3]" + if s != expected { + t.Errorf("Sorted keys mismatch 5:\n %v %v", s, expected) + } + } + + s = cfg.Sprint(map[customError]int{customError(1): 1, customError(3): 3, customError(2): 2}) + expected = "map[error: 1:1 error: 2:2 error: 3:3]" + if s != expected { + t.Errorf("Sorted keys mismatch 6:\n %v %v", s, expected) + } +} + // formatterTest is used to describe a test to be performed against NewFormatter. type formatterTest struct { format string @@ -82,41 +147,62 @@ type formatterTest struct { wants []string } -// formatterTests houses all of the tests to be performed against NewFormatter. -var formatterTests = make([]formatterTest, 0) - -// addFormatterTest is a helper method to append the passed input and desired -// result to formatterTests. -func addFormatterTest(format string, in any, wants ...string) { - test := formatterTest{format, in, wants} - formatterTests = append(formatterTests, test) +// formatterTestCases returns an iterator over all formatter test cases. +func formatterTestCases() iter.Seq[formatterTest] { + return slices.Values(slices.Concat( + intFormatterTests(), + uintFormatterTests(), + boolFormatterTests(), + floatFormatterTests(), + complexFormatterTests(), + arrayFormatterTests(), + sliceFormatterTests(), + stringFormatterTests(), + interfaceFormatterTests(), + mapFormatterTests(), + structFormatterTests(), + uintptrFormatterTests(), + unsafePointerFormatterTests(), + chanFormatterTests(), + funcFormatterTests(), + circularFormatterTests(), + panicFormatterTests(), + errorFormatterTests(), + passthroughFormatterTests(), + )) } -func addIntFormatterTests() { +//nolint:dupl // int/uint test data follows the same pattern by design +func intFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Max int8. v := int8(127) nv := (*int8)(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "int8" + vt := typeInt8 vs := "127" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") + add("%v", v, vs) + add("%v", pv, "<*>"+vs) + add("%v", &pv, "<**>"+vs) + add("%v", nv, "") + add("%+v", v, vs) + add("%+v", pv, "<*>("+vAddr+")"+vs) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + add("%+v", nv, "") + add("%#v", v, "("+vt+")"+vs) + add("%#v", pv, "(*"+vt+")"+vs) + add("%#v", &pv, "(**"+vt+")"+vs) + add("%#v", nv, "(*"+vt+")"+"") + add("%#+v", v, "("+vt+")"+vs) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + add("%#+v", nv, "(*"+vt+")"+"") // Max int16. v2 := int16(32767) @@ -126,22 +212,22 @@ func addIntFormatterTests() { pv2Addr := fmt.Sprintf("%p", &pv2) v2t := "int16" v2s := "32767" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%v", nv2, "") - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") + add("%v", v2, v2s) + add("%v", pv2, "<*>"+v2s) + add("%v", &pv2, "<**>"+v2s) + add("%v", nv2, "") + add("%+v", v2, v2s) + add("%+v", pv2, "<*>("+v2Addr+")"+v2s) + add("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%+v", nv2, "") + add("%#v", v2, "("+v2t+")"+v2s) + add("%#v", pv2, "(*"+v2t+")"+v2s) + add("%#v", &pv2, "(**"+v2t+")"+v2s) + add("%#v", nv2, "(*"+v2t+")"+"") + add("%#+v", v2, "("+v2t+")"+v2s) + add("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + add("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%#+v", nv2, "(*"+v2t+")"+"") // Max int32. v3 := int32(2147483647) @@ -151,22 +237,22 @@ func addIntFormatterTests() { pv3Addr := fmt.Sprintf("%p", &pv3) v3t := "int32" v3s := "2147483647" - addFormatterTest("%v", v3, v3s) - addFormatterTest("%v", pv3, "<*>"+v3s) - addFormatterTest("%v", &pv3, "<**>"+v3s) - addFormatterTest("%v", nv3, "") - addFormatterTest("%+v", v3, v3s) - addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) - addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%#v", v3, "("+v3t+")"+v3s) - addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s) - addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") - addFormatterTest("%#+v", v3, "("+v3t+")"+v3s) - addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s) - addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") + add("%v", v3, v3s) + add("%v", pv3, "<*>"+v3s) + add("%v", &pv3, "<**>"+v3s) + add("%v", nv3, "") + add("%+v", v3, v3s) + add("%+v", pv3, "<*>("+v3Addr+")"+v3s) + add("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) + add("%+v", nv3, "") + add("%#v", v3, "("+v3t+")"+v3s) + add("%#v", pv3, "(*"+v3t+")"+v3s) + add("%#v", &pv3, "(**"+v3t+")"+v3s) + add("%#v", nv3, "(*"+v3t+")"+"") + add("%#+v", v3, "("+v3t+")"+v3s) + add("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s) + add("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s) + add("%#v", nv3, "(*"+v3t+")"+"") // Max int64. v4 := int64(9223372036854775807) @@ -176,22 +262,22 @@ func addIntFormatterTests() { pv4Addr := fmt.Sprintf("%p", &pv4) v4t := "int64" v4s := "9223372036854775807" - addFormatterTest("%v", v4, v4s) - addFormatterTest("%v", pv4, "<*>"+v4s) - addFormatterTest("%v", &pv4, "<**>"+v4s) - addFormatterTest("%v", nv4, "") - addFormatterTest("%+v", v4, v4s) - addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s) - addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s) - addFormatterTest("%+v", nv4, "") - addFormatterTest("%#v", v4, "("+v4t+")"+v4s) - addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s) - addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s) - addFormatterTest("%#v", nv4, "(*"+v4t+")"+"") - addFormatterTest("%#+v", v4, "("+v4t+")"+v4s) - addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s) - addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s) - addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"") + add("%v", v4, v4s) + add("%v", pv4, "<*>"+v4s) + add("%v", &pv4, "<**>"+v4s) + add("%v", nv4, "") + add("%+v", v4, v4s) + add("%+v", pv4, "<*>("+v4Addr+")"+v4s) + add("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s) + add("%+v", nv4, "") + add("%#v", v4, "("+v4t+")"+v4s) + add("%#v", pv4, "(*"+v4t+")"+v4s) + add("%#v", &pv4, "(**"+v4t+")"+v4s) + add("%#v", nv4, "(*"+v4t+")"+"") + add("%#+v", v4, "("+v4t+")"+v4s) + add("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s) + add("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s) + add("%#+v", nv4, "(*"+v4t+")"+"") // Max int. v5 := int(2147483647) @@ -199,51 +285,59 @@ func addIntFormatterTests() { pv5 := &v5 v5Addr := fmt.Sprintf("%p", pv5) pv5Addr := fmt.Sprintf("%p", &pv5) - v5t := "int" + v5t := typeInt v5s := "2147483647" - addFormatterTest("%v", v5, v5s) - addFormatterTest("%v", pv5, "<*>"+v5s) - addFormatterTest("%v", &pv5, "<**>"+v5s) - addFormatterTest("%v", nv5, "") - addFormatterTest("%+v", v5, v5s) - addFormatterTest("%+v", pv5, "<*>("+v5Addr+")"+v5s) - addFormatterTest("%+v", &pv5, "<**>("+pv5Addr+"->"+v5Addr+")"+v5s) - addFormatterTest("%+v", nv5, "") - addFormatterTest("%#v", v5, "("+v5t+")"+v5s) - addFormatterTest("%#v", pv5, "(*"+v5t+")"+v5s) - addFormatterTest("%#v", &pv5, "(**"+v5t+")"+v5s) - addFormatterTest("%#v", nv5, "(*"+v5t+")"+"") - addFormatterTest("%#+v", v5, "("+v5t+")"+v5s) - addFormatterTest("%#+v", pv5, "(*"+v5t+")("+v5Addr+")"+v5s) - addFormatterTest("%#+v", &pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")"+v5s) - addFormatterTest("%#+v", nv5, "(*"+v5t+")"+"") + add("%v", v5, v5s) + add("%v", pv5, "<*>"+v5s) + add("%v", &pv5, "<**>"+v5s) + add("%v", nv5, "") + add("%+v", v5, v5s) + add("%+v", pv5, "<*>("+v5Addr+")"+v5s) + add("%+v", &pv5, "<**>("+pv5Addr+"->"+v5Addr+")"+v5s) + add("%+v", nv5, "") + add("%#v", v5, "("+v5t+")"+v5s) + add("%#v", pv5, "(*"+v5t+")"+v5s) + add("%#v", &pv5, "(**"+v5t+")"+v5s) + add("%#v", nv5, "(*"+v5t+")"+"") + add("%#+v", v5, "("+v5t+")"+v5s) + add("%#+v", pv5, "(*"+v5t+")("+v5Addr+")"+v5s) + add("%#+v", &pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")"+v5s) + add("%#+v", nv5, "(*"+v5t+")"+"") + + return tests } -func addUintFormatterTests() { +//nolint:dupl // int/uint test data follows the same pattern by design +func uintFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Max uint8. v := uint8(255) nv := (*uint8)(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "uint8" + vt := typeUint8 vs := "255" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") + add("%v", v, vs) + add("%v", pv, "<*>"+vs) + add("%v", &pv, "<**>"+vs) + add("%v", nv, "") + add("%+v", v, vs) + add("%+v", pv, "<*>("+vAddr+")"+vs) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + add("%+v", nv, "") + add("%#v", v, "("+vt+")"+vs) + add("%#v", pv, "(*"+vt+")"+vs) + add("%#v", &pv, "(**"+vt+")"+vs) + add("%#v", nv, "(*"+vt+")"+"") + add("%#+v", v, "("+vt+")"+vs) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + add("%#+v", nv, "(*"+vt+")"+"") // Max uint16. v2 := uint16(65535) @@ -253,22 +347,22 @@ func addUintFormatterTests() { pv2Addr := fmt.Sprintf("%p", &pv2) v2t := "uint16" v2s := "65535" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%v", nv2, "") - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") + add("%v", v2, v2s) + add("%v", pv2, "<*>"+v2s) + add("%v", &pv2, "<**>"+v2s) + add("%v", nv2, "") + add("%+v", v2, v2s) + add("%+v", pv2, "<*>("+v2Addr+")"+v2s) + add("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%+v", nv2, "") + add("%#v", v2, "("+v2t+")"+v2s) + add("%#v", pv2, "(*"+v2t+")"+v2s) + add("%#v", &pv2, "(**"+v2t+")"+v2s) + add("%#v", nv2, "(*"+v2t+")"+"") + add("%#+v", v2, "("+v2t+")"+v2s) + add("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + add("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%#+v", nv2, "(*"+v2t+")"+"") // Max uint32. v3 := uint32(4294967295) @@ -278,22 +372,22 @@ func addUintFormatterTests() { pv3Addr := fmt.Sprintf("%p", &pv3) v3t := "uint32" v3s := "4294967295" - addFormatterTest("%v", v3, v3s) - addFormatterTest("%v", pv3, "<*>"+v3s) - addFormatterTest("%v", &pv3, "<**>"+v3s) - addFormatterTest("%v", nv3, "") - addFormatterTest("%+v", v3, v3s) - addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) - addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%#v", v3, "("+v3t+")"+v3s) - addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s) - addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") - addFormatterTest("%#+v", v3, "("+v3t+")"+v3s) - addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s) - addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") + add("%v", v3, v3s) + add("%v", pv3, "<*>"+v3s) + add("%v", &pv3, "<**>"+v3s) + add("%v", nv3, "") + add("%+v", v3, v3s) + add("%+v", pv3, "<*>("+v3Addr+")"+v3s) + add("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) + add("%+v", nv3, "") + add("%#v", v3, "("+v3t+")"+v3s) + add("%#v", pv3, "(*"+v3t+")"+v3s) + add("%#v", &pv3, "(**"+v3t+")"+v3s) + add("%#v", nv3, "(*"+v3t+")"+"") + add("%#+v", v3, "("+v3t+")"+v3s) + add("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s) + add("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s) + add("%#v", nv3, "(*"+v3t+")"+"") // Max uint64. v4 := uint64(18446744073709551615) @@ -303,22 +397,22 @@ func addUintFormatterTests() { pv4Addr := fmt.Sprintf("%p", &pv4) v4t := "uint64" v4s := "18446744073709551615" - addFormatterTest("%v", v4, v4s) - addFormatterTest("%v", pv4, "<*>"+v4s) - addFormatterTest("%v", &pv4, "<**>"+v4s) - addFormatterTest("%v", nv4, "") - addFormatterTest("%+v", v4, v4s) - addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s) - addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s) - addFormatterTest("%+v", nv4, "") - addFormatterTest("%#v", v4, "("+v4t+")"+v4s) - addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s) - addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s) - addFormatterTest("%#v", nv4, "(*"+v4t+")"+"") - addFormatterTest("%#+v", v4, "("+v4t+")"+v4s) - addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s) - addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s) - addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"") + add("%v", v4, v4s) + add("%v", pv4, "<*>"+v4s) + add("%v", &pv4, "<**>"+v4s) + add("%v", nv4, "") + add("%+v", v4, v4s) + add("%+v", pv4, "<*>("+v4Addr+")"+v4s) + add("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s) + add("%+v", nv4, "") + add("%#v", v4, "("+v4t+")"+v4s) + add("%#v", pv4, "(*"+v4t+")"+v4s) + add("%#v", &pv4, "(**"+v4t+")"+v4s) + add("%#v", nv4, "(*"+v4t+")"+"") + add("%#+v", v4, "("+v4t+")"+v4s) + add("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s) + add("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s) + add("%#+v", nv4, "(*"+v4t+")"+"") // Max uint. v5 := uint(4294967295) @@ -326,74 +420,88 @@ func addUintFormatterTests() { pv5 := &v5 v5Addr := fmt.Sprintf("%p", pv5) pv5Addr := fmt.Sprintf("%p", &pv5) - v5t := "uint" + v5t := typeUint v5s := "4294967295" - addFormatterTest("%v", v5, v5s) - addFormatterTest("%v", pv5, "<*>"+v5s) - addFormatterTest("%v", &pv5, "<**>"+v5s) - addFormatterTest("%v", nv5, "") - addFormatterTest("%+v", v5, v5s) - addFormatterTest("%+v", pv5, "<*>("+v5Addr+")"+v5s) - addFormatterTest("%+v", &pv5, "<**>("+pv5Addr+"->"+v5Addr+")"+v5s) - addFormatterTest("%+v", nv5, "") - addFormatterTest("%#v", v5, "("+v5t+")"+v5s) - addFormatterTest("%#v", pv5, "(*"+v5t+")"+v5s) - addFormatterTest("%#v", &pv5, "(**"+v5t+")"+v5s) - addFormatterTest("%#v", nv5, "(*"+v5t+")"+"") - addFormatterTest("%#+v", v5, "("+v5t+")"+v5s) - addFormatterTest("%#+v", pv5, "(*"+v5t+")("+v5Addr+")"+v5s) - addFormatterTest("%#+v", &pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")"+v5s) - addFormatterTest("%#v", nv5, "(*"+v5t+")"+"") + add("%v", v5, v5s) + add("%v", pv5, "<*>"+v5s) + add("%v", &pv5, "<**>"+v5s) + add("%v", nv5, "") + add("%+v", v5, v5s) + add("%+v", pv5, "<*>("+v5Addr+")"+v5s) + add("%+v", &pv5, "<**>("+pv5Addr+"->"+v5Addr+")"+v5s) + add("%+v", nv5, "") + add("%#v", v5, "("+v5t+")"+v5s) + add("%#v", pv5, "(*"+v5t+")"+v5s) + add("%#v", &pv5, "(**"+v5t+")"+v5s) + add("%#v", nv5, "(*"+v5t+")"+"") + add("%#+v", v5, "("+v5t+")"+v5s) + add("%#+v", pv5, "(*"+v5t+")("+v5Addr+")"+v5s) + add("%#+v", &pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")"+v5s) + add("%#v", nv5, "(*"+v5t+")"+"") + + return tests } -func addBoolFormatterTests() { +func boolFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Boolean true. v := bool(true) nv := (*bool)(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "bool" + vt := typeBool vs := "true" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") + add("%v", v, vs) + add("%v", pv, "<*>"+vs) + add("%v", &pv, "<**>"+vs) + add("%v", nv, "") + add("%+v", v, vs) + add("%+v", pv, "<*>("+vAddr+")"+vs) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + add("%+v", nv, "") + add("%#v", v, "("+vt+")"+vs) + add("%#v", pv, "(*"+vt+")"+vs) + add("%#v", &pv, "(**"+vt+")"+vs) + add("%#v", nv, "(*"+vt+")"+"") + add("%#+v", v, "("+vt+")"+vs) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + add("%#+v", nv, "(*"+vt+")"+"") // Boolean false. v2 := bool(false) pv2 := &v2 v2Addr := fmt.Sprintf("%p", pv2) pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "bool" + v2t := typeBool v2s := "false" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%v", v2, v2s) + add("%v", pv2, "<*>"+v2s) + add("%v", &pv2, "<**>"+v2s) + add("%+v", v2, v2s) + add("%+v", pv2, "<*>("+v2Addr+")"+v2s) + add("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%#v", v2, "("+v2t+")"+v2s) + add("%#v", pv2, "(*"+v2t+")"+v2s) + add("%#v", &pv2, "(**"+v2t+")"+v2s) + add("%#+v", v2, "("+v2t+")"+v2s) + add("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + add("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + + return tests } -func addFloatFormatterTests() { +func floatFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Standard float32. v := float32(3.1415) nv := (*float32)(nil) @@ -402,22 +510,22 @@ func addFloatFormatterTests() { pvAddr := fmt.Sprintf("%p", &pv) vt := "float32" vs := "3.1415" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") + add("%v", v, vs) + add("%v", pv, "<*>"+vs) + add("%v", &pv, "<**>"+vs) + add("%v", nv, "") + add("%+v", v, vs) + add("%+v", pv, "<*>("+vAddr+")"+vs) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + add("%+v", nv, "") + add("%#v", v, "("+vt+")"+vs) + add("%#v", pv, "(*"+vt+")"+vs) + add("%#v", &pv, "(**"+vt+")"+vs) + add("%#v", nv, "(*"+vt+")"+"") + add("%#+v", v, "("+vt+")"+vs) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + add("%#+v", nv, "(*"+vt+")"+"") // Standard float64. v2 := float64(3.1415926) @@ -427,25 +535,32 @@ func addFloatFormatterTests() { pv2Addr := fmt.Sprintf("%p", &pv2) v2t := "float64" v2s := "3.1415926" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") + add("%v", v2, v2s) + add("%v", pv2, "<*>"+v2s) + add("%v", &pv2, "<**>"+v2s) + add("%+v", nv2, "") + add("%+v", v2, v2s) + add("%+v", pv2, "<*>("+v2Addr+")"+v2s) + add("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%+v", nv2, "") + add("%#v", v2, "("+v2t+")"+v2s) + add("%#v", pv2, "(*"+v2t+")"+v2s) + add("%#v", &pv2, "(**"+v2t+")"+v2s) + add("%#v", nv2, "(*"+v2t+")"+"") + add("%#+v", v2, "("+v2t+")"+v2s) + add("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + add("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%#+v", nv2, "(*"+v2t+")"+"") + + return tests } -func addComplexFormatterTests() { +func complexFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Standard complex64. v := complex(float32(6), -2) nv := (*complex64)(nil) @@ -454,22 +569,22 @@ func addComplexFormatterTests() { pvAddr := fmt.Sprintf("%p", &pv) vt := "complex64" vs := "(6-2i)" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") + add("%v", v, vs) + add("%v", pv, "<*>"+vs) + add("%v", &pv, "<**>"+vs) + add("%+v", nv, "") + add("%+v", v, vs) + add("%+v", pv, "<*>("+vAddr+")"+vs) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + add("%+v", nv, "") + add("%#v", v, "("+vt+")"+vs) + add("%#v", pv, "(*"+vt+")"+vs) + add("%#v", &pv, "(**"+vt+")"+vs) + add("%#v", nv, "(*"+vt+")"+"") + add("%#+v", v, "("+vt+")"+vs) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + add("%#+v", nv, "(*"+vt+")"+"") // Standard complex128. v2 := complex(float64(-6), 2) @@ -479,25 +594,32 @@ func addComplexFormatterTests() { pv2Addr := fmt.Sprintf("%p", &pv2) v2t := "complex128" v2s := "(-6+2i)" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") + add("%v", v2, v2s) + add("%v", pv2, "<*>"+v2s) + add("%v", &pv2, "<**>"+v2s) + add("%+v", nv2, "") + add("%+v", v2, v2s) + add("%+v", pv2, "<*>("+v2Addr+")"+v2s) + add("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%+v", nv2, "") + add("%#v", v2, "("+v2t+")"+v2s) + add("%#v", pv2, "(*"+v2t+")"+v2s) + add("%#v", &pv2, "(**"+v2t+")"+v2s) + add("%#v", nv2, "(*"+v2t+")"+"") + add("%#+v", v2, "("+v2t+")"+v2s) + add("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + add("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%#+v", nv2, "(*"+v2t+")"+"") + + return tests } -func addArrayFormatterTests() { +func arrayFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Array containing standard ints. v := [3]int{1, 2, 3} nv := (*[3]int)(nil) @@ -506,22 +628,22 @@ func addArrayFormatterTests() { pvAddr := fmt.Sprintf("%p", &pv) vt := "[3]int" vs := "[1 2 3]" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") + add("%v", v, vs) + add("%v", pv, "<*>"+vs) + add("%v", &pv, "<**>"+vs) + add("%+v", nv, "") + add("%+v", v, vs) + add("%+v", pv, "<*>("+vAddr+")"+vs) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + add("%+v", nv, "") + add("%#v", v, "("+vt+")"+vs) + add("%#v", pv, "(*"+vt+")"+vs) + add("%#v", &pv, "(**"+vt+")"+vs) + add("%#v", nv, "(*"+vt+")"+"") + add("%#+v", v, "("+vt+")"+vs) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + add("%#+v", nv, "(*"+vt+")"+"") // Array containing type with custom formatter on pointer receiver only. v2 := [3]pstringer{"1", "2", "3"} @@ -535,22 +657,22 @@ func addArrayFormatterTests() { if spew.UnsafeDisabled { v2s = "[1 2 3]" } - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2sp) - addFormatterTest("%v", &pv2, "<**>"+v2sp) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2sp) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2sp) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2sp) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2sp) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2sp) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2sp) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") + add("%v", v2, v2s) + add("%v", pv2, "<*>"+v2sp) + add("%v", &pv2, "<**>"+v2sp) + add("%+v", nv2, "") + add("%+v", v2, v2s) + add("%+v", pv2, "<*>("+v2Addr+")"+v2sp) + add("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2sp) + add("%+v", nv2, "") + add("%#v", v2, "("+v2t+")"+v2s) + add("%#v", pv2, "(*"+v2t+")"+v2sp) + add("%#v", &pv2, "(**"+v2t+")"+v2sp) + add("%#v", nv2, "(*"+v2t+")"+"") + add("%#+v", v2, "("+v2t+")"+v2s) + add("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2sp) + add("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2sp) + add("%#+v", nv2, "(*"+v2t+")"+"") // Array containing interfaces. v3 := [3]any{"one", int(2), uint(3)} @@ -558,31 +680,38 @@ func addArrayFormatterTests() { pv3 := &v3 v3Addr := fmt.Sprintf("%p", pv3) pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "[3]interface {}" - v3t2 := "string" - v3t3 := "int" - v3t4 := "uint" + v3t := "[3]" + typeInterface + v3t2 := typeString + v3t3 := typeInt + v3t4 := typeUint v3s := "[one 2 3]" v3s2 := "[(" + v3t2 + ")one (" + v3t3 + ")2 (" + v3t4 + ")3]" - addFormatterTest("%v", v3, v3s) - addFormatterTest("%v", pv3, "<*>"+v3s) - addFormatterTest("%v", &pv3, "<**>"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%+v", v3, v3s) - addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) - addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%#v", v3, "("+v3t+")"+v3s2) - addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s2) - addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s2) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") - addFormatterTest("%#+v", v3, "("+v3t+")"+v3s2) - addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2) - addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2) - addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"") + add("%v", v3, v3s) + add("%v", pv3, "<*>"+v3s) + add("%v", &pv3, "<**>"+v3s) + add("%+v", nv3, "") + add("%+v", v3, v3s) + add("%+v", pv3, "<*>("+v3Addr+")"+v3s) + add("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) + add("%+v", nv3, "") + add("%#v", v3, "("+v3t+")"+v3s2) + add("%#v", pv3, "(*"+v3t+")"+v3s2) + add("%#v", &pv3, "(**"+v3t+")"+v3s2) + add("%#v", nv3, "(*"+v3t+")"+"") + add("%#+v", v3, "("+v3t+")"+v3s2) + add("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2) + add("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2) + add("%#+v", nv3, "(*"+v3t+")"+"") + + return tests } -func addSliceFormatterTests() { +func sliceFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Slice containing standard float32 values. v := []float32{3.14, 6.28, 12.56} nv := (*[]float32)(nil) @@ -591,22 +720,22 @@ func addSliceFormatterTests() { pvAddr := fmt.Sprintf("%p", &pv) vt := "[]float32" vs := "[3.14 6.28 12.56]" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") + add("%v", v, vs) + add("%v", pv, "<*>"+vs) + add("%v", &pv, "<**>"+vs) + add("%+v", nv, "") + add("%+v", v, vs) + add("%+v", pv, "<*>("+vAddr+")"+vs) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + add("%+v", nv, "") + add("%#v", v, "("+vt+")"+vs) + add("%#v", pv, "(*"+vt+")"+vs) + add("%#v", &pv, "(**"+vt+")"+vs) + add("%#v", nv, "(*"+vt+")"+"") + add("%#+v", v, "("+vt+")"+vs) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + add("%#+v", nv, "(*"+vt+")"+"") // Slice containing type with custom formatter on pointer receiver only. v2 := []pstringer{"1", "2", "3"} @@ -616,22 +745,22 @@ func addSliceFormatterTests() { pv2Addr := fmt.Sprintf("%p", &pv2) v2t := "[]spew_test.pstringer" v2s := "[stringer 1 stringer 2 stringer 3]" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") + add("%v", v2, v2s) + add("%v", pv2, "<*>"+v2s) + add("%v", &pv2, "<**>"+v2s) + add("%+v", nv2, "") + add("%+v", v2, v2s) + add("%+v", pv2, "<*>("+v2Addr+")"+v2s) + add("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%+v", nv2, "") + add("%#v", v2, "("+v2t+")"+v2s) + add("%#v", pv2, "(*"+v2t+")"+v2s) + add("%#v", &pv2, "(**"+v2t+")"+v2s) + add("%#v", nv2, "(*"+v2t+")"+"") + add("%#+v", v2, "("+v2t+")"+v2s) + add("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + add("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%#+v", nv2, "(*"+v2t+")"+"") // Slice containing interfaces. v3 := []any{"one", int(2), uint(3), nil} @@ -639,30 +768,30 @@ func addSliceFormatterTests() { pv3 := &v3 v3Addr := fmt.Sprintf("%p", pv3) pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "[]interface {}" - v3t2 := "string" - v3t3 := "int" - v3t4 := "uint" - v3t5 := "interface {}" + v3t := "[]" + typeInterface + v3t2 := typeString + v3t3 := typeInt + v3t4 := typeUint + v3t5 := typeInterface v3s := "[one 2 3 ]" v3s2 := "[(" + v3t2 + ")one (" + v3t3 + ")2 (" + v3t4 + ")3 (" + v3t5 + ")]" - addFormatterTest("%v", v3, v3s) - addFormatterTest("%v", pv3, "<*>"+v3s) - addFormatterTest("%v", &pv3, "<**>"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%+v", v3, v3s) - addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) - addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%#v", v3, "("+v3t+")"+v3s2) - addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s2) - addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s2) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") - addFormatterTest("%#+v", v3, "("+v3t+")"+v3s2) - addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2) - addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2) - addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"") + add("%v", v3, v3s) + add("%v", pv3, "<*>"+v3s) + add("%v", &pv3, "<**>"+v3s) + add("%+v", nv3, "") + add("%+v", v3, v3s) + add("%+v", pv3, "<*>("+v3Addr+")"+v3s) + add("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) + add("%+v", nv3, "") + add("%#v", v3, "("+v3t+")"+v3s2) + add("%#v", pv3, "(*"+v3t+")"+v3s2) + add("%#v", &pv3, "(**"+v3t+")"+v3s2) + add("%#v", nv3, "(*"+v3t+")"+"") + add("%#+v", v3, "("+v3t+")"+v3s2) + add("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2) + add("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2) + add("%#+v", nv3, "(*"+v3t+")"+"") // Nil slice. var v4 []int @@ -671,77 +800,91 @@ func addSliceFormatterTests() { v4Addr := fmt.Sprintf("%p", pv4) pv4Addr := fmt.Sprintf("%p", &pv4) v4t := "[]int" - v4s := "" - addFormatterTest("%v", v4, v4s) - addFormatterTest("%v", pv4, "<*>"+v4s) - addFormatterTest("%v", &pv4, "<**>"+v4s) - addFormatterTest("%+v", nv4, "") - addFormatterTest("%+v", v4, v4s) - addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s) - addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s) - addFormatterTest("%+v", nv4, "") - addFormatterTest("%#v", v4, "("+v4t+")"+v4s) - addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s) - addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s) - addFormatterTest("%#v", nv4, "(*"+v4t+")"+"") - addFormatterTest("%#+v", v4, "("+v4t+")"+v4s) - addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s) - addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s) - addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"") + v4s := typeNil + add("%v", v4, v4s) + add("%v", pv4, "<*>"+v4s) + add("%v", &pv4, "<**>"+v4s) + add("%+v", nv4, "") + add("%+v", v4, v4s) + add("%+v", pv4, "<*>("+v4Addr+")"+v4s) + add("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s) + add("%+v", nv4, "") + add("%#v", v4, "("+v4t+")"+v4s) + add("%#v", pv4, "(*"+v4t+")"+v4s) + add("%#v", &pv4, "(**"+v4t+")"+v4s) + add("%#v", nv4, "(*"+v4t+")"+"") + add("%#+v", v4, "("+v4t+")"+v4s) + add("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s) + add("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s) + add("%#+v", nv4, "(*"+v4t+")"+"") + + return tests } -func addStringFormatterTests() { +func stringFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Standard string. v := "test" nv := (*string)(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "string" + vt := typeString vs := "test" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") + add("%v", v, vs) + add("%v", pv, "<*>"+vs) + add("%v", &pv, "<**>"+vs) + add("%+v", nv, "") + add("%+v", v, vs) + add("%+v", pv, "<*>("+vAddr+")"+vs) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + add("%+v", nv, "") + add("%#v", v, "("+vt+")"+vs) + add("%#v", pv, "(*"+vt+")"+vs) + add("%#v", &pv, "(**"+vt+")"+vs) + add("%#v", nv, "(*"+vt+")"+"") + add("%#+v", v, "("+vt+")"+vs) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + add("%#+v", nv, "(*"+vt+")"+"") + + return tests } -func addInterfaceFormatterTests() { +func interfaceFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Nil interface. var v any nv := (*any)(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "interface {}" - vs := "" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") + vt := typeInterface + vs := typeNil + add("%v", v, vs) + add("%v", pv, "<*>"+vs) + add("%v", &pv, "<**>"+vs) + add("%+v", nv, "") + add("%+v", v, vs) + add("%+v", pv, "<*>("+vAddr+")"+vs) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + add("%+v", nv, "") + add("%#v", v, "("+vt+")"+vs) + add("%#v", pv, "(*"+vt+")"+vs) + add("%#v", &pv, "(**"+vt+")"+vs) + add("%#v", nv, "(*"+vt+")"+"") + add("%#+v", v, "("+vt+")"+vs) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + add("%#+v", nv, "(*"+vt+")"+"") // Sub-interface. v2 := any(uint16(65535)) @@ -750,21 +893,28 @@ func addInterfaceFormatterTests() { pv2Addr := fmt.Sprintf("%p", &pv2) v2t := "uint16" v2s := "65535" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%v", v2, v2s) + add("%v", pv2, "<*>"+v2s) + add("%v", &pv2, "<**>"+v2s) + add("%+v", v2, v2s) + add("%+v", pv2, "<*>("+v2Addr+")"+v2s) + add("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%#v", v2, "("+v2t+")"+v2s) + add("%#v", pv2, "(*"+v2t+")"+v2s) + add("%#v", &pv2, "(**"+v2t+")"+v2s) + add("%#+v", v2, "("+v2t+")"+v2s) + add("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + add("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + + return tests } -func addMapFormatterTests() { +func mapFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Map with string keys and int vals. v := map[string]int{"one": 1, "two": 2} nilMap := map[string]int(nil) @@ -775,29 +925,29 @@ func addMapFormatterTests() { vt := "map[string]int" vs := "map[one:1 two:2]" vs2 := "map[two:2 one:1]" - addFormatterTest("%v", v, vs, vs2) - addFormatterTest("%v", pv, "<*>"+vs, "<*>"+vs2) - addFormatterTest("%v", &pv, "<**>"+vs, "<**>"+vs2) - addFormatterTest("%+v", nilMap, "") - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs, vs2) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs, "<*>("+vAddr+")"+vs2) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs, + add("%v", v, vs, vs2) + add("%v", pv, "<*>"+vs, "<*>"+vs2) + add("%v", &pv, "<**>"+vs, "<**>"+vs2) + add("%+v", nilMap, "") + add("%+v", nv, "") + add("%+v", v, vs, vs2) + add("%+v", pv, "<*>("+vAddr+")"+vs, "<*>("+vAddr+")"+vs2) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs, "<**>("+pvAddr+"->"+vAddr+")"+vs2) - addFormatterTest("%+v", nilMap, "") - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs, "("+vt+")"+vs2) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs, "(*"+vt+")"+vs2) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs, "(**"+vt+")"+vs2) - addFormatterTest("%#v", nilMap, "("+vt+")"+"") - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs, "("+vt+")"+vs2) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs, + add("%+v", nilMap, "") + add("%+v", nv, "") + add("%#v", v, "("+vt+")"+vs, "("+vt+")"+vs2) + add("%#v", pv, "(*"+vt+")"+vs, "(*"+vt+")"+vs2) + add("%#v", &pv, "(**"+vt+")"+vs, "(**"+vt+")"+vs2) + add("%#v", nilMap, "("+vt+")"+"") + add("%#v", nv, "(*"+vt+")"+"") + add("%#+v", v, "("+vt+")"+vs, "("+vt+")"+vs2) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs, "(*"+vt+")("+vAddr+")"+vs2) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs, + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs2) - addFormatterTest("%#+v", nilMap, "("+vt+")"+"") - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") + add("%#+v", nilMap, "("+vt+")"+"") + add("%#+v", nv, "(*"+vt+")"+"") // Map with custom formatter type on pointer receiver only keys and vals. v2 := map[pstringer]pstringer{"one": "1"} @@ -810,22 +960,22 @@ func addMapFormatterTests() { if spew.UnsafeDisabled { v2s = "map[one:1]" } - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") + add("%v", v2, v2s) + add("%v", pv2, "<*>"+v2s) + add("%v", &pv2, "<**>"+v2s) + add("%+v", nv2, "") + add("%+v", v2, v2s) + add("%+v", pv2, "<*>("+v2Addr+")"+v2s) + add("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%+v", nv2, "") + add("%#v", v2, "("+v2t+")"+v2s) + add("%#v", pv2, "(*"+v2t+")"+v2s) + add("%#v", &pv2, "(**"+v2t+")"+v2s) + add("%#v", nv2, "(*"+v2t+")"+"") + add("%#+v", v2, "("+v2t+")"+v2s) + add("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + add("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%#+v", nv2, "(*"+v2t+")"+"") // Map with interface keys and values. v3 := map[any]any{"one": 1} @@ -833,27 +983,27 @@ func addMapFormatterTests() { pv3 := &v3 v3Addr := fmt.Sprintf("%p", pv3) pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "map[interface {}]interface {}" - v3t1 := "string" - v3t2 := "int" + v3t := "map[" + typeInterface + "]" + typeInterface + v3t1 := typeString + v3t2 := typeInt v3s := "map[one:1]" v3s2 := "map[(" + v3t1 + ")one:(" + v3t2 + ")1]" - addFormatterTest("%v", v3, v3s) - addFormatterTest("%v", pv3, "<*>"+v3s) - addFormatterTest("%v", &pv3, "<**>"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%+v", v3, v3s) - addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) - addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%#v", v3, "("+v3t+")"+v3s2) - addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s2) - addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s2) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") - addFormatterTest("%#+v", v3, "("+v3t+")"+v3s2) - addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2) - addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2) - addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"") + add("%v", v3, v3s) + add("%v", pv3, "<*>"+v3s) + add("%v", &pv3, "<**>"+v3s) + add("%+v", nv3, "") + add("%+v", v3, v3s) + add("%+v", pv3, "<*>("+v3Addr+")"+v3s) + add("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) + add("%+v", nv3, "") + add("%#v", v3, "("+v3t+")"+v3s2) + add("%#v", pv3, "(*"+v3t+")"+v3s2) + add("%#v", &pv3, "(**"+v3t+")"+v3s2) + add("%#v", nv3, "(*"+v3t+")"+"") + add("%#+v", v3, "("+v3t+")"+v3s2) + add("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2) + add("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2) + add("%#+v", nv3, "(*"+v3t+")"+"") // Map with nil interface value v4 := map[string]any{"nil": nil} @@ -861,29 +1011,36 @@ func addMapFormatterTests() { pv4 := &v4 v4Addr := fmt.Sprintf("%p", pv4) pv4Addr := fmt.Sprintf("%p", &pv4) - v4t := "map[string]interface {}" - v4t1 := "interface {}" + v4t := "map[string]" + typeInterface + v4t1 := typeInterface v4s := "map[nil:]" v4s2 := "map[nil:(" + v4t1 + ")]" - addFormatterTest("%v", v4, v4s) - addFormatterTest("%v", pv4, "<*>"+v4s) - addFormatterTest("%v", &pv4, "<**>"+v4s) - addFormatterTest("%+v", nv4, "") - addFormatterTest("%+v", v4, v4s) - addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s) - addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s) - addFormatterTest("%+v", nv4, "") - addFormatterTest("%#v", v4, "("+v4t+")"+v4s2) - addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s2) - addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s2) - addFormatterTest("%#v", nv4, "(*"+v4t+")"+"") - addFormatterTest("%#+v", v4, "("+v4t+")"+v4s2) - addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s2) - addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s2) - addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"") + add("%v", v4, v4s) + add("%v", pv4, "<*>"+v4s) + add("%v", &pv4, "<**>"+v4s) + add("%+v", nv4, "") + add("%+v", v4, v4s) + add("%+v", pv4, "<*>("+v4Addr+")"+v4s) + add("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s) + add("%+v", nv4, "") + add("%#v", v4, "("+v4t+")"+v4s2) + add("%#v", pv4, "(*"+v4t+")"+v4s2) + add("%#v", &pv4, "(**"+v4t+")"+v4s2) + add("%#v", nv4, "(*"+v4t+")"+"") + add("%#+v", v4, "("+v4t+")"+v4s2) + add("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s2) + add("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s2) + add("%#+v", nv4, "(*"+v4t+")"+"") + + return tests } -func addStructFormatterTests() { +func structFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Struct with primitives. type s1 struct { a int8 @@ -895,27 +1052,27 @@ func addStructFormatterTests() { vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) vt := "spew_test.s1" - vt2 := "int8" - vt3 := "uint8" + vt2 := typeInt8 + vt3 := typeUint8 vs := "{127 255}" vs2 := "{a:127 b:255}" vs3 := "{a:(" + vt2 + ")127 b:(" + vt3 + ")255}" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs2) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs2) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs2) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs3) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs3) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs3) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs3) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs3) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs3) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") + add("%v", v, vs) + add("%v", pv, "<*>"+vs) + add("%v", &pv, "<**>"+vs) + add("%+v", nv, "") + add("%+v", v, vs2) + add("%+v", pv, "<*>("+vAddr+")"+vs2) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs2) + add("%+v", nv, "") + add("%#v", v, "("+vt+")"+vs3) + add("%#v", pv, "(*"+vt+")"+vs3) + add("%#v", &pv, "(**"+vt+")"+vs3) + add("%#v", nv, "(*"+vt+")"+"") + add("%#+v", v, "("+vt+")"+vs3) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs3) + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs3) + add("%#+v", nv, "(*"+vt+")"+"") // Struct that contains another struct. type s2 struct { @@ -929,29 +1086,29 @@ func addStructFormatterTests() { pv2Addr := fmt.Sprintf("%p", &pv2) v2t := "spew_test.s2" v2t2 := "spew_test.s1" - v2t3 := "int8" - v2t4 := "uint8" - v2t5 := "bool" + v2t3 := typeInt8 + v2t4 := typeUint8 + v2t5 := typeBool v2s := "{{127 255} true}" v2s2 := "{s1:{a:127 b:255} b:true}" v2s3 := "{s1:(" + v2t2 + "){a:(" + v2t3 + ")127 b:(" + v2t4 + ")255} b:(" + v2t5 + ")true}" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%+v", v2, v2s2) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s2) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s2) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s3) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s3) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s3) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s3) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s3) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s3) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") + add("%v", v2, v2s) + add("%v", pv2, "<*>"+v2s) + add("%v", &pv2, "<**>"+v2s) + add("%+v", nv2, "") + add("%+v", v2, v2s2) + add("%+v", pv2, "<*>("+v2Addr+")"+v2s2) + add("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s2) + add("%+v", nv2, "") + add("%#v", v2, "("+v2t+")"+v2s3) + add("%#v", pv2, "(*"+v2t+")"+v2s3) + add("%#v", &pv2, "(**"+v2t+")"+v2s3) + add("%#v", nv2, "(*"+v2t+")"+"") + add("%#+v", v2, "("+v2t+")"+v2s3) + add("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s3) + add("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s3) + add("%#+v", nv2, "(*"+v2t+")"+"") // Struct that contains custom type with Stringer pointer interface via both // exported and unexported fields. @@ -980,22 +1137,22 @@ func addStructFormatterTests() { v3s3 = "{s:(" + v3t2 + ")test S:(" + v3t2 + ")test2}" v3s3p = "{s:(" + v3t2 + ")test S:(" + v3t2 + ")stringer test2}" } - addFormatterTest("%v", v3, v3s) - addFormatterTest("%v", pv3, "<*>"+v3sp) - addFormatterTest("%v", &pv3, "<**>"+v3sp) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%+v", v3, v3s2) - addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s2p) - addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s2p) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%#v", v3, "("+v3t+")"+v3s3) - addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s3p) - addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s3p) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") - addFormatterTest("%#+v", v3, "("+v3t+")"+v3s3) - addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s3p) - addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s3p) - addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"") + add("%v", v3, v3s) + add("%v", pv3, "<*>"+v3sp) + add("%v", &pv3, "<**>"+v3sp) + add("%+v", nv3, "") + add("%+v", v3, v3s2) + add("%+v", pv3, "<*>("+v3Addr+")"+v3s2p) + add("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s2p) + add("%+v", nv3, "") + add("%#v", v3, "("+v3t+")"+v3s3) + add("%#v", pv3, "(*"+v3t+")"+v3s3p) + add("%#v", &pv3, "(**"+v3t+")"+v3s3p) + add("%#v", nv3, "(*"+v3t+")"+"") + add("%#+v", v3, "("+v3t+")"+v3s3) + add("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s3p) + add("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s3p) + add("%#+v", nv3, "(*"+v3t+")"+"") // Struct that contains embedded struct and field to same struct. e := embed{"embedstr"} @@ -1007,7 +1164,7 @@ func addStructFormatterTests() { pv4Addr := fmt.Sprintf("%p", &pv4) v4t := "spew_test.embedwrap" v4t2 := "spew_test.embed" - v4t3 := "string" + v4t3 := typeString v4s := "{<*>{embedstr} <*>{embedstr}}" v4s2 := "{embed:<*>(" + eAddr + "){a:embedstr} e:<*>(" + eAddr + "){a:embedstr}}" @@ -1015,25 +1172,32 @@ func addStructFormatterTests() { "){a:(" + v4t3 + ")embedstr}}" v4s4 := "{embed:(*" + v4t2 + ")(" + eAddr + "){a:(" + v4t3 + ")embedstr} e:(*" + v4t2 + ")(" + eAddr + "){a:(" + v4t3 + ")embedstr}}" - addFormatterTest("%v", v4, v4s) - addFormatterTest("%v", pv4, "<*>"+v4s) - addFormatterTest("%v", &pv4, "<**>"+v4s) - addFormatterTest("%+v", nv4, "") - addFormatterTest("%+v", v4, v4s2) - addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s2) - addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s2) - addFormatterTest("%+v", nv4, "") - addFormatterTest("%#v", v4, "("+v4t+")"+v4s3) - addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s3) - addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s3) - addFormatterTest("%#v", nv4, "(*"+v4t+")"+"") - addFormatterTest("%#+v", v4, "("+v4t+")"+v4s4) - addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s4) - addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s4) - addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"") + add("%v", v4, v4s) + add("%v", pv4, "<*>"+v4s) + add("%v", &pv4, "<**>"+v4s) + add("%+v", nv4, "") + add("%+v", v4, v4s2) + add("%+v", pv4, "<*>("+v4Addr+")"+v4s2) + add("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s2) + add("%+v", nv4, "") + add("%#v", v4, "("+v4t+")"+v4s3) + add("%#v", pv4, "(*"+v4t+")"+v4s3) + add("%#v", &pv4, "(**"+v4t+")"+v4s3) + add("%#v", nv4, "(*"+v4t+")"+"") + add("%#+v", v4, "("+v4t+")"+v4s4) + add("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s4) + add("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s4) + add("%#+v", nv4, "(*"+v4t+")"+"") + + return tests } -func addUintptrFormatterTests() { +func uintptrFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Null pointer. v := uintptr(0) nv := (*uintptr)(nil) @@ -1041,23 +1205,23 @@ func addUintptrFormatterTests() { vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) vt := "uintptr" - vs := "" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") + vs := typeNil + add("%v", v, vs) + add("%v", pv, "<*>"+vs) + add("%v", &pv, "<**>"+vs) + add("%+v", nv, "") + add("%+v", v, vs) + add("%+v", pv, "<*>("+vAddr+")"+vs) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + add("%+v", nv, "") + add("%#v", v, "("+vt+")"+vs) + add("%#v", pv, "(*"+vt+")"+vs) + add("%#v", &pv, "(**"+vt+")"+vs) + add("%#v", nv, "(*"+vt+")"+"") + add("%#+v", v, "("+vt+")"+vs) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + add("%#+v", nv, "(*"+vt+")"+"") // Address of real variable. i := 1 @@ -1067,21 +1231,28 @@ func addUintptrFormatterTests() { pv2Addr := fmt.Sprintf("%p", &pv2) v2t := "uintptr" v2s := fmt.Sprintf("%p", &i) - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%v", v2, v2s) + add("%v", pv2, "<*>"+v2s) + add("%v", &pv2, "<**>"+v2s) + add("%+v", v2, v2s) + add("%+v", pv2, "<*>("+v2Addr+")"+v2s) + add("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%#v", v2, "("+v2t+")"+v2s) + add("%#v", pv2, "(*"+v2t+")"+v2s) + add("%#v", &pv2, "(**"+v2t+")"+v2s) + add("%#+v", v2, "("+v2t+")"+v2s) + add("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + add("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + + return tests } -func addUnsafePointerFormatterTests() { +func unsafePointerFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Null pointer. v := unsafe.Pointer(nil) nv := (*unsafe.Pointer)(nil) @@ -1089,23 +1260,23 @@ func addUnsafePointerFormatterTests() { vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) vt := "unsafe.Pointer" - vs := "" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") + vs := typeNil + add("%v", v, vs) + add("%v", pv, "<*>"+vs) + add("%v", &pv, "<**>"+vs) + add("%+v", nv, "") + add("%+v", v, vs) + add("%+v", pv, "<*>("+vAddr+")"+vs) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + add("%+v", nv, "") + add("%#v", v, "("+vt+")"+vs) + add("%#v", pv, "(*"+vt+")"+vs) + add("%#v", &pv, "(**"+vt+")"+vs) + add("%#v", nv, "(*"+vt+")"+"") + add("%#+v", v, "("+vt+")"+vs) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + add("%#+v", nv, "(*"+vt+")"+"") // Address of real variable. i := 1 @@ -1115,21 +1286,28 @@ func addUnsafePointerFormatterTests() { pv2Addr := fmt.Sprintf("%p", &pv2) v2t := "unsafe.Pointer" v2s := fmt.Sprintf("%p", &i) - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%v", v2, v2s) + add("%v", pv2, "<*>"+v2s) + add("%v", &pv2, "<**>"+v2s) + add("%+v", v2, v2s) + add("%+v", pv2, "<*>("+v2Addr+")"+v2s) + add("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%#v", v2, "("+v2t+")"+v2s) + add("%#v", pv2, "(*"+v2t+")"+v2s) + add("%#v", &pv2, "(**"+v2t+")"+v2s) + add("%#+v", v2, "("+v2t+")"+v2s) + add("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + add("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + + return tests } -func addChanFormatterTests() { +func chanFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Nil channel. var v chan int pv := &v @@ -1137,23 +1315,23 @@ func addChanFormatterTests() { vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) vt := "chan int" - vs := "" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") + vs := typeNil + add("%v", v, vs) + add("%v", pv, "<*>"+vs) + add("%v", &pv, "<**>"+vs) + add("%+v", nv, "") + add("%+v", v, vs) + add("%+v", pv, "<*>("+vAddr+")"+vs) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + add("%+v", nv, "") + add("%#v", v, "("+vt+")"+vs) + add("%#v", pv, "(*"+vt+")"+vs) + add("%#v", &pv, "(**"+vt+")"+vs) + add("%#v", nv, "(*"+vt+")"+"") + add("%#+v", v, "("+vt+")"+vs) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + add("%#+v", nv, "(*"+vt+")"+"") // Real channel. v2 := make(chan int) @@ -1162,45 +1340,52 @@ func addChanFormatterTests() { pv2Addr := fmt.Sprintf("%p", &pv2) v2t := "chan int" v2s := fmt.Sprintf("%p", v2) - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%v", v2, v2s) + add("%v", pv2, "<*>"+v2s) + add("%v", &pv2, "<**>"+v2s) + add("%+v", v2, v2s) + add("%+v", pv2, "<*>("+v2Addr+")"+v2s) + add("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%#v", v2, "("+v2t+")"+v2s) + add("%#v", pv2, "(*"+v2t+")"+v2s) + add("%#v", &pv2, "(**"+v2t+")"+v2s) + add("%#+v", v2, "("+v2t+")"+v2s) + add("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + add("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + + return tests } -func addFuncFormatterTests() { +func funcFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Function with no params and no returns. - v := addIntFormatterTests - nv := (*func())(nil) + v := intFormatterTests + nv := (*func() []formatterTest)(nil) pv := &v vAddr := fmt.Sprintf("%p", pv) pvAddr := fmt.Sprintf("%p", &pv) - vt := "func()" + vt := "func() []spew_test.formatterTest" vs := fmt.Sprintf("%p", v) - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") + add("%v", v, vs) + add("%v", pv, "<*>"+vs) + add("%v", &pv, "<**>"+vs) + add("%+v", nv, "") + add("%+v", v, vs) + add("%+v", pv, "<*>("+vAddr+")"+vs) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + add("%+v", nv, "") + add("%#v", v, "("+vt+")"+vs) + add("%#v", pv, "(*"+vt+")"+vs) + add("%#v", &pv, "(**"+vt+")"+vs) + add("%#v", nv, "(*"+vt+")"+"") + add("%#+v", v, "("+vt+")"+vs) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + add("%#+v", nv, "(*"+vt+")"+"") // Function with param and no returns. v2 := TestFormatter @@ -1210,25 +1395,25 @@ func addFuncFormatterTests() { pv2Addr := fmt.Sprintf("%p", &pv2) v2t := "func(*testing.T)" v2s := fmt.Sprintf("%p", v2) - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") + add("%v", v2, v2s) + add("%v", pv2, "<*>"+v2s) + add("%v", &pv2, "<**>"+v2s) + add("%+v", nv2, "") + add("%+v", v2, v2s) + add("%+v", pv2, "<*>("+v2Addr+")"+v2s) + add("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%+v", nv2, "") + add("%#v", v2, "("+v2t+")"+v2s) + add("%#v", pv2, "(*"+v2t+")"+v2s) + add("%#v", &pv2, "(**"+v2t+")"+v2s) + add("%#v", nv2, "(*"+v2t+")"+"") + add("%#+v", v2, "("+v2t+")"+v2s) + add("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) + add("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) + add("%#+v", nv2, "(*"+v2t+")"+"") // Function with multiple params and multiple returns. - var v3 = func(i int, s string) (b bool, err error) { + v3 := func(_ int, _ string) (b bool, err error) { return true, nil } nv3 := (*func(int, string) (bool, error))(nil) @@ -1237,25 +1422,32 @@ func addFuncFormatterTests() { pv3Addr := fmt.Sprintf("%p", &pv3) v3t := "func(int, string) (bool, error)" v3s := fmt.Sprintf("%p", v3) - addFormatterTest("%v", v3, v3s) - addFormatterTest("%v", pv3, "<*>"+v3s) - addFormatterTest("%v", &pv3, "<**>"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%+v", v3, v3s) - addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) - addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%#v", v3, "("+v3t+")"+v3s) - addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s) - addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") - addFormatterTest("%#+v", v3, "("+v3t+")"+v3s) - addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s) - addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"") + add("%v", v3, v3s) + add("%v", pv3, "<*>"+v3s) + add("%v", &pv3, "<**>"+v3s) + add("%+v", nv3, "") + add("%+v", v3, v3s) + add("%+v", pv3, "<*>("+v3Addr+")"+v3s) + add("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) + add("%+v", nv3, "") + add("%#v", v3, "("+v3t+")"+v3s) + add("%#v", pv3, "(*"+v3t+")"+v3s) + add("%#v", &pv3, "(**"+v3t+")"+v3s) + add("%#v", nv3, "(*"+v3t+")"+"") + add("%#+v", v3, "("+v3t+")"+v3s) + add("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s) + add("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s) + add("%#+v", nv3, "(*"+v3t+")"+"") + + return tests } -func addCircularFormatterTests() { +func circularFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Struct that is circular through self referencing. type circular struct { c *circular @@ -1275,18 +1467,18 @@ func addCircularFormatterTests() { vs7 := "{c:(*" + vt + ")(" + vAddr + "){c:(*" + vt + ")(" + vAddr + ")}}" vs8 := "{c:(*" + vt + ")(" + vAddr + ")}" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs2) - addFormatterTest("%v", &pv, "<**>"+vs2) - addFormatterTest("%+v", v, vs3) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs4) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs4) - addFormatterTest("%#v", v, "("+vt+")"+vs5) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs6) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs6) - addFormatterTest("%#+v", v, "("+vt+")"+vs7) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs8) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs8) + add("%v", v, vs) + add("%v", pv, "<*>"+vs2) + add("%v", &pv, "<**>"+vs2) + add("%+v", v, vs3) + add("%+v", pv, "<*>("+vAddr+")"+vs4) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs4) + add("%#v", v, "("+vt+")"+vs5) + add("%#v", pv, "(*"+vt+")"+vs6) + add("%#v", &pv, "(**"+vt+")"+vs6) + add("%#+v", v, "("+vt+")"+vs7) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs8) + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs8) // Structs that are circular through cross referencing. v2 := xref1{nil} @@ -1311,18 +1503,18 @@ func addCircularFormatterTests() { ")}}}" v2s8 := "{ps2:(*" + v2t2 + ")(" + ts2Addr + "){ps1:(*" + v2t + ")(" + v2Addr + ")}}" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s2) - addFormatterTest("%v", &pv2, "<**>"+v2s2) - addFormatterTest("%+v", v2, v2s3) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s4) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s4) - addFormatterTest("%#v", v2, "("+v2t+")"+v2s5) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s6) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s6) - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s7) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s8) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s8) + add("%v", v2, v2s) + add("%v", pv2, "<*>"+v2s2) + add("%v", &pv2, "<**>"+v2s2) + add("%+v", v2, v2s3) + add("%+v", pv2, "<*>("+v2Addr+")"+v2s4) + add("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s4) + add("%#v", v2, "("+v2t+")"+v2s5) + add("%#v", pv2, "(*"+v2t+")"+v2s6) + add("%#v", &pv2, "(**"+v2t+")"+v2s6) + add("%#+v", v2, "("+v2t+")"+v2s7) + add("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s8) + add("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s8) // Structs that are indirectly circular. v3 := indirCir1{nil} @@ -1353,21 +1545,29 @@ func addCircularFormatterTests() { ")(" + tic2Addr + ")}}}}" v3s8 := "{ps2:(*" + v3t2 + ")(" + tic2Addr + "){ps3:(*" + v3t3 + ")(" + tic3Addr + "){ps1:(*" + v3t + ")(" + v3Addr + ")}}}" - addFormatterTest("%v", v3, v3s) - addFormatterTest("%v", pv3, "<*>"+v3s2) - addFormatterTest("%v", &pv3, "<**>"+v3s2) - addFormatterTest("%+v", v3, v3s3) - addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s4) - addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s4) - addFormatterTest("%#v", v3, "("+v3t+")"+v3s5) - addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s6) - addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s6) - addFormatterTest("%#+v", v3, "("+v3t+")"+v3s7) - addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s8) - addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s8) + add("%v", v3, v3s) + add("%v", pv3, "<*>"+v3s2) + add("%v", &pv3, "<**>"+v3s2) + add("%+v", v3, v3s3) + add("%+v", pv3, "<*>("+v3Addr+")"+v3s4) + add("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s4) + add("%#v", v3, "("+v3t+")"+v3s5) + add("%#v", pv3, "(*"+v3t+")"+v3s6) + add("%#v", &pv3, "(**"+v3t+")"+v3s6) + add("%#+v", v3, "("+v3t+")"+v3s7) + add("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s8) + add("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s8) + + return tests } -func addPanicFormatterTests() { +//nolint:dupl // panic/error test data follows the same pattern by design +func panicFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Type that panics in its Stringer interface. v := panicer(127) nv := (*panicer)(nil) @@ -1376,25 +1576,33 @@ func addPanicFormatterTests() { pvAddr := fmt.Sprintf("%p", &pv) vt := "spew_test.panicer" vs := "(PANIC=test panic)127" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") + add("%v", v, vs) + add("%v", pv, "<*>"+vs) + add("%v", &pv, "<**>"+vs) + add("%v", nv, "") + add("%+v", v, vs) + add("%+v", pv, "<*>("+vAddr+")"+vs) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + add("%+v", nv, "") + add("%#v", v, "("+vt+")"+vs) + add("%#v", pv, "(*"+vt+")"+vs) + add("%#v", &pv, "(**"+vt+")"+vs) + add("%#v", nv, "(*"+vt+")"+"") + add("%#+v", v, "("+vt+")"+vs) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + add("%#+v", nv, "(*"+vt+")"+"") + + return tests } -func addErrorFormatterTests() { +//nolint:dupl // panic/error test data follows the same pattern by design +func errorFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // Type that has a custom Error interface. v := customError(127) nv := (*customError)(nil) @@ -1403,34 +1611,40 @@ func addErrorFormatterTests() { pvAddr := fmt.Sprintf("%p", &pv) vt := "spew_test.customError" vs := "error: 127" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") + add("%v", v, vs) + add("%v", pv, "<*>"+vs) + add("%v", &pv, "<**>"+vs) + add("%v", nv, "") + add("%+v", v, vs) + add("%+v", pv, "<*>("+vAddr+")"+vs) + add("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) + add("%+v", nv, "") + add("%#v", v, "("+vt+")"+vs) + add("%#v", pv, "(*"+vt+")"+vs) + add("%#v", &pv, "(**"+vt+")"+vs) + add("%#v", nv, "(*"+vt+")"+"") + add("%#+v", v, "("+vt+")"+vs) + add("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) + add("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) + add("%#+v", nv, "(*"+vt+")"+"") + return tests } -func addPassthroughFormatterTests() { +func passthroughFormatterTests() []formatterTest { + var tests []formatterTest + add := func(format string, in any, wants ...string) { + tests = append(tests, formatterTest{format, in, wants}) + } + // %x passthrough with uint. v := uint(4294967295) pv := &v vAddr := fmt.Sprintf("%x", pv) pvAddr := fmt.Sprintf("%x", &pv) vs := "ffffffff" - addFormatterTest("%x", v, vs) - addFormatterTest("%x", pv, vAddr) - addFormatterTest("%x", &pv, pvAddr) + add("%x", v, vs) + add("%x", pv, vAddr) + add("%x", &pv, pvAddr) // %#x passthrough with uint. v2 := int(2147483647) @@ -1438,65 +1652,35 @@ func addPassthroughFormatterTests() { v2Addr := fmt.Sprintf("%#x", pv2) pv2Addr := fmt.Sprintf("%#x", &pv2) v2s := "0x7fffffff" - addFormatterTest("%#x", v2, v2s) - addFormatterTest("%#x", pv2, v2Addr) - addFormatterTest("%#x", &pv2, pv2Addr) + add("%#x", v2, v2s) + add("%#x", pv2, v2Addr) + add("%#x", &pv2, pv2Addr) // %f passthrough with precision. - addFormatterTest("%.2f", 3.1415, "3.14") - addFormatterTest("%.3f", 3.1415, "3.142") - addFormatterTest("%.4f", 3.1415, "3.1415") + add("%.2f", 3.1415, "3.14") + add("%.3f", 3.1415, "3.142") + add("%.4f", 3.1415, "3.1415") // %f passthrough with width and precision. - addFormatterTest("%5.2f", 3.1415, " 3.14") - addFormatterTest("%6.3f", 3.1415, " 3.142") - addFormatterTest("%7.4f", 3.1415, " 3.1415") + add("%5.2f", 3.1415, " 3.14") + add("%6.3f", 3.1415, " 3.142") + add("%7.4f", 3.1415, " 3.1415") // %d passthrough with width. - addFormatterTest("%3d", 127, "127") - addFormatterTest("%4d", 127, " 127") - addFormatterTest("%5d", 127, " 127") + add("%3d", 127, "127") + add("%4d", 127, " 127") + add("%5d", 127, " 127") // %q passthrough with string. - addFormatterTest("%q", "test", "\"test\"") -} + add("%q", "test", "\"test\"") -// TestFormatter executes all of the tests described by formatterTests. -func TestFormatter(t *testing.T) { - // Setup tests. - addIntFormatterTests() - addUintFormatterTests() - addBoolFormatterTests() - addFloatFormatterTests() - addComplexFormatterTests() - addArrayFormatterTests() - addSliceFormatterTests() - addStringFormatterTests() - addInterfaceFormatterTests() - addMapFormatterTests() - addStructFormatterTests() - addUintptrFormatterTests() - addUnsafePointerFormatterTests() - addChanFormatterTests() - addFuncFormatterTests() - addCircularFormatterTests() - addPanicFormatterTests() - addErrorFormatterTests() - addPassthroughFormatterTests() - - t.Logf("Running %d tests", len(formatterTests)) - for i, test := range formatterTests { - buf := new(bytes.Buffer) - spew.Fprintf(buf, test.format, test.in) - s := buf.String() - if testFailed(s, test.wants) { - t.Errorf("Formatter #%d format: %s got: %s %s", i, test.format, s, - stringizeWants(test.wants)) - continue - } - } + return tests } +// ================================= +// Types for TestPrintSortedKeys +// ================================= + type testStruct struct { x int } @@ -1512,47 +1696,3 @@ type testStructP struct { func (ts *testStructP) String() string { return fmt.Sprintf("ts.%d", ts.x) } - -func TestPrintSortedKeys(t *testing.T) { - cfg := spew.ConfigState{SortKeys: true} - s := cfg.Sprint(map[int]string{1: "1", 3: "3", 2: "2"}) - expected := "map[1:1 2:2 3:3]" - if s != expected { - t.Errorf("Sorted keys mismatch 1:\n %v %v", s, expected) - } - - s = cfg.Sprint(map[stringer]int{"1": 1, "3": 3, "2": 2}) - expected = "map[stringer 1:1 stringer 2:2 stringer 3:3]" - if s != expected { - t.Errorf("Sorted keys mismatch 2:\n %v %v", s, expected) - } - - s = cfg.Sprint(map[pstringer]int{pstringer("1"): 1, pstringer("3"): 3, pstringer("2"): 2}) - expected = "map[stringer 1:1 stringer 2:2 stringer 3:3]" - if spew.UnsafeDisabled { - expected = "map[1:1 2:2 3:3]" - } - if s != expected { - t.Errorf("Sorted keys mismatch 3:\n %v %v", s, expected) - } - - s = cfg.Sprint(map[testStruct]int{{1}: 1, {3}: 3, {2}: 2}) - expected = "map[ts.1:1 ts.2:2 ts.3:3]" - if s != expected { - t.Errorf("Sorted keys mismatch 4:\n %v %v", s, expected) - } - - if !spew.UnsafeDisabled { - s = cfg.Sprint(map[testStructP]int{{1}: 1, {3}: 3, {2}: 2}) - expected = "map[ts.1:1 ts.2:2 ts.3:3]" - if s != expected { - t.Errorf("Sorted keys mismatch 5:\n %v %v", s, expected) - } - } - - s = cfg.Sprint(map[customError]int{customError(1): 1, customError(3): 3, customError(2): 2}) - expected = "map[error: 1:1 error: 2:2 error: 3:3]" - if s != expected { - t.Errorf("Sorted keys mismatch 6:\n %v %v", s, expected) - } -} diff --git a/internal/spew/internal_test.go b/internal/spew/internal_test.go index 570fbd009..74f6340a6 100644 --- a/internal/spew/internal_test.go +++ b/internal/spew/internal_test.go @@ -52,6 +52,8 @@ func (dfs *dummyFmtState) Width() (int, bool) { // should never happen in real code and therefore can't be tested via the public // API. func TestInvalidReflectValue(t *testing.T) { + t.Parallel() + i := 1 // Dump invalid reflect value. diff --git a/internal/spew/internalunsafe_test.go b/internal/spew/internalunsafe_test.go index bfd9d6d3f..3261d7804 100644 --- a/internal/spew/internalunsafe_test.go +++ b/internal/spew/internalunsafe_test.go @@ -32,24 +32,12 @@ import ( "testing" ) -// changeKind uses unsafe to intentionally change the kind of a reflect.Value to -// the maximum kind value which does not exist. This is needed to test the -// fallback code which punts to the standard fmt library for new types that -// might get added to the language. -func changeKind(v *reflect.Value, readOnly bool) { - flags := flagField(v) - if readOnly { - *flags |= flagRO - } else { - *flags &^= flagRO - } - *flags |= flagKindMask -} - // TestAddedReflectValue tests functionally of the dump and formatter code which // falls back to the standard fmt library for new types that might get added to // the language. func TestAddedReflectValue(t *testing.T) { + t.Parallel() + i := 1 // Dump using a reflect.Value that is exported. @@ -99,3 +87,17 @@ func TestAddedReflectValue(t *testing.T) { t.Errorf("TestAddedReflectValue #%d got: %s want: %s", i, s, want) } } + +// changeKind uses unsafe to intentionally change the kind of a reflect.Value to +// the maximum kind value which does not exist. This is needed to test the +// fallback code which punts to the standard fmt library for new types that +// might get added to the language. +func changeKind(v *reflect.Value, readOnly bool) { + flags := flagField(v) + if readOnly { + *flags |= flagRO + } else { + *flags &^= flagRO + } + *flags |= flagKindMask +} diff --git a/internal/spew/spew.go b/internal/spew/spew.go index 83bb6be6a..c983f4529 100644 --- a/internal/spew/spew.go +++ b/internal/spew/spew.go @@ -77,7 +77,7 @@ func Fprintln(w io.Writer, a ...any) (n int, err error) { // // fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) func Print(a ...any) (n int, err error) { - return fmt.Print(convertArgs(a)...) + return fmt.Print(convertArgs(a)...) //nolint:forbidigo // public API wrapping fmt.Print } // Printf is a wrapper for fmt.Printf that treats each argument as if it were @@ -89,7 +89,7 @@ func Print(a ...any) (n int, err error) { // // fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) func Printf(format string, a ...any) (n int, err error) { - return fmt.Printf(format, convertArgs(a)...) + return fmt.Printf(format, convertArgs(a)...) //nolint:forbidigo // public API wrapping fmt.Printf } // Println is a wrapper for fmt.Println that treats each argument as if it were @@ -101,7 +101,7 @@ func Printf(format string, a ...any) (n int, err error) { // // fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) func Println(a ...any) (n int, err error) { - return fmt.Println(convertArgs(a)...) + return fmt.Println(convertArgs(a)...) //nolint:forbidigo // public API wrapping fmt.Println } // Sprint is a wrapper for fmt.Sprint that treats each argument as if it were diff --git a/internal/spew/spew_test.go b/internal/spew/spew_test.go index 1c3d5b108..2b8ba318a 100644 --- a/internal/spew/spew_test.go +++ b/internal/spew/spew_test.go @@ -19,13 +19,43 @@ package spew_test import ( "bytes" "fmt" + "iter" "os" + "slices" "testing" "time" "github.com/go-openapi/testify/v2/internal/spew" ) +// TestSpew executes all of the tests described by spewTestCases. +func TestSpew(t *testing.T) { + t.Parallel() + + runners := spewTestRunners() + + i := 0 + for tc := range spewTestCases() { + buf := new(bytes.Buffer) + runner, ok := runners[tc.f] + if !ok { + t.Errorf("test configuration: %v #%d unrecognized function", tc.f, i) + continue + } + + if errMsg := runner(t, buf, tc); errMsg != "" { + t.Errorf("%v #%d %s", tc.f, i, errMsg) + continue + } + + if s := buf.String(); tc.want != s { + t.Errorf("ConfigState #%d\n got: %s want: %s", i, s, tc.want) + } + + i++ + } +} + // spewFunc is used to identify which public function of the spew package or // ConfigState a test applies to. type spewFunc int @@ -54,35 +84,35 @@ const ( fSprintln ) -// Map of spewFunc values to names for pretty printing. -var spewFuncStrings = map[spewFunc]string{ - fCSFdump: "ConfigState.Fdump", - fCSFprint: "ConfigState.Fprint", - fCSFprintf: "ConfigState.Fprintf", - fCSFprintln: "ConfigState.Fprintln", - fCSSdump: "ConfigState.Sdump", - fCSPrint: "ConfigState.Print", - fCSPrintln: "ConfigState.Println", - fCSSprint: "ConfigState.Sprint", - fCSSprintf: "ConfigState.Sprintf", - fCSSprintln: "ConfigState.Sprintln", - fCSErrorf: "ConfigState.Errorf", - fCSNewFormatter: "ConfigState.NewFormatter", - fErrorf: "spew.Errorf", - fFprint: "spew.Fprint", - fFprintln: "spew.Fprintln", - fPrint: "spew.Print", - fPrintln: "spew.Println", - fSdump: "spew.Sdump", - fSprint: "spew.Sprint", - fSprintf: "spew.Sprintf", - fSprintln: "spew.Sprintln", -} - func (f spewFunc) String() string { - if s, ok := spewFuncStrings[f]; ok { + names := map[spewFunc]string{ + fCSFdump: "ConfigState.Fdump", + fCSFprint: "ConfigState.Fprint", + fCSFprintf: "ConfigState.Fprintf", + fCSFprintln: "ConfigState.Fprintln", + fCSSdump: "ConfigState.Sdump", + fCSPrint: "ConfigState.Print", + fCSPrintln: "ConfigState.Println", + fCSSprint: "ConfigState.Sprint", + fCSSprintf: "ConfigState.Sprintf", + fCSSprintln: "ConfigState.Sprintln", + fCSErrorf: "ConfigState.Errorf", + fCSNewFormatter: "ConfigState.NewFormatter", + fErrorf: "spew.Errorf", + fFprint: "spew.Fprint", + fFprintln: "spew.Fprintln", + fPrint: "spew.Print", + fPrintln: "spew.Println", + fSdump: "spew.Sdump", + fSprint: "spew.Sprint", + fSprintf: "spew.Sprintf", + fSprintln: "spew.Sprintln", + } + + if s, ok := names[f]; ok { return s } + return fmt.Sprintf("Unknown spewFunc (%d)", int(f)) } @@ -96,34 +126,13 @@ type spewTest struct { want string } -// spewTests houses the tests to be performed against the public functions of -// the spew package and ConfigState. +// spewTestCases returns the tests to be performed against the public functions +// of the spew package and ConfigState. // // These tests are only intended to ensure the public functions are exercised // and are intentionally not exhaustive of types. The exhaustive type // tests are handled in the dump and format tests. -var spewTests []spewTest - -// redirStdout is a helper function to return the standard output from f as a -// byte slice. -func redirStdout(f func()) ([]byte, error) { - tempFile, err := os.CreateTemp("", "ss-test") - if err != nil { - return nil, err - } - fileName := tempFile.Name() - defer os.Remove(fileName) // Ignore error - - origStdout := os.Stdout - os.Stdout = tempFile - f() - os.Stdout = origStdout - tempFile.Close() - - return os.ReadFile(fileName) -} - -func initSpewTests() { +func spewTestCases() iter.Seq[spewTest] { // Config states with various settings. scsDefault := spew.NewDefaultConfig() scsNoMethods := &spew.ConfigState{Indent: " ", DisableMethods: true} @@ -152,6 +161,7 @@ func initSpewTests() { slice []string m map[string]int } + dt := depthTester{ indirCir1{nil}, [1]string{"arr"}, @@ -171,7 +181,7 @@ func initSpewTests() { redeclaredTime: redeclaredTime(tm), } - spewTests = []spewTest{ + return slices.Values([]spewTest{ // default config {scsDefault, fCSFdump, "", int8(127), "(int8) 127\n"}, {scsDefault, fCSFprint, "", int16(32767), "32767"}, @@ -268,118 +278,109 @@ func initSpewTests() { {scsNoMethodsButTimeStringer, fCSFdump, "", em, "(spew_test.embeddedTime) {\n Time: (time.Time) 2006-01-02 15:04:05.999999999 +0000 UTC\n}\n"}, {scsNoMethodsButTimeStringer, fCSFprint, "", emptr, "{<*>2006-01-02 15:04:05.999999999 +0000 UTC}"}, {scsNoMethodsButTimeStringer, fCSFdump, "", emptr, "(spew_test.embeddedTimePtr) {\n Time: (*time.Time)(" + tmAddr + ")(2006-01-02 15:04:05.999999999 +0000 UTC)\n}\n"}, - } + }) } -// TestSpew executes all of the tests described by spewTests. -func TestSpew(t *testing.T) { - initSpewTests() - - t.Logf("Running %d tests", len(spewTests)) - for i, test := range spewTests { - buf := new(bytes.Buffer) - switch test.f { - case fCSFdump: - test.cs.Fdump(buf, test.in) - - case fCSFprint: - test.cs.Fprint(buf, test.in) - - case fCSFprintf: +// spewTestRunner is a function that executes a spew test case, writing output +// to the buffer. It returns an error string if something goes wrong, or empty +// string on success. +type spewTestRunner func(t *testing.T, buf *bytes.Buffer, test spewTest) string + +// spewTestRunners returns the dispatch table mapping each spewFunc to its +// test execution logic. +func spewTestRunners() map[spewFunc]spewTestRunner { + return map[spewFunc]spewTestRunner{ + fCSFdump: func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { test.cs.Fdump(buf, test.in); return "" }, + fCSFprint: func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { test.cs.Fprint(buf, test.in); return "" }, + fCSFprintf: func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { test.cs.Fprintf(buf, test.format, test.in) - - case fCSFprintln: + return "" + }, + fCSFprintln: func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { test.cs.Fprintln(buf, test.in) - - case fCSPrint: - b, err := redirStdout(func() { test.cs.Print(test.in) }) - if err != nil { - t.Errorf("%v #%d %v", test.f, i, err) - continue - } - buf.Write(b) - - case fCSPrintln: - b, err := redirStdout(func() { test.cs.Println(test.in) }) - if err != nil { - t.Errorf("%v #%d %v", test.f, i, err) - continue - } - buf.Write(b) - - case fCSSdump: - str := test.cs.Sdump(test.in) - buf.WriteString(str) - - case fCSSprint: - str := test.cs.Sprint(test.in) - buf.WriteString(str) - - case fCSSprintf: - str := test.cs.Sprintf(test.format, test.in) - buf.WriteString(str) - - case fCSSprintln: - str := test.cs.Sprintln(test.in) - buf.WriteString(str) - - case fCSErrorf: - err := test.cs.Errorf(test.format, test.in) - buf.WriteString(err.Error()) - - case fCSNewFormatter: + return "" + }, + fCSPrint: runWithStdoutRedirect(func(test spewTest) { test.cs.Print(test.in) }), + fCSPrintln: runWithStdoutRedirect(func(test spewTest) { test.cs.Println(test.in) }), + fCSSdump: func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { + buf.WriteString(test.cs.Sdump(test.in)) + return "" + }, + fCSSprint: func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { + buf.WriteString(test.cs.Sprint(test.in)) + return "" + }, + fCSSprintf: func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { + buf.WriteString(test.cs.Sprintf(test.format, test.in)) + return "" + }, + fCSSprintln: func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { + buf.WriteString(test.cs.Sprintln(test.in)) + return "" + }, + fCSErrorf: func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { + buf.WriteString(test.cs.Errorf(test.format, test.in).Error()) + return "" + }, + fCSNewFormatter: func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { fmt.Fprintf(buf, test.format, test.cs.NewFormatter(test.in)) + return "" + }, + fErrorf: func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { + buf.WriteString(spew.Errorf(test.format, test.in).Error()) + return "" + }, + fFprint: func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { spew.Fprint(buf, test.in); return "" }, + fFprintln: func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { spew.Fprintln(buf, test.in); return "" }, + fPrint: runWithStdoutRedirect(func(test spewTest) { spew.Print(test.in) }), + fPrintln: runWithStdoutRedirect(func(test spewTest) { spew.Println(test.in) }), + fSdump: func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { + buf.WriteString(spew.Sdump(test.in)) + return "" + }, + fSprint: func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { + buf.WriteString(spew.Sprint(test.in)) + return "" + }, + fSprintf: func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { + buf.WriteString(spew.Sprintf(test.format, test.in)) + return "" + }, + fSprintln: func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { + buf.WriteString(spew.Sprintln(test.in)) + return "" + }, + } +} - case fErrorf: - err := spew.Errorf(test.format, test.in) - buf.WriteString(err.Error()) - - case fFprint: - spew.Fprint(buf, test.in) - - case fFprintln: - spew.Fprintln(buf, test.in) - - case fPrint: - b, err := redirStdout(func() { spew.Print(test.in) }) - if err != nil { - t.Errorf("%v #%d %v", test.f, i, err) - continue - } - buf.Write(b) - - case fPrintln: - b, err := redirStdout(func() { spew.Println(test.in) }) - if err != nil { - t.Errorf("%v #%d %v", test.f, i, err) - continue - } - buf.Write(b) - - case fSdump: - str := spew.Sdump(test.in) - buf.WriteString(str) +// runWithStdoutRedirect returns a spewTestRunner that captures stdout output. +func runWithStdoutRedirect(fn func(spewTest)) spewTestRunner { + return func(_ *testing.T, buf *bytes.Buffer, test spewTest) string { + b, err := redirStdout(func() { fn(test) }) + if err != nil { + return err.Error() + } - case fSprint: - str := spew.Sprint(test.in) - buf.WriteString(str) + buf.Write(b) + return "" + } +} - case fSprintf: - str := spew.Sprintf(test.format, test.in) - buf.WriteString(str) +// redirStdout is a helper function to return the standard output from f as a +// byte slice. +func redirStdout(f func()) ([]byte, error) { + tempFile, err := os.CreateTemp("", "ss-test") + if err != nil { + return nil, err + } + fileName := tempFile.Name() + defer os.Remove(fileName) // Ignore error - case fSprintln: - str := spew.Sprintln(test.in) - buf.WriteString(str) + origStdout := os.Stdout + os.Stdout = tempFile + f() + os.Stdout = origStdout + tempFile.Close() - default: - t.Errorf("%v #%d unrecognized function", test.f, i) - continue - } - s := buf.String() - if test.want != s { - t.Errorf("ConfigState #%d\n got: %s want: %s", i, s, test.want) - continue - } - } + return os.ReadFile(fileName) } diff --git a/internal/testintegration/README.md b/internal/testintegration/README.md index 13501fae6..176a154fb 100644 --- a/internal/testintegration/README.md +++ b/internal/testintegration/README.md @@ -1,10 +1,18 @@ # Integration Testing Module -This is a separate Go module dedicated to property-based and fuzz testing of internal packages. +This is a separate Go module dedicated to cross-module integration testing of features that require +external dependencies. ## Purpose -This module uses external testing libraries (like `rapid`) to perform comprehensive black-box testing without polluting the main module's dependency tree. This maintains our zero-dependency goal while enabling powerful testing techniques. +Some testify features are opt-in and require importing external dependencies (YAML support, colorized +output). These features cannot be tested in the main module without breaking the zero-dependency +guarantee. This module provides a place to: + +- **Exercise opt-in features** that activate via the `enable/` import pattern (YAML, colors) +- **Run property-based and fuzz testing** using external testing libraries (rapid) + +This maintains our zero-dependency goal while enabling thorough testing of the full feature set. ## Structure @@ -12,7 +20,15 @@ This module uses external testing libraries (like `rapid`) to perform comprehens internal/testintegration/ ├── go.mod # Separate module with test dependencies ├── go.sum # Dependency checksums +├── doc.go # Package documentation ├── README.md # This file +├── colors/ +│ ├── doc.go # Package documentation +│ └── assertions_test.go # Tests for colorized assertion output +├── yaml/ +│ ├── doc.go # Package documentation +│ ├── enable.go # YAML enablement via stubs +│ └── assertions_test.go # Tests for YAML assertions (YAMLEq) └── spew/ ├── doc.go # Package documentation ├── generator.go # Reflection-based random value generator @@ -21,30 +37,49 @@ internal/testintegration/ ├── edgecases_test.go # Edge case focused tests ├── dump_test.go # Main property-based tests (rapid.Check) ├── dump_fuzz_test.go # Go native fuzz tests - └── testdata/ # Fuzz corpus and rapid failure files + └── options.go # Generator options (e.g., WithSkipCircularMap) ``` ## Dependencies - **pgregory.net/rapid** - Property-based testing library with fuzzing capabilities +- **go.yaml.in/yaml/v3** - YAML parsing (for YAML assertion integration tests) +- **github.com/go-openapi/testify/enable/colors/v2** - Colorized output activation -## Bugs Fixed in spew +## Test Packages -This test suite helped identify and validate fixes for the following issues: +### `colors/` — Colorized Output -### Circular Reference Hangs +Tests that the `enable/colors` import pattern correctly activates ANSI color codes in assertion +failure messages. -1. **Pointer wrapped as interface** - `self = &self` pattern caused infinite loop -2. **Map containing itself** - `m["key"] = m` pattern caused infinite loop +- Imports `_ "github.com/go-openapi/testify/enable/colors/v2"` to activate colors +- Forces the `-testify.colorized` and `-testify.colorized.notty` flags via `init()` +- Verifies that assertion output contains ANSI escape sequences (`\x1b`) -Both are now correctly handled with `` markers. +### `yaml/` — YAML Assertions + +Tests that YAML assertions work when the YAML feature is enabled via the stubs mechanism. + +- Calls `yamlstub.EnableYAMLWithUnmarshal(yaml.Unmarshal)` to wire in the real YAML parser +- Exercises `YAMLEq` with matching and non-matching YAML documents + +### `spew/` — Property-Based and Fuzz Testing -### Historical Issues Addressed +Uses `pgregory.net/rapid` to perform comprehensive black-box testing of `internal/spew`. -- **#1828** - Panic on structs with unexported fields -- **#1829** - Time rendering in diffs +#### Bugs Fixed -## Generator Architecture +1. **Pointer wrapped as interface** — `self = &self` pattern caused infinite loop +2. **Map containing itself** — `m["key"] = m` pattern caused infinite loop + +Both are now correctly handled with `` markers. + +Historical issues addressed: +- **#1828** — Panic on structs with unexported fields +- **#1829** — Time rendering in diffs + +## Generator Architecture (spew) ### Two-Layer Generator System @@ -129,11 +164,13 @@ Generator(WithSkipCircularMap()) ```bash cd internal/testintegration -# Run all tests (100,000 rapid checks by default) +# Run all integration tests go test ./... -# Run with verbose output -go test -v ./spew +# Run feature-specific tests +go test -v ./colors # Colorized output tests +go test -v ./yaml # YAML assertion tests +go test -v ./spew # Property-based spew tests # Run specific test go test -v ./spew -run TestSdump @@ -145,6 +182,11 @@ go test -fuzz=FuzzSdump ./spew -fuzztime=30s go test ./spew -rapid.checks=1000000 ``` +> [!NOTE] +> +> In CI, rapid's value generator generates 10,000 values only (local tests: 100,000) +> and fuzz tests run for 5min + ## Test Types ### Property-Based Tests (`dump_test.go`) @@ -208,57 +250,25 @@ Values that cannot be generated via reflection: ## Adding New Tests -To add fuzz tests for other internal packages: +To add integration tests for a new feature or internal package: 1. Create a new subdirectory under `internal/testintegration/` -2. Add test files with generators specific to that package -3. Use `NoPanicProp` pattern for hang detection +2. Add a `doc.go` with package documentation +3. Add test files exercising the feature -Example structure: - -```go -package mypackage - -import ( - "context" - "testing" - "time" - - "pgregory.net/rapid" -) - -func TestMyFunction(t *testing.T) { - rapid.Check(t, func(rt *rapid.T) { - input := myGenerator().Draw(rt, "input") - - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - - done := make(chan struct{}) - go func() { - defer close(done) - _ = mypackage.MyFunction(input) - }() - - select { - case <-done: - // success - case <-ctx.Done(): - rt.Fatal("function timed out") - } - }) -} -``` +For features that require the `enable/` import pattern, follow the `yaml/` or `colors/` examples. +For property-based or fuzz testing, follow the `spew/` example using `pgregory.net/rapid`. ## Why a Separate Module? This approach: -- **Isolates test dependencies** - rapid is only needed for integration testing -- **Maintains zero dependencies** - Main module stays clean -- **Enables powerful testing** - Use best-in-class testing tools -- **Clear separation** - Test infrastructure vs production code -- **Flexible versioning** - Can update test tools independently +- **Isolates test dependencies** — rapid, yaml, and colors dependencies stay out of the main module +- **Maintains zero dependencies** — Main module keeps its zero-dependency guarantee +- **Tests the enable pattern end-to-end** — Verifies that opt-in features work when activated +- **Enables powerful testing** — Property-based and fuzz testing with best-in-class tools +- **Clear separation** — Test infrastructure vs production code +- **Flexible versioning** — Can update test tools independently ## Rapid Quick Reference diff --git a/internal/testintegration/colors/assertions_test.go b/internal/testintegration/colors/assertions_test.go new file mode 100644 index 000000000..d88e1322d --- /dev/null +++ b/internal/testintegration/colors/assertions_test.go @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +//go:build testcolorized + +package colors + +import ( + "fmt" + "testing" + + _ "github.com/go-openapi/testify/enable/colors/v2" + target "github.com/go-openapi/testify/v2/assert" +) + +// This test executes correctly with build tag "testcolorized". +// +// This tag enables an "escape hatch" in testify/enable/colors/v2 to run the init with colors enabled +// without the normally required command line arguments or environment variables. +// +// This setup allows us to run a full integration test on colorized output targeted only for this test only. + +func TestColorsAssertJSONEq(t *testing.T) { + t.Parallel() + + mockT := new(mockT) + res := target.JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "worldwide", "foo": "bar"}`) + + target.False(t, res) + + output := mockT.errorString() + t.Log(output) // best to visualize the output + target.Contains(t, output, "\x1b") +} + +type mockT struct { + errorFmt string + args []any +} + +func (m *mockT) Errorf(format string, args ...any) { + m.errorFmt = format + m.args = args +} + +func (m *mockT) errorString() string { + return fmt.Sprintf(m.errorFmt, m.args...) +} diff --git a/internal/testintegration/colors/doc.go b/internal/testintegration/colors/doc.go new file mode 100644 index 000000000..b0bcc5deb --- /dev/null +++ b/internal/testintegration/colors/doc.go @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +// Package colors exercises the colors feature (colorized output). +// +// It requires build tag "testcolorized" to actually run the test. +package colors diff --git a/internal/testintegration/go.mod b/internal/testintegration/go.mod index 72b6d709c..8be09c715 100644 --- a/internal/testintegration/go.mod +++ b/internal/testintegration/go.mod @@ -3,9 +3,18 @@ module github.com/go-openapi/testify/v2/internal/testintegration/v2 go 1.24.0 require ( - github.com/go-openapi/testify/v2 v2.1.8 + github.com/go-openapi/testify/enable/colors/v2 v2.0.0-00010101000000-000000000000 + github.com/go-openapi/testify/v2 v2.2.0 go.yaml.in/yaml/v3 v3.0.4 pgregory.net/rapid v1.2.0 ) -replace github.com/go-openapi/testify/v2 => ../.. +require ( + golang.org/x/sys v0.40.0 // indirect + golang.org/x/term v0.39.0 // indirect +) + +replace ( + github.com/go-openapi/testify/enable/colors/v2 => ../../enable/colors + github.com/go-openapi/testify/v2 => ../.. +) diff --git a/internal/testintegration/go.sum b/internal/testintegration/go.sum index aa1842d5b..3e4fefb60 100644 --- a/internal/testintegration/go.sum +++ b/internal/testintegration/go.sum @@ -1,5 +1,9 @@ go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= +golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= 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= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= diff --git a/internal/testintegration/spew/generator_test.go b/internal/testintegration/spew/generator_test.go index 120582232..0fba5eca2 100644 --- a/internal/testintegration/spew/generator_test.go +++ b/internal/testintegration/spew/generator_test.go @@ -125,5 +125,5 @@ func testLoad() int { return 100 } - return 100000 // local testing explores more cases + return 100_000 // local testing explores more cases } diff --git a/internal/testintegration/yaml/assertions_test.go b/internal/testintegration/yaml/assertions_test.go index c95876213..86b953053 100644 --- a/internal/testintegration/yaml/assertions_test.go +++ b/internal/testintegration/yaml/assertions_test.go @@ -15,94 +15,5 @@ func TestYAMLEq_EqualYAMLString(t *testing.T) { enableYAML() mockT := new(testing.T) target.True(t, target.YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`)) + target.False(t, target.YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "buzz", "foo": "lightyear"}`)) } - -/* -func TestYAMLEq_EquivalentButNotEqual(t *testing.T) { - t.Parallel() - - mockT := new(testing.T) - target.True(t, target.YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) -} - -func TestYAMLEq_HashOfArraysAndHashes(t *testing.T) { - t.Parallel() - - mockT := new(testing.T) - target.True(t, target.YAMLEq(mockT, expectedYAML, actualYAML)) -} - -func TestYAMLEq_Array(t *testing.T) { - t.Parallel() - - mockT := new(testing.T) - target.True(t, target.YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`)) -} - -func TestYAMLEq_HashAndArrayNotEquivalent(t *testing.T) { - t.Parallel() - - mockT := new(testing.T) - target.False(t, target.YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`)) -} - -func TestYAMLEq_HashesNotEquivalent(t *testing.T) { - t.Parallel() - - mockT := new(testing.T) - target.False(t, target.YAMLEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) -} - -func TestYAMLEq_ActualIsSimpleString(t *testing.T) { - t.Parallel() - - mockT := new(testing.T) - target.False(t, target.YAMLEq(mockT, `{"foo": "bar"}`, "Simple String")) -} - -func TestYAMLEq_ExpectedIsSimpleString(t *testing.T) { - t.Parallel() - - mockT := new(testing.T) - target.False(t, target.YAMLEq(mockT, "Simple String", `{"foo": "bar", "hello": "world"}`)) -} - -func TestYAMLEq_ExpectedAndActualSimpleString(t *testing.T) { - t.Parallel() - - mockT := new(testing.T) - target.True(t, target.YAMLEq(mockT, "Simple String", "Simple String")) -} - -func TestYAMLEq_ArraysOfDifferentOrder(t *testing.T) { - t.Parallel() - - mockT := new(testing.T) - target.False(t, target.YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`)) -} - -func TestYAMLEq_OnlyFirstDocument(t *testing.T) { - t.Parallel() - - mockT := new(testing.T) - target.True(t, target.YAMLEq(mockT, - `--- -doc1: same ---- -doc2: different -`, - `--- -doc1: same ---- -doc2: notsame -`, - )) -} - -func TestYAMLEq_InvalidIdenticalYAML(t *testing.T) { - t.Parallel() - - mockT := new(testing.T) - target.False(t, target.YAMLEq(mockT, `}`, `}`)) -} -*/ diff --git a/internal/testintegration/yaml/enable.go b/internal/testintegration/yaml/enable.go index c0a88bd4f..cd1a0de8b 100644 --- a/internal/testintegration/yaml/enable.go +++ b/internal/testintegration/yaml/enable.go @@ -4,8 +4,9 @@ package yaml import ( - yamlstub "github.com/go-openapi/testify/v2/enable/stubs/yaml" yaml "go.yaml.in/yaml/v3" + + yamlstub "github.com/go-openapi/testify/v2/enable/stubs/yaml" ) func enableYAML() { diff --git a/require/assert_adhoc_example_6_test.go b/require/assert_adhoc_example_6_test.go new file mode 100644 index 000000000..5360e348b --- /dev/null +++ b/require/assert_adhoc_example_6_test.go @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package require_test + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func ExampleAssertions_with_generics() { + t := new(testing.T) // normally provided by test + + a := require.New(t) + + const expected = "hello" + goodValue := "hello" + + a.Equal(expected, goodValue) // classic reflect-based assertion + fmt.Println("good value") + + require.EqualT(a.T, expected, goodValue) // usage with generic assertion + fmt.Println("good value") + + // Output: + // good value + // good value +} diff --git a/require/require_assertions.go b/require/require_assertions.go index 9272cfd4c..77476858b 100644 --- a/require/require_assertions.go +++ b/require/require_assertions.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package require @@ -479,7 +478,7 @@ func Eventually(t T, condition func() bool, waitFor time.Duration, tick time.Dur t.FailNow() } -// EventuallyWithT asserts that the given condition will be met in waitFor time, +// EventuallyWith asserts that the given condition will be met in waitFor time, // periodically checking the target function at each tick. // // In contrast to [Eventually], the condition function is supplied with a [CollectT] @@ -501,7 +500,7 @@ func Eventually(t T, condition func() bool, waitFor time.Duration, tick time.Dur // externalValue = true // }() // -// assertions.EventuallyWithT(t, func(c *assertions.CollectT) { +// assertions.EventuallyWith(t, func(c *assertions.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assertions.True(c, externalValue, "expected 'externalValue' to be true") // }, @@ -521,11 +520,11 @@ func Eventually(t T, condition func() bool, waitFor time.Duration, tick time.Dur // failure: func(c *CollectT) { False(c,true) }, 100*time.Millisecond, 20*time.Millisecond // // Upon failure, the test [T] is marked as failed and stops execution. -func EventuallyWithT(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) { +func EventuallyWith(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.EventuallyWithT(t, condition, waitFor, tick, msgAndArgs...) { + if assertions.EventuallyWith(t, condition, waitFor, tick, msgAndArgs...) { return } @@ -1639,6 +1638,76 @@ func JSONEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any t.FailNow() } +// JSONMarshalAsT wraps [JSONEq] after [json.Marshal]. +// +// The input JSON may be a string or []byte. +// +// It fails if the marshaling returns an error or if the expected JSON bytes differ semantically +// from the expected ones. +// +// # Usage +// +// actual := struct { +// A int `json:"a"` +// }{ +// A: 10, +// } +// +// assertions.JSONUnmarshalAsT(t,expected, `{"a": 10}`) +// +// # Examples +// +// success: []byte(`{"A": "a"}`), dummyStruct{A: "a"} +// failure: `[{"foo": "bar"}, {"hello": "world"}]`, 1 +// +// Upon failure, the test [T] is marked as failed and stops execution. +func JSONMarshalAsT[EDoc Text](t T, expected EDoc, object any, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.JSONMarshalAsT[EDoc](t, expected, object, msgAndArgs...) { + return + } + + t.FailNow() +} + +// JSONUnmarshalAsT wraps [Equal] after [json.Unmarshal]. +// +// The input JSON may be a string or []byte. +// +// It fails if the unmarshaling returns an error or if the resulting object is not equal to the expected one. +// +// Be careful not to wrap the expected object into an "any" interface if this is not what you expected: +// the unmarshaling would take this type to unmarshal as a map[string]any. +// +// # Usage +// +// expected := struct { +// A int `json:"a"` +// }{ +// A: 10, +// } +// +// assertions.JSONUnmarshalAsT(t,expected, `{"a": 10}`) +// +// # Examples +// +// success: dummyStruct{A: "a"} , []byte(`{"A": "a"}`) +// failure: 1, `[{"foo": "bar"}, {"hello": "world"}]` +// +// Upon failure, the test [T] is marked as failed and stops execution. +func JSONUnmarshalAsT[Object any, ADoc Text](t T, expected Object, jazon ADoc, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.JSONUnmarshalAsT[Object, ADoc](t, expected, jazon, msgAndArgs...) { + return + } + + t.FailNow() +} + // Kind asserts that the [reflect.Kind] of a given object matches the expected [reflect.Kind]. // // Kind reflects the concrete value stored in the object. The nil value (or interface with nil value) @@ -2004,6 +2073,20 @@ func NoError(t T, err error, msgAndArgs ...any) { t.FailNow() } +// NoGoRoutineLeak ensures that no goroutine did leak from inside the tested function. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func NoGoRoutineLeak(t T, inside func(), msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.NoGoRoutineLeak(t, inside, msgAndArgs...) { + return + } + + t.FailNow() +} + // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // @@ -3185,6 +3268,76 @@ func YAMLEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any t.FailNow() } +// YAMLMarshalAsT wraps [YAMLEq] after [yaml.Marshal]. +// +// The input YAML may be a string or []byte. +// +// It fails if the marshaling returns an error or if the expected YAML bytes differ semantically +// from the expected ones. +// +// # Usage +// +// actual := struct { +// A int `yaml:"a"` +// }{ +// A: 10, +// } +// +// assertions.YAMLUnmarshalAsT(t,expected, `{"a": 10}`) +// +// # Examples +// +// panic: "key: value", "key: value" +// should panic without the yaml feature enabled. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func YAMLMarshalAsT[EDoc Text](t T, expected EDoc, object any, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.YAMLMarshalAsT[EDoc](t, expected, object, msgAndArgs...) { + return + } + + t.FailNow() +} + +// YAMLUnmarshalAsT wraps [Equal] after [yaml.Unmarshal]. +// +// The input YAML may be a string or []byte. +// +// It fails if the unmarshaling returns an error or if the resulting object is not equal to the expected one. +// +// Be careful not to wrap the expected object into an "any" interface if this is not what you expected: +// the unmarshaling would take this type to unmarshal as a map[string]any. +// +// # Usage +// +// expected := struct { +// A int `yaml:"a"` +// }{ +// A: 10, +// } +// +// assertions.YAMLUnmarshalAsT(t,expected, `{"a": 10}`) +// +// # Examples +// +// panic: "key: value", "key: value" +// should panic without the yaml feature enabled. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func YAMLUnmarshalAsT[Object any, ADoc Text](t T, expected Object, jazon ADoc, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.YAMLUnmarshalAsT[Object, ADoc](t, expected, jazon, msgAndArgs...) { + return + } + + t.FailNow() +} + // Zero asserts that i is the zero value for its type. // // # Usage diff --git a/require/require_assertions_test.go b/require/require_assertions_test.go index 1468f1e73..8e2658b43 100644 --- a/require/require_assertions_test.go +++ b/require/require_assertions_test.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package require @@ -358,11 +357,11 @@ func TestEventually(t *testing.T) { }) } -func TestEventuallyWithT(t *testing.T) { +func TestEventuallyWith(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - EventuallyWithT(t, func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond) + EventuallyWith(t, func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond) // require functions don't return a value }) @@ -370,10 +369,10 @@ func TestEventuallyWithT(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - EventuallyWithT(mock, func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond) + EventuallyWith(mock, func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond) // require functions don't return a value if !mock.failed { - t.Error("EventuallyWithT should call FailNow()") + t.Error("EventuallyWith should call FailNow()") } }) } @@ -1206,6 +1205,46 @@ func TestJSONEqT(t *testing.T) { }) } +func TestJSONMarshalAsT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + JSONMarshalAsT(t, []byte(`{"A": "a"}`), dummyStruct{A: "a"}) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + JSONMarshalAsT(mock, `[{"foo": "bar"}, {"hello": "world"}]`, 1) + // require functions don't return a value + if !mock.failed { + t.Error("JSONMarshalAsT should call FailNow()") + } + }) +} + +func TestJSONUnmarshalAsT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + JSONUnmarshalAsT(t, dummyStruct{A: "a"}, []byte(`{"A": "a"}`)) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + JSONUnmarshalAsT(mock, 1, `[{"foo": "bar"}, {"hello": "world"}]`) + // require functions don't return a value + if !mock.failed { + t.Error("JSONUnmarshalAsT should call FailNow()") + } + }) +} + func TestKind(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1466,6 +1505,11 @@ func TestNoError(t *testing.T) { }) } +func TestNoGoRoutineLeak(t *testing.T) { + t.Parallel() + t.Skip() // this function doesn't have tests yet: feed the original function with examples to test. +} + func TestNotContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -2359,6 +2403,28 @@ func TestYAMLEqT(t *testing.T) { }) } +func TestYAMLMarshalAsT(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + Panics(t, func() { + YAMLMarshalAsT(t, "key: value", "key: value") + }, "should panic without the yaml feature enabled.") + }) +} + +func TestYAMLUnmarshalAsT(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + Panics(t, func() { + YAMLUnmarshalAsT(t, "key: value", "key: value") + }, "should panic without the yaml feature enabled.") + }) +} + func TestZero(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { diff --git a/require/require_examples_test.go b/require/require_examples_test.go index e005cddd1..00e395958 100644 --- a/require/require_examples_test.go +++ b/require/require_examples_test.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package require_test @@ -22,7 +21,7 @@ import ( ) func ExampleCondition() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestCondition(t *testing.T) require.Condition(t, func() bool { return true }) @@ -32,7 +31,7 @@ func ExampleCondition() { } func ExampleContains() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestContains(t *testing.T) require.Contains(t, []string{"A", "B"}, "A") fmt.Println("passed") @@ -40,7 +39,7 @@ func ExampleContains() { } func ExampleDirExists() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestDirExists(t *testing.T) require.DirExists(t, filepath.Join(testDataPath(), "existing_dir")) fmt.Println("passed") @@ -48,7 +47,7 @@ func ExampleDirExists() { } func ExampleDirNotExists() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestDirNotExists(t *testing.T) require.DirNotExists(t, filepath.Join(testDataPath(), "non_existing_dir")) fmt.Println("passed") @@ -56,7 +55,7 @@ func ExampleDirNotExists() { } func ExampleElementsMatch() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestElementsMatch(t *testing.T) require.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) fmt.Println("passed") @@ -64,7 +63,7 @@ func ExampleElementsMatch() { } func ExampleElementsMatchT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestElementsMatchT(t *testing.T) require.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) fmt.Println("passed") @@ -72,7 +71,7 @@ func ExampleElementsMatchT() { } func ExampleEmpty() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestEmpty(t *testing.T) require.Empty(t, "") fmt.Println("passed") @@ -80,7 +79,7 @@ func ExampleEmpty() { } func ExampleEqual() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestEqual(t *testing.T) require.Equal(t, 123, 123) fmt.Println("passed") @@ -88,7 +87,7 @@ func ExampleEqual() { } func ExampleEqualError() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestEqualError(t *testing.T) require.EqualError(t, require.ErrTest, "assert.ErrTest general error for testing") fmt.Println("passed") @@ -96,7 +95,7 @@ func ExampleEqualError() { } func ExampleEqualExportedValues() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestEqualExportedValues(t *testing.T) require.EqualExportedValues(t, &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2}) fmt.Println("passed") @@ -104,7 +103,7 @@ func ExampleEqualExportedValues() { } func ExampleEqualT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestEqualT(t *testing.T) require.EqualT(t, 123, 123) fmt.Println("passed") @@ -112,7 +111,7 @@ func ExampleEqualT() { } func ExampleEqualValues() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestEqualValues(t *testing.T) require.EqualValues(t, uint32(123), int32(123)) fmt.Println("passed") @@ -120,7 +119,7 @@ func ExampleEqualValues() { } func ExampleError() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestError(t *testing.T) require.Error(t, require.ErrTest) fmt.Println("passed") @@ -128,7 +127,7 @@ func ExampleError() { } func ExampleErrorAs() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestErrorAs(t *testing.T) require.ErrorAs(t, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) fmt.Println("passed") @@ -136,7 +135,7 @@ func ExampleErrorAs() { } func ExampleErrorContains() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestErrorContains(t *testing.T) require.ErrorContains(t, require.ErrTest, "general error") fmt.Println("passed") @@ -144,7 +143,7 @@ func ExampleErrorContains() { } func ExampleErrorIs() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestErrorIs(t *testing.T) require.ErrorIs(t, fmt.Errorf("wrap: %w", io.EOF), io.EOF) fmt.Println("passed") @@ -152,7 +151,7 @@ func ExampleErrorIs() { } func ExampleEventually() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestEventually(t *testing.T) require.Eventually(t, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond) @@ -161,9 +160,9 @@ func ExampleEventually() { // Output: passed } -func ExampleEventuallyWithT() { - t := new(testing.T) - require.EventuallyWithT(t, func(c *assert.CollectT) { +func ExampleEventuallyWith() { + t := new(testing.T) // should come from testing, e.g. func TestEventuallyWith(t *testing.T) + require.EventuallyWith(t, func(c *assert.CollectT) { assert.True(c, true) }, 100*time.Millisecond, 20*time.Millisecond) fmt.Println("passed") @@ -172,7 +171,7 @@ func ExampleEventuallyWithT() { } func ExampleExactly() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestExactly(t *testing.T) require.Exactly(t, int32(123), int32(123)) fmt.Println("passed") @@ -188,7 +187,7 @@ func ExampleExactly() { // } func ExampleFalse() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestFalse(t *testing.T) require.False(t, 1 == 0) fmt.Println("passed") @@ -196,7 +195,7 @@ func ExampleFalse() { } func ExampleFalseT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestFalseT(t *testing.T) require.FalseT(t, 1 == 0) fmt.Println("passed") @@ -204,7 +203,7 @@ func ExampleFalseT() { } func ExampleFileEmpty() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestFileEmpty(t *testing.T) require.FileEmpty(t, filepath.Join(testDataPath(), "empty_file")) fmt.Println("passed") @@ -212,7 +211,7 @@ func ExampleFileEmpty() { } func ExampleFileExists() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestFileExists(t *testing.T) require.FileExists(t, filepath.Join(testDataPath(), "existing_file")) fmt.Println("passed") @@ -220,7 +219,7 @@ func ExampleFileExists() { } func ExampleFileNotEmpty() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestFileNotEmpty(t *testing.T) require.FileNotEmpty(t, filepath.Join(testDataPath(), "existing_file")) fmt.Println("passed") @@ -228,7 +227,7 @@ func ExampleFileNotEmpty() { } func ExampleFileNotExists() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestFileNotExists(t *testing.T) require.FileNotExists(t, filepath.Join(testDataPath(), "non_existing_file")) fmt.Println("passed") @@ -236,7 +235,7 @@ func ExampleFileNotExists() { } func ExampleGreater() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestGreater(t *testing.T) require.Greater(t, 2, 1) fmt.Println("passed") @@ -244,7 +243,7 @@ func ExampleGreater() { } func ExampleGreaterOrEqual() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestGreaterOrEqual(t *testing.T) require.GreaterOrEqual(t, 2, 1) fmt.Println("passed") @@ -252,7 +251,7 @@ func ExampleGreaterOrEqual() { } func ExampleGreaterOrEqualT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestGreaterOrEqualT(t *testing.T) require.GreaterOrEqualT(t, 2, 1) fmt.Println("passed") @@ -260,7 +259,7 @@ func ExampleGreaterOrEqualT() { } func ExampleGreaterT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestGreaterT(t *testing.T) require.GreaterT(t, 2, 1) fmt.Println("passed") @@ -268,7 +267,7 @@ func ExampleGreaterT() { } func ExampleHTTPBodyContains() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestHTTPBodyContains(t *testing.T) require.HTTPBodyContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!") fmt.Println("passed") @@ -276,7 +275,7 @@ func ExampleHTTPBodyContains() { } func ExampleHTTPBodyNotContains() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestHTTPBodyNotContains(t *testing.T) require.HTTPBodyNotContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!") fmt.Println("passed") @@ -284,7 +283,7 @@ func ExampleHTTPBodyNotContains() { } func ExampleHTTPError() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestHTTPError(t *testing.T) require.HTTPError(t, httpError, "GET", "/", nil) fmt.Println("passed") @@ -292,7 +291,7 @@ func ExampleHTTPError() { } func ExampleHTTPRedirect() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestHTTPRedirect(t *testing.T) require.HTTPRedirect(t, httpRedirect, "GET", "/", nil) fmt.Println("passed") @@ -300,7 +299,7 @@ func ExampleHTTPRedirect() { } func ExampleHTTPStatusCode() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestHTTPStatusCode(t *testing.T) require.HTTPStatusCode(t, httpOK, "GET", "/", nil, http.StatusOK) fmt.Println("passed") @@ -308,7 +307,7 @@ func ExampleHTTPStatusCode() { } func ExampleHTTPSuccess() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestHTTPSuccess(t *testing.T) require.HTTPSuccess(t, httpOK, "GET", "/", nil) fmt.Println("passed") @@ -316,7 +315,7 @@ func ExampleHTTPSuccess() { } func ExampleImplements() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestImplements(t *testing.T) require.Implements(t, ptr(dummyInterface), new(testing.T)) fmt.Println("passed") @@ -324,7 +323,7 @@ func ExampleImplements() { } func ExampleInDelta() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestInDelta(t *testing.T) require.InDelta(t, 1.0, 1.01, 0.02) fmt.Println("passed") @@ -332,7 +331,7 @@ func ExampleInDelta() { } func ExampleInDeltaMapValues() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestInDeltaMapValues(t *testing.T) require.InDeltaMapValues(t, map[string]float64{"a": 1.0}, map[string]float64{"a": 1.01}, 0.02) fmt.Println("passed") @@ -340,7 +339,7 @@ func ExampleInDeltaMapValues() { } func ExampleInDeltaSlice() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestInDeltaSlice(t *testing.T) require.InDeltaSlice(t, []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02) fmt.Println("passed") @@ -348,7 +347,7 @@ func ExampleInDeltaSlice() { } func ExampleInDeltaT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestInDeltaT(t *testing.T) require.InDeltaT(t, 1.0, 1.01, 0.02) fmt.Println("passed") @@ -356,7 +355,7 @@ func ExampleInDeltaT() { } func ExampleInEpsilon() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestInEpsilon(t *testing.T) require.InEpsilon(t, 100.0, 101.0, 0.02) fmt.Println("passed") @@ -364,7 +363,7 @@ func ExampleInEpsilon() { } func ExampleInEpsilonSlice() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestInEpsilonSlice(t *testing.T) require.InEpsilonSlice(t, []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02) fmt.Println("passed") @@ -372,7 +371,7 @@ func ExampleInEpsilonSlice() { } func ExampleInEpsilonT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestInEpsilonT(t *testing.T) require.InEpsilonT(t, 100.0, 101.0, 0.02) fmt.Println("passed") @@ -380,7 +379,7 @@ func ExampleInEpsilonT() { } func ExampleIsDecreasing() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsDecreasing(t *testing.T) require.IsDecreasing(t, []int{3, 2, 1}) fmt.Println("passed") @@ -388,7 +387,7 @@ func ExampleIsDecreasing() { } func ExampleIsDecreasingT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsDecreasingT(t *testing.T) require.IsDecreasingT(t, []int{3, 2, 1}) fmt.Println("passed") @@ -396,7 +395,7 @@ func ExampleIsDecreasingT() { } func ExampleIsIncreasing() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsIncreasing(t *testing.T) require.IsIncreasing(t, []int{1, 2, 3}) fmt.Println("passed") @@ -404,7 +403,7 @@ func ExampleIsIncreasing() { } func ExampleIsIncreasingT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsIncreasingT(t *testing.T) require.IsIncreasingT(t, []int{1, 2, 3}) fmt.Println("passed") @@ -412,7 +411,7 @@ func ExampleIsIncreasingT() { } func ExampleIsNonDecreasing() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsNonDecreasing(t *testing.T) require.IsNonDecreasing(t, []int{1, 1, 2}) fmt.Println("passed") @@ -420,7 +419,7 @@ func ExampleIsNonDecreasing() { } func ExampleIsNonDecreasingT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsNonDecreasingT(t *testing.T) require.IsNonDecreasingT(t, []int{1, 1, 2}) fmt.Println("passed") @@ -428,7 +427,7 @@ func ExampleIsNonDecreasingT() { } func ExampleIsNonIncreasing() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsNonIncreasing(t *testing.T) require.IsNonIncreasing(t, []int{2, 1, 1}) fmt.Println("passed") @@ -436,7 +435,7 @@ func ExampleIsNonIncreasing() { } func ExampleIsNonIncreasingT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsNonIncreasingT(t *testing.T) require.IsNonIncreasingT(t, []int{2, 1, 1}) fmt.Println("passed") @@ -444,7 +443,7 @@ func ExampleIsNonIncreasingT() { } func ExampleIsNotOfTypeT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsNotOfTypeT(t *testing.T) require.IsNotOfTypeT[myType](t, 123.123) fmt.Println("passed") @@ -452,7 +451,7 @@ func ExampleIsNotOfTypeT() { } func ExampleIsNotType() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsNotType(t *testing.T) require.IsNotType(t, int32(123), int64(456)) fmt.Println("passed") @@ -460,7 +459,7 @@ func ExampleIsNotType() { } func ExampleIsOfTypeT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsOfTypeT(t *testing.T) require.IsOfTypeT[myType](t, myType(123.123)) fmt.Println("passed") @@ -468,7 +467,7 @@ func ExampleIsOfTypeT() { } func ExampleIsType() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestIsType(t *testing.T) require.IsType(t, 123, 456) fmt.Println("passed") @@ -476,7 +475,7 @@ func ExampleIsType() { } func ExampleJSONEq() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestJSONEq(t *testing.T) require.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) fmt.Println("passed") @@ -484,7 +483,7 @@ func ExampleJSONEq() { } func ExampleJSONEqBytes() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestJSONEqBytes(t *testing.T) require.JSONEqBytes(t, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`)) fmt.Println("passed") @@ -492,15 +491,31 @@ func ExampleJSONEqBytes() { } func ExampleJSONEqT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestJSONEqT(t *testing.T) require.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) fmt.Println("passed") // Output: passed } +func ExampleJSONMarshalAsT() { + t := new(testing.T) // should come from testing, e.g. func TestJSONMarshalAsT(t *testing.T) + require.JSONMarshalAsT(t, []byte(`{"A": "a"}`), dummyStruct{A: "a"}) + fmt.Println("passed") + + // Output: passed +} + +func ExampleJSONUnmarshalAsT() { + t := new(testing.T) // should come from testing, e.g. func TestJSONUnmarshalAsT(t *testing.T) + require.JSONUnmarshalAsT(t, dummyStruct{A: "a"}, []byte(`{"A": "a"}`)) + fmt.Println("passed") + + // Output: passed +} + func ExampleKind() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestKind(t *testing.T) require.Kind(t, reflect.String, "hello") fmt.Println("passed") @@ -508,7 +523,7 @@ func ExampleKind() { } func ExampleLen() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestLen(t *testing.T) require.Len(t, []string{"A", "B"}, 2) fmt.Println("passed") @@ -516,7 +531,7 @@ func ExampleLen() { } func ExampleLess() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestLess(t *testing.T) require.Less(t, 1, 2) fmt.Println("passed") @@ -524,7 +539,7 @@ func ExampleLess() { } func ExampleLessOrEqual() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestLessOrEqual(t *testing.T) require.LessOrEqual(t, 1, 2) fmt.Println("passed") @@ -532,7 +547,7 @@ func ExampleLessOrEqual() { } func ExampleLessOrEqualT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestLessOrEqualT(t *testing.T) require.LessOrEqualT(t, 1, 2) fmt.Println("passed") @@ -540,7 +555,7 @@ func ExampleLessOrEqualT() { } func ExampleLessT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestLessT(t *testing.T) require.LessT(t, 1, 2) fmt.Println("passed") @@ -548,7 +563,7 @@ func ExampleLessT() { } func ExampleMapContainsT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestMapContainsT(t *testing.T) require.MapContainsT(t, map[string]string{"A": "B"}, "A") fmt.Println("passed") @@ -556,7 +571,7 @@ func ExampleMapContainsT() { } func ExampleMapNotContainsT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestMapNotContainsT(t *testing.T) require.MapNotContainsT(t, map[string]string{"A": "B"}, "C") fmt.Println("passed") @@ -564,7 +579,7 @@ func ExampleMapNotContainsT() { } func ExampleNegative() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNegative(t *testing.T) require.Negative(t, -1) fmt.Println("passed") @@ -572,7 +587,7 @@ func ExampleNegative() { } func ExampleNegativeT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNegativeT(t *testing.T) require.NegativeT(t, -1) fmt.Println("passed") @@ -580,7 +595,7 @@ func ExampleNegativeT() { } func ExampleNever() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNever(t *testing.T) require.Never(t, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond) @@ -590,7 +605,7 @@ func ExampleNever() { } func ExampleNil() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNil(t *testing.T) require.Nil(t, nil) fmt.Println("passed") @@ -598,15 +613,19 @@ func ExampleNil() { } func ExampleNoError() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNoError(t *testing.T) require.NoError(t, nil) fmt.Println("passed") // Output: passed } +// func ExampleNoGoRoutineLeak() { +// no success example available. Please add some examples to produce a testable example. +// } + func ExampleNotContains() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotContains(t *testing.T) require.NotContains(t, []string{"A", "B"}, "C") fmt.Println("passed") @@ -614,7 +633,7 @@ func ExampleNotContains() { } func ExampleNotElementsMatch() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotElementsMatch(t *testing.T) require.NotElementsMatch(t, []int{1, 2, 3}, []int{1, 2, 4}) fmt.Println("passed") @@ -622,7 +641,7 @@ func ExampleNotElementsMatch() { } func ExampleNotElementsMatchT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotElementsMatchT(t *testing.T) require.NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4}) fmt.Println("passed") @@ -630,7 +649,7 @@ func ExampleNotElementsMatchT() { } func ExampleNotEmpty() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotEmpty(t *testing.T) require.NotEmpty(t, "not empty") fmt.Println("passed") @@ -638,7 +657,7 @@ func ExampleNotEmpty() { } func ExampleNotEqual() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotEqual(t *testing.T) require.NotEqual(t, 123, 456) fmt.Println("passed") @@ -646,7 +665,7 @@ func ExampleNotEqual() { } func ExampleNotEqualT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotEqualT(t *testing.T) require.NotEqualT(t, 123, 456) fmt.Println("passed") @@ -654,7 +673,7 @@ func ExampleNotEqualT() { } func ExampleNotEqualValues() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotEqualValues(t *testing.T) require.NotEqualValues(t, uint32(123), int32(456)) fmt.Println("passed") @@ -662,7 +681,7 @@ func ExampleNotEqualValues() { } func ExampleNotErrorAs() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotErrorAs(t *testing.T) require.NotErrorAs(t, require.ErrTest, new(*dummyError)) fmt.Println("passed") @@ -670,7 +689,7 @@ func ExampleNotErrorAs() { } func ExampleNotErrorIs() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotErrorIs(t *testing.T) require.NotErrorIs(t, require.ErrTest, io.EOF) fmt.Println("passed") @@ -678,7 +697,7 @@ func ExampleNotErrorIs() { } func ExampleNotImplements() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotImplements(t *testing.T) require.NotImplements(t, (*error)(nil), new(testing.T)) fmt.Println("passed") @@ -686,7 +705,7 @@ func ExampleNotImplements() { } func ExampleNotKind() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotKind(t *testing.T) require.NotKind(t, reflect.String, 0) fmt.Println("passed") @@ -694,7 +713,7 @@ func ExampleNotKind() { } func ExampleNotNil() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotNil(t *testing.T) require.NotNil(t, "not nil") fmt.Println("passed") @@ -702,7 +721,7 @@ func ExampleNotNil() { } func ExampleNotPanics() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotPanics(t *testing.T) require.NotPanics(t, func() { }) fmt.Println("passed") @@ -711,7 +730,7 @@ func ExampleNotPanics() { } func ExampleNotRegexp() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotRegexp(t *testing.T) require.NotRegexp(t, "^start", "not starting") fmt.Println("passed") @@ -719,7 +738,7 @@ func ExampleNotRegexp() { } func ExampleNotRegexpT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotRegexpT(t *testing.T) require.NotRegexpT(t, "^start", "not starting") fmt.Println("passed") @@ -727,7 +746,7 @@ func ExampleNotRegexpT() { } func ExampleNotSame() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotSame(t *testing.T) require.NotSame(t, &staticVar, ptr("static string")) fmt.Println("passed") @@ -735,7 +754,7 @@ func ExampleNotSame() { } func ExampleNotSameT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotSameT(t *testing.T) require.NotSameT(t, &staticVar, ptr("static string")) fmt.Println("passed") @@ -743,7 +762,7 @@ func ExampleNotSameT() { } func ExampleNotSortedT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotSortedT(t *testing.T) require.NotSortedT(t, []int{3, 1, 3}) fmt.Println("passed") @@ -751,7 +770,7 @@ func ExampleNotSortedT() { } func ExampleNotSubset() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotSubset(t *testing.T) require.NotSubset(t, []int{1, 2, 3}, []int{4, 5}) fmt.Println("passed") @@ -759,7 +778,7 @@ func ExampleNotSubset() { } func ExampleNotZero() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestNotZero(t *testing.T) require.NotZero(t, 1) fmt.Println("passed") @@ -767,7 +786,7 @@ func ExampleNotZero() { } func ExamplePanics() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestPanics(t *testing.T) require.Panics(t, func() { panic("panicking") }) @@ -777,7 +796,7 @@ func ExamplePanics() { } func ExamplePanicsWithError() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestPanicsWithError(t *testing.T) require.PanicsWithError(t, assert.ErrTest.Error(), func() { panic(assert.ErrTest) }) @@ -787,7 +806,7 @@ func ExamplePanicsWithError() { } func ExamplePanicsWithValue() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestPanicsWithValue(t *testing.T) require.PanicsWithValue(t, "panicking", func() { panic("panicking") }) @@ -797,7 +816,7 @@ func ExamplePanicsWithValue() { } func ExamplePositive() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestPositive(t *testing.T) require.Positive(t, 1) fmt.Println("passed") @@ -805,7 +824,7 @@ func ExamplePositive() { } func ExamplePositiveT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestPositiveT(t *testing.T) require.PositiveT(t, 1) fmt.Println("passed") @@ -813,7 +832,7 @@ func ExamplePositiveT() { } func ExampleRegexp() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestRegexp(t *testing.T) require.Regexp(t, "^start", "starting") fmt.Println("passed") @@ -821,7 +840,7 @@ func ExampleRegexp() { } func ExampleRegexpT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestRegexpT(t *testing.T) require.RegexpT(t, "^start", "starting") fmt.Println("passed") @@ -829,7 +848,7 @@ func ExampleRegexpT() { } func ExampleSame() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSame(t *testing.T) require.Same(t, &staticVar, staticVarPtr) fmt.Println("passed") @@ -837,7 +856,7 @@ func ExampleSame() { } func ExampleSameT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSameT(t *testing.T) require.SameT(t, &staticVar, staticVarPtr) fmt.Println("passed") @@ -845,7 +864,7 @@ func ExampleSameT() { } func ExampleSeqContainsT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSeqContainsT(t *testing.T) require.SeqContainsT(t, slices.Values([]string{"A", "B"}), "A") fmt.Println("passed") @@ -853,7 +872,7 @@ func ExampleSeqContainsT() { } func ExampleSeqNotContainsT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSeqNotContainsT(t *testing.T) require.SeqNotContainsT(t, slices.Values([]string{"A", "B"}), "C") fmt.Println("passed") @@ -861,7 +880,7 @@ func ExampleSeqNotContainsT() { } func ExampleSliceContainsT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSliceContainsT(t *testing.T) require.SliceContainsT(t, []string{"A", "B"}, "A") fmt.Println("passed") @@ -869,7 +888,7 @@ func ExampleSliceContainsT() { } func ExampleSliceNotContainsT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSliceNotContainsT(t *testing.T) require.SliceNotContainsT(t, []string{"A", "B"}, "C") fmt.Println("passed") @@ -877,7 +896,7 @@ func ExampleSliceNotContainsT() { } func ExampleSliceNotSubsetT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSliceNotSubsetT(t *testing.T) require.SliceNotSubsetT(t, []int{1, 2, 3}, []int{4, 5}) fmt.Println("passed") @@ -885,7 +904,7 @@ func ExampleSliceNotSubsetT() { } func ExampleSliceSubsetT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSliceSubsetT(t *testing.T) require.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) fmt.Println("passed") @@ -893,7 +912,7 @@ func ExampleSliceSubsetT() { } func ExampleSortedT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSortedT(t *testing.T) require.SortedT(t, []int{1, 1, 3}) fmt.Println("passed") @@ -901,7 +920,7 @@ func ExampleSortedT() { } func ExampleStringContainsT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestStringContainsT(t *testing.T) require.StringContainsT(t, "AB", "A") fmt.Println("passed") @@ -909,7 +928,7 @@ func ExampleStringContainsT() { } func ExampleStringNotContainsT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestStringNotContainsT(t *testing.T) require.StringNotContainsT(t, "AB", "C") fmt.Println("passed") @@ -917,7 +936,7 @@ func ExampleStringNotContainsT() { } func ExampleSubset() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestSubset(t *testing.T) require.Subset(t, []int{1, 2, 3}, []int{1, 2}) fmt.Println("passed") @@ -925,7 +944,7 @@ func ExampleSubset() { } func ExampleTrue() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestTrue(t *testing.T) require.True(t, 1 == 1) fmt.Println("passed") @@ -933,7 +952,7 @@ func ExampleTrue() { } func ExampleTrueT() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestTrueT(t *testing.T) require.TrueT(t, 1 == 1) fmt.Println("passed") @@ -941,7 +960,7 @@ func ExampleTrueT() { } func ExampleWithinDuration() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestWithinDuration(t *testing.T) require.WithinDuration(t, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second) fmt.Println("passed") @@ -949,7 +968,7 @@ func ExampleWithinDuration() { } func ExampleWithinRange() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestWithinRange(t *testing.T) require.WithinRange(t, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC)) fmt.Println("passed") @@ -968,8 +987,16 @@ func ExampleWithinRange() { // no success example available. Please add some examples to produce a testable example. // } +// func ExampleYAMLMarshalAsT() { +// no success example available. Please add some examples to produce a testable example. +// } + +// func ExampleYAMLUnmarshalAsT() { +// no success example available. Please add some examples to produce a testable example. +// } + func ExampleZero() { - t := new(testing.T) + t := new(testing.T) // should come from testing, e.g. func TestZero(t *testing.T) require.Zero(t, 0) fmt.Println("passed") diff --git a/require/require_format.go b/require/require_format.go index 725c7b742..13089f2c4 100644 --- a/require/require_format.go +++ b/require/require_format.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package require @@ -254,14 +253,14 @@ func Eventuallyf(t T, condition func() bool, waitFor time.Duration, tick time.Du t.FailNow() } -// EventuallyWithTf is the same as [EventuallyWithT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// EventuallyWithf is the same as [EventuallyWith], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. -func EventuallyWithTf(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...any) { +func EventuallyWithf(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.EventuallyWithT(t, condition, waitFor, tick, forwardArgs(msg, args)) { + if assertions.EventuallyWith(t, condition, waitFor, tick, forwardArgs(msg, args)) { return } @@ -852,6 +851,34 @@ func JSONEqTf[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msg string, args t.FailNow() } +// JSONMarshalAsTf is the same as [JSONMarshalAsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func JSONMarshalAsTf[EDoc Text](t T, expected EDoc, object any, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.JSONMarshalAsT[EDoc](t, expected, object, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// JSONUnmarshalAsTf is the same as [JSONUnmarshalAsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func JSONUnmarshalAsTf[Object any, ADoc Text](t T, expected Object, jazon ADoc, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.JSONUnmarshalAsT[Object, ADoc](t, expected, jazon, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // Kindf is the same as [Kind], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. @@ -1034,6 +1061,20 @@ func NoErrorf(t T, err error, msg string, args ...any) { t.FailNow() } +// NoGoRoutineLeakf is the same as [NoGoRoutineLeak], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func NoGoRoutineLeakf(t T, inside func(), msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.NoGoRoutineLeak(t, inside, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // NotContainsf is the same as [NotContains], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. @@ -1678,6 +1719,34 @@ func YAMLEqTf[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msg string, args t.FailNow() } +// YAMLMarshalAsTf is the same as [YAMLMarshalAsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func YAMLMarshalAsTf[EDoc Text](t T, expected EDoc, object any, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.YAMLMarshalAsT[EDoc](t, expected, object, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// YAMLUnmarshalAsTf is the same as [YAMLUnmarshalAsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func YAMLUnmarshalAsTf[Object any, ADoc Text](t T, expected Object, jazon ADoc, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.YAMLUnmarshalAsT[Object, ADoc](t, expected, jazon, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // Zerof is the same as [Zero], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. diff --git a/require/require_format_test.go b/require/require_format_test.go index 01f4d01c2..d8e5f0b5f 100644 --- a/require/require_format_test.go +++ b/require/require_format_test.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package require @@ -358,11 +357,11 @@ func TestEventuallyf(t *testing.T) { }) } -func TestEventuallyWithTf(t *testing.T) { +func TestEventuallyWithf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - EventuallyWithTf(t, func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") + EventuallyWithf(t, func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") // require functions don't return a value }) @@ -370,10 +369,10 @@ func TestEventuallyWithTf(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - EventuallyWithTf(mock, func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") + EventuallyWithf(mock, func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") // require functions don't return a value if !mock.failed { - t.Error("EventuallyWithT should call FailNow()") + t.Error("EventuallyWith should call FailNow()") } }) } @@ -1206,6 +1205,46 @@ func TestJSONEqTf(t *testing.T) { }) } +func TestJSONMarshalAsTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + JSONMarshalAsTf(t, []byte(`{"A": "a"}`), dummyStruct{A: "a"}, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + JSONMarshalAsTf(mock, `[{"foo": "bar"}, {"hello": "world"}]`, 1, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("JSONMarshalAsT should call FailNow()") + } + }) +} + +func TestJSONUnmarshalAsTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + JSONUnmarshalAsTf(t, dummyStruct{A: "a"}, []byte(`{"A": "a"}`), "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + JSONUnmarshalAsTf(mock, 1, `[{"foo": "bar"}, {"hello": "world"}]`, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("JSONUnmarshalAsT should call FailNow()") + } + }) +} + func TestKindf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1466,6 +1505,11 @@ func TestNoErrorf(t *testing.T) { }) } +func TestNoGoRoutineLeakf(t *testing.T) { + t.Parallel() + t.Skip() // this function doesn't have tests yet: feed the original function with examples to test. +} + func TestNotContainsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -2359,6 +2403,28 @@ func TestYAMLEqTf(t *testing.T) { }) } +func TestYAMLMarshalAsTf(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + Panicsf(t, func() { + YAMLMarshalAsTf(t, "key: value", "key: value", "test message") + }, "should panic without the yaml feature enabled.") + }) +} + +func TestYAMLUnmarshalAsTf(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + Panicsf(t, func() { + YAMLUnmarshalAsTf(t, "key: value", "key: value", "test message") + }, "should panic without the yaml feature enabled.") + }) +} + func TestZerof(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { diff --git a/require/require_forward.go b/require/require_forward.go index b5febedb2..1f9a9c2f4 100644 --- a/require/require_forward.go +++ b/require/require_forward.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package require @@ -21,13 +20,13 @@ import ( // // Upon failure, the test [T] is marked as failed and stops execution. type Assertions struct { - t T + T } // New makes a new [Assertions] object for the specified [T] (e.g. [testing.T]). func New(t T) *Assertions { return &Assertions{ - t: t, + T: t, } } @@ -35,2286 +34,2314 @@ func New(t T) *Assertions { // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Condition(comp Comparison, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Condition(a.t, comp, msgAndArgs...) { + if assertions.Condition(a.T, comp, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Conditionf is the same as [Assertions.Condition], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Conditionf(comp Comparison, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Condition(a.t, comp, forwardArgs(msg, args)) { + if assertions.Condition(a.T, comp, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Contains is the same as [Contains], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Contains(s any, contains any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Contains(a.t, s, contains, msgAndArgs...) { + if assertions.Contains(a.T, s, contains, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Containsf is the same as [Assertions.Contains], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Containsf(s any, contains any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Contains(a.t, s, contains, forwardArgs(msg, args)) { + if assertions.Contains(a.T, s, contains, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // DirExists is the same as [DirExists], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) DirExists(path string, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.DirExists(a.t, path, msgAndArgs...) { + if assertions.DirExists(a.T, path, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // DirExistsf is the same as [Assertions.DirExists], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) DirExistsf(path string, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.DirExists(a.t, path, forwardArgs(msg, args)) { + if assertions.DirExists(a.T, path, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // DirNotExists is the same as [DirNotExists], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) DirNotExists(path string, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.DirNotExists(a.t, path, msgAndArgs...) { + if assertions.DirNotExists(a.T, path, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // DirNotExistsf is the same as [Assertions.DirNotExists], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) DirNotExistsf(path string, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.DirNotExists(a.t, path, forwardArgs(msg, args)) { + if assertions.DirNotExists(a.T, path, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // ElementsMatch is the same as [ElementsMatch], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) ElementsMatch(listA any, listB any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.ElementsMatch(a.t, listA, listB, msgAndArgs...) { + if assertions.ElementsMatch(a.T, listA, listB, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // ElementsMatchf is the same as [Assertions.ElementsMatch], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) ElementsMatchf(listA any, listB any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.ElementsMatch(a.t, listA, listB, forwardArgs(msg, args)) { + if assertions.ElementsMatch(a.T, listA, listB, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Empty is the same as [Empty], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Empty(object any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Empty(a.t, object, msgAndArgs...) { + if assertions.Empty(a.T, object, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Emptyf is the same as [Assertions.Empty], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Emptyf(object any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Empty(a.t, object, forwardArgs(msg, args)) { + if assertions.Empty(a.T, object, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Equal is the same as [Equal], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Equal(expected any, actual any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Equal(a.t, expected, actual, msgAndArgs...) { + if assertions.Equal(a.T, expected, actual, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Equalf is the same as [Assertions.Equal], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Equalf(expected any, actual any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Equal(a.t, expected, actual, forwardArgs(msg, args)) { + if assertions.Equal(a.T, expected, actual, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // EqualError is the same as [EqualError], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) EqualError(err error, errString string, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.EqualError(a.t, err, errString, msgAndArgs...) { + if assertions.EqualError(a.T, err, errString, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // EqualErrorf is the same as [Assertions.EqualError], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) EqualErrorf(err error, errString string, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.EqualError(a.t, err, errString, forwardArgs(msg, args)) { + if assertions.EqualError(a.T, err, errString, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // EqualExportedValues is the same as [EqualExportedValues], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) EqualExportedValues(expected any, actual any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.EqualExportedValues(a.t, expected, actual, msgAndArgs...) { + if assertions.EqualExportedValues(a.T, expected, actual, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // EqualExportedValuesf is the same as [Assertions.EqualExportedValues], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) EqualExportedValuesf(expected any, actual any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.EqualExportedValues(a.t, expected, actual, forwardArgs(msg, args)) { + if assertions.EqualExportedValues(a.T, expected, actual, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // EqualValues is the same as [EqualValues], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) EqualValues(expected any, actual any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.EqualValues(a.t, expected, actual, msgAndArgs...) { + if assertions.EqualValues(a.T, expected, actual, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // EqualValuesf is the same as [Assertions.EqualValues], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) EqualValuesf(expected any, actual any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.EqualValues(a.t, expected, actual, forwardArgs(msg, args)) { + if assertions.EqualValues(a.T, expected, actual, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Error is the same as [Error], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Error(err error, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Error(a.t, err, msgAndArgs...) { + if assertions.Error(a.T, err, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Errorf is the same as [Assertions.Error], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Errorf(err error, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Error(a.t, err, forwardArgs(msg, args)) { + if assertions.Error(a.T, err, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // ErrorAs is the same as [ErrorAs], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) ErrorAs(err error, target any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.ErrorAs(a.t, err, target, msgAndArgs...) { + if assertions.ErrorAs(a.T, err, target, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // ErrorAsf is the same as [Assertions.ErrorAs], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) ErrorAsf(err error, target any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.ErrorAs(a.t, err, target, forwardArgs(msg, args)) { + if assertions.ErrorAs(a.T, err, target, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // ErrorContains is the same as [ErrorContains], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) ErrorContains(err error, contains string, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.ErrorContains(a.t, err, contains, msgAndArgs...) { + if assertions.ErrorContains(a.T, err, contains, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // ErrorContainsf is the same as [Assertions.ErrorContains], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) ErrorContainsf(err error, contains string, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.ErrorContains(a.t, err, contains, forwardArgs(msg, args)) { + if assertions.ErrorContains(a.T, err, contains, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // ErrorIs is the same as [ErrorIs], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.ErrorIs(a.t, err, target, msgAndArgs...) { + if assertions.ErrorIs(a.T, err, target, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // ErrorIsf is the same as [Assertions.ErrorIs], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.ErrorIs(a.t, err, target, forwardArgs(msg, args)) { + if assertions.ErrorIs(a.T, err, target, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Eventually is the same as [Eventually], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Eventually(a.t, condition, waitFor, tick, msgAndArgs...) { + if assertions.Eventually(a.T, condition, waitFor, tick, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Eventuallyf is the same as [Assertions.Eventually], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Eventually(a.t, condition, waitFor, tick, forwardArgs(msg, args)) { + if assertions.Eventually(a.T, condition, waitFor, tick, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } -// EventuallyWithT is the same as [EventuallyWithT], as a method rather than a package-level function. +// EventuallyWith is the same as [EventuallyWith], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. -func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { +func (a *Assertions) EventuallyWith(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) { + if assertions.EventuallyWith(a.T, condition, waitFor, tick, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } -// EventuallyWithTf is the same as [Assertions.EventuallyWithT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// EventuallyWithf is the same as [Assertions.EventuallyWith], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. -func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...any) { - if h, ok := a.t.(H); ok { +func (a *Assertions) EventuallyWithf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...any) { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.EventuallyWithT(a.t, condition, waitFor, tick, forwardArgs(msg, args)) { + if assertions.EventuallyWith(a.T, condition, waitFor, tick, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Exactly is the same as [Exactly], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Exactly(expected any, actual any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Exactly(a.t, expected, actual, msgAndArgs...) { + if assertions.Exactly(a.T, expected, actual, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Exactlyf is the same as [Assertions.Exactly], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Exactlyf(expected any, actual any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Exactly(a.t, expected, actual, forwardArgs(msg, args)) { + if assertions.Exactly(a.T, expected, actual, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Fail is the same as [Fail], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Fail(failureMessage string, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - _ = assertions.Fail(a.t, failureMessage, msgAndArgs...) + _ = assertions.Fail(a.T, failureMessage, msgAndArgs...) - a.t.FailNow() + a.T.FailNow() } // Failf is the same as [Assertions.Fail], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Failf(failureMessage string, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - _ = assertions.Fail(a.t, failureMessage, forwardArgs(msg, args)) + _ = assertions.Fail(a.T, failureMessage, forwardArgs(msg, args)) - a.t.FailNow() + a.T.FailNow() } // FailNow is the same as [FailNow], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - _ = assertions.FailNow(a.t, failureMessage, msgAndArgs...) + _ = assertions.FailNow(a.T, failureMessage, msgAndArgs...) - a.t.FailNow() + a.T.FailNow() } // FailNowf is the same as [Assertions.FailNow], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) FailNowf(failureMessage string, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - _ = assertions.FailNow(a.t, failureMessage, forwardArgs(msg, args)) + _ = assertions.FailNow(a.T, failureMessage, forwardArgs(msg, args)) - a.t.FailNow() + a.T.FailNow() } // False is the same as [False], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) False(value bool, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.False(a.t, value, msgAndArgs...) { + if assertions.False(a.T, value, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Falsef is the same as [Assertions.False], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Falsef(value bool, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.False(a.t, value, forwardArgs(msg, args)) { + if assertions.False(a.T, value, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // FileEmpty is the same as [FileEmpty], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) FileEmpty(path string, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.FileEmpty(a.t, path, msgAndArgs...) { + if assertions.FileEmpty(a.T, path, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // FileEmptyf is the same as [Assertions.FileEmpty], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) FileEmptyf(path string, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.FileEmpty(a.t, path, forwardArgs(msg, args)) { + if assertions.FileEmpty(a.T, path, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // FileExists is the same as [FileExists], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) FileExists(path string, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.FileExists(a.t, path, msgAndArgs...) { + if assertions.FileExists(a.T, path, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // FileExistsf is the same as [Assertions.FileExists], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) FileExistsf(path string, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.FileExists(a.t, path, forwardArgs(msg, args)) { + if assertions.FileExists(a.T, path, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // FileNotEmpty is the same as [FileNotEmpty], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) FileNotEmpty(path string, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.FileNotEmpty(a.t, path, msgAndArgs...) { + if assertions.FileNotEmpty(a.T, path, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // FileNotEmptyf is the same as [Assertions.FileNotEmpty], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) FileNotEmptyf(path string, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.FileNotEmpty(a.t, path, forwardArgs(msg, args)) { + if assertions.FileNotEmpty(a.T, path, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // FileNotExists is the same as [FileNotExists], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) FileNotExists(path string, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.FileNotExists(a.t, path, msgAndArgs...) { + if assertions.FileNotExists(a.T, path, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // FileNotExistsf is the same as [Assertions.FileNotExists], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) FileNotExistsf(path string, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.FileNotExists(a.t, path, forwardArgs(msg, args)) { + if assertions.FileNotExists(a.T, path, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Greater is the same as [Greater], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Greater(e1 any, e2 any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Greater(a.t, e1, e2, msgAndArgs...) { + if assertions.Greater(a.T, e1, e2, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Greaterf is the same as [Assertions.Greater], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Greaterf(e1 any, e2 any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Greater(a.t, e1, e2, forwardArgs(msg, args)) { + if assertions.Greater(a.T, e1, e2, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // GreaterOrEqual is the same as [GreaterOrEqual], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) GreaterOrEqual(e1 any, e2 any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.GreaterOrEqual(a.t, e1, e2, msgAndArgs...) { + if assertions.GreaterOrEqual(a.T, e1, e2, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // GreaterOrEqualf is the same as [Assertions.GreaterOrEqual], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) GreaterOrEqualf(e1 any, e2 any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.GreaterOrEqual(a.t, e1, e2, forwardArgs(msg, args)) { + if assertions.GreaterOrEqual(a.T, e1, e2, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // HTTPBodyContains is the same as [HTTPBodyContains], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...) { + if assertions.HTTPBodyContains(a.T, handler, method, url, values, str, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // HTTPBodyContainsf is the same as [Assertions.HTTPBodyContains], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.HTTPBodyContains(a.t, handler, method, url, values, str, forwardArgs(msg, args)) { + if assertions.HTTPBodyContains(a.T, handler, method, url, values, str, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // HTTPBodyNotContains is the same as [HTTPBodyNotContains], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...) { + if assertions.HTTPBodyNotContains(a.T, handler, method, url, values, str, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // HTTPBodyNotContainsf is the same as [Assertions.HTTPBodyNotContains], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.HTTPBodyNotContains(a.t, handler, method, url, values, str, forwardArgs(msg, args)) { + if assertions.HTTPBodyNotContains(a.T, handler, method, url, values, str, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // HTTPError is the same as [HTTPError], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.HTTPError(a.t, handler, method, url, values, msgAndArgs...) { + if assertions.HTTPError(a.T, handler, method, url, values, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // HTTPErrorf is the same as [Assertions.HTTPError], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.HTTPError(a.t, handler, method, url, values, forwardArgs(msg, args)) { + if assertions.HTTPError(a.T, handler, method, url, values, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // HTTPRedirect is the same as [HTTPRedirect], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...) { + if assertions.HTTPRedirect(a.T, handler, method, url, values, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // HTTPRedirectf is the same as [Assertions.HTTPRedirect], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.HTTPRedirect(a.t, handler, method, url, values, forwardArgs(msg, args)) { + if assertions.HTTPRedirect(a.T, handler, method, url, values, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // HTTPStatusCode is the same as [HTTPStatusCode], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...) { + if assertions.HTTPStatusCode(a.T, handler, method, url, values, statuscode, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // HTTPStatusCodef is the same as [Assertions.HTTPStatusCode], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.HTTPStatusCode(a.t, handler, method, url, values, statuscode, forwardArgs(msg, args)) { + if assertions.HTTPStatusCode(a.T, handler, method, url, values, statuscode, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // HTTPSuccess is the same as [HTTPSuccess], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...) { + if assertions.HTTPSuccess(a.T, handler, method, url, values, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // HTTPSuccessf is the same as [Assertions.HTTPSuccess], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.HTTPSuccess(a.t, handler, method, url, values, forwardArgs(msg, args)) { + if assertions.HTTPSuccess(a.T, handler, method, url, values, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Implements is the same as [Implements], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Implements(interfaceObject any, object any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Implements(a.t, interfaceObject, object, msgAndArgs...) { + if assertions.Implements(a.T, interfaceObject, object, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Implementsf is the same as [Assertions.Implements], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Implementsf(interfaceObject any, object any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Implements(a.t, interfaceObject, object, forwardArgs(msg, args)) { + if assertions.Implements(a.T, interfaceObject, object, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // InDelta is the same as [InDelta], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) InDelta(expected any, actual any, delta float64, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.InDelta(a.t, expected, actual, delta, msgAndArgs...) { + if assertions.InDelta(a.T, expected, actual, delta, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // InDeltaf is the same as [Assertions.InDelta], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) InDeltaf(expected any, actual any, delta float64, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.InDelta(a.t, expected, actual, delta, forwardArgs(msg, args)) { + if assertions.InDelta(a.T, expected, actual, delta, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // InDeltaMapValues is the same as [InDeltaMapValues], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) InDeltaMapValues(expected any, actual any, delta float64, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...) { + if assertions.InDeltaMapValues(a.T, expected, actual, delta, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // InDeltaMapValuesf is the same as [Assertions.InDeltaMapValues], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) InDeltaMapValuesf(expected any, actual any, delta float64, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.InDeltaMapValues(a.t, expected, actual, delta, forwardArgs(msg, args)) { + if assertions.InDeltaMapValues(a.T, expected, actual, delta, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // InDeltaSlice is the same as [InDeltaSlice], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) InDeltaSlice(expected any, actual any, delta float64, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) { + if assertions.InDeltaSlice(a.T, expected, actual, delta, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // InDeltaSlicef is the same as [Assertions.InDeltaSlice], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) InDeltaSlicef(expected any, actual any, delta float64, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.InDeltaSlice(a.t, expected, actual, delta, forwardArgs(msg, args)) { + if assertions.InDeltaSlice(a.T, expected, actual, delta, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // InEpsilon is the same as [InEpsilon], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) InEpsilon(expected any, actual any, epsilon float64, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) { + if assertions.InEpsilon(a.T, expected, actual, epsilon, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // InEpsilonf is the same as [Assertions.InEpsilon], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) InEpsilonf(expected any, actual any, epsilon float64, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.InEpsilon(a.t, expected, actual, epsilon, forwardArgs(msg, args)) { + if assertions.InEpsilon(a.T, expected, actual, epsilon, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // InEpsilonSlice is the same as [InEpsilonSlice], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) InEpsilonSlice(expected any, actual any, epsilon float64, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) { + if assertions.InEpsilonSlice(a.T, expected, actual, epsilon, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // InEpsilonSlicef is the same as [Assertions.InEpsilonSlice], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) InEpsilonSlicef(expected any, actual any, epsilon float64, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.InEpsilonSlice(a.t, expected, actual, epsilon, forwardArgs(msg, args)) { + if assertions.InEpsilonSlice(a.T, expected, actual, epsilon, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // IsDecreasing is the same as [IsDecreasing], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) IsDecreasing(collection any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.IsDecreasing(a.t, collection, msgAndArgs...) { + if assertions.IsDecreasing(a.T, collection, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // IsDecreasingf is the same as [Assertions.IsDecreasing], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) IsDecreasingf(collection any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.IsDecreasing(a.t, collection, forwardArgs(msg, args)) { + if assertions.IsDecreasing(a.T, collection, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // IsIncreasing is the same as [IsIncreasing], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) IsIncreasing(collection any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.IsIncreasing(a.t, collection, msgAndArgs...) { + if assertions.IsIncreasing(a.T, collection, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // IsIncreasingf is the same as [Assertions.IsIncreasing], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) IsIncreasingf(collection any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.IsIncreasing(a.t, collection, forwardArgs(msg, args)) { + if assertions.IsIncreasing(a.T, collection, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // IsNonDecreasing is the same as [IsNonDecreasing], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) IsNonDecreasing(collection any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.IsNonDecreasing(a.t, collection, msgAndArgs...) { + if assertions.IsNonDecreasing(a.T, collection, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // IsNonDecreasingf is the same as [Assertions.IsNonDecreasing], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) IsNonDecreasingf(collection any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.IsNonDecreasing(a.t, collection, forwardArgs(msg, args)) { + if assertions.IsNonDecreasing(a.T, collection, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // IsNonIncreasing is the same as [IsNonIncreasing], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) IsNonIncreasing(collection any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.IsNonIncreasing(a.t, collection, msgAndArgs...) { + if assertions.IsNonIncreasing(a.T, collection, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // IsNonIncreasingf is the same as [Assertions.IsNonIncreasing], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) IsNonIncreasingf(collection any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.IsNonIncreasing(a.t, collection, forwardArgs(msg, args)) { + if assertions.IsNonIncreasing(a.T, collection, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // IsNotType is the same as [IsNotType], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) IsNotType(theType any, object any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.IsNotType(a.t, theType, object, msgAndArgs...) { + if assertions.IsNotType(a.T, theType, object, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // IsNotTypef is the same as [Assertions.IsNotType], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) IsNotTypef(theType any, object any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.IsNotType(a.t, theType, object, forwardArgs(msg, args)) { + if assertions.IsNotType(a.T, theType, object, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // IsType is the same as [IsType], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) IsType(expectedType any, object any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.IsType(a.t, expectedType, object, msgAndArgs...) { + if assertions.IsType(a.T, expectedType, object, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // IsTypef is the same as [Assertions.IsType], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) IsTypef(expectedType any, object any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.IsType(a.t, expectedType, object, forwardArgs(msg, args)) { + if assertions.IsType(a.T, expectedType, object, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // JSONEq is the same as [JSONEq], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.JSONEq(a.t, expected, actual, msgAndArgs...) { + if assertions.JSONEq(a.T, expected, actual, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // JSONEqf is the same as [Assertions.JSONEq], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.JSONEq(a.t, expected, actual, forwardArgs(msg, args)) { + if assertions.JSONEq(a.T, expected, actual, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // JSONEqBytes is the same as [JSONEqBytes], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) JSONEqBytes(expected []byte, actual []byte, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.JSONEqBytes(a.t, expected, actual, msgAndArgs...) { + if assertions.JSONEqBytes(a.T, expected, actual, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // JSONEqBytesf is the same as [Assertions.JSONEqBytes], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) JSONEqBytesf(expected []byte, actual []byte, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.JSONEqBytes(a.t, expected, actual, forwardArgs(msg, args)) { + if assertions.JSONEqBytes(a.T, expected, actual, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Kind is the same as [Kind], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Kind(expectedKind reflect.Kind, object any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Kind(a.t, expectedKind, object, msgAndArgs...) { + if assertions.Kind(a.T, expectedKind, object, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Kindf is the same as [Assertions.Kind], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Kindf(expectedKind reflect.Kind, object any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Kind(a.t, expectedKind, object, forwardArgs(msg, args)) { + if assertions.Kind(a.T, expectedKind, object, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Len is the same as [Len], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Len(object any, length int, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Len(a.t, object, length, msgAndArgs...) { + if assertions.Len(a.T, object, length, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Lenf is the same as [Assertions.Len], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Lenf(object any, length int, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Len(a.t, object, length, forwardArgs(msg, args)) { + if assertions.Len(a.T, object, length, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Less is the same as [Less], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Less(e1 any, e2 any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Less(a.t, e1, e2, msgAndArgs...) { + if assertions.Less(a.T, e1, e2, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Lessf is the same as [Assertions.Less], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Lessf(e1 any, e2 any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Less(a.t, e1, e2, forwardArgs(msg, args)) { + if assertions.Less(a.T, e1, e2, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // LessOrEqual is the same as [LessOrEqual], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) LessOrEqual(e1 any, e2 any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.LessOrEqual(a.t, e1, e2, msgAndArgs...) { + if assertions.LessOrEqual(a.T, e1, e2, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // LessOrEqualf is the same as [Assertions.LessOrEqual], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) LessOrEqualf(e1 any, e2 any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.LessOrEqual(a.t, e1, e2, forwardArgs(msg, args)) { + if assertions.LessOrEqual(a.T, e1, e2, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Negative is the same as [Negative], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Negative(e any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Negative(a.t, e, msgAndArgs...) { + if assertions.Negative(a.T, e, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Negativef is the same as [Assertions.Negative], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Negativef(e any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Negative(a.t, e, forwardArgs(msg, args)) { + if assertions.Negative(a.T, e, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Never is the same as [Never], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Never(a.t, condition, waitFor, tick, msgAndArgs...) { + if assertions.Never(a.T, condition, waitFor, tick, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Neverf is the same as [Assertions.Never], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Never(a.t, condition, waitFor, tick, forwardArgs(msg, args)) { + if assertions.Never(a.T, condition, waitFor, tick, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Nil is the same as [Nil], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Nil(object any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Nil(a.t, object, msgAndArgs...) { + if assertions.Nil(a.T, object, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Nilf is the same as [Assertions.Nil], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Nilf(object any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Nil(a.t, object, forwardArgs(msg, args)) { + if assertions.Nil(a.T, object, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // NoError is the same as [NoError], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NoError(err error, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NoError(a.t, err, msgAndArgs...) { + if assertions.NoError(a.T, err, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // NoErrorf is the same as [Assertions.NoError], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NoErrorf(err error, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NoError(a.t, err, forwardArgs(msg, args)) { + if assertions.NoError(a.T, err, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() +} + +// NoGoRoutineLeak is the same as [NoGoRoutineLeak], as a method rather than a package-level function. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func (a *Assertions) NoGoRoutineLeak(inside func(), msgAndArgs ...any) { + if h, ok := a.T.(H); ok { + h.Helper() + } + if assertions.NoGoRoutineLeak(a.T, inside, msgAndArgs...) { + return + } + + a.T.FailNow() +} + +// NoGoRoutineLeakf is the same as [Assertions.NoGoRoutineLeak], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func (a *Assertions) NoGoRoutineLeakf(inside func(), msg string, args ...any) { + if h, ok := a.T.(H); ok { + h.Helper() + } + if assertions.NoGoRoutineLeak(a.T, inside, forwardArgs(msg, args)) { + return + } + + a.T.FailNow() } // NotContains is the same as [NotContains], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotContains(s any, contains any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotContains(a.t, s, contains, msgAndArgs...) { + if assertions.NotContains(a.T, s, contains, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // NotContainsf is the same as [Assertions.NotContains], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotContainsf(s any, contains any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotContains(a.t, s, contains, forwardArgs(msg, args)) { + if assertions.NotContains(a.T, s, contains, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // NotElementsMatch is the same as [NotElementsMatch], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotElementsMatch(listA any, listB any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotElementsMatch(a.t, listA, listB, msgAndArgs...) { + if assertions.NotElementsMatch(a.T, listA, listB, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // NotElementsMatchf is the same as [Assertions.NotElementsMatch], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotElementsMatchf(listA any, listB any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotElementsMatch(a.t, listA, listB, forwardArgs(msg, args)) { + if assertions.NotElementsMatch(a.T, listA, listB, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // NotEmpty is the same as [NotEmpty], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotEmpty(object any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotEmpty(a.t, object, msgAndArgs...) { + if assertions.NotEmpty(a.T, object, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // NotEmptyf is the same as [Assertions.NotEmpty], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotEmptyf(object any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotEmpty(a.t, object, forwardArgs(msg, args)) { + if assertions.NotEmpty(a.T, object, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // NotEqual is the same as [NotEqual], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotEqual(expected any, actual any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotEqual(a.t, expected, actual, msgAndArgs...) { + if assertions.NotEqual(a.T, expected, actual, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // NotEqualf is the same as [Assertions.NotEqual], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotEqualf(expected any, actual any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotEqual(a.t, expected, actual, forwardArgs(msg, args)) { + if assertions.NotEqual(a.T, expected, actual, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // NotEqualValues is the same as [NotEqualValues], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotEqualValues(expected any, actual any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotEqualValues(a.t, expected, actual, msgAndArgs...) { + if assertions.NotEqualValues(a.T, expected, actual, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // NotEqualValuesf is the same as [Assertions.NotEqualValues], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotEqualValuesf(expected any, actual any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotEqualValues(a.t, expected, actual, forwardArgs(msg, args)) { + if assertions.NotEqualValues(a.T, expected, actual, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // NotErrorAs is the same as [NotErrorAs], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotErrorAs(err error, target any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotErrorAs(a.t, err, target, msgAndArgs...) { + if assertions.NotErrorAs(a.T, err, target, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // NotErrorAsf is the same as [Assertions.NotErrorAs], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotErrorAsf(err error, target any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotErrorAs(a.t, err, target, forwardArgs(msg, args)) { + if assertions.NotErrorAs(a.T, err, target, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // NotErrorIs is the same as [NotErrorIs], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotErrorIs(a.t, err, target, msgAndArgs...) { + if assertions.NotErrorIs(a.T, err, target, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // NotErrorIsf is the same as [Assertions.NotErrorIs], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotErrorIs(a.t, err, target, forwardArgs(msg, args)) { + if assertions.NotErrorIs(a.T, err, target, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // NotImplements is the same as [NotImplements], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotImplements(interfaceObject any, object any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotImplements(a.t, interfaceObject, object, msgAndArgs...) { + if assertions.NotImplements(a.T, interfaceObject, object, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // NotImplementsf is the same as [Assertions.NotImplements], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotImplementsf(interfaceObject any, object any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotImplements(a.t, interfaceObject, object, forwardArgs(msg, args)) { + if assertions.NotImplements(a.T, interfaceObject, object, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // NotKind is the same as [NotKind], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotKind(expectedKind reflect.Kind, object any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotKind(a.t, expectedKind, object, msgAndArgs...) { + if assertions.NotKind(a.T, expectedKind, object, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // NotKindf is the same as [Assertions.NotKind], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotKindf(expectedKind reflect.Kind, object any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotKind(a.t, expectedKind, object, forwardArgs(msg, args)) { + if assertions.NotKind(a.T, expectedKind, object, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // NotNil is the same as [NotNil], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotNil(object any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotNil(a.t, object, msgAndArgs...) { + if assertions.NotNil(a.T, object, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // NotNilf is the same as [Assertions.NotNil], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotNilf(object any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotNil(a.t, object, forwardArgs(msg, args)) { + if assertions.NotNil(a.T, object, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // NotPanics is the same as [NotPanics], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotPanics(f func(), msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotPanics(a.t, f, msgAndArgs...) { + if assertions.NotPanics(a.T, f, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // NotPanicsf is the same as [Assertions.NotPanics], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotPanicsf(f func(), msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotPanics(a.t, f, forwardArgs(msg, args)) { + if assertions.NotPanics(a.T, f, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // NotRegexp is the same as [NotRegexp], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotRegexp(rx any, actual any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotRegexp(a.t, rx, actual, msgAndArgs...) { + if assertions.NotRegexp(a.T, rx, actual, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // NotRegexpf is the same as [Assertions.NotRegexp], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotRegexpf(rx any, actual any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotRegexp(a.t, rx, actual, forwardArgs(msg, args)) { + if assertions.NotRegexp(a.T, rx, actual, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // NotSame is the same as [NotSame], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotSame(expected any, actual any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotSame(a.t, expected, actual, msgAndArgs...) { + if assertions.NotSame(a.T, expected, actual, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // NotSamef is the same as [Assertions.NotSame], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotSamef(expected any, actual any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotSame(a.t, expected, actual, forwardArgs(msg, args)) { + if assertions.NotSame(a.T, expected, actual, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // NotSubset is the same as [NotSubset], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotSubset(list any, subset any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotSubset(a.t, list, subset, msgAndArgs...) { + if assertions.NotSubset(a.T, list, subset, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // NotSubsetf is the same as [Assertions.NotSubset], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotSubsetf(list any, subset any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotSubset(a.t, list, subset, forwardArgs(msg, args)) { + if assertions.NotSubset(a.T, list, subset, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // NotZero is the same as [NotZero], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotZero(i any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotZero(a.t, i, msgAndArgs...) { + if assertions.NotZero(a.T, i, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // NotZerof is the same as [Assertions.NotZero], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) NotZerof(i any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.NotZero(a.t, i, forwardArgs(msg, args)) { + if assertions.NotZero(a.T, i, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Panics is the same as [Panics], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Panics(f func(), msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Panics(a.t, f, msgAndArgs...) { + if assertions.Panics(a.T, f, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Panicsf is the same as [Assertions.Panics], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Panicsf(f func(), msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Panics(a.t, f, forwardArgs(msg, args)) { + if assertions.Panics(a.T, f, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // PanicsWithError is the same as [PanicsWithError], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) PanicsWithError(errString string, f func(), msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.PanicsWithError(a.t, errString, f, msgAndArgs...) { + if assertions.PanicsWithError(a.T, errString, f, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // PanicsWithErrorf is the same as [Assertions.PanicsWithError], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) PanicsWithErrorf(errString string, f func(), msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.PanicsWithError(a.t, errString, f, forwardArgs(msg, args)) { + if assertions.PanicsWithError(a.T, errString, f, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // PanicsWithValue is the same as [PanicsWithValue], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) PanicsWithValue(expected any, f func(), msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.PanicsWithValue(a.t, expected, f, msgAndArgs...) { + if assertions.PanicsWithValue(a.T, expected, f, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // PanicsWithValuef is the same as [Assertions.PanicsWithValue], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) PanicsWithValuef(expected any, f func(), msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.PanicsWithValue(a.t, expected, f, forwardArgs(msg, args)) { + if assertions.PanicsWithValue(a.T, expected, f, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Positive is the same as [Positive], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Positive(e any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Positive(a.t, e, msgAndArgs...) { + if assertions.Positive(a.T, e, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Positivef is the same as [Assertions.Positive], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Positivef(e any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Positive(a.t, e, forwardArgs(msg, args)) { + if assertions.Positive(a.T, e, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Regexp is the same as [Regexp], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Regexp(rx any, actual any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Regexp(a.t, rx, actual, msgAndArgs...) { + if assertions.Regexp(a.T, rx, actual, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Regexpf is the same as [Assertions.Regexp], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Regexpf(rx any, actual any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Regexp(a.t, rx, actual, forwardArgs(msg, args)) { + if assertions.Regexp(a.T, rx, actual, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Same is the same as [Same], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Same(expected any, actual any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Same(a.t, expected, actual, msgAndArgs...) { + if assertions.Same(a.T, expected, actual, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Samef is the same as [Assertions.Same], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Samef(expected any, actual any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Same(a.t, expected, actual, forwardArgs(msg, args)) { + if assertions.Same(a.T, expected, actual, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Subset is the same as [Subset], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Subset(list any, subset any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Subset(a.t, list, subset, msgAndArgs...) { + if assertions.Subset(a.T, list, subset, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Subsetf is the same as [Assertions.Subset], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Subsetf(list any, subset any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Subset(a.t, list, subset, forwardArgs(msg, args)) { + if assertions.Subset(a.T, list, subset, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // True is the same as [True], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) True(value bool, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.True(a.t, value, msgAndArgs...) { + if assertions.True(a.T, value, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Truef is the same as [Assertions.True], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Truef(value bool, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.True(a.t, value, forwardArgs(msg, args)) { + if assertions.True(a.T, value, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // WithinDuration is the same as [WithinDuration], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.WithinDuration(a.t, expected, actual, delta, msgAndArgs...) { + if assertions.WithinDuration(a.T, expected, actual, delta, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // WithinDurationf is the same as [Assertions.WithinDuration], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.WithinDuration(a.t, expected, actual, delta, forwardArgs(msg, args)) { + if assertions.WithinDuration(a.T, expected, actual, delta, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // WithinRange is the same as [WithinRange], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.WithinRange(a.t, actual, start, end, msgAndArgs...) { + if assertions.WithinRange(a.T, actual, start, end, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // WithinRangef is the same as [Assertions.WithinRange], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.WithinRange(a.t, actual, start, end, forwardArgs(msg, args)) { + if assertions.WithinRange(a.T, actual, start, end, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // YAMLEq is the same as [YAMLEq], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.YAMLEq(a.t, expected, actual, msgAndArgs...) { + if assertions.YAMLEq(a.T, expected, actual, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // YAMLEqf is the same as [Assertions.YAMLEq], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.YAMLEq(a.t, expected, actual, forwardArgs(msg, args)) { + if assertions.YAMLEq(a.T, expected, actual, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // YAMLEqBytes is the same as [YAMLEqBytes], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) YAMLEqBytes(expected []byte, actual []byte, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.YAMLEqBytes(a.t, expected, actual, msgAndArgs...) { + if assertions.YAMLEqBytes(a.T, expected, actual, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // YAMLEqBytesf is the same as [Assertions.YAMLEqBytes], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) YAMLEqBytesf(expected []byte, actual []byte, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.YAMLEqBytes(a.t, expected, actual, forwardArgs(msg, args)) { + if assertions.YAMLEqBytes(a.T, expected, actual, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } // Zero is the same as [Zero], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Zero(i any, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Zero(a.t, i, msgAndArgs...) { + if assertions.Zero(a.T, i, msgAndArgs...) { return } - a.t.FailNow() + a.T.FailNow() } // Zerof is the same as [Assertions.Zero], but it accepts a format msg string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. func (a *Assertions) Zerof(i any, msg string, args ...any) { - if h, ok := a.t.(H); ok { + if h, ok := a.T.(H); ok { h.Helper() } - if assertions.Zero(a.t, i, forwardArgs(msg, args)) { + if assertions.Zero(a.T, i, forwardArgs(msg, args)) { return } - a.t.FailNow() + a.T.FailNow() } diff --git a/require/require_forward_test.go b/require/require_forward_test.go index f5bd6a4e3..b00c65ab5 100644 --- a/require/require_forward_test.go +++ b/require/require_forward_test.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package require @@ -752,13 +751,13 @@ func TestAssertionsEventuallyf(t *testing.T) { }) } -func TestAssertionsEventuallyWithT(t *testing.T) { +func TestAssertionsEventuallyWith(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() a := New(t) - a.EventuallyWithT(func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond) + a.EventuallyWith(func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond) // require functions don't return a value }) @@ -767,21 +766,21 @@ func TestAssertionsEventuallyWithT(t *testing.T) { mock := new(mockFailNowT) a := New(mock) - a.EventuallyWithT(func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond) + a.EventuallyWith(func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond) // require functions don't return a value if !mock.failed { - t.Error("EventuallyWithT should call FailNow()") + t.Error("EventuallyWith should call FailNow()") } }) } -func TestAssertionsEventuallyWithTf(t *testing.T) { +func TestAssertionsEventuallyWithf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() a := New(t) - a.EventuallyWithTf(func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") + a.EventuallyWithf(func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") // require functions don't return a value }) @@ -790,13 +789,13 @@ func TestAssertionsEventuallyWithTf(t *testing.T) { mock := new(mockFailNowT) a := New(mock) - a.EventuallyWithTf(func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") + a.EventuallyWithf(func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") // require functions don't return a value if !mock.failed { - t.Error("Assertions.EventuallyWithT should mark test as failed") + t.Error("Assertions.EventuallyWith should mark test as failed") } if !mock.failed { - t.Error("Assertions.EventuallyWithTf should call FailNow()") + t.Error("Assertions.EventuallyWithf should call FailNow()") } }) } @@ -2631,6 +2630,16 @@ func TestAssertionsNoErrorf(t *testing.T) { }) } +func TestAssertionsNoGoRoutineLeak(t *testing.T) { + t.Parallel() + t.Skip() // this function doesn't have tests yet: feed the original function with examples to test. +} + +func TestAssertionsNoGoRoutineLeakf(t *testing.T) { + t.Parallel() + t.Skip() // this function doesn't have tests yet: feed the original function with examples to test. +} + func TestAssertionsNotContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { diff --git a/require/require_helpers.go b/require/require_helpers.go index 3c1f793d5..e5a12478e 100644 --- a/require/require_helpers.go +++ b/require/require_helpers.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package require diff --git a/require/require_helpers_test.go b/require/require_helpers_test.go index e7d023a38..844cff43d 100644 --- a/require/require_helpers_test.go +++ b/require/require_helpers_test.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package require diff --git a/require/require_types.go b/require/require_types.go index def97aa54..fe025e629 100644 --- a/require/require_types.go +++ b/require/require_types.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-27 (version 98658ef) using codegen version v2.2.1-0.20260127181549-98658ef85ebb [sha: 98658ef85ebb5f0990ed1c8408af6defef6c6d5c] package require @@ -29,7 +28,7 @@ type ( // CollectT implements the [T] interface and collects all errors. // - // [CollectT] is specifically intended to be used with [EventuallyWithT] and + // [CollectT] is specifically intended to be used with [EventuallyWith] and // should not be used outside of that context. CollectT = assertions.CollectT