From 687f293c1bf97807f2680b5b76eb0926f4c8ba82 Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Wed, 14 Jan 2026 19:07:14 +0100 Subject: [PATCH 1/6] feat: added a few generic assertions Signed-off-by: Frederic BIDON --- internal/assertions/benchmarks_test.go | 234 ++++++ internal/assertions/boolean.go | 49 ++ internal/assertions/boolean_test.go | 38 +- internal/assertions/collection.go | 141 +++- internal/assertions/collection_test.go | 181 +++++ internal/assertions/compare.go | 374 +++++++-- internal/assertions/compare_test.go | 757 +++++++++++++++--- internal/assertions/embed_test.go | 16 - internal/assertions/error.go | 12 +- internal/assertions/generics.go | 62 ++ internal/assertions/ifaces.go | 21 + internal/assertions/json.go | 36 +- internal/assertions/json_test.go | 267 +++--- internal/assertions/number.go | 284 ++++++- internal/assertions/number_test.go | 480 ++++++++++- internal/assertions/string.go | 208 ++++- internal/assertions/string_test.go | 299 +++++-- .../assertions/testdata/json/fixtures.json | 19 - internal/assertions/yaml.go | 60 +- internal/assertions/yaml_test.go | 74 +- 20 files changed, 3109 insertions(+), 503 deletions(-) delete mode 100644 internal/assertions/embed_test.go create mode 100644 internal/assertions/generics.go delete mode 100644 internal/assertions/testdata/json/fixtures.json diff --git a/internal/assertions/benchmarks_test.go b/internal/assertions/benchmarks_test.go index 3fc2d2304..b2946e593 100644 --- a/internal/assertions/benchmarks_test.go +++ b/internal/assertions/benchmarks_test.go @@ -38,3 +38,237 @@ func BenchmarkBytesEqual(b *testing.B) { Equal(mockT, s, s2) } } + +/* + * Benchmarks comparing reflect-based vs generic implementations. + * + * These benchmarks measure the performance difference between: + * - Reflect-based versions (e.g., Greater, ElementsMatch) + * - Generic versions (e.g., GreaterT, ElementsMatchT) + */ + +// BenchmarkGreater compares Greater (reflect) vs GreaterT (generic). +func BenchmarkGreater(b *testing.B) { + mockT := &mockT{} + + b.Run("reflect/int", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + Greater(mockT, 100, 50) + } + }) + + b.Run("generic/int", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + GreaterT(mockT, 100, 50) + } + }) + + b.Run("reflect/float64", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + Greater(mockT, 100.5, 50.5) + } + }) + + b.Run("generic/float64", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + GreaterT(mockT, 100.5, 50.5) + } + }) + + b.Run("reflect/string", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + Greater(mockT, "beta", "alpha") + } + }) + + b.Run("generic/string", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + GreaterT(mockT, "beta", "alpha") + } + }) +} + +// BenchmarkLess compares Less (reflect) vs LessT (generic). +func BenchmarkLess(b *testing.B) { + mockT := &mockT{} + + b.Run("reflect/int", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + Less(mockT, 50, 100) + } + }) + + b.Run("generic/int", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + LessT(mockT, 50, 100) + } + }) +} + +// BenchmarkElementsMatch compares ElementsMatch (reflect) vs ElementsMatchT (generic). +func BenchmarkElementsMatch(b *testing.B) { + mockT := &mockT{} + + // Small slices (10 elements) + smallA := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + smallB := []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1} + + b.Run("reflect/small_10", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + ElementsMatch(mockT, smallA, smallB) + } + }) + + b.Run("generic/small_10", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + ElementsMatchT(mockT, smallA, smallB) + } + }) + + // Medium slices (100 elements) + mediumA := make([]int, 100) + mediumB := make([]int, 100) + for i := range mediumA { + mediumA[i] = i + mediumB[99-i] = i + } + + b.Run("reflect/medium_100", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + ElementsMatch(mockT, mediumA, mediumB) + } + }) + + b.Run("generic/medium_100", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + ElementsMatchT(mockT, mediumA, mediumB) + } + }) + + // Large slices (1000 elements) + largeA := make([]int, 1000) + largeB := make([]int, 1000) + for i := range largeA { + largeA[i] = i + largeB[999-i] = i + } + + b.Run("reflect/large_1000", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + ElementsMatch(mockT, largeA, largeB) + } + }) + + b.Run("generic/large_1000", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + ElementsMatchT(mockT, largeA, largeB) + } + }) + + // String slices + stringsA := []string{"apple", "banana", "cherry", "date", "elderberry"} + stringsB := []string{"elderberry", "date", "cherry", "banana", "apple"} + + b.Run("reflect/strings_5", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + ElementsMatch(mockT, stringsA, stringsB) + } + }) + + b.Run("generic/strings_5", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + ElementsMatchT(mockT, stringsA, stringsB) + } + }) +} + +// BenchmarkNotElementsMatch compares NotElementsMatch (reflect) vs NotElementsMatchT (generic). +func BenchmarkNotElementsMatch(b *testing.B) { + mockT := &mockT{} + + // Slices that don't match + sliceA := []int{1, 2, 3, 4, 5} + sliceB := []int{1, 2, 3, 4, 6} + + b.Run("reflect", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + NotElementsMatch(mockT, sliceA, sliceB) + } + }) + + b.Run("generic", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + NotElementsMatchT(mockT, sliceA, sliceB) + } + }) +} + +// BenchmarkPositive compares Positive (reflect) vs PositiveT (generic). +func BenchmarkPositive(b *testing.B) { + mockT := &mockT{} + + b.Run("reflect/int", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + Positive(mockT, 42) + } + }) + + b.Run("generic/int", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + PositiveT(mockT, 42) + } + }) + + b.Run("reflect/float64", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + Positive(mockT, 42.5) + } + }) + + b.Run("generic/float64", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + PositiveT(mockT, 42.5) + } + }) +} + +// BenchmarkNegative compares Negative (reflect) vs NegativeT (generic). +func BenchmarkNegative(b *testing.B) { + mockT := &mockT{} + + b.Run("reflect/int", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + Negative(mockT, -42) + } + }) + + b.Run("generic/int", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + NegativeT(mockT, -42) + } + }) +} diff --git a/internal/assertions/boolean.go b/internal/assertions/boolean.go index 13b27fbde..9830e8c56 100644 --- a/internal/assertions/boolean.go +++ b/internal/assertions/boolean.go @@ -25,6 +25,30 @@ func True(t T, value bool, msgAndArgs ...any) bool { return true } +// TrueT asserts that the specified value is true. +// +// # Usage +// +// type B bool +// var b B = true +// +// assertions.True(t, b) +// +// # Examples +// +// success: 1 == 1 +// failure: 1 == 0 +func TrueT[B Boolean](t T, value B, msgAndArgs ...any) bool { + if !bool(value) { + if h, ok := t.(H); ok { + h.Helper() + } + return Fail(t, "Should be true", msgAndArgs...) + } + + return true +} + // False asserts that the specified value is false. // // # Usage @@ -46,3 +70,28 @@ func False(t T, value bool, msgAndArgs ...any) bool { return true } + +// FalseT asserts that the specified value is false. +// +// # Usage +// +// type B bool +// var b B = true +// +// assertions.FalseT(t, b) +// +// # Examples +// +// success: 1 == 0 +// failure: 1 == 1 +func FalseT[B Boolean](t T, value B, msgAndArgs ...any) bool { + // Domain: boolean + if bool(value) { + if h, ok := t.(H); ok { + h.Helper() + } + return Fail(t, "Should be true", msgAndArgs...) + } + + return true +} diff --git a/internal/assertions/boolean_test.go b/internal/assertions/boolean_test.go index feb84d705..f77f1170c 100644 --- a/internal/assertions/boolean_test.go +++ b/internal/assertions/boolean_test.go @@ -17,10 +17,6 @@ func TestBooleanTrue(t *testing.T) { if True(mock, false) { t.Error("True should return false") } - - if !True(mock, true) { - t.Error("check error") - } } func TestBooleanFalse(t *testing.T) { @@ -35,3 +31,37 @@ func TestBooleanFalse(t *testing.T) { t.Error("False should return false") } } + +func TestBooleanTrueTFalseT(t *testing.T) { + t.Parallel() + + type X bool + var truthy X = true + var falsy X = false + + t.Run("with TrueT on redeclared bool type", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + + if !TrueT(mock, truthy) { + t.Error("TrueT should return true") + } + if TrueT(mock, falsy) { + t.Error("TrueT should return false") + } + }) + + t.Run("with FalseT on redeclared bool type", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + + if FalseT(mock, truthy) { + t.Error("TrueT should return true") + } + if !FalseT(mock, falsy) { + t.Error("FalseT should return false") + } + }) +} diff --git a/internal/assertions/collection.go b/internal/assertions/collection.go index b13eb8bb4..c27b8dc6d 100644 --- a/internal/assertions/collection.go +++ b/internal/assertions/collection.go @@ -30,13 +30,12 @@ import ( // failure: []string{"A","B"}, 1 func Len(t T, object any, length int, msgAndArgs ...any) bool { // Domain: collection - // Maintainer: The implementation is based on [reflect.Len]. The potential panic is handled with recover. - // A better approach could be to check for the [reflect.Type] before calling [reflect.Len]. // // Note: (proposals) this does not currently support iterators, or collection objects that have a Len() method. if h, ok := t.(H); ok { h.Helper() } + l, ok := getLen(object) if !ok { return Fail(t, fmt.Sprintf("%q could not be applied builtin len()", truncatingFormat("%v", object)), msgAndArgs...) @@ -116,10 +115,10 @@ func NotContains(t T, s, contains any, msgAndArgs ...any) bool { // // # Usage // -// assertions.Subset(t, [1, 2, 3], [1, 2]) -// assertions.Subset(t, {"x": 1, "y": 2}, {"x": 1}) -// assertions.Subset(t, [1, 2, 3], {1: "one", 2: "two"}) -// assertions.Subset(t, {"x": 1, "y": 2}, ["x"]) +// assertions.Subset(t, []int{1, 2, 3}, []int{1, 2}) +// assertions.Subset(t, []string{"x": 1, "y": 2}, []string{"x": 1}) +// assertions.Subset(t, []int{1, 2, 3}, map[int]string{1: "one", 2: "two"}) +// assertions.Subset(t, map[string]int{"x": 1, "y": 2}, []string{"x"}) // // # Examples // @@ -268,7 +267,7 @@ func NotSubset(t T, list, subset any, msgAndArgs ...any) (ok bool) { // // # Usage // -// assertions.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) +// assertions.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) // // # Examples // @@ -303,9 +302,9 @@ func ElementsMatch(t T, listA, listB any, msgAndArgs ...any) (ok bool) { // // # Usage // -// assertions.NotElementsMatch(t, [1, 1, 2, 3], [1, 1, 2, 3]) -> false -// assertions.NotElementsMatch(t, [1, 1, 2, 3], [1, 2, 3]) -> true -// assertions.NotElementsMatch(t, [1, 2, 3], [1, 2, 4]) -> true +// 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 // // # Examples // @@ -335,6 +334,75 @@ func NotElementsMatch(t T, listA, listB any, msgAndArgs ...any) (ok bool) { return true } +// ElementsMatchT asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// # Usage +// +// assertions.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) +// +// # Examples +// +// success: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} +// failure: []int{1, 2, 3}, []int{1, 2, 4} +func ElementsMatchT[E comparable](t T, listA, listB []E, msgAndArgs ...any) bool { + // Domain: collection + if h, ok := t.(H); ok { + h.Helper() + } + + lA := len(listA) + lB := len(listB) + + if lA == 0 && lB == 0 { + return true + } + + extraA, extraB := diffListsT(listA, listB) + if len(extraA) == 0 && len(extraB) == 0 { + return true + } + + return Fail(t, formatListDiff(listA, listB, extraA, extraB), msgAndArgs...) +} + +// NotElementsMatchT asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// # Usage +// +// 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 +// +// # Examples +// +// success: []int{1, 2, 3}, []int{1, 2, 4} +// failure: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} +func NotElementsMatchT[E comparable](t T, listA, listB []E, msgAndArgs ...any) (ok bool) { + // Domain: collection + if h, ok := t.(H); ok { + h.Helper() + } + + lA := len(listA) + lB := len(listB) + + if lA == 0 && lB == 0 { + return Fail(t, "listA and listB contain the same elements", msgAndArgs) + } + + extraA, extraB := diffListsT(listA, listB) + if len(extraA) == 0 && len(extraB) == 0 { + return Fail(t, "listA and listB contain the same elements", msgAndArgs) + } + + return true +} + // containsElement tries to loop over the list check if the list includes the element. // // return (false, false) if impossible. @@ -428,6 +496,42 @@ func diffLists(listA, listB any) (extraA, extraB []any) { return extraA, extraB } +func diffListsT[E comparable](listA, listB []E) (extraA, extraB []any) { + aLen := len(listA) + bLen := len(listB) + + extraA = make([]any, 0, aLen) + extraB = make([]any, 0, bLen) + visited := make([]bool, bLen) + + for i := range aLen { + element := listA[i] + found := false + for j := range bLen { + if visited[j] { + continue + } + if element == listB[j] { + visited[j] = true + found = true + break + } + } + if !found { + extraA = append(extraA, element) + } + } + + for j := range bLen { + if visited[j] { + continue + } + extraB = append(extraB, listB[j]) + } + + return extraA, extraB +} + func formatListDiff(listA, listB any, extraA, extraB []any) string { var msg bytes.Buffer @@ -449,11 +553,20 @@ func formatListDiff(listA, listB any, extraA, extraB []any) string { } // getLen tries to get the length of an object. +// // It returns (0, false) if impossible. func getLen(x any) (length int, ok bool) { v := reflect.ValueOf(x) - defer func() { - ok = recover() == nil - }() - return v.Len(), true + switch v.Kind() { + case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: + return v.Len(), true + case reflect.Pointer: + v = v.Elem() + if v.Kind() != reflect.Array { + return 0, false + } + return v.Len(), true + default: + return 0, false + } } diff --git a/internal/assertions/collection_test.go b/internal/assertions/collection_test.go index 968cd0147..871607e42 100644 --- a/internal/assertions/collection_test.go +++ b/internal/assertions/collection_test.go @@ -321,6 +321,14 @@ func TestCollectionElementsMatch(t *testing.T) { } } +func TestCollectionElementsMatchT(t *testing.T) { + t.Parallel() + + for tc := range elementsMatchTCases() { + t.Run(tc.name, tc.test) + } +} + func TestCollectionNotElementsMatch(t *testing.T) { t.Parallel() @@ -338,6 +346,14 @@ func TestCollectionNotElementsMatch(t *testing.T) { } } +func TestCollectionNotElementsMatchT(t *testing.T) { + t.Parallel() + + for tc := range notElementsMatchTCases() { + t.Run(tc.name, tc.test) + } +} + /* iterators for test cases */ type collectionValidLenCase struct { @@ -553,3 +569,168 @@ func collectionNotElementsMatchCases() iter.Seq[collectionNotElementsMatch] { {[3]string{"hello", "hello", "world"}, [3]string{"hello", "world", "hello"}, false}, }) } + +// elementsMatchTTestPairs builds test cases for both ElementsMatchT and NotElementsMatchT +// from shared test data, ensuring consistency between the inverse functions. +func elementsMatchTTestPairs() (matchCases, notMatchCases []genericTestCase) { + // addPair adds corresponding test cases for both ElementsMatchT and NotElementsMatchT. + addPair := func(name string, matchTest, notMatchTest func(*testing.T)) { + matchCases = append(matchCases, genericTestCase{name, matchTest}) + notMatchCases = append(notMatchCases, genericTestCase{name, notMatchTest}) + } + + // Numeric types - test data defined once, used for both functions + m, n := testElementsMatchTPair[int]([]int{1, 2, 3}, []int{3, 1, 2}, []int{1, 2, 3}, []int{1, 2, 4}) + addPair("int", m, n) + m, n = testElementsMatchTPair[int8]([]int8{1, 2, 3}, []int8{3, 1, 2}, []int8{1, 2, 3}, []int8{1, 2, 4}) + addPair("int8", m, n) + m, n = testElementsMatchTPair[int16]([]int16{1, 2, 3}, []int16{3, 1, 2}, []int16{1, 2, 3}, []int16{1, 2, 4}) + addPair("int16", m, n) + m, n = testElementsMatchTPair[int32]([]int32{1, 2, 3}, []int32{3, 1, 2}, []int32{1, 2, 3}, []int32{1, 2, 4}) + addPair("int32", m, n) + m, n = testElementsMatchTPair[int64]([]int64{1, 2, 3}, []int64{3, 1, 2}, []int64{1, 2, 3}, []int64{1, 2, 4}) + addPair("int64", m, n) + m, n = testElementsMatchTPair[uint]([]uint{1, 2, 3}, []uint{3, 1, 2}, []uint{1, 2, 3}, []uint{1, 2, 4}) + addPair("uint", m, n) + m, n = testElementsMatchTPair[uint8]([]uint8{1, 2, 3}, []uint8{3, 1, 2}, []uint8{1, 2, 3}, []uint8{1, 2, 4}) + addPair("uint8", m, n) + m, n = testElementsMatchTPair[uint16]([]uint16{1, 2, 3}, []uint16{3, 1, 2}, []uint16{1, 2, 3}, []uint16{1, 2, 4}) + addPair("uint16", m, n) + m, n = testElementsMatchTPair[uint32]([]uint32{1, 2, 3}, []uint32{3, 1, 2}, []uint32{1, 2, 3}, []uint32{1, 2, 4}) + addPair("uint32", m, n) + m, n = testElementsMatchTPair[uint64]([]uint64{1, 2, 3}, []uint64{3, 1, 2}, []uint64{1, 2, 3}, []uint64{1, 2, 4}) + addPair("uint64", m, n) + m, n = testElementsMatchTPair[float32]([]float32{1.5, 2.5, 3.5}, []float32{3.5, 1.5, 2.5}, []float32{1.5, 2.5, 3.5}, []float32{1.5, 2.5, 4.5}) + addPair("float32", m, n) + m, n = testElementsMatchTPair[float64]([]float64{1.5, 2.5, 3.5}, []float64{3.5, 1.5, 2.5}, []float64{1.5, 2.5, 3.5}, []float64{1.5, 2.5, 4.5}) + addPair("float64", m, n) + m, n = testElementsMatchTPair[string]([]string{"a", "b", "c"}, []string{"c", "a", "b"}, []string{"a", "b", "c"}, []string{"a", "b", "d"}) + addPair("string", m, n) + m, n = testElementsMatchTPair[bool]([]bool{true, false}, []bool{false, true}, []bool{true, true}, []bool{true, false}) + addPair("bool", m, n) + + // Special cases + m, n = testElementsMatchTEmptyPair() + addPair("empty slices", m, n) + m, n = testElementsMatchTDuplicatesPair() + addPair("with duplicates", m, n) + m, n = testElementsMatchTCustomTypePair() + addPair("custom type", m, n) + m, n = testElementsMatchTStructPair() + addPair("struct type", m, n) + + return matchCases, notMatchCases +} + +// elementsMatchTCases returns test cases for ElementsMatchT with various comparable types. +func elementsMatchTCases() iter.Seq[genericTestCase] { + matchCases, _ := elementsMatchTTestPairs() + return slices.Values(matchCases) +} + +// notElementsMatchTCases returns test cases for NotElementsMatchT with various comparable types. +func notElementsMatchTCases() iter.Seq[genericTestCase] { + _, notMatchCases := elementsMatchTTestPairs() + return slices.Values(notMatchCases) +} + +// testElementsMatchTPair creates test functions for both ElementsMatchT and NotElementsMatchT +// from the same test data, ensuring consistency between inverse functions. +// matchA/matchB are slices that should match; noMatchA/noMatchB are slices that should not match. +// +//nolint:thelper // linter false positive: these are not helpers +func testElementsMatchTPair[E comparable](matchA, matchB, noMatchA, noMatchB []E) (matchTest, notMatchTest func(*testing.T)) { + matchTest = func(t *testing.T) { + t.Parallel() + mock := new(mockT) + True(t, ElementsMatchT(mock, matchA, matchB)) + False(t, ElementsMatchT(mock, noMatchA, noMatchB)) + } + notMatchTest = func(t *testing.T) { + t.Parallel() + mock := new(mockT) + True(t, NotElementsMatchT(mock, noMatchA, noMatchB)) + False(t, NotElementsMatchT(mock, matchA, matchB)) + } + return matchTest, notMatchTest +} + +//nolint:thelper // linter false positive: these are not helpers +func testElementsMatchTEmptyPair() (matchTest, notMatchTest func(*testing.T)) { + matchTest = func(t *testing.T) { + t.Parallel() + mock := new(mockT) + True(t, ElementsMatchT(mock, []int{}, []int{})) + True(t, ElementsMatchT(mock, []string(nil), []string(nil))) + True(t, ElementsMatchT(mock, []int(nil), []int{})) + } + notMatchTest = func(t *testing.T) { + t.Parallel() + mock := new(mockT) + // Empty slices match, so NotElementsMatchT should return false + False(t, NotElementsMatchT(mock, []int{}, []int{})) + False(t, NotElementsMatchT(mock, []string(nil), []string(nil))) + // One empty, one not - they don't match + True(t, NotElementsMatchT(mock, []int{1}, []int{})) + True(t, NotElementsMatchT(mock, []int{}, []int{1})) + } + return matchTest, notMatchTest +} + +//nolint:thelper // linter false positive: these are not helpers +func testElementsMatchTDuplicatesPair() (matchTest, notMatchTest func(*testing.T)) { + matchTest = func(t *testing.T) { + t.Parallel() + mock := new(mockT) + True(t, ElementsMatchT(mock, []int{1, 1, 2}, []int{2, 1, 1})) + False(t, ElementsMatchT(mock, []int{1, 1, 2}, []int{1, 2, 2})) + False(t, ElementsMatchT(mock, []int{1, 1, 2}, []int{1, 2})) + } + notMatchTest = func(t *testing.T) { + t.Parallel() + mock := new(mockT) + // Different duplicate counts - should not match + True(t, NotElementsMatchT(mock, []int{1, 1, 2}, []int{1, 2, 2})) + True(t, NotElementsMatchT(mock, []int{1, 1, 2}, []int{1, 2})) + // Same duplicates, different order - should match (NotElementsMatchT returns false) + False(t, NotElementsMatchT(mock, []int{1, 1, 2}, []int{2, 1, 1})) + } + return matchTest, notMatchTest +} + +//nolint:thelper // linter false positive: these are not helpers +func testElementsMatchTCustomTypePair() (matchTest, notMatchTest func(*testing.T)) { + type myInt int + matchTest = func(t *testing.T) { + t.Parallel() + mock := new(mockT) + True(t, ElementsMatchT(mock, []myInt{1, 2, 3}, []myInt{3, 2, 1})) + False(t, ElementsMatchT(mock, []myInt{1, 2, 3}, []myInt{1, 2, 4})) + } + notMatchTest = func(t *testing.T) { + t.Parallel() + mock := new(mockT) + True(t, NotElementsMatchT(mock, []myInt{1, 2, 3}, []myInt{1, 2, 4})) + False(t, NotElementsMatchT(mock, []myInt{1, 2, 3}, []myInt{3, 2, 1})) + } + return matchTest, notMatchTest +} + +//nolint:thelper // linter false positive: these are not helpers +func testElementsMatchTStructPair() (matchTest, notMatchTest func(*testing.T)) { + type point struct{ x, y int } + p1, p2, p3 := point{1, 2}, point{3, 4}, point{5, 6} + matchTest = func(t *testing.T) { + t.Parallel() + mock := new(mockT) + True(t, ElementsMatchT(mock, []point{p1, p2, p3}, []point{p3, p1, p2})) + False(t, ElementsMatchT(mock, []point{p1, p2}, []point{p1, p3})) + } + notMatchTest = func(t *testing.T) { + t.Parallel() + mock := new(mockT) + True(t, NotElementsMatchT(mock, []point{p1, p2}, []point{p1, p3})) + False(t, NotElementsMatchT(mock, []point{p1, p2, p3}, []point{p3, p1, p2})) + } + return matchTest, notMatchTest +} diff --git a/internal/assertions/compare.go b/internal/assertions/compare.go index 7d40bdd01..303882409 100644 --- a/internal/assertions/compare.go +++ b/internal/assertions/compare.go @@ -12,37 +12,10 @@ import ( "time" ) -type ( - // ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful - // for table driven tests. - ComparisonAssertionFunc func(T, any, any, ...any) bool - - // ValueAssertionFunc is a common function prototype when validating a single value. Can be useful - // for table driven tests. - ValueAssertionFunc func(T, any, ...any) bool - - // BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful - // for table driven tests. - BoolAssertionFunc func(T, bool, ...any) bool - - // ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful - // for table driven tests. - ErrorAssertionFunc func(T, error, ...any) bool - - // Comparison is a custom function that returns true on success and false on failure. - Comparison func() (success bool) -) - -type compareResult int - -const ( - compareLess compareResult = iota - 1 - compareEqual - compareGreater -) - // Greater asserts that the first element is strictly greater than the second. // +// Both elements must be of the same type in the [reflect.Kind] sense. +// // # Usage // // assertions.Greater(t, 2, 1) @@ -62,6 +35,43 @@ func Greater(t T, e1 any, e2 any, msgAndArgs ...any) bool { return compareTwoValues(t, e1, e2, []compareResult{compareGreater}, failMessage, msgAndArgs...) } +// GreaterT asserts that for two elements of the same type, +// the first element is strictly greater than the second. +// +// The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), +// []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. +// +// Notice that pointers are not [Ordered], but uintptr are. So you can't call [GreaterT] with [*time.Time]. +// +// [GreaterT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, use [Greater] instead. +// +// To compare values that need a type conversion (e.g. float32 against float64), you should use [Greater] instead. +// +// # Usage +// +// assertions.GreaterT(t, 2, 1) +// 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()) +// +// # Examples +// +// success: 2, 1 +// failure: 1, 2 +func GreaterT[Orderable Ordered](t T, e1, e2 Orderable, msgAndArgs ...any) bool { + // Domain: comparison + if h, ok := t.(H); ok { + h.Helper() + } + + result := compareOrderedWithAny(e1, e2) + if result > 0 { + return true + } + + return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2), msgAndArgs...) +} + // GreaterOrEqual asserts that the first element is greater than or equal to the second. // // # Usage @@ -84,6 +94,44 @@ func GreaterOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) bool { return compareTwoValues(t, e1, e2, []compareResult{compareGreater, compareEqual}, failMessage, msgAndArgs...) } +// GreaterOrEqualT asserts that for two elements of the same type, +// the first element is greater than or equal to the second. +// +// The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), +// []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. +// +// Notice that pointers are not [Ordered], but uintptr are. So you can't call [GreaterOrEqualT] with [*time.Time]. +// +// [GreaterOrEqualT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, +// use [GreaterOrEqual] instead. +// +// To compare values that need a type conversion (e.g. float32 against float64), you should use [GreaterOrEqual] instead. +// +// # Usage +// +// assertions.GreaterOrEqualT(t, 2, 1) +// assertions.GreaterOrEqualT(t, 2, 2) +// assertions.GreaterOrEqualT(t, "b", "a") +// assertions.GreaterOrEqualT(t, "b", "b") +// +// # Examples +// +// success: 2, 1 +// failure: 1, 2 +func GreaterOrEqualT[Orderable Ordered](t T, e1, e2 Orderable, msgAndArgs ...any) bool { + // Domain: comparison + if h, ok := t.(H); ok { + h.Helper() + } + + result := compareOrderedWithAny(e1, e2) + if result >= 0 { + return true + } + + return Fail(t, fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2), msgAndArgs...) +} + // Less asserts that the first element is strictly less than the second. // // # Usage @@ -105,6 +153,42 @@ func Less(t T, e1 any, e2 any, msgAndArgs ...any) bool { return compareTwoValues(t, e1, e2, []compareResult{compareLess}, failMessage, msgAndArgs...) } +// LessT asserts that for two elements of the same type, the first element is strictly less than the second. +// +// The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), +// []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. +// +// Notice that pointers are not [Ordered], but uintptr are. So you can't call [LessT] with [*time.Time]. +// +// [LessT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, +// use [Less] instead. +// +// To compare values that need a type conversion (e.g. float32 against float64), you should use [Less] instead. +// +// # Usage +// +// assertions.LessT(t, 1, 2) +// assertions.LessT(t, float64(1), float64(2)) +// assertions.LessT(t, "a", "b") +// +// # Examples +// +// success: 1, 2 +// failure: 2, 1 +func LessT[Orderable Ordered](t T, e1, e2 Orderable, msgAndArgs ...any) bool { + // Domain: comparison + if h, ok := t.(H); ok { + h.Helper() + } + + result := compareOrderedWithAny(e1, e2) + if result < 0 { + return true + } + + return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2), msgAndArgs...) +} + // LessOrEqual asserts that the first element is less than or equal to the second. // // # Usage @@ -127,6 +211,43 @@ func LessOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) bool { return compareTwoValues(t, e1, e2, []compareResult{compareLess, compareEqual}, failMessage, msgAndArgs...) } +// LessOrEqualT asserts that for two elements of the same type, the first element is less than or equal to the second. +// +// The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), +// []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. +// +// Notice that pointers are not [Ordered], but uintptr are. So you can't call [LessOrEqualT] with [*time.Time]. +// +// [LessOrEqualT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, +// use [LessOrEqual] instead. +// +// To compare values that need a type conversion (e.g. float32 against float64), you should use [LessOrEqual] instead. +// +// # Usage +// +// assertions.LessOrEqualT(t, 1, 2) +// assertions.LessOrEqualT(t, 2, 2) +// assertions.LessOrEqualT(t, "a", "b") +// assertions.LessOrEqualT(t, "b", "b") +// +// # Examples +// +// success: 1, 2 +// failure: 2, 1 +func LessOrEqualT[Orderable Ordered](t T, e1, e2 Orderable, msgAndArgs ...any) bool { + // Domain: comparison + if h, ok := t.(H); ok { + h.Helper() + } + + result := compareOrderedWithAny(e1, e2) + if result <= 0 { + return true + } + + return Fail(t, fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2), msgAndArgs...) +} + // Positive asserts that the specified element is strictly positive. // // # Usage @@ -148,6 +269,31 @@ func Positive(t T, e any, msgAndArgs ...any) bool { return compareTwoValues(t, e, zero.Interface(), []compareResult{compareGreater}, failMessage, msgAndArgs...) } +// PositiveT asserts that the specified element of a signed numeric type is strictly positive. +// +// # Usage +// +// assertions.PositiveT(t, 1) +// assertions.PositiveT(t, 1.23) +// +// # Examples +// +// success: 1 +// failure: -1 +func PositiveT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool { + // Domain: comparison + if h, ok := t.(H); ok { + h.Helper() + } + + result := cmp.Compare(e, SignedNumber(0)) + if result > 0 { + return true + } + + return Fail(t, fmt.Sprintf("\"%v\" is not positive", e), msgAndArgs...) +} + // Negative asserts that the specified element is strictly negative. // // # Usage @@ -169,6 +315,39 @@ func Negative(t T, e any, msgAndArgs ...any) bool { return compareTwoValues(t, e, zero.Interface(), []compareResult{compareLess}, failMessage, msgAndArgs...) } +// NegativeT asserts that the specified element of a signed numeric type is strictly negative. +// +// # Usage +// +// assertions.NegativeT(t, -1) +// assertions.NegativeT(t, -1.23) +// +// # Examples +// +// success: -1 +// failure: 1 +func NegativeT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool { + // Domain: comparison + if h, ok := t.(H); ok { + h.Helper() + } + + result := cmp.Compare(e, SignedNumber(0)) + if result < 0 { + return true + } + + return Fail(t, fmt.Sprintf("\"%v\" is not negative", e), msgAndArgs...) +} + +type compareResult = int + +const ( + compareLess compareResult = iota - 1 + compareEqual + compareGreater +) + func compareTwoValues(t T, e1 any, e2 any, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() @@ -180,12 +359,12 @@ func compareTwoValues(t T, e1 any, e2 any, allowedComparesResults []compareResul return Fail(t, "Elements should be the same type", msgAndArgs...) } - compareResult, isComparable := compare(e1, e2, e1Kind) + result, isComparable := compare(e1, e2, e1Kind) if !isComparable { return Fail(t, fmt.Sprintf(`Can not compare type "%T"`, e1), msgAndArgs...) } - if !containsValue(allowedComparesResults, compareResult) { + if !containsValue(allowedComparesResults, result) { return Fail(t, failMessage, msgAndArgs...) } @@ -205,67 +384,67 @@ func compare(obj1, obj2 any, kind reflect.Kind) (compareResult, bool) { intobj1 := convertReflectValue[int](obj1, obj1Value) intobj2 := convertReflectValue[int](obj2, obj2Value) - return compareOrdered(intobj1, intobj2) + return cmp.Compare(intobj1, intobj2), true case reflect.Int8: int8obj1 := convertReflectValue[int8](obj1, obj1Value) int8obj2 := convertReflectValue[int8](obj2, obj2Value) - return compareOrdered(int8obj1, int8obj2) + return cmp.Compare(int8obj1, int8obj2), true case reflect.Int16: int16obj1 := convertReflectValue[int16](obj1, obj1Value) int16obj2 := convertReflectValue[int16](obj2, obj2Value) - return compareOrdered(int16obj1, int16obj2) + return cmp.Compare(int16obj1, int16obj2), true case reflect.Int32: int32obj1 := convertReflectValue[int32](obj1, obj1Value) int32obj2 := convertReflectValue[int32](obj2, obj2Value) - return compareOrdered(int32obj1, int32obj2) + return cmp.Compare(int32obj1, int32obj2), true case reflect.Int64: int64obj1 := convertReflectValue[int64](obj1, obj1Value) int64obj2 := convertReflectValue[int64](obj2, obj2Value) - return compareOrdered(int64obj1, int64obj2) + return cmp.Compare(int64obj1, int64obj2), true case reflect.Uint: uintobj1 := convertReflectValue[uint](obj1, obj1Value) uintobj2 := convertReflectValue[uint](obj2, obj2Value) - return compareOrdered(uintobj1, uintobj2) + return cmp.Compare(uintobj1, uintobj2), true case reflect.Uint8: uint8obj1 := convertReflectValue[uint8](obj1, obj1Value) uint8obj2 := convertReflectValue[uint8](obj2, obj2Value) - return compareOrdered(uint8obj1, uint8obj2) + return cmp.Compare(uint8obj1, uint8obj2), true case reflect.Uint16: uint16obj1 := convertReflectValue[uint16](obj1, obj1Value) uint16obj2 := convertReflectValue[uint16](obj2, obj2Value) - return compareOrdered(uint16obj1, uint16obj2) + return cmp.Compare(uint16obj1, uint16obj2), true case reflect.Uint32: uint32obj1 := convertReflectValue[uint32](obj1, obj1Value) uint32obj2 := convertReflectValue[uint32](obj2, obj2Value) - return compareOrdered(uint32obj1, uint32obj2) + return cmp.Compare(uint32obj1, uint32obj2), true case reflect.Uint64: uint64obj1 := convertReflectValue[uint64](obj1, obj1Value) uint64obj2 := convertReflectValue[uint64](obj2, obj2Value) - return compareOrdered(uint64obj1, uint64obj2) + return cmp.Compare(uint64obj1, uint64obj2), true case reflect.Float32: float32obj1 := convertReflectValue[float32](obj1, obj1Value) float32obj2 := convertReflectValue[float32](obj2, obj2Value) - return compareOrdered(float32obj1, float32obj2) + return cmp.Compare(float32obj1, float32obj2), true case reflect.Float64: float64obj1 := convertReflectValue[float64](obj1, obj1Value) float64obj2 := convertReflectValue[float64](obj2, obj2Value) - return compareOrdered(float64obj1, float64obj2) + return cmp.Compare(float64obj1, float64obj2), true case reflect.String: stringobj1 := convertReflectValue[string](obj1, obj1Value) stringobj2 := convertReflectValue[string](obj2, obj2Value) - return compareOrdered(stringobj1, stringobj2) + return cmp.Compare(stringobj1, stringobj2), true // Check for known struct types we can check for compare results. case reflect.Struct: @@ -273,19 +452,15 @@ func compare(obj1, obj2 any, kind reflect.Kind) (compareResult, bool) { case reflect.Slice: return compareSlice(obj1, obj2, obj1Value, obj2Value) case reflect.Uintptr: - uintptrobj1 := convertReflectValue[string](obj1, obj1Value) - uintptrobj2 := convertReflectValue[string](obj2, obj2Value) + uintptrobj1 := convertReflectValue[uintptr](obj1, obj1Value) + uintptrobj2 := convertReflectValue[uintptr](obj2, obj2Value) - return compareOrdered(uintptrobj1, uintptrobj2) + return cmp.Compare(uintptrobj1, uintptrobj2), true default: return compareEqual, false } } -func compareOrdered[T cmp.Ordered](obj1, obj2 T) (compareResult, bool) { - return compareResult(cmp.Compare(obj1, obj2)), true -} - func compareStruct(obj1, obj2 any, obj1Value, obj2Value reflect.Value) (compareResult, bool) { // all structs enter here. We're not interested in most types. if !obj1Value.CanConvert(reflect.TypeFor[time.Time]()) { @@ -296,7 +471,7 @@ func compareStruct(obj1, obj2 any, obj1Value, obj2Value reflect.Value) (compareR timeobj1 := convertReflectValue[time.Time](obj1, obj1Value) timeobj2 := convertReflectValue[time.Time](obj2, obj2Value) - return compareTime(timeobj1, timeobj2) + return timeobj1.Compare(timeobj2), true } func compareSlice(obj1, obj2 any, obj1Value, obj2Value reflect.Value) (compareResult, bool) { @@ -309,30 +484,15 @@ func compareSlice(obj1, obj2 any, obj1Value, obj2Value reflect.Value) (compareRe bytesobj1 := convertReflectValue[[]byte](obj1, obj1Value) bytesobj2 := convertReflectValue[[]byte](obj2, obj2Value) - return compareBytes(bytesobj1, bytesobj2) -} - -func compareTime(obj1, obj2 time.Time) (compareResult, bool) { - switch { - case obj1.Before(obj2): - return compareLess, true - case obj1.Equal(obj2): - return compareEqual, true - default: - return compareGreater, true - } -} - -func compareBytes(obj1, obj2 []byte) (compareResult, bool) { - return compareResult(bytes.Compare(obj1, obj2)), true + return bytes.Compare(bytesobj1, bytesobj2), true } -func convertReflectValue[T any](obj any, value reflect.Value) T { //nolint:ireturn // false positive +func convertReflectValue[V any](obj any, value reflect.Value) V { //nolint:ireturn // false positive // we try and avoid calling [reflect.Value.Convert()] whenever possible, // as this has a pretty big performance impact - converted, ok := obj.(T) + converted, ok := obj.(V) if !ok { - converted, ok = value.Convert(reflect.TypeFor[T]()).Interface().(T) + converted, ok = value.Convert(reflect.TypeFor[V]()).Interface().(V) if !ok { panic("internal error: expected that reflect.Value.Convert yields its target type") } @@ -340,3 +500,75 @@ func convertReflectValue[T any](obj any, value reflect.Value) T { //nolint:iretu return converted } + +// compareOrderedWithAny compares two [Ordered] values. +// +// This is an internal function that should only be called with actually [Ordered] types, +// even though it doesn't enforce this a build time. +// +//nolint:forcetypeassert // e2 is guaranteed to be of the same type as e1 +func compareOrderedWithAny[Orderable any](e1, e2 Orderable) int { + v := any(e1) + o := any(e2) + switch value := v.(type) { + case time.Time: + other := o.(time.Time) + return value.Compare(other) + case []byte: + other := o.([]byte) + return bytes.Compare(value, other) + case string: + other := o.(string) + return cmp.Compare(value, other) + case int: + other := o.(int) + return cmp.Compare(value, other) + case int8: + other := o.(int8) + return cmp.Compare(value, other) + case int16: + other := o.(int16) + return cmp.Compare(value, other) + case int32: + other := o.(int32) + return cmp.Compare(value, other) + case int64: + other := o.(int64) + return cmp.Compare(value, other) + case uint: + other := o.(uint) + return cmp.Compare(value, other) + case uint8: + other := o.(uint8) + return cmp.Compare(value, other) + case uint16: + other := o.(uint16) + return cmp.Compare(value, other) + case uint32: + other := o.(uint32) + return cmp.Compare(value, other) + case uint64: + other := o.(uint64) + return cmp.Compare(value, other) + case float32: + other := o.(float32) + return cmp.Compare(value, other) + case float64: + other := o.(float64) + return cmp.Compare(value, other) + case uintptr: + other := o.(uintptr) + return cmp.Compare(value, other) + default: + // we have a custom type: convert with reflection. + // We have less edge cases to guard than when comparing with the purely reflection-based call. + e1Kind := reflect.ValueOf(e1).Kind() + result, ok := compare(e1, e2, e1Kind) + if !ok { + // should never get there + panic(fmt.Errorf("internal error: expected that reflect.Value.Convert yields its target type for %T", e1)) + } + + return result + } +} diff --git a/internal/assertions/compare_test.go b/internal/assertions/compare_test.go index aadc61b37..ac5e718e2 100644 --- a/internal/assertions/compare_test.go +++ b/internal/assertions/compare_test.go @@ -15,103 +15,293 @@ import ( const pkg = "github.com/go-openapi/testify/v2/internal/assertions" +//nolint:dupl // no this is not a duplicate: it just looks almost the same! func TestCompareGreater(t *testing.T) { t.Parallel() - mock := new(testing.T) - if !Greater(mock, 2, 1) { - t.Error("Greater should return true") - } + t.Run("with basic input", func(t *testing.T) { + t.Parallel() - if Greater(mock, 1, 1) { - t.Error("Greater should return false") - } + mock := new(mockT) + if !Greater(mock, 2, 1) { + t.Error("Greater should return true") + } + + if Greater(mock, 1, 1) { + t.Error("Greater should return false") + } + + if Greater(mock, 1, 2) { + t.Error("Greater should return false") + } + }) - if Greater(mock, 1, 2) { - t.Error("Greater should return false") + for currCase := range compareStrictlyGreaterCases() { + t.Run("should NOT be strictly greater, with expected error message", func(t *testing.T) { + t.Parallel() + + mock := &outputT{buf: bytes.NewBuffer(nil)} // check error report + False(t, Greater(mock, currCase.less, currCase.greater), + "expected %v NOT to be strictly greater than %v", + currCase.less, currCase.greater, + ) + + Contains(t, mock.buf.String(), currCase.msg) + Contains(t, mock.helpers, pkg+".Greater") + }) + + t.Run("should be strictly greater", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) // don't check output + True(t, Greater(mock, currCase.greater, currCase.less), + "expected %v to be strictly greater than %v", + currCase.less, currCase.greater, + ) + }) } - // check error report - for currCase := range compareIncreasingFixtures() { - out := &outputT{buf: bytes.NewBuffer(nil)} - False(t, Greater(out, currCase.less, currCase.greater)) - Contains(t, out.buf.String(), currCase.msg) - Contains(t, out.helpers, pkg+".Greater") + for currCase := range compareEqualCases() { + t.Run("equal values should NOT be strictly greater", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + False(t, Greater(mock, currCase.less, currCase.greater), + "expected (equal) %v NOT to be strictly greater than %v", + currCase.less, currCase.greater, + ) + }) } } func TestCompareGreaterOrEqual(t *testing.T) { t.Parallel() - mock := new(testing.T) - if !GreaterOrEqual(mock, 2, 1) { - t.Error("GreaterOrEqual should return true") - } + t.Run("with basic input", func(t *testing.T) { + t.Parallel() - if !GreaterOrEqual(mock, 1, 1) { - t.Error("GreaterOrEqual should return true") - } + mock := new(mockT) + if !GreaterOrEqual(mock, 2, 1) { + t.Error("GreaterOrEqual should return true") + } - if GreaterOrEqual(mock, 1, 2) { - t.Error("GreaterOrEqual should return false") + if !GreaterOrEqual(mock, 1, 1) { + t.Error("GreaterOrEqual should return true") + } + + if GreaterOrEqual(mock, 1, 2) { + t.Error("GreaterOrEqual should return false") + } + }) + + for currCase := range compareStrictlyGreaterCases() { + t.Run("should NOT be greater or equal, with expected error message", func(t *testing.T) { + t.Parallel() + + mock := &outputT{buf: bytes.NewBuffer(nil)} // check error report + + False(t, GreaterOrEqual(mock, currCase.less, currCase.greater), + "expected %v NOT to be greater than or equal to %v", + currCase.less, currCase.greater, + ) + + Contains(t, mock.buf.String(), strings.ReplaceAll(currCase.msg, "than", "than or equal to")) + Contains(t, mock.helpers, pkg+".GreaterOrEqual") + }) + + t.Run("should be greater or equal", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + + True(t, GreaterOrEqual(mock, currCase.greater, currCase.less), + "expected %v to be greater than or equal to %v", + currCase.less, currCase.greater, + ) + }) } - // check error report - for currCase := range compareIncreasingFixtures() { - out := &outputT{buf: bytes.NewBuffer(nil)} - False(t, GreaterOrEqual(out, currCase.less, currCase.greater)) - Contains(t, out.buf.String(), strings.ReplaceAll(currCase.msg, "than", "than or equal to")) - Contains(t, out.helpers, pkg+".GreaterOrEqual") + for currCase := range compareEqualCases() { + t.Run("equal values should be greater or equal", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + + True(t, GreaterOrEqual(mock, currCase.less, currCase.greater), + "expected (equal) %v to be greater than or equal to %v", + currCase.less, currCase.greater, + ) + }) } } +//nolint:dupl // no this is not a duplicate: it just looks almost the same! func TestCompareLess(t *testing.T) { t.Parallel() - mock := new(testing.T) + t.Run("with basic input", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + + if !Less(mock, 1, 2) { + t.Error("Less should return true") + } + + if Less(mock, 1, 1) { + t.Error("Less should return false") + } + + if Less(mock, 2, 1) { + t.Error("Less should return false") + } + }) + + for currCase := range compareStrictlyLessCases() { + t.Run("should NOT be stricly less, with expected error message", func(t *testing.T) { + t.Parallel() + + mock := &outputT{buf: bytes.NewBuffer(nil)} // check error report + False(t, Less(mock, currCase.greater, currCase.less), + "expected %v NOT to be stricly less than %v", + currCase.greater, currCase.less, + ) + + Contains(t, mock.buf.String(), currCase.msg) + Contains(t, mock.helpers, pkg+".Less") + }) + + t.Run("should be stricly less", func(t *testing.T) { + t.Parallel() - if !Less(mock, 1, 2) { - t.Error("Less should return true") + mock := new(mockT) + + True(t, Less(mock, currCase.less, currCase.greater), + "expected %v be stricly less than %v", + currCase.less, currCase.greater, + ) + }) } - if Less(mock, 1, 1) { - t.Error("Less should return false") + for currCase := range compareEqualCases() { + t.Run("equal values should NOT be strictly less", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + + True(t, GreaterOrEqual(mock, currCase.less, currCase.greater), + "expected (equal) %v NOT to be strictly less than %v", + currCase.less, currCase.greater, + ) + }) } +} + +func TestCompareLessOrEqual(t *testing.T) { + t.Parallel() + + t.Run("with basic input", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + + if !LessOrEqual(mock, 1, 2) { + t.Error("LessOrEqual should return true") + } + + if !LessOrEqual(mock, 1, 1) { + t.Error("LessOrEqual should return true") + } - if Less(mock, 2, 1) { - t.Error("Less should return false") + if LessOrEqual(mock, 2, 1) { + t.Error("LessOrEqual should return false") + } + }) + + for currCase := range compareStrictlyLessCases() { + t.Run("should NOT be less or equal, with expected error message", func(t *testing.T) { + t.Parallel() + + mock := &outputT{buf: bytes.NewBuffer(nil)} // check error report + + False(t, LessOrEqual(mock, currCase.greater, currCase.less), + "expected %v NOT to be less than or equal to %v", + currCase.less, currCase.greater, + ) + + Contains(t, mock.buf.String(), strings.ReplaceAll(currCase.msg, "than", "than or equal to")) + Contains(t, mock.helpers, pkg+".LessOrEqual") + }) + + t.Run("should be stricly less", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + True(t, LessOrEqual(mock, currCase.less, currCase.greater), + "expected %v to be less than or equal to %v", + currCase.less, currCase.greater, + ) + }) + + for currCase := range compareEqualCases() { + t.Run("equal values should be less or equal", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + + True(t, GreaterOrEqual(mock, currCase.less, currCase.greater), + "expected (equal) %v to be less than or equal to %v", + currCase.less, currCase.greater, + ) + }) + } + } +} + +func TestCompareGreaterT(t *testing.T) { + t.Parallel() + + for tc := range greaterTCases() { + t.Run(tc.name, tc.test) } +} - // check error report - for currCase := range compareIncreasingFixtures3() { - out := &outputT{buf: bytes.NewBuffer(nil)} - False(t, Less(out, currCase.greater, currCase.less)) - Contains(t, out.buf.String(), currCase.msg) - Contains(t, out.helpers, pkg+".Less") +func TestCompareGreaterOrEqualT(t *testing.T) { + t.Parallel() + + for tc := range greaterOrEqualTCases() { + t.Run(tc.name, tc.test) } } -func TestCompareLessOrEqual(t *testing.T) { +func TestCompareLessT(t *testing.T) { t.Parallel() - mock := new(testing.T) - if !LessOrEqual(mock, 1, 2) { - t.Error("LessOrEqual should return true") + for tc := range lessTCases() { + t.Run(tc.name, tc.test) } +} + +func TestCompareLessOrEqualT(t *testing.T) { + t.Parallel() - if !LessOrEqual(mock, 1, 1) { - t.Error("LessOrEqual should return true") + for tc := range lessOrEqualTCases() { + t.Run(tc.name, tc.test) } +} + +func TestComparePositiveT(t *testing.T) { + t.Parallel() - if LessOrEqual(mock, 2, 1) { - t.Error("LessOrEqual should return false") + for tc := range positiveTCases() { + t.Run(tc.name, tc.test) } +} - // check error report - for currCase := range compareIncreasingFixtures3() { - out := &outputT{buf: bytes.NewBuffer(nil)} - False(t, LessOrEqual(out, currCase.greater, currCase.less)) - Contains(t, out.buf.String(), strings.ReplaceAll(currCase.msg, "than", "than or equal to")) - Contains(t, out.helpers, pkg+".LessOrEqual") +func TestCompareNegativeT(t *testing.T) { + t.Parallel() + + for tc := range negativeTCases() { + t.Run(tc.name, tc.test) } } @@ -175,7 +365,7 @@ func TestCompareNegative(t *testing.T) { func TestCompareMsgAndArgsForwarding(t *testing.T) { msgAndArgs := []any{"format %s %x", "this", 0xc001} - expectedOutput := "format this c001\n" + const expectedOutput = "format this c001\n" funcs := []func(t T){ func(t T) { Greater(t, 1, 2, msgAndArgs...) }, @@ -193,6 +383,343 @@ func TestCompareMsgAndArgsForwarding(t *testing.T) { } } +// genericTestCase wraps a test function with its name for table-driven tests of generic functions. +type genericTestCase struct { + name string + test func(*testing.T) +} + +// greaterTCases returns test cases for GreaterT with various Ordered types. +func greaterTCases() iter.Seq[genericTestCase] { + return slices.Values([]genericTestCase{ + {"int", testGreaterT[int](2, 1, 1, 2)}, + {"int8", testGreaterT[int8](2, 1, 1, 2)}, + {"int16", testGreaterT[int16](2, 1, 1, 2)}, + {"int32", testGreaterT[int32](2, 1, 1, 2)}, + {"int64", testGreaterT[int64](2, 1, 1, 2)}, + {"uint", testGreaterT[uint](2, 1, 1, 2)}, + {"uint8", testGreaterT[uint8](2, 1, 1, 2)}, + {"uint16", testGreaterT[uint16](2, 1, 1, 2)}, + {"uint32", testGreaterT[uint32](2, 1, 1, 2)}, + {"uint64", testGreaterT[uint64](2, 1, 1, 2)}, + {"float32", testGreaterT[float32](2.5, 1.5, 1.5, 2.5)}, + {"float64", testGreaterT[float64](2.5, 1.5, 1.5, 2.5)}, + {"string", testGreaterT[string]("b", "a", "a", "b")}, + {"uintptr", testGreaterT[uintptr](2, 1, 1, 2)}, + {"time.Time", testGreaterTTime()}, + {"[]byte", testGreaterTBytes()}, + {"custom int type", testGreaterTCustomInt()}, + }) +} + +// greaterOrEqualTCases returns test cases for GreaterOrEqualT with various Ordered types. +func greaterOrEqualTCases() iter.Seq[genericTestCase] { + return slices.Values([]genericTestCase{ + {"int", testGreaterOrEqualT[int](2, 1, 1, 1, 0, 1)}, + {"int8", testGreaterOrEqualT[int8](2, 1, 1, 1, 0, 1)}, + {"int16", testGreaterOrEqualT[int16](2, 1, 1, 1, 0, 1)}, + {"int32", testGreaterOrEqualT[int32](2, 1, 1, 1, 0, 1)}, + {"int64", testGreaterOrEqualT[int64](2, 1, 1, 1, 0, 1)}, + {"uint", testGreaterOrEqualT[uint](2, 1, 1, 1, 0, 1)}, + {"uint8", testGreaterOrEqualT[uint8](2, 1, 1, 1, 0, 1)}, + {"uint16", testGreaterOrEqualT[uint16](2, 1, 1, 1, 0, 1)}, + {"uint32", testGreaterOrEqualT[uint32](2, 1, 1, 1, 0, 1)}, + {"uint64", testGreaterOrEqualT[uint64](2, 1, 1, 1, 0, 1)}, + {"float32", testGreaterOrEqualT[float32](2.5, 1.5, 1.5, 1.5, 0.5, 1.5)}, + {"float64", testGreaterOrEqualT[float64](2.5, 1.5, 1.5, 1.5, 0.5, 1.5)}, + {"string", testGreaterOrEqualT[string]("b", "a", "a", "a", "a", "b")}, + {"uintptr", testGreaterOrEqualT[uintptr](2, 1, 1, 1, 0, 1)}, + {"time.Time", testGreaterOrEqualTTime()}, + {"[]byte", testGreaterOrEqualTBytes()}, + }) +} + +// lessTCases returns test cases for LessT with various Ordered types. +func lessTCases() iter.Seq[genericTestCase] { + return slices.Values([]genericTestCase{ + {"int", testLessT[int](1, 2, 2, 1)}, + {"int8", testLessT[int8](1, 2, 2, 1)}, + {"int16", testLessT[int16](1, 2, 2, 1)}, + {"int32", testLessT[int32](1, 2, 2, 1)}, + {"int64", testLessT[int64](1, 2, 2, 1)}, + {"uint", testLessT[uint](1, 2, 2, 1)}, + {"uint8", testLessT[uint8](1, 2, 2, 1)}, + {"uint16", testLessT[uint16](1, 2, 2, 1)}, + {"uint32", testLessT[uint32](1, 2, 2, 1)}, + {"uint64", testLessT[uint64](1, 2, 2, 1)}, + {"float32", testLessT[float32](1.5, 2.5, 2.5, 1.5)}, + {"float64", testLessT[float64](1.5, 2.5, 2.5, 1.5)}, + {"string", testLessT[string]("a", "b", "b", "a")}, + {"uintptr", testLessT[uintptr](1, 2, 2, 1)}, + {"time.Time", testLessTTime()}, + {"[]byte", testLessTBytes()}, + }) +} + +// lessOrEqualTCases returns test cases for LessOrEqualT with various Ordered types. +func lessOrEqualTCases() iter.Seq[genericTestCase] { + return slices.Values([]genericTestCase{ + {"int", testLessOrEqualT[int](1, 2, 1, 1, 2, 1)}, + {"int8", testLessOrEqualT[int8](1, 2, 1, 1, 2, 1)}, + {"int16", testLessOrEqualT[int16](1, 2, 1, 1, 2, 1)}, + {"int32", testLessOrEqualT[int32](1, 2, 1, 1, 2, 1)}, + {"int64", testLessOrEqualT[int64](1, 2, 1, 1, 2, 1)}, + {"uint", testLessOrEqualT[uint](1, 2, 1, 1, 2, 1)}, + {"uint8", testLessOrEqualT[uint8](1, 2, 1, 1, 2, 1)}, + {"uint16", testLessOrEqualT[uint16](1, 2, 1, 1, 2, 1)}, + {"uint32", testLessOrEqualT[uint32](1, 2, 1, 1, 2, 1)}, + {"uint64", testLessOrEqualT[uint64](1, 2, 1, 1, 2, 1)}, + {"float32", testLessOrEqualT[float32](1.5, 2.5, 1.5, 1.5, 2.5, 1.5)}, + {"float64", testLessOrEqualT[float64](1.5, 2.5, 1.5, 1.5, 2.5, 1.5)}, + {"string", testLessOrEqualT[string]("a", "b", "a", "a", "b", "a")}, + {"uintptr", testLessOrEqualT[uintptr](1, 2, 1, 1, 2, 1)}, + {"time.Time", testLessOrEqualTTime()}, + {"[]byte", testLessOrEqualTBytes()}, + }) +} + +// positiveTCases returns test cases for PositiveT with various SignedNumeric types. +func positiveTCases() iter.Seq[genericTestCase] { + return slices.Values([]genericTestCase{ + {"int", testPositiveT[int](1, -1)}, + {"int8", testPositiveT[int8](1, -1)}, + {"int16", testPositiveT[int16](1, -1)}, + {"int32", testPositiveT[int32](1, -1)}, + {"int64", testPositiveT[int64](1, -1)}, + {"float32", testPositiveT[float32](1.5, -1.5)}, + {"float64", testPositiveT[float64](1.5, -1.5)}, + {"zero is not positive", testPositiveTZero()}, + }) +} + +// negativeTCases returns test cases for NegativeT with various SignedNumeric types. +func negativeTCases() iter.Seq[genericTestCase] { + return slices.Values([]genericTestCase{ + {"int", testNegativeT[int](-1, 1)}, + {"int8", testNegativeT[int8](-1, 1)}, + {"int16", testNegativeT[int16](-1, 1)}, + {"int32", testNegativeT[int32](-1, 1)}, + {"int64", testNegativeT[int64](-1, 1)}, + {"float32", testNegativeT[float32](-1.5, 1.5)}, + {"float64", testNegativeT[float64](-1.5, 1.5)}, + {"zero is not negative", testNegativeTZero()}, + }) +} + +// Test helper functions for generic comparison assertions + +func testGreaterT[V Ordered](successE1, successE2, failE1, failE2 V) func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + True(t, GreaterT(mock, successE1, successE2)) + False(t, GreaterT(mock, failE1, failE2)) + False(t, GreaterT(mock, successE1, successE1)) // equal values + } +} + +func testGreaterTTime() func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + t0 := time.Now() + t1 := t0.Add(-time.Second) + + True(t, GreaterT(mock, t0, t1)) + False(t, GreaterT(mock, t1, t0)) + False(t, GreaterT(mock, t0, t0)) + } +} + +func testGreaterTBytes() func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + True(t, GreaterT(mock, []byte{2}, []byte{1})) + False(t, GreaterT(mock, []byte{1}, []byte{2})) + False(t, GreaterT(mock, []byte{1}, []byte{1})) + } +} + +func testGreaterTCustomInt() func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + type MyInt int + True(t, GreaterT(mock, MyInt(2), MyInt(1))) + False(t, GreaterT(mock, MyInt(1), MyInt(2))) + } +} + +func testGreaterOrEqualT[V Ordered](gtE1, gtE2, eqE1, eqE2, failE1, failE2 V) func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + True(t, GreaterOrEqualT(mock, gtE1, gtE2)) // greater + True(t, GreaterOrEqualT(mock, eqE1, eqE2)) // equal + False(t, GreaterOrEqualT(mock, failE1, failE2)) // less + } +} + +func testGreaterOrEqualTTime() func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + t0 := time.Now() + t1 := t0.Add(-time.Second) + + True(t, GreaterOrEqualT(mock, t0, t1)) // greater + True(t, GreaterOrEqualT(mock, t0, t0)) // equal + False(t, GreaterOrEqualT(mock, t1, t0)) // less + } +} + +func testGreaterOrEqualTBytes() func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + True(t, GreaterOrEqualT(mock, []byte{2}, []byte{1})) + True(t, GreaterOrEqualT(mock, []byte{1}, []byte{1})) + False(t, GreaterOrEqualT(mock, []byte{1}, []byte{2})) + } +} + +func testLessT[V Ordered](successE1, successE2, failE1, failE2 V) func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + True(t, LessT(mock, successE1, successE2)) + False(t, LessT(mock, failE1, failE2)) + False(t, LessT(mock, successE1, successE1)) // equal values + } +} + +func testLessTTime() func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + t0 := time.Now() + t1 := t0.Add(time.Second) + + True(t, LessT(mock, t0, t1)) + False(t, LessT(mock, t1, t0)) + False(t, LessT(mock, t0, t0)) + } +} + +func testLessTBytes() func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + True(t, LessT(mock, []byte{1}, []byte{2})) + False(t, LessT(mock, []byte{2}, []byte{1})) + False(t, LessT(mock, []byte{1}, []byte{1})) + } +} + +func testLessOrEqualT[V Ordered](ltE1, ltE2, eqE1, eqE2, failE1, failE2 V) func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + True(t, LessOrEqualT(mock, ltE1, ltE2)) // less + True(t, LessOrEqualT(mock, eqE1, eqE2)) // equal + False(t, LessOrEqualT(mock, failE1, failE2)) // greater + } +} + +func testLessOrEqualTTime() func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + t0 := time.Now() + t1 := t0.Add(time.Second) + + True(t, LessOrEqualT(mock, t0, t1)) // less + True(t, LessOrEqualT(mock, t0, t0)) // equal + False(t, LessOrEqualT(mock, t1, t0)) // greater + } +} + +func testLessOrEqualTBytes() func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + True(t, LessOrEqualT(mock, []byte{1}, []byte{2})) + True(t, LessOrEqualT(mock, []byte{1}, []byte{1})) + False(t, LessOrEqualT(mock, []byte{2}, []byte{1})) + } +} + +func testPositiveT[V SignedNumeric](positive, negative V) func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + True(t, PositiveT(mock, positive)) + False(t, PositiveT(mock, negative)) + } +} + +func testPositiveTZero() func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + False(t, PositiveT(mock, 0)) + False(t, PositiveT(mock, 0.0)) + } +} + +func testNegativeT[V SignedNumeric](negative, positive V) func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + True(t, NegativeT(mock, negative)) + False(t, NegativeT(mock, positive)) + } +} + +func testNegativeTZero() func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + False(t, NegativeT(mock, 0)) + False(t, NegativeT(mock, 0.0)) + } +} + // 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 { @@ -213,52 +740,94 @@ type compareFixture struct { msg string } -//nolint:dupl // factoring further the message to save a little duplication would make the test harder to read -func compareIncreasingFixtures() iter.Seq[compareFixture] { +func compareStrictlyGreaterCases() iter.Seq[compareFixture] { + const genMsg = `"1" is not greater than "2"` + + type redefinedUintptr uintptr + return slices.Values( []compareFixture{ {less: "a", greater: "b", msg: `"a" is not greater than "b"`}, - {less: int(1), greater: int(2), msg: `"1" is not greater than "2"`}, - {less: int8(1), greater: int8(2), msg: `"1" is not greater than "2"`}, - {less: int16(1), greater: int16(2), msg: `"1" is not greater than "2"`}, - {less: int32(1), greater: int32(2), msg: `"1" is not greater than "2"`}, - {less: int64(1), greater: int64(2), msg: `"1" is not greater than "2"`}, - {less: uint8(1), greater: uint8(2), msg: `"1" is not greater than "2"`}, - {less: uint16(1), greater: uint16(2), msg: `"1" is not greater than "2"`}, - {less: uint32(1), greater: uint32(2), msg: `"1" is not greater than "2"`}, - {less: uint64(1), greater: uint64(2), msg: `"1" is not greater than "2"`}, + {less: int(1), greater: int(2), msg: genMsg}, + {less: int8(1), greater: int8(2), msg: genMsg}, + {less: int16(1), greater: int16(2), msg: genMsg}, + {less: int32(1), greater: int32(2), msg: genMsg}, + {less: int64(1), greater: int64(2), msg: genMsg}, + {less: uint8(1), greater: uint8(2), msg: genMsg}, + {less: uint16(1), greater: uint16(2), msg: genMsg}, + {less: uint32(1), greater: uint32(2), msg: genMsg}, + {less: uint64(1), greater: uint64(2), msg: genMsg}, {less: float32(1.23), greater: float32(2.34), msg: `"1.23" is not greater than "2.34"`}, {less: float64(1.23), greater: float64(2.34), msg: `"1.23" is not greater than "2.34"`}, - {less: uintptr(1), greater: uintptr(2), msg: `"1" is not greater than "2"`}, + {less: uintptr(1), greater: uintptr(2), msg: genMsg}, + {less: uintptr(9), greater: uintptr(10), msg: `"9" is not greater than "10"`}, + {less: redefinedUintptr(9), greater: redefinedUintptr(10), msg: `"9" is not greater than "10"`}, {less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `"0001-01-01 00:00:00 +0000 UTC" is not greater than "0001-01-01 01:00:00 +0000 UTC"`}, {less: []byte{1, 1}, greater: []byte{1, 2}, msg: `"[1 1]" is not greater than "[1 2]"`}, }, ) } -//nolint:dupl // factoring further the message to save a little duplication would make the test harder to read -func compareIncreasingFixtures3() iter.Seq[compareFixture] { +func compareStrictlyLessCases() iter.Seq[compareFixture] { + const genMsg = `"2" is not less than "1"` + return slices.Values( []compareFixture{ {less: "a", greater: "b", msg: `"b" is not less than "a"`}, - {less: int(1), greater: int(2), msg: `"2" is not less than "1"`}, - {less: int8(1), greater: int8(2), msg: `"2" is not less than "1"`}, - {less: int16(1), greater: int16(2), msg: `"2" is not less than "1"`}, - {less: int32(1), greater: int32(2), msg: `"2" is not less than "1"`}, - {less: int64(1), greater: int64(2), msg: `"2" is not less than "1"`}, - {less: uint8(1), greater: uint8(2), msg: `"2" is not less than "1"`}, - {less: uint16(1), greater: uint16(2), msg: `"2" is not less than "1"`}, - {less: uint32(1), greater: uint32(2), msg: `"2" is not less than "1"`}, - {less: uint64(1), greater: uint64(2), msg: `"2" is not less than "1"`}, + {less: int(1), greater: int(2), msg: genMsg}, + {less: int8(1), greater: int8(2), msg: genMsg}, + {less: int16(1), greater: int16(2), msg: genMsg}, + {less: int32(1), greater: int32(2), msg: genMsg}, + {less: int64(1), greater: int64(2), msg: genMsg}, + {less: uint8(1), greater: uint8(2), msg: genMsg}, + {less: uint16(1), greater: uint16(2), msg: genMsg}, + {less: uint32(1), greater: uint32(2), msg: genMsg}, + {less: uint64(1), greater: uint64(2), msg: genMsg}, {less: float32(1.23), greater: float32(2.34), msg: `"2.34" is not less than "1.23"`}, {less: float64(1.23), greater: float64(2.34), msg: `"2.34" is not less than "1.23"`}, - {less: uintptr(1), greater: uintptr(2), msg: `"2" is not less than "1"`}, + {less: uintptr(1), greater: uintptr(2), msg: genMsg}, {less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `"0001-01-01 01:00:00 +0000 UTC" is not less than "0001-01-01 00:00:00 +0000 UTC"`}, {less: []byte{1, 1}, greater: []byte{1, 2}, msg: `"[1 2]" is not less than "[1 1]"`}, }, ) } +func compareEqualCases() iter.Seq[compareFixture] { + // This iterator produces equal-values to check edge cases with strict comparisons. + // The message cannot be used for error message checks. + return func(yield func(compareFixture) bool) { + for greater := range compareStrictlyGreaterCases() { + greater.msg = "" + equal1 := greater + equal1.less = equal1.greater + if !yield(equal1) { + return + } + + equal2 := greater + equal2.greater = equal2.less + if !yield(equal2) { + return + } + } + + for less := range compareStrictlyLessCases() { + less.msg = "" + equal1 := less + equal1.less = equal1.greater + if !yield(equal1) { + return + } + + equal2 := less + equal2.greater = equal2.less + if !yield(equal2) { + return + } + } + } +} + type compareTestCase struct { e any msg string @@ -269,24 +838,28 @@ type comparePositiveCase = compareTestCase type compareNegativeCase = compareTestCase func comparePositiveCases() iter.Seq[comparePositiveCase] { + const genMsg = `"-1" is not positive` + return slices.Values([]comparePositiveCase{ - {e: int(-1), msg: `"-1" is not positive`}, - {e: int8(-1), msg: `"-1" is not positive`}, - {e: int16(-1), msg: `"-1" is not positive`}, - {e: int32(-1), msg: `"-1" is not positive`}, - {e: int64(-1), msg: `"-1" is not positive`}, + {e: int(-1), msg: genMsg}, + {e: int8(-1), msg: genMsg}, + {e: int16(-1), msg: genMsg}, + {e: int32(-1), msg: genMsg}, + {e: int64(-1), msg: genMsg}, {e: float32(-1.23), msg: `"-1.23" is not positive`}, {e: float64(-1.23), msg: `"-1.23" is not positive`}, }) } func compareNegativeCases() iter.Seq[compareNegativeCase] { + const genMsg = `"1" is not negative` + return slices.Values([]compareNegativeCase{ - {e: int(1), msg: `"1" is not negative`}, - {e: int8(1), msg: `"1" is not negative`}, - {e: int16(1), msg: `"1" is not negative`}, - {e: int32(1), msg: `"1" is not negative`}, - {e: int64(1), msg: `"1" is not negative`}, + {e: int(1), msg: genMsg}, + {e: int8(1), msg: genMsg}, + {e: int16(1), msg: genMsg}, + {e: int32(1), msg: genMsg}, + {e: int64(1), msg: genMsg}, {e: float32(1.23), msg: `"1.23" is not negative`}, {e: float64(1.23), msg: `"1.23" is not negative`}, }) diff --git a/internal/assertions/embed_test.go b/internal/assertions/embed_test.go deleted file mode 100644 index b928cf7de..000000000 --- a/internal/assertions/embed_test.go +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -package assertions - -/* Enhancement proposal: move json literals to file fixtures in the embedded FS -import ( - "embed" -) - -//nolint:gochecknoglobals // it's okay to have embedded test fixtures a globals -var ( - //go:embed testdata/json/*.json - fixtureJSONAssets embed.FS -) -*/ diff --git a/internal/assertions/error.go b/internal/assertions/error.go index 78b350a32..0db9b4bc9 100644 --- a/internal/assertions/error.go +++ b/internal/assertions/error.go @@ -77,16 +77,16 @@ func Error(t T, err error, msgAndArgs ...any) bool { // // success: ErrTest, "assert.ErrTest general error for testing" // failure: ErrTest, "wrong error message" -func EqualError(t T, theError error, errString string, msgAndArgs ...any) bool { +func EqualError(t T, err error, errString string, msgAndArgs ...any) bool { // Domain: error if h, ok := t.(H); ok { h.Helper() } - if !Error(t, theError, msgAndArgs...) { + if !Error(t, err, msgAndArgs...) { return false } expected := errString - actual := theError.Error() + actual := err.Error() // don't need to use deep equals here, we know they are both strings if expected != actual { return Fail(t, fmt.Sprintf("Error message not equal:\n"+ @@ -108,16 +108,16 @@ func EqualError(t T, theError error, errString string, msgAndArgs ...any) bool { // // success: ErrTest, "general error" // failure: ErrTest, "not in message" -func ErrorContains(t T, theError error, contains string, msgAndArgs ...any) bool { +func ErrorContains(t T, err error, contains string, msgAndArgs ...any) bool { // Domain: error if h, ok := t.(H); ok { h.Helper() } - if !Error(t, theError, msgAndArgs...) { + if !Error(t, err, msgAndArgs...) { return false } - actual := theError.Error() + actual := err.Error() if !strings.Contains(actual, contains) { return Fail(t, fmt.Sprintf("Error %s does not contain %#v", truncatingFormat("%#v", actual), contains), msgAndArgs...) } diff --git a/internal/assertions/generics.go b/internal/assertions/generics.go new file mode 100644 index 000000000..eb2c73262 --- /dev/null +++ b/internal/assertions/generics.go @@ -0,0 +1,62 @@ +package assertions + +import ( + "cmp" + "regexp" + "time" +) + +// Type constraint definitions for generic variants of assertions. +type ( + // Boolean is a bool or any type that can convert to a bool. + Boolean interface { + ~bool + } + + // Text is any type of underlying type string or []byte. + // + // This is used by [RegexpT], [NotRegexpT], [JSONEqT], and [YAMLEqT]. + // + // NOTE: unfortunately, []rune is not supported. + Text interface { + ~string | ~[]byte + } + + // Ordered is a standard ordered type (i.e. types which support "<") plus [time.Time]. + // + // This is used by [GreaterT], [GreaterOrEqualT], [LessT], and [LessOrEqualT]. + // + // NOTE: since [time.Time] is a struct, custom types which redeclare [time.Time] are not supported. + Ordered interface { + cmp.Ordered | []byte | time.Time + } + + // SignedNumeric is an signed integer or a floating point number. + SignedNumeric interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~float32 | ~float64 + } + + // UnsignedNumeric is an unsigned integer. + // + // There a no unsigned floating point numbers. + UnsignedNumeric interface { + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 + } + + // Measurable is any number for which we can compute a delta. + // + // This is used by [InDeltaT] and [InEpsilonT]. + // + // NOTE: unfortunately complex64 and complex128 are not supported. + Measurable interface { + SignedNumeric | UnsignedNumeric | ~float32 | ~float64 + } + + // RegExp is either a text containing a regular expression to compile, or directly the compiled regexp. + // + // This is used by [RegexpT] and [NotRegexpT]. + RegExp interface { + Text | *regexp.Regexp + } +) diff --git a/internal/assertions/ifaces.go b/internal/assertions/ifaces.go index eab6737d0..3bc5d00d0 100644 --- a/internal/assertions/ifaces.go +++ b/internal/assertions/ifaces.go @@ -16,6 +16,27 @@ type H interface { Helper() } +type ( + // ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful + // for table driven tests. + ComparisonAssertionFunc func(T, any, any, ...any) bool + + // ValueAssertionFunc is a common function prototype when validating a single value. Can be useful + // for table driven tests. + ValueAssertionFunc func(T, any, ...any) bool + + // BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful + // for table driven tests. + BoolAssertionFunc func(T, bool, ...any) bool + + // ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful + // for table driven tests. + ErrorAssertionFunc func(T, error, ...any) bool + + // Comparison is a custom function that returns true on success and false on failure. + Comparison func() (success bool) +) + type failNower interface { FailNow() } diff --git a/internal/assertions/json.go b/internal/assertions/json.go index a343b66db..28cdb40e7 100644 --- a/internal/assertions/json.go +++ b/internal/assertions/json.go @@ -1,6 +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. package assertions import ( @@ -9,7 +10,9 @@ import ( "fmt" ) -// JSONEqBytes asserts that two JSON byte slices are equivalent. +// JSONEqBytes asserts that two JSON slices of bytes are equivalent. +// +// Expected and actual must be valid JSON. // // # Usage // @@ -21,6 +24,8 @@ import ( // failure: []byte(`{"hello": "world", "foo": "bar"}`), []byte(`[{"foo": "bar"}, {"hello": "world"}]`) func JSONEqBytes(t T, expected, actual []byte, msgAndArgs ...any) bool { // Domain: json + // Maintainer: proposal for enhancement. We could use and indirection for users to inject their favorite JSON + // library like we do for YAML. if h, ok := t.(H); ok { h.Helper() } @@ -44,6 +49,8 @@ func JSONEqBytes(t T, expected, actual []byte, msgAndArgs ...any) bool { // JSONEq asserts that two JSON strings are equivalent. // +// Expected and actual must be valid JSON. +// // # Usage // // assertions.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) @@ -54,5 +61,32 @@ func JSONEqBytes(t T, expected, actual []byte, msgAndArgs ...any) bool { // failure: `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]` func JSONEq(t T, expected, actual string, msgAndArgs ...any) bool { // Domain: json + if h, ok := t.(H); ok { + h.Helper() + } + + return JSONEqBytes(t, []byte(expected), []byte(actual), msgAndArgs) +} + +// JSONEqT asserts that two JSON documents are equivalent. +// +// The expected and actual arguments may be string or []byte. They do not need to be of the same type. +// +// Expected and actual must be valid JSON. +// +// # Usage +// +// assertions.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) +// +// # Examples +// +// success: `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`) +// failure: `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]` +func JSONEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool { + // Domain: json + if h, ok := t.(H); ok { + h.Helper() + } + return JSONEqBytes(t, []byte(expected), []byte(actual), msgAndArgs) } diff --git a/internal/assertions/json_test.go b/internal/assertions/json_test.go index 2ed28f2ae..e01a7035f 100644 --- a/internal/assertions/json_test.go +++ b/internal/assertions/json_test.go @@ -3,136 +3,145 @@ package assertions -import "testing" +import ( + "iter" + "slices" + "testing" +) -// Proposal for enhancement: load fixtures and assertions from embedded testdata - -func TestJSONEq_EqualSONString(t *testing.T) { - t.Parallel() - mock := new(testing.T) - - True(t, JSONEq(mock, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`)) -} - -func TestJSONEq_EquivalentButNotEqual(t *testing.T) { - t.Parallel() - mock := new(testing.T) - - True(t, JSONEq(mock, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) -} - -func TestJSONEq_HashOfArraysAndHashes(t *testing.T) { - t.Parallel() - - mock := new(testing.T) - True(t, JSONEq(mock, - "{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],"+ - "\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}", - "{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", "+ - "\"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}", - )) -} - -func TestJSONEq_Array(t *testing.T) { - t.Parallel() - - mock := new(testing.T) - True(t, JSONEq(mock, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`)) -} - -func TestJSONEq_HashAndArrayNotEquivalent(t *testing.T) { - t.Parallel() - - mock := new(testing.T) - False(t, JSONEq(mock, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`)) -} - -func TestJSONEq_HashesNotEquivalent(t *testing.T) { - t.Parallel() - - mock := new(testing.T) - False(t, JSONEq(mock, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) -} - -func TestJSONEq_ActualIsNotJSON(t *testing.T) { - t.Parallel() - - mock := new(testing.T) - False(t, JSONEq(mock, `{"foo": "bar"}`, "Not JSON")) -} - -func TestJSONEq_ExpectedIsNotJSON(t *testing.T) { +func TestJSONEq(t *testing.T) { t.Parallel() - mock := new(testing.T) - False(t, JSONEq(mock, "Not JSON", `{"foo": "bar", "hello": "world"}`)) -} - -func TestJSONEq_ExpectedAndActualNotJSON(t *testing.T) { - t.Parallel() - - mock := new(testing.T) - False(t, JSONEq(mock, "Not JSON", "Not JSON")) -} - -func TestJSONEq_ArraysOfDifferentOrder(t *testing.T) { - t.Parallel() - - mock := new(testing.T) - False(t, JSONEq(mock, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`)) -} - -func TestJSONEqBytes_EqualSONString(t *testing.T) { - mock := new(testing.T) - True(t, JSONEqBytes(mock, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"hello": "world", "foo": "bar"}`))) -} - -func TestJSONEqBytes_EquivalentButNotEqual(t *testing.T) { - mock := new(testing.T) - True(t, JSONEqBytes(mock, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`))) -} - -func TestJSONEqBytes_HashOfArraysAndHashes(t *testing.T) { - mock := new(testing.T) - True(t, JSONEqBytes(mock, - []byte("{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],"+ - "\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}"), - []byte("{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", "+ - "\"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}", - ))) -} - -func TestJSONEqBytes_Array(t *testing.T) { - mock := new(testing.T) - True(t, JSONEqBytes(mock, []byte(`["foo", {"hello": "world", "nested": "hash"}]`), []byte(`["foo", {"nested": "hash", "hello": "world"}]`))) -} - -func TestJSONEqBytes_HashAndArrayNotEquivalent(t *testing.T) { - mock := new(testing.T) - False(t, JSONEqBytes(mock, []byte(`["foo", {"hello": "world", "nested": "hash"}]`), []byte(`{"foo": "bar", {"nested": "hash", "hello": "world"}}`))) -} - -func TestJSONEqBytes_HashesNotEquivalent(t *testing.T) { - mock := new(testing.T) - False(t, JSONEqBytes(mock, []byte(`{"foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`))) -} - -func TestJSONEqBytes_ActualIsNotJSON(t *testing.T) { - mock := new(testing.T) - False(t, JSONEqBytes(mock, []byte(`{"foo": "bar"}`), []byte("Not JSON"))) -} - -func TestJSONEqBytes_ExpectedIsNotJSON(t *testing.T) { - mock := new(testing.T) - False(t, JSONEqBytes(mock, []byte("Not JSON"), []byte(`{"foo": "bar", "hello": "world"}`))) -} - -func TestJSONEqBytes_ExpectedAndActualNotJSON(t *testing.T) { - mock := new(testing.T) - False(t, JSONEqBytes(mock, []byte("Not JSON"), []byte("Not JSON"))) -} - -func TestJSONEqBytes_ArraysOfDifferentOrder(t *testing.T) { - mock := new(testing.T) - False(t, JSONEqBytes(mock, []byte(`["foo", {"hello": "world", "nested": "hash"}]`), []byte(`[{ "hello": "world", "nested": "hash"}, "foo"]`))) + for tc := range jsonCases() { + t.Run(tc.name, tc.test) + } +} + +// 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 + return func(t *testing.T) { + t.Run("with JSONEq", testJSONEq(expected, actual, success)) + t.Run("with JSONEqBytes", testJSONEqBytes(expected, actual, success)) + t.Run("with JSONEqT[[]byte,string]", testJSONEqT[[]byte, string](expected, actual, success)) + t.Run("with JSONEqT[string,[]byte]", testJSONEqT[string, []byte](expected, actual, success)) + t.Run("with JSONEqT[byte,byte]", testJSONEqT[[]byte, []byte](expected, actual, success)) + t.Run("with JSONEqT[string,string]", testJSONEqT[string, string](expected, actual, success)) + } +} + +func testJSONEq(expected, actual string, success bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + res := JSONEq(mock, expected, actual) + if res != success { + if success { + croakWantEquiv(t, expected, actual) + return + } + croakWantNotEquiv(t, expected, actual) + } + } +} + +func testJSONEqBytes(expected, actual string, success bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + res := JSONEqBytes(mock, []byte(expected), []byte(actual)) + if res != success { + if success { + croakWantEquiv(t, expected, actual) + return + } + croakWantNotEquiv(t, expected, actual) + } + } +} + +func testJSONEqT[EDoc, ADoc Text](expected, actual string, success bool) func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + res := JSONEqT(mock, EDoc(expected), ADoc(actual)) + if res != success { + if success { + croakWantEquiv(t, expected, actual) + return + } + croakWantNotEquiv(t, expected, actual) + } + } +} + +func croakWantEquiv(t *testing.T, expected, actual string) { + t.Helper() + t.Errorf("expected %q to be equivalent to %q", expected, actual) +} + +func croakWantNotEquiv(t *testing.T, expected, actual string) { + t.Helper() + t.Errorf("expected %q NOT to be equivalent to %q", expected, actual) +} + +func jsonCases() iter.Seq[genericTestCase] { + return slices.Values([]genericTestCase{ + {"should be equivalent JSON", testAllJSONEq( + `{"hello": "world", "foo": "bar"}`, + `{"hello": "world", "foo": "bar"}`, + true, + )}, + {"should be equivalent but not equal JSON", testAllJSONEq( + `{"hello": "world", "foo": "bar"}`, + `{"foo": "bar", "hello": "world"}`, + true, + )}, + {"should be equivalent object with array and nested objects", testAllJSONEq( + "{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],"+ + "\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}", + "{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", "+ + "\"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}", + true, + )}, + {"should be equivalent array (ordered)", testAllJSONEq( + `["foo", {"hello": "world", "nested": "hash"}]`, + `["foo", {"nested": "hash", "hello": "world"}]`, + true, + )}, + {"should NOT be equivalent array (elements are different)", testAllJSONEq( + `["foo", {"hello": "world", "nested": "hash"}]`, + `{"foo": "bar", {"nested": "hash", "hello": "world"}}`, + false, + )}, + {"should NOT be equivalent array (order is different)", testAllJSONEq( + `["foo", {"hello": "world", "nested": "hash"}]`, + `[{ "hello": "world", "nested": "hash"}, "foo"]`, + false, + )}, + {"should NOT be equivalent objects (keys are different)", testAllJSONEq( + `{"foo": "bar"}`, + `{"foo": "bar", "hello": "world"}`, + false, + )}, + {"should fail with actual invalid JSON", testAllJSONEq( + `{"foo": "bar"}`, + "Not JSON", + false, + )}, + {"should fail with expected invalid JSON", testAllJSONEq( + "Not JSON", + `{"foo": "bar", "hello": "world"}`, + false, + )}, + {"should fail with expected and actual invalid JSON", testAllJSONEq( + "Not JSON", + "Not JSON", + false, + )}, + }) } diff --git a/internal/assertions/number.go b/internal/assertions/number.go index e8cad406b..5eda842ff 100644 --- a/internal/assertions/number.go +++ b/internal/assertions/number.go @@ -39,7 +39,7 @@ func InDelta(t T, expected, actual any, delta float64, msgAndArgs ...any) bool { } if math.IsNaN(af) { - return Fail(t, "Expected must not be NaN", msgAndArgs...) + return Fail(t, "Expected must not be NaN", msgAndArgs...) // Proposal for enhancement: wrong message (this is accepted above) } if math.IsNaN(bf) { @@ -54,6 +54,234 @@ func InDelta(t T, expected, actual any, delta float64, msgAndArgs ...any) bool { return true } +// InDeltaT asserts that the two numerals of the same type numerical type are within delta of each other. +// +// [InDeltaT] accepts any go numeric type, including integer types. +// +// The main difference with [InDelta] is that the delta is expressed with the same type as the values, not necessarily a float64. +// +// Delta must be greater than or equal to zero. +// +// # Behavior with IEEE floating point arithmetics +// +// - expected NaN is matched only by a NaN, e.g. this works: InDeltaT(math.NaN(), math.Sqrt(-1), 0.0) +// - expected +Inf is matched only by a +Inf +// - expected -Inf is matched only by a -Inf +// +// # Usage +// +// assertions.InDeltaT(t, math.Pi, 22/7.0, 0.01) +// +// # Examples +// +// success: 1.0, 1.01, 0.02 +// failure: 1.0, 1.1, 0.05 +func InDeltaT[Number Measurable](t T, expected, actual, delta Number, msgAndArgs ...any) bool { + // Domain: number + if h, ok := t.(H); ok { + h.Helper() + } + + if delta < 0 { + return Fail(t, "Delta must not be negative", msgAndArgs...) // TODO: add it to the original version + } + + // IEEE float edge cases: NaN, +Inf/-Inf + if isNaN(delta) || isInf(delta, 0) { + return Fail(t, "Delta must not be NaN or Inf", msgAndArgs...) // TODO: add it to the original version + } + + expectedInf := isInf(expected, 0) + actualInf := isInf(actual, 0) + if expectedInf { + // expected -Inf/+Inf + if !actualInf { + return Fail(t, "Expected an Inf value", msgAndArgs...) + } + + if isInf(expected, 1) && !isInf(actual, 1) { + return Fail(t, "Expected a +Inf value but got -Inf", msgAndArgs...) + } + + if isInf(expected, -1) && !isInf(actual, -1) { + return Fail(t, "Expected a -Inf value but got +Inf", msgAndArgs...) + } + + // Both are Inf and match - success + return true + } + + if actualInf { + return Fail(t, "Actual is Inf", msgAndArgs...) + } + + expectedNaN := isNaN(expected) + actualNaN := isNaN(actual) + + if expectedNaN && actualNaN { + // expected NaN + return true + } + + if expectedNaN { + return Fail(t, "Expected a NaN value but actual is finite", msgAndArgs...) + } + + if actualNaN { + return Fail(t, fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), msgAndArgs...) + } + + var ( + dt Number + failed bool + ) + + // check is a little slower than straight delta, but it can handle unsigned numbers without errors + if expected > actual { + dt = expected - actual + } else { + dt = actual - expected + } + failed = dt > delta + if failed { + return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) + } + + return true +} + +// InEpsilon asserts that expected and actual have a relative error less than epsilon. +// +// # Usage +// +// assertions.InEpsilon(t, 100.0, 101.0, 0.02) +// +// # Examples +// +// success: 100.0, 101.0, 0.02 +// failure: 100.0, 110.0, 0.05 +func InEpsilon(t T, expected, actual any, epsilon float64, msgAndArgs ...any) bool { + // Domain: number + if h, ok := t.(H); ok { + h.Helper() + } + if math.IsNaN(epsilon) { + return Fail(t, "epsilon must not be NaN", msgAndArgs...) + } + actualEpsilon, err := calcRelativeError(expected, actual) + if err != nil { + return Fail(t, err.Error(), msgAndArgs...) + } + if math.IsNaN(actualEpsilon) { + return Fail(t, "relative error is NaN", msgAndArgs...) + } + if actualEpsilon > epsilon { + return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ + " < %#v (actual)", epsilon, actualEpsilon), msgAndArgs...) + } + + return true +} + +// InEpsilonT asserts that expected and actual have a relative error less than epsilon. +// +// When expected is zero, epsilon is interpreted as an absolute error threshold, +// since relative error is mathematically undefined for zero values. +// +// Formula: +// - If expected == 0: fail if |actual - expected| > epsilon +// - If expected != 0: fail if |actual - expected| > epsilon * |expected| +// +// This allows InEpsilonT to work naturally across the full numeric range including zero. +// +// # Usage +// +// assertions.InEpsilon(t, 100.0, 101.0, 0.02) +// +// # Examples +// +// success: 100.0, 101.0, 0.02 +// failure: 100.0, 110.0, 0.05 +func InEpsilonT[Number Measurable](t T, expected, actual Number, epsilon float64, msgAndArgs ...any) bool { + // Domain: number + if h, ok := t.(H); ok { + h.Helper() + } + + if epsilon < 0 { + return Fail(t, "Epsilon must not be negative", msgAndArgs...) + } + + // IEEE float edge cases: NaN, +Inf/-Inf + if isNaN(epsilon) || isInf(epsilon, 0) { + return Fail(t, "Epsilon must not be NaN or Inf", msgAndArgs...) + } + + expectedInf := isInf(expected, 0) + actualInf := isInf(actual, 0) + if expectedInf { + // expected -Inf/+Inf + if !actualInf { + return Fail(t, "Expected an Inf value", msgAndArgs...) + } + + if isInf(expected, 1) && !isInf(actual, 1) { + return Fail(t, "Expected a +Inf value but got -Inf", msgAndArgs...) + } + + if isInf(expected, -1) && !isInf(actual, -1) { + return Fail(t, "Expected a -Inf value but got +Inf", msgAndArgs...) + } + + // Both are Inf and match - success + return true + } + + if actualInf { + return Fail(t, "Actual is Inf", msgAndArgs...) + } + + expectedNaN := isNaN(expected) + actualNaN := isNaN(actual) + + if expectedNaN && actualNaN { + // expected NaN + return true + } + + if expectedNaN { + return Fail(t, "Expected a NaN value but actual is finite", msgAndArgs...) + } + + if actualNaN { + return Fail(t, fmt.Sprintf("Expected %v with epsilon %v, but was NaN", expected, epsilon), msgAndArgs...) + } + + af := float64(expected) + bf := float64(actual) + + delta := math.Abs(af - bf) + if delta == 0 { + return true + } + if af == 0 { + if delta > epsilon { + return Fail(t, fmt.Sprintf( + "Expected value is zero, using absolute error comparison.\n"+ + "Absolute difference is too high: %#v (expected)\n"+ + " < %#v (actual)", epsilon, delta), msgAndArgs...) + } + return true + } + + if delta > epsilon*math.Abs(af) { + return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ + " < %#v (actual)", epsilon, delta/math.Abs(af)), msgAndArgs...) + } + + return true +} + // InDeltaSlice is the same as InDelta, except it compares two slices. // // # Usage @@ -142,39 +370,6 @@ func InDeltaMapValues(t T, expected, actual any, delta float64, msgAndArgs ...an return true } -// InEpsilon asserts that expected and actual have a relative error less than epsilon. -// -// # Usage -// -// assertions.InEpsilon(t, 100.0, 101.0, 0.02) -// -// # Examples -// -// success: 100.0, 101.0, 0.02 -// failure: 100.0, 110.0, 0.05 -func InEpsilon(t T, expected, actual any, epsilon float64, msgAndArgs ...any) bool { - // Domain: number - if h, ok := t.(H); ok { - h.Helper() - } - if math.IsNaN(epsilon) { - return Fail(t, "epsilon must not be NaN", msgAndArgs...) - } - actualEpsilon, err := calcRelativeError(expected, actual) - if err != nil { - return Fail(t, err.Error(), msgAndArgs...) - } - if math.IsNaN(actualEpsilon) { - return Fail(t, "relative error is NaN", msgAndArgs...) - } - if actualEpsilon > epsilon { - return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ - " < %#v (actual)", epsilon, actualEpsilon), msgAndArgs...) - } - - return true -} - // InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. // // # Usage @@ -275,3 +470,24 @@ func toFloat(x any) (float64, bool) { return xf, xok } + +func isNaN[Number Measurable](f Number) bool { + return f != f //nolint:gocritic // yes this weird property is held by NaN only +} + +func isInf[Number Measurable](f Number, sign int) bool { + v := any(f) + + switch ff := v.(type) { + case float32: + return isInf32(ff, sign) + case float64: + return math.IsInf(ff, sign) + default: + return false + } +} + +func isInf32(f float32, sign int) bool { + return (sign >= 0 && f > math.MaxFloat32) || (sign <= 0 && f < -math.MaxFloat32) +} diff --git a/internal/assertions/number_test.go b/internal/assertions/number_test.go index b6eef3404..8aa766865 100644 --- a/internal/assertions/number_test.go +++ b/internal/assertions/number_test.go @@ -4,6 +4,7 @@ package assertions import ( + "fmt" "iter" "math" "slices" @@ -13,20 +14,70 @@ import ( func TestNumberInDelta(t *testing.T) { t.Parallel() - mock := new(testing.T) - True(t, InDelta(mock, 1.001, 1, 0.01), "|1.001 - 1| <= 0.01") - True(t, InDelta(mock, 1, 1.001, 0.01), "|1 - 1.001| <= 0.01") - True(t, InDelta(mock, 1, 2, 1), "|1 - 2| <= 1") - False(t, InDelta(mock, 1, 2, 0.5), "Expected |1 - 2| <= 0.5 to fail") - False(t, InDelta(mock, 2, 1, 0.5), "Expected |2 - 1| <= 0.5 to fail") - False(t, InDelta(mock, "", nil, 1), "Expected non numerals to fail") - False(t, InDelta(mock, 42, math.NaN(), 0.01), "Expected NaN for actual to fail") - False(t, InDelta(mock, math.NaN(), 42, 0.01), "Expected NaN for expected to fail") - True(t, InDelta(mock, math.NaN(), math.NaN(), 0.01), "Expected NaN for both to pass") - - for tc := range numberInDeltaCases() { - True(t, InDelta(mock, tc.a, tc.b, tc.delta), "Expected |%V - %V| <= %v", tc.a, tc.b, tc.delta) + t.Run("with simple input", func(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + + True(t, InDelta(mock, 1.001, 1, 0.01), "|1.001 - 1| <= 0.01") + True(t, InDelta(mock, 1, 1.001, 0.01), "|1 - 1.001| <= 0.01") + True(t, InDelta(mock, 1, 2, 1), "|1 - 2| <= 1") + False(t, InDelta(mock, 1, 2, 0.5), "Expected |1 - 2| <= 0.5 to fail") + False(t, InDelta(mock, 2, 1, 0.5), "Expected |2 - 1| <= 0.5 to fail") + False(t, InDelta(mock, "", nil, 1), "Expected non numerals to fail") + }) + + t.Run("with edge cases", func(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + + False(t, InDelta(mock, 42, math.NaN(), 0.01), "Expected NaN for actual to fail") + False(t, InDelta(mock, math.NaN(), 42, 0.01), "Expected NaN for expected to fail") + True(t, InDelta(mock, math.NaN(), math.NaN(), 0.01), "Expected NaN for both to pass") + }) + + t.Run("should be within delta", func(t *testing.T) { + for tc := range numberInDeltaCases() { + t.Run(fmt.Sprintf("%f - %f", tc.a, tc.b), func(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + + True(t, InDelta(mock, tc.a, tc.b, tc.delta), "Expected |%V - %V| <= %v", tc.a, tc.b, tc.delta) + }) + } + }) +} + +func TestNumberInDeltaT(t *testing.T) { + t.Parallel() + + t.Run("with simple input", func(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + + True(t, InDeltaT(mock, 1.001, 1, 0.01), "|1.001 - 1| <= 0.01") + True(t, InDeltaT(mock, 1, 1.001, 0.01), "|1 - 1.001| <= 0.01") + True(t, InDeltaT(mock, 1, 2, 1), "|1 - 2| <= 1") + False(t, InDeltaT[float32](mock, 1, 2, 0.5), "Expected |1 - 2| <= 0.5 to fail") + False(t, InDeltaT(mock, 2, 1, 0.5), "Expected |2 - 1| <= 0.5 to fail") + }) + + t.Run("with edge cases", func(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + + False(t, InDeltaT(mock, 42, math.NaN(), 0.01), "Expected NaN for actual to fail") + False(t, InDeltaT(mock, math.NaN(), 42, 0.01), "Expected NaN for expected to fail") + True(t, InDeltaT(mock, math.NaN(), math.NaN(), 0.01), "Expected NaN for both to pass") + }) + + for tc := range deltaTCases() { + t.Run(tc.name, tc.test) } } @@ -63,14 +114,35 @@ func TestNumberInDeltaMapValues(t *testing.T) { func TestNumberInEpsilon(t *testing.T) { t.Parallel() - mock := new(testing.T) for tc := range numberInEpsilonTrueCases() { - True(t, InEpsilon(t, tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon), "test: %q", tc) + t.Run("with InEpsilon true", func(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + True(t, + InEpsilon(mock, tc.a, tc.b, tc.epsilon, + "Expected %V and %V to have a relative difference of %v", + tc.a, tc.b, tc.epsilon, + ), + "test: %q", tc, + ) + }) } for tc := range numberInEpsilonFalseCases() { - False(t, InEpsilon(mock, tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon)) + t.Run("with InEpsilon false", func(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + False(t, + InEpsilon(mock, tc.a, tc.b, tc.epsilon, + "Expected %V and %V to have a relative difference of %v", + tc.a, tc.b, tc.epsilon, + ), + "test: %q", tc, + ) + }) } } @@ -113,6 +185,148 @@ func numberInDeltaCases() iter.Seq[numberInDeltaCase] { }) } +func testDeltaT[Number Measurable](a, b, delta Number, success bool) func(*testing.T) { + //nolint:thelper // linter false positive: this is not a helper + return func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + + if success { + True(t, InDeltaT(mock, a, b, delta)) + return + } + + False(t, InDeltaT(mock, a, b, delta)) + } +} + +func deltaTCases() iter.Seq[genericTestCase] { + return slices.Values([]genericTestCase{ + // All integer types - basic success cases + {"int/success", testDeltaT[int](2, 1, 1, true)}, + {"int8/success", testDeltaT[int8](2, 1, 1, true)}, + {"int16/success", testDeltaT[int16](2, 1, 1, true)}, + {"int32/success", testDeltaT[int32](2, 1, 1, true)}, + {"int64/success", testDeltaT[int64](2, 1, 1, true)}, + {"uint/success", testDeltaT[uint](2, 1, 1, true)}, + {"uint8/success", testDeltaT[uint8](2, 1, 1, true)}, + {"uint16/success", testDeltaT[uint16](2, 1, 1, true)}, + {"uint32/success", testDeltaT[uint32](2, 1, 1, true)}, + {"uint64/success", testDeltaT[uint64](2, 1, 1, true)}, + {"float32/success", testDeltaT[float32](2.0, 1.0, 1.0, true)}, + {"float64/success", testDeltaT[float64](2.0, 1.0, 1.0, true)}, + + // Basic failure cases + {"int/failure", testDeltaT[int](10, 1, 5, false)}, + {"uint/failure", testDeltaT[uint](10, 1, 5, false)}, + {"float64/failure", testDeltaT[float64](10.0, 1.0, 5.0, false)}, + + // Exact match (zero delta) + {"int/exact", testDeltaT[int](5, 5, 0, true)}, + {"uint/exact", testDeltaT[uint](5, 5, 0, true)}, + {"float64/exact", testDeltaT[float64](5.0, 5.0, 0.0, true)}, + + // Zero values + {"int/zero", testDeltaT[int](0, 0, 0, true)}, + {"uint/zero", testDeltaT[uint](0, 0, 0, true)}, + {"float64/zero", testDeltaT[float64](0.0, 0.0, 0.0, true)}, + {"int/near-zero", testDeltaT[int](1, 0, 1, true)}, + {"float64/near-zero", testDeltaT[float64](0.01, 0.0, 0.02, true)}, + + // Negative numbers + {"int/negative", testDeltaT[int](-5, -4, 2, true)}, + {"int/negative-fail", testDeltaT[int](-10, -1, 5, false)}, + {"float64/negative", testDeltaT[float64](-5.0, -4.0, 2.0, true)}, + + // Mixed positive/negative + {"int/mixed", testDeltaT[int](5, -5, 10, true)}, + {"int/mixed-fail", testDeltaT[int](5, -5, 9, false)}, + {"float64/mixed", testDeltaT[float64](5.0, -5.0, 10.0, true)}, + + // Unsigned integer edge cases (overflow protection) + {"uint/expected-greater", testDeltaT[uint](100, 50, 60, true)}, + {"uint/actual-greater", testDeltaT[uint](50, 100, 60, true)}, + {"uint8/max-value", testDeltaT[uint8](255, 250, 10, true)}, + {"uint8/max-value-fail", testDeltaT[uint8](255, 250, 4, false)}, + {"uint16/large-diff", testDeltaT[uint16](60000, 40000, 25000, true)}, + {"uint32/large-diff", testDeltaT[uint32](4000000000, 3000000000, 1000000001, true)}, + {"uint64/large-diff", testDeltaT[uint64](10000000000, 5000000000, 5000000001, true)}, + + // Boundary testing for unsigned (expected > actual path) + {"uint8/boundary-expected-gt-actual", testDeltaT[uint8](200, 100, 100, true)}, + {"uint8/boundary-expected-gt-actual-fail", testDeltaT[uint8](200, 100, 99, false)}, + // Boundary testing for unsigned (actual > expected path) + {"uint8/boundary-actual-gt-expected", testDeltaT[uint8](100, 200, 100, true)}, + {"uint8/boundary-actual-gt-expected-fail", testDeltaT[uint8](100, 200, 99, false)}, + + // Float32 NaN cases + {"float32/both-nan", testDeltaT[float32](float32(math.NaN()), float32(math.NaN()), 1.0, true)}, + {"float32/expected-nan", testDeltaT[float32](float32(math.NaN()), 1.0, 1.0, false)}, + {"float32/actual-nan", testDeltaT[float32](1.0, float32(math.NaN()), 1.0, false)}, + + // Float64 NaN cases + {"float64/both-nan", testDeltaT[float64](math.NaN(), math.NaN(), 1.0, true)}, + {"float64/expected-nan", testDeltaT[float64](math.NaN(), 1.0, 1.0, false)}, + {"float64/actual-nan", testDeltaT[float64](1.0, math.NaN(), 1.0, false)}, + + // Float32 +Inf cases + {"float32/both-plus-inf", testDeltaT[float32](float32(math.Inf(1)), float32(math.Inf(1)), 1.0, true)}, + {"float32/expected-plus-inf-actual-minus-inf", testDeltaT[float32](float32(math.Inf(1)), float32(math.Inf(-1)), 1.0, false)}, + {"float32/expected-plus-inf-actual-finite", testDeltaT[float32](float32(math.Inf(1)), 1.0, 1.0, false)}, + {"float32/expected-finite-actual-plus-inf", testDeltaT[float32](1.0, float32(math.Inf(1)), 1.0, false)}, + + // Float64 +Inf cases + {"float64/both-plus-inf", testDeltaT[float64](math.Inf(1), math.Inf(1), 1.0, true)}, + {"float64/expected-plus-inf-actual-minus-inf", testDeltaT[float64](math.Inf(1), math.Inf(-1), 1.0, false)}, + {"float64/expected-plus-inf-actual-finite", testDeltaT[float64](math.Inf(1), 1.0, 1.0, false)}, + {"float64/expected-finite-actual-plus-inf", testDeltaT[float64](1.0, math.Inf(1), 1.0, false)}, + + // Float32 -Inf cases + {"float32/both-minus-inf", testDeltaT[float32](float32(math.Inf(-1)), float32(math.Inf(-1)), 1.0, true)}, + {"float32/expected-minus-inf-actual-plus-inf", testDeltaT[float32](float32(math.Inf(-1)), float32(math.Inf(1)), 1.0, false)}, + {"float32/expected-minus-inf-actual-finite", testDeltaT[float32](float32(math.Inf(-1)), 1.0, 1.0, false)}, + {"float32/expected-finite-actual-minus-inf", testDeltaT[float32](1.0, float32(math.Inf(-1)), 1.0, false)}, + + // Float64 -Inf cases + {"float64/both-minus-inf", testDeltaT[float64](math.Inf(-1), math.Inf(-1), 1.0, true)}, + {"float64/expected-minus-inf-actual-plus-inf", testDeltaT[float64](math.Inf(-1), math.Inf(1), 1.0, false)}, + {"float64/expected-minus-inf-actual-finite", testDeltaT[float64](math.Inf(-1), 1.0, 1.0, false)}, + {"float64/expected-finite-actual-minus-inf", testDeltaT[float64](1.0, math.Inf(-1), 1.0, false)}, + + // Delta validation - NaN delta + {"float64/delta-nan", testDeltaT[float64](1.0, 1.0, math.NaN(), false)}, + {"float32/delta-nan", testDeltaT[float32](1.0, 1.0, float32(math.NaN()), false)}, + + // Delta validation - Inf delta + {"float64/delta-plus-inf", testDeltaT[float64](1.0, 1.0, math.Inf(1), false)}, + {"float64/delta-minus-inf", testDeltaT[float64](1.0, 1.0, math.Inf(-1), false)}, + {"float32/delta-plus-inf", testDeltaT[float32](1.0, 1.0, float32(math.Inf(1)), false)}, + {"float32/delta-minus-inf", testDeltaT[float32](1.0, 1.0, float32(math.Inf(-1)), false)}, + + // Very small deltas (precision testing) + {"float64/small-delta", testDeltaT[float64](1.0, 1.0000001, 0.000001, true)}, + {"float64/small-delta-fail", testDeltaT[float64](1.0, 1.0000001, 0.00000001, false)}, + {"float32/small-delta", testDeltaT[float32](1.0, 1.00001, 0.0001, true)}, + {"float32/small-delta-fail", testDeltaT[float32](1.0, 1.00001, 0.00001, false)}, + + // Large values + {"int64/large-values", testDeltaT[int64](9223372036854775800, 9223372036854775700, 200, true)}, + {"uint64/large-values", testDeltaT[uint64](18446744073709551600, 18446744073709551500, 200, true)}, + {"float64/large-values", testDeltaT[float64](1e308, 1e308-1e307, 2e307, true)}, + + // Edge case: delta is zero with different values + {"int/zero-delta-different", testDeltaT[int](5, 6, 0, false)}, + {"float64/zero-delta-different", testDeltaT[float64](5.0, 5.00001, 0.0, false)}, + + // Commutative property (order shouldn't matter) + {"int/commutative-1", testDeltaT[int](10, 5, 6, true)}, + {"int/commutative-2", testDeltaT[int](5, 10, 6, true)}, + {"float64/commutative-1", testDeltaT[float64](10.0, 5.0, 6.0, true)}, + {"float64/commutative-2", testDeltaT[float64](5.0, 10.0, 6.0, true)}, + }) +} + type numberInDeltaMapCase struct { title string expect any @@ -233,3 +447,237 @@ func numberInEpsilonFalseCases() iter.Seq[numberInEpsilonCase] { {math.Inf(-1), math.Inf(-1), 1}, }) } + +func TestNumberInEpsilonT(t *testing.T) { + t.Parallel() + + t.Run("with simple input", func(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + + // Basic relative error: 100 vs 101 has 1% error, within 2% epsilon + True(t, InEpsilonT(mock, 100.0, 101.0, 0.02), "1% error should be within 2% epsilon") + // 100 vs 105 has 5% error, exceeds 2% epsilon + False(t, InEpsilonT(mock, 100.0, 105.0, 0.02), "5% error should exceed 2% epsilon") + // Exact match always passes + True(t, InEpsilonT(mock, 100, 100, 0.0), "Exact match should pass even with 0 epsilon") + }) + + t.Run("with edge cases", func(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + + // NaN cases + False(t, InEpsilonT(mock, 42.0, math.NaN(), 0.01), "Expected NaN for actual to fail") + False(t, InEpsilonT(mock, math.NaN(), 42.0, 0.01), "Expected NaN for expected to fail") + True(t, InEpsilonT(mock, math.NaN(), math.NaN(), 0.01), "Expected NaN for both to pass") + + // Zero expected value uses absolute error + True(t, InEpsilonT(mock, 0.0, 0.009, 0.01), "Zero expected: |0.009| <= 0.01 should pass") + False(t, InEpsilonT(mock, 0.0, 0.011, 0.01), "Zero expected: |0.011| > 0.01 should fail") + }) + + for tc := range epsilonTCases() { + t.Run(tc.name, tc.test) + } +} + +func TestInDeltaTErrorMessage(t *testing.T) { + t.Parallel() + + mock := new(mockT) + + // Test that error message shows correct difference + InDeltaT(mock, 10, 1, 5) + + if !mock.Failed() { + t.Error("Expected test to fail but it passed") + } + + // Verify the error message contains the actual difference (9) + errorMsg := mock.errorString() + if !Contains(t, errorMsg, "difference was 9") { + t.Errorf("Error message should contain 'difference was 9', got: %s", errorMsg) + } +} + +func TestInEpsilonTErrorMessage(t *testing.T) { + t.Parallel() + + t.Run("relative error message", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + + // Test relative error: 100 vs 110 has 10% error, exceeds 5% epsilon + InEpsilonT(mock, 100.0, 110.0, 0.05) + + if !mock.Failed() { + t.Error("Expected test to fail but it passed") + } + + // Verify the error message contains relative error + errorMsg := mock.errorString() + if !Contains(t, 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) + } + }) + + t.Run("absolute error message for zero expected", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + + // Test absolute error: expected=0, actual=0.5, epsilon=0.1 + InEpsilonT(mock, 0.0, 0.5, 0.1) + + if !mock.Failed() { + t.Error("Expected test to fail but it passed") + } + + // Verify the error message mentions absolute error + errorMsg := mock.errorString() + if !Contains(t, 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 !Contains(t, errorMsg, "0.5") { + t.Errorf("Error message should contain '0.5' (absolute difference), got: %s", errorMsg) + } + }) +} + +func epsilonTCases() iter.Seq[genericTestCase] { + return slices.Values([]genericTestCase{ + // All numeric types - basic success cases (12 cases) + {"int/success", testEpsilonT[int](100, 101, 0.02, true)}, // 1% error < 2% epsilon + {"int8/success", testEpsilonT[int8](100, 101, 0.02, true)}, // 1% error < 2% epsilon + {"int16/success", testEpsilonT[int16](100, 101, 0.02, true)}, // 1% error < 2% epsilon + {"int32/success", testEpsilonT[int32](100, 101, 0.02, true)}, // 1% error < 2% epsilon + {"int64/success", testEpsilonT[int64](100, 101, 0.02, true)}, // 1% error < 2% epsilon + {"uint/success", testEpsilonT[uint](100, 101, 0.02, true)}, // 1% error < 2% epsilon + {"uint8/success", testEpsilonT[uint8](100, 101, 0.02, true)}, // 1% error < 2% epsilon + {"uint16/success", testEpsilonT[uint16](100, 101, 0.02, true)}, // 1% error < 2% epsilon + {"uint32/success", testEpsilonT[uint32](100, 101, 0.02, true)}, // 1% error < 2% epsilon + {"uint64/success", testEpsilonT[uint64](100, 101, 0.02, true)}, // 1% error < 2% epsilon + {"float32/success", testEpsilonT[float32](100.0, 101.0, 0.02, true)}, // 1% error < 2% epsilon + {"float64/success", testEpsilonT[float64](100.0, 101.0, 0.02, true)}, // 1% error < 2% epsilon + + // Basic failure cases (3 cases) + {"int/failure", testEpsilonT[int](100, 110, 0.05, false)}, // 10% error > 5% epsilon + {"uint/failure", testEpsilonT[uint](100, 110, 0.05, false)}, // 10% error > 5% epsilon + {"float64/failure", testEpsilonT[float64](100.0, 110.0, 0.05, false)}, // 10% error > 5% epsilon + + // Exact match (3 cases) + {"int/exact", testEpsilonT[int](100, 100, 0.0, true)}, // Exact match + {"uint/exact", testEpsilonT[uint](100, 100, 0.0, true)}, // Exact match + {"float64/exact", testEpsilonT[float64](100.0, 100.0, 0.0, true)}, // Exact match + + // Zero expected value - uses absolute error (8 cases) + {"int/both-zero", testEpsilonT[int](0, 0, 0.01, true)}, // Both zero + {"uint/both-zero", testEpsilonT[uint](0, 0, 0.01, true)}, // Both zero + {"float64/both-zero", testEpsilonT[float64](0.0, 0.0, 0.01, true)}, // Both zero + {"float64/zero-expected-within", testEpsilonT[float64](0.0, 0.009, 0.01, true)}, // |0.009| <= 0.01 + {"float64/zero-expected-at-boundary", testEpsilonT[float64](0.0, 0.01, 0.01, true)}, // |0.01| <= 0.01 + {"float64/zero-expected-exceed", testEpsilonT[float64](0.0, 0.011, 0.01, false)}, // |0.011| > 0.01 + {"float64/zero-expected-large", testEpsilonT[float64](0.0, 100.0, 0.01, false)}, // |100| > 0.01 + {"int/zero-expected-negative", testEpsilonT[int](0, -5, 10, true)}, // |-5| <= 10 (absolute) + + // Near-zero values (2 cases) + {"float64/near-zero-success", testEpsilonT[float64](0.001, 0.00101, 0.02, true)}, // 1% error < 2% + {"float64/near-zero-failure", testEpsilonT[float64](0.001, 0.00110, 0.05, false)}, // 10% error > 5% + + // Negative numbers (3 cases) + {"int/negative", testEpsilonT[int](-100, -101, 0.02, true)}, // 1% error < 2% + {"int/negative-fail", testEpsilonT[int](-100, -110, 0.05, false)}, // 10% error > 5% + {"float64/negative", testEpsilonT[float64](-100.0, -101.0, 0.02, true)}, // 1% error < 2% + + // Mixed positive/negative (3 cases) + {"int/mixed-small", testEpsilonT[int](100, -100, 2.1, true)}, // 200% error <= 210% epsilon + {"int/mixed-fail", testEpsilonT[int](100, -100, 1.9, false)}, // 200% error > 190% epsilon + {"float64/mixed", testEpsilonT[float64](100.0, -100.0, 2.1, true)}, // 200% error <= 210% epsilon + + // Float32 NaN cases (3 cases) + {"float32/both-nan", testEpsilonT[float32](float32(math.NaN()), float32(math.NaN()), 0.01, true)}, + {"float32/expected-nan", testEpsilonT[float32](float32(math.NaN()), 42.0, 0.01, false)}, + {"float32/actual-nan", testEpsilonT[float32](42.0, float32(math.NaN()), 0.01, false)}, + + // Float64 NaN cases (3 cases) + {"float64/both-nan", testEpsilonT[float64](math.NaN(), math.NaN(), 0.01, true)}, + {"float64/expected-nan", testEpsilonT[float64](math.NaN(), 42.0, 0.01, false)}, + {"float64/actual-nan", testEpsilonT[float64](42.0, math.NaN(), 0.01, false)}, + + // Float32 +Inf cases (4 cases) + {"float32/both-plus-inf", testEpsilonT[float32](float32(math.Inf(1)), float32(math.Inf(1)), 0.01, true)}, + {"float32/expected-plus-inf-actual-minus-inf", testEpsilonT[float32](float32(math.Inf(1)), float32(math.Inf(-1)), 0.01, false)}, + {"float32/expected-plus-inf-actual-finite", testEpsilonT[float32](float32(math.Inf(1)), 100.0, 0.01, false)}, + {"float32/expected-finite-actual-plus-inf", testEpsilonT[float32](100.0, float32(math.Inf(1)), 0.01, false)}, + + // Float64 +Inf cases (4 cases) + {"float64/both-plus-inf", testEpsilonT[float64](math.Inf(1), math.Inf(1), 0.01, true)}, + {"float64/expected-plus-inf-actual-minus-inf", testEpsilonT[float64](math.Inf(1), math.Inf(-1), 0.01, false)}, + {"float64/expected-plus-inf-actual-finite", testEpsilonT[float64](math.Inf(1), 100.0, 0.01, false)}, + {"float64/expected-finite-actual-plus-inf", testEpsilonT[float64](100.0, math.Inf(1), 0.01, false)}, + + // Float32 -Inf cases (4 cases) + {"float32/both-minus-inf", testEpsilonT[float32](float32(math.Inf(-1)), float32(math.Inf(-1)), 0.01, true)}, + {"float32/expected-minus-inf-actual-plus-inf", testEpsilonT[float32](float32(math.Inf(-1)), float32(math.Inf(1)), 0.01, false)}, + {"float32/expected-minus-inf-actual-finite", testEpsilonT[float32](float32(math.Inf(-1)), 100.0, 0.01, false)}, + {"float32/expected-finite-actual-minus-inf", testEpsilonT[float32](100.0, float32(math.Inf(-1)), 0.01, false)}, + + // Float64 -Inf cases (4 cases) + {"float64/both-minus-inf", testEpsilonT[float64](math.Inf(-1), math.Inf(-1), 0.01, true)}, + {"float64/expected-minus-inf-actual-plus-inf", testEpsilonT[float64](math.Inf(-1), math.Inf(1), 0.01, false)}, + {"float64/expected-minus-inf-actual-finite", testEpsilonT[float64](math.Inf(-1), 100.0, 0.01, false)}, + {"float64/expected-finite-actual-minus-inf", testEpsilonT[float64](100.0, math.Inf(-1), 0.01, false)}, + + // Epsilon validation (6 cases) + {"float64/epsilon-negative", testEpsilonT[float64](100.0, 100.0, -0.01, false)}, // Negative epsilon + {"float64/epsilon-nan", testEpsilonT[float64](100.0, 100.0, math.NaN(), false)}, // NaN epsilon + {"float32/epsilon-nan", testEpsilonT[float32](100.0, 100.0, math.NaN(), false)}, // NaN epsilon + {"float64/epsilon-plus-inf", testEpsilonT[float64](100.0, 100.0, math.Inf(1), false)}, // +Inf epsilon + {"float64/epsilon-minus-inf", testEpsilonT[float64](100.0, 100.0, math.Inf(-1), false)}, // -Inf epsilon + {"float32/epsilon-plus-inf", testEpsilonT[float32](100.0, 100.0, math.Inf(1), false)}, // +Inf epsilon + + // Precision testing (4 cases) + {"float64/small-epsilon-pass", testEpsilonT[float64](1.0, 1.000001, 0.00001, true)}, // Very small error + {"float64/small-epsilon-fail", testEpsilonT[float64](1.0, 1.000011, 0.00001, false)}, // Exceeds small epsilon + {"float32/small-epsilon-pass", testEpsilonT[float32](1.0, 1.000001, 0.00001, true)}, // Very small error + {"float32/small-epsilon-fail", testEpsilonT[float32](1.0, 1.000011, 0.00001, false)}, // Exceeds small epsilon + + // Large values (3 cases) + {"int64/large-values", testEpsilonT[int64](1000000000, 1010000000, 0.02, true)}, // 1% error < 2% + {"uint64/large-values", testEpsilonT[uint64](1000000000, 1010000000, 0.02, true)}, // 1% error < 2% + {"float64/large-values", testEpsilonT[float64](1e15, 1.01e15, 0.02, true)}, // 1% error < 2% + + // Edge cases (4 cases) + {"int/zero-epsilon-same", testEpsilonT[int](100, 100, 0.0, true)}, // Zero epsilon, exact match + {"float64/zero-epsilon-different", testEpsilonT[float64](100.0, 100.1, 0.0, false)}, // Zero epsilon, different + {"int/large-epsilon", testEpsilonT[int](100, 200, 1.5, true)}, // 100% error < 150% epsilon + {"float64/boundary", testEpsilonT[float64](100.0, 102.0, 0.02, true)}, // Exactly 2% error with 2% epsilon + }) +} + +//nolint:thelper // linter false positive: this is not a helper +func testEpsilonT[Number Measurable](expected, actual Number, epsilon float64, shouldPass bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + mock := &mockT{} + result := InEpsilonT(mock, expected, actual, epsilon) + + if shouldPass { + True(t, result, "Expected InEpsilonT(%v, %v, %v) to pass", expected, actual, epsilon) + False(t, mock.Failed(), "Mock should not have failed") + } else { + False(t, result, "Expected InEpsilonT(%v, %v, %v) to fail", expected, actual, epsilon) + True(t, mock.Failed(), "Mock should have failed") + } + } +} diff --git a/internal/assertions/string.go b/internal/assertions/string.go index 097d72c59..45e57f080 100644 --- a/internal/assertions/string.go +++ b/internal/assertions/string.go @@ -5,10 +5,15 @@ package assertions import ( "fmt" + "reflect" "regexp" ) -// Regexp asserts that a specified regexp matches a string. +// Regexp asserts that a specified regular expression matches a string. +// +// The regular expression may be passed as a [regexp.Regexp], a string or a []byte and will be compiled. +// +// The actual argument to be matched may be a string, []byte or anything that prints as a string with [fmt.Sprint]. // // # Usage // @@ -19,27 +24,59 @@ import ( // // success: "^start", "starting" // failure: "^start", "not starting" -func Regexp(t T, rx any, str any, msgAndArgs ...any) bool { +func Regexp(t T, rx any, actual any, msgAndArgs ...any) bool { // Domain: string if h, ok := t.(H); ok { h.Helper() } - match, err := matchRegexp(rx, str) + re, err := buildRegex(rx) if err != nil { - Fail(t, fmt.Sprintf("invalid regular expression %q: %v", rx, err), msgAndArgs...) + return Fail(t, err.Error(), msgAndArgs...) + } + + switch v := actual.(type) { + case []byte: + return matchRegex(t, re, v, true, msgAndArgs...) + case string: + return matchRegex(t, re, v, true, msgAndArgs...) + default: + // reflection-based check for uncommon usage + str, ok := asString(actual) + if !ok { + return matchRegex(t, re, fmt.Sprint(actual), true, msgAndArgs...) + } + return matchRegex(t, re, str, true, msgAndArgs...) + } +} - return false +// RegexpT asserts that a specified regular expression matches a string. +// +// The actual argument to be matched may be a string or []byte. +// +// See [Regexp]. +// +// # Examples +// +// success: "^start", "starting" +// failure: "^start", "not starting" +func RegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool { + // Domain: string + if h, ok := t.(H); ok { + h.Helper() } - if !match { - Fail(t, fmt.Sprintf(`Expect "%v" to match "%v"`, str, rx), msgAndArgs...) + re, err := buildRegex(rx) + if err != nil { + return Fail(t, err.Error(), msgAndArgs...) } - return match + return matchRegex(t, re, actual, true, msgAndArgs...) } -// NotRegexp asserts that a specified regexp does not match a string. +// NotRegexp asserts that a specified regular expression does not match a string. +// +// See [Regexp]. // // # Usage // @@ -50,48 +87,155 @@ func Regexp(t T, rx any, str any, msgAndArgs ...any) bool { // // success: "^start", "not starting" // failure: "^start", "starting" -func NotRegexp(t T, rx any, str any, msgAndArgs ...any) bool { +func NotRegexp(t T, rx any, actual any, msgAndArgs ...any) bool { // Domain: string if h, ok := t.(H); ok { h.Helper() } - match, err := matchRegexp(rx, str) + re, err := buildRegex(rx) if err != nil { - Fail(t, fmt.Sprintf("invalid regular expression %q: %v", rx, err), msgAndArgs...) + return Fail(t, err.Error(), msgAndArgs...) + } + + switch v := actual.(type) { + case []byte: + return matchRegex(t, re, v, false, msgAndArgs...) + case string: + return matchRegex(t, re, v, false, msgAndArgs...) + default: + // reflection-based check for uncommon usage + str, ok := asString(actual) + if !ok { + return matchRegex(t, re, fmt.Sprint(actual), false, msgAndArgs...) + } - return false + // handle ~string, ~[]byte + return matchRegex(t, re, str, false, msgAndArgs...) } +} - if match { - Fail(t, fmt.Sprintf("Expect \"%v\" to NOT match \"%v\"", str, rx), msgAndArgs...) +// NotRegexpT asserts that a specified regular expression does not match a string. +// +// See [RegexpT]. +// +// # Usage +// +// assertions.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assertions.NotRegexp(t, "^start", "it's not starting") +// +// # Examples +// +// success: "^start", "not starting" +// failure: "^start", "starting" +func NotRegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool { + // Domain: string + if h, ok := t.(H); ok { + h.Helper() + } + + re, err := buildRegex(rx) + if err != nil { + return Fail(t, err.Error(), msgAndArgs...) } - return !match + return matchRegex(t, re, actual, false, msgAndArgs...) } -// matchRegexp returns whether the compiled regular expression matches the provided value. -// -// If rx is not a *[regexp.Regexp], rx is formatted with fmt.Sprint and compiled. -// When compilation fails, an error is returned instead of panicking. -func matchRegexp(rx any, str any) (bool, error) { - var r *regexp.Regexp - if rr, ok := rx.(*regexp.Regexp); ok { - r = rr - } else { - var err error - r, err = regexp.Compile(fmt.Sprint(rx)) - if err != nil { - return false, err +func buildRegex(re any) (*regexp.Regexp, error) { + // Maintainer: proposal for enhancement(perf): cache regexp + switch v := re.(type) { + case *regexp.Regexp: + if v == nil { + return nil, fmt.Errorf("regexp must not be nil: %w", errAssertions) + } + + return v, nil + case string: + return compileRegex(v) + case []byte: + if v == nil { + return nil, fmt.Errorf("regexp must not be nil: %w", errAssertions) + } + + return compileRegex(string(v)) + default: + // reflection-based check for uncommon usage + str, ok := asString(re) + if !ok { + return nil, fmt.Errorf( + "type for regexp is not supported. Want string, []byte or anything that converts to those, but got %T", + re, + ) } + // handle ~string, ~[]byte + + return compileRegex(str) + } +} + +func compileRegex(rx string) (*regexp.Regexp, error) { + const errMsg = "invalid error expression %q: %w" + rex, err := regexp.Compile(rx) + if err != nil { + return nil, fmt.Errorf(errMsg, rx, err) } + return rex, nil +} + +func matchRegex[ADoc Text](t T, rx *regexp.Regexp, actual ADoc, wantMatch bool, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + + var matched bool + str := any(actual) + switch v := str.(type) { case []byte: - return r.Match(v), nil + matched = rx.Match(v) case string: - return r.MatchString(v), nil + matched = rx.MatchString(v) default: - return r.MatchString(fmt.Sprint(v)), nil + // safeguard: should never get there + matched = rx.MatchString(string(actual)) + } + + switch { + case wantMatch && !matched: + return Fail(t, fmt.Sprintf(`Expect %q to match %q"`, string(actual), rx), msgAndArgs...) + case !wantMatch && matched: + return Fail(t, fmt.Sprintf("Expect %q to NOT match %q", string(actual), rx), msgAndArgs...) + default: + return true } } + +func asString(v any) (string, bool) { + typeString := reflect.TypeFor[string]() + val := reflect.ValueOf(v) + kind := val.Kind() + + if !val.CanConvert(typeString) { + return "", false + } + + // weird reflection: numbers CanConvert but their string rep is wrong. Need to check further. + typ := val.Type() + if kind != reflect.String && !(kind == reflect.Slice && typ.Elem().Kind() == reflect.Uint8) { //nolint:staticcheck // we want type.Elem() to be called only when kind is a slice + return "", false + } + + // handle ~string, ~[]byte + return val.Convert(typeString).String(), true +} + +// assertionsError reports an error from a bad usage of an assertion. +type assertionsError string + +func (e assertionsError) Error() string { + return string(e) +} + +const errAssertions assertionsError = "error from assertions" diff --git a/internal/assertions/string_test.go b/internal/assertions/string_test.go index aa7d7eb07..3ab379f2e 100644 --- a/internal/assertions/string_test.go +++ b/internal/assertions/string_test.go @@ -23,7 +23,7 @@ func TestStringEqual(t *testing.T) { } } -func TestSringEqualFormatting(t *testing.T) { +func TestStringEqualFormatting(t *testing.T) { t.Parallel() i := 0 @@ -38,58 +38,228 @@ func TestSringEqualFormatting(t *testing.T) { func TestStringRegexp(t *testing.T) { t.Parallel() - mock := new(testing.T) + // run test cases with all combinations of supported types for tc := range stringRegexpCases() { - True(t, Regexp(mock, tc.rx, tc.str)) - True(t, Regexp(mock, regexp.MustCompile(tc.rx), tc.str)) - True(t, Regexp(mock, regexp.MustCompile(tc.rx), []byte(tc.str))) + t.Run(tc.name, tc.test) + } + + t.Run("with edge cases", func(t *testing.T) { + t.Run("with unsupported regexp type", func(t *testing.T) { + t.Parallel() + + const ( + str = "whatever" + msg = "expected this invalid call to fail (regexp=%v)" + ) + + mock := new(mockT) + + t.Run("should fail (invalid regexp type)", func(t *testing.T) { + invalidRex := struct{ a string }{a: "invalid"} + + if Regexp(mock, invalidRex, str) { + t.Errorf(msg, invalidRex) + } + if NotRegexp(mock, invalidRex, str) { + t.Errorf(msg, invalidRex) + } + }) + + t.Run("should fail (nil regexp)", func(t *testing.T) { + invalidRex := []byte(nil) + + if Regexp(mock, invalidRex, str) { + t.Errorf(msg, invalidRex) + } + if NotRegexp(mock, invalidRex, str) { + t.Errorf(msg, invalidRex) + } + }) + }) + + t.Run("with fmt.Sprint conversion", func(t *testing.T) { + t.Parallel() + + const ( + numeric = 1234 + msg = "expected %q to match %q" + rex = "^[0-9]+$" + ) + + mock := new(mockT) + + t.Run("should match string representation of a number", func(t *testing.T) { + if !Regexp(mock, rex, numeric) { + t.Errorf(msg, numeric, rex) + } + if NotRegexp(mock, rex, numeric) { + t.Errorf(msg, numeric, rex) + } + }) + }) + }) +} + +// test all Regexp variants with the same input (possibly converted). +// +//nolint:thelper // linter false positive: this is not a helper +func testAllRegexpWithTypes(regString, str string, success, valid bool) func(*testing.T) { + type ( + // redefined types to check for ~string and ~[]byte type constraints + MyString string + MyBytes []byte + ) - False(t, NotRegexp(mock, tc.rx, tc.str)) - False(t, NotRegexp(mock, tc.rx, []byte(tc.str))) - False(t, NotRegexp(mock, regexp.MustCompile(tc.rx), tc.str)) + return func(t *testing.T) { + t.Run("with all type combinations", func(t *testing.T) { + // generic version : 5 x 4 combinations of input types + t.Run("with [string,string]", testAllRegexp[string, string](regString, str, success, valid)) + t.Run("with [string,[]byte]", testAllRegexp[string, []byte](regString, []byte(str), success, valid)) + t.Run("with [string,~string]", testAllRegexp[string, MyString](regString, MyString(str), success, valid)) + t.Run("with [string,~[]byte]", testAllRegexp[string, MyBytes](regString, MyBytes(str), success, valid)) + // + t.Run("with [[]byte,string]", testAllRegexp[[]byte, string]([]byte(regString), str, success, valid)) + t.Run("with [[]byte,[]byte]", testAllRegexp[[]byte, []byte]([]byte(regString), []byte(str), success, valid)) + t.Run("with [[]byte,~string]", testAllRegexp[[]byte, MyString]([]byte(regString), MyString(str), success, valid)) + t.Run("with [[]byte,~[]byte]", testAllRegexp[[]byte, MyBytes]([]byte(regString), MyBytes(str), success, valid)) + // + t.Run("with [~string,string]", testAllRegexp[MyString, string](MyString(regString), str, success, valid)) + t.Run("with [~string,[]byte]", testAllRegexp[MyString, []byte](MyString(regString), []byte(str), success, valid)) + t.Run("with [~string,~string]", testAllRegexp[MyString, MyString](MyString(regString), MyString(str), success, valid)) + t.Run("with [~string,~[]byte]", testAllRegexp[MyString, MyBytes](MyString(regString), MyBytes(str), success, valid)) + // + t.Run("with [~[]byte,string]", testAllRegexp[MyBytes, string](MyBytes(regString), str, success, valid)) + t.Run("with [~[]byte,[]byte]", testAllRegexp[MyBytes, []byte](MyBytes(regString), []byte(str), success, valid)) + t.Run("with [~[]byte,~string]", testAllRegexp[MyBytes, MyString](MyBytes(regString), MyString(str), success, valid)) + t.Run("with [~[]byte,~[]byte]", testAllRegexp[MyBytes, MyBytes](MyBytes(regString), MyBytes(str), success, valid)) + // + t.Run("with [*regexp.Regexp,string]", testAllRegexp[*regexp.Regexp, string](testRex(regString), str, success, valid)) + t.Run("with [*regexp.Regexp,[]byte]", testAllRegexp[*regexp.Regexp, []byte](testRex(regString), []byte(str), success, valid)) + t.Run("with [*regexp.Regexp,~string]", testAllRegexp[*regexp.Regexp, MyString](testRex(regString), MyString(str), success, valid)) + t.Run("with [*regexp.Regexp,~[]byte]", testAllRegexp[*regexp.Regexp, MyBytes](testRex(regString), MyBytes(str), success, valid)) + }) } +} + +//nolint:thelper // linter false positive: this is not a helper +func testAllRegexp[Rex RegExp, ADoc Text](rx Rex, actual ADoc, success, valid bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() - for tc := range stringNotRegexpCases() { - False(t, Regexp(mock, tc.rx, tc.str), "Expected %q to not match %q", tc.rx, tc.str) - False(t, Regexp(mock, regexp.MustCompile(tc.rx), tc.str)) - False(t, Regexp(mock, regexp.MustCompile(tc.rx), []byte(tc.str))) + if !valid { + // all assertions fail on invalid regexp + t.Run("should fail", func(t *testing.T) { + t.Run("with Regexp", testRegexp(rx, actual, false)) + t.Run("with RegexpT", testRegexpT(rx, actual, false)) + t.Run("with NoRegexp", testNotRegexp(rx, actual, false)) + t.Run("with NoRegexpT", testNotRegexpT(rx, actual, false)) + }) - True(t, NotRegexp(mock, tc.rx, tc.str)) - True(t, NotRegexp(mock, tc.rx, []byte(tc.str))) - True(t, NotRegexp(mock, regexp.MustCompile(tc.rx), tc.str)) + return + } + + if success { + t.Run("should match", func(t *testing.T) { + t.Run("with Regexp", testRegexp(rx, actual, true)) + t.Run("with RegexpT", testRegexpT(rx, actual, true)) + }) + + t.Run("should fail", func(t *testing.T) { + t.Run("with NoRegexp", testNotRegexp(rx, actual, false)) + t.Run("with NoRegexpT", testNotRegexpT(rx, actual, false)) + }) + } else { + t.Run("should NOT match", func(t *testing.T) { + t.Run("with NoRegexp", testNotRegexp(rx, actual, true)) + t.Run("with NoRegexpT", testNotRegexpT(rx, actual, true)) + }) + + t.Run("should fail", func(t *testing.T) { + t.Run("with Regexp", testRegexp(rx, actual, false)) + t.Run("with RegexpT", testRegexpT(rx, actual, false)) + }) + } } } -// Verifies that invalid patterns no longer cause a panic when using Regexp/NotRegexp. -// Instead, the assertion should fail and return false. -func TestStringRegexp_InvalidPattern(t *testing.T) { - t.Parallel() +func testRegexp[Rex RegExp, ADoc Text](rx Rex, str ADoc, success bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() - const ( - invalidPattern = "\\C" - msg = "whatever" - ) + mock := new(testing.T) + res := Regexp(mock, rx, str) + if res != success { + if success { + croakWantMatch(t, rx, str) + return + } + croakWantNotMatch(t, rx, str) + } + } +} - t.Run("Regexp should not panic on invalid patterns", func(t *testing.T) { - result := NotPanics(t, func() { - mockT := new(testing.T) - False(t, Regexp(mockT, invalidPattern, msg)) - }) - if !result { - t.Failed() +func testNotRegexp[Rex RegExp, ADoc Text](rx Rex, str ADoc, success bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + res := NotRegexp(mock, rx, str) + if res != success { + if success { + croakWantMatch(t, rx, str) + return + } + croakWantNotMatch(t, rx, str) } - }) + } +} - t.Run("NoRegexp should not panic on invalid patterns", func(t *testing.T) { - result := NotPanics(t, func() { - mockT := new(testing.T) - False(t, NotRegexp(mockT, invalidPattern, msg)) - }) - if !result { - t.Failed() +func testRegexpT[Rex RegExp, ADoc Text](rx Rex, str ADoc, success bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + res := RegexpT(mock, rx, str) + if res != success { + if success { + croakWantMatch(t, rx, str) + return + } + croakWantNotMatch(t, rx, str) } - }) + } +} + +func testNotRegexpT[Rex RegExp, ADoc Text](rx Rex, str ADoc, success bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + res := NotRegexpT(mock, rx, str) + if res != success { + if success { + croakWantMatch(t, rx, str) + return + } + croakWantNotMatch(t, rx, str) + } + } +} + +func croakWantMatch(t *testing.T, rx any, str any) { + t.Helper() + t.Errorf("expected %q to match %q", str, rx) +} + +func croakWantNotMatch(t *testing.T, rx, str any) { + t.Helper() + t.Errorf("expected %q NOT to match %q", str, rx) +} + +func testRex(rex string) *regexp.Regexp { + rx, _ := compileRegex(rex) + return rx } type stringEqualCase struct { @@ -189,23 +359,38 @@ func stringEqualFormattingCases() iter.Seq[stringEqualCase] { }) } -type stringRegexpCase struct { - rx, str string -} - -func stringRegexpCases() iter.Seq[stringRegexpCase] { - return slices.Values([]stringRegexpCase{ - {"^start", "start of the line"}, - {"end$", "in the end"}, - {"end$", "in the end"}, - {"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12.34"}, - }) -} - -func stringNotRegexpCases() iter.Seq[stringRegexpCase] { - return slices.Values([]stringRegexpCase{ - {"^asdfastart", "Not the start of the line"}, - {"end$", "in the end."}, - {"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12a.34"}, +// Values to populate the test harness: +// +// - valid and invalid patterns +// - matching and not matching expressions. +func stringRegexpCases() iter.Seq[genericTestCase] { + return slices.Values([]genericTestCase{ + // successful matches + {"^start (match)", testAllRegexpWithTypes( + "^start", "start of the line", true, true, + )}, + {"end$ (match)", testAllRegexpWithTypes( + "end$", "in the end", true, true, + )}, + {"end$ (match)", testAllRegexpWithTypes( + "end$", "in the end", true, true, + )}, + {"phone number (match)", testAllRegexpWithTypes( + "[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12.34", true, true, + )}, + // failed matches + {"start (no match)", testAllRegexpWithTypes( + "^asdfastart", "Not the start of the line", false, true, + )}, + {"end$ (no match)", testAllRegexpWithTypes( + "end$", "in the end.", false, true, + )}, + {"phone number (no match)", testAllRegexpWithTypes( + "[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12a.34", false, true, + )}, + // invalid pattern + {"invalid regexp", testAllRegexpWithTypes( + "\\C", "whatever", false, false, + )}, }) } diff --git a/internal/assertions/testdata/json/fixtures.json b/internal/assertions/testdata/json/fixtures.json deleted file mode 100644 index 63d7dd74f..000000000 --- a/internal/assertions/testdata/json/fixtures.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "contents": "JSON fixtures to test JSON-related assertions", - "testcases": { - "helloworld": { - "data": { - "hello": "world", - "foo": "bar", - }, - "string": "\{\"hello\":\"world\",\"foo\":\"bar\"}" - }, - "reverse-helloworld": { - "data": { - "foo": "bar", - "hello": "world", - }, - "string": "\{\"bar\":\"bar\",\"hello\":\"world\"}" - } - } -} diff --git a/internal/assertions/yaml.go b/internal/assertions/yaml.go index 8836d1f65..898a24a71 100644 --- a/internal/assertions/yaml.go +++ b/internal/assertions/yaml.go @@ -1,15 +1,29 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 +//nolint:dupl // we need to duplicate at least some godoc. package assertions import ( + "bytes" "fmt" "github.com/go-openapi/testify/v2/internal/assertions/enable/yaml" ) -// YAMLEq asserts that the first documents in the two YAML strings are equivalent. +// YAMLEqBytes asserts that two YAML slices of bytes are equivalent. +// +// Expected and actual must be valid YAML. +// +// # Important +// +// By default, this function is disabled and will panic. +// +// To enable it, you should add a blank import like so: +// +// import( +// "github.com/go-openapi/testify/enable/yaml/v2" +// ) // // # Usage // @@ -29,25 +43,61 @@ import ( // // panic: "key: value", "key: value" // should panic without the yaml feature enabled. -func YAMLEq(t T, expected string, actual string, msgAndArgs ...any) bool { +func YAMLEqBytes(t T, expected, actual []byte, msgAndArgs ...any) bool { // Domain: yaml if h, ok := t.(H); ok { h.Helper() } var expectedYAMLAsInterface, actualYAMLAsInterface any - if err := yaml.Unmarshal([]byte(expected), &expectedYAMLAsInterface); err != nil { + 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...) } // Shortcut if same bytes - if actual == expected { + if bytes.Equal(actual, expected) { return true } - if err := yaml.Unmarshal([]byte(actual), &actualYAMLAsInterface); err != nil { + 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 Equal(t, expectedYAMLAsInterface, actualYAMLAsInterface, msgAndArgs...) } + +// YAMLEq asserts that two YAML strings are equivalent. +// +// See [YAMLEqBytes]. +// +// # Examples +// +// panic: "key: value", "key: value" +// should panic without the yaml feature enabled. +func YAMLEq(t T, expected, actual string, msgAndArgs ...any) bool { + // Domain: yaml + if h, ok := t.(H); ok { + h.Helper() + } + + return YAMLEqBytes(t, []byte(expected), []byte(actual), msgAndArgs) +} + +// YAMLEqT asserts that two YAML documents are equivalent. +// +// The expected and actual arguments may be string or []byte. They do not need to be of the same type. +// +// See [YAMLEqBytes]. +// +// # Examples +// +// panic: "key: value", "key: value" +// should panic without the yaml feature enabled. +func YAMLEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool { + // Domain: yaml + if h, ok := t.(H); ok { + h.Helper() + } + + return YAMLEqBytes(t, []byte(expected), []byte(actual), msgAndArgs) +} diff --git a/internal/assertions/yaml_test.go b/internal/assertions/yaml_test.go index 0e4c8544f..8ef460b8a 100644 --- a/internal/assertions/yaml_test.go +++ b/internal/assertions/yaml_test.go @@ -5,16 +5,76 @@ package assertions import "testing" -func TestYAMLEq(t *testing.T) { - mock := new(mockT) - const yamlDoc = ` +func TestYAML(t *testing.T) { + t.Parallel() + + t.Run("should panic", testAllYAMLEq()) +} + +func testAllYAMLEq() func(*testing.T) { + return func(t *testing.T) { + const ( + actual = ` --- a: 1 ` + expected = "" + success = false + ) - if !Panics(t, func() { - _ = YAMLEq(mock, "", yamlDoc) - }) { - Fail(t, "expected YAMLEq to panic with default settings") + t.Run("with YAMLEq", testYAMLEq(expected, actual, success)) + t.Run("with YAMLEqBytes", testYAMLEqBytes(expected, actual, success)) + t.Run("with YAMLEqT[string,string]", testYAMLEqT[string, string](expected, actual, success)) + 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)) } } + +func testYAMLEq(expected, actual string, success bool) func(*testing.T) { + _ = success + return func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + if !Panics(t, func() { + _ = YAMLEq(mock, expected, actual) + }) { + croakWantPanic(t, "YAMLEq") + } + } +} + +func testYAMLEqBytes(expected, actual string, success bool) func(*testing.T) { + _ = success + return func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + if !Panics(t, func() { + _ = YAMLEqBytes(mock, []byte(expected), []byte(actual)) + }) { + croakWantPanic(t, "YAMLEqBytes") + } + } +} + +//nolint:thelper // linter false positive: this is not a helper +func testYAMLEqT[ADoc, EDoc Text](expected, actual string, success bool) func(*testing.T) { + _ = success + return func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + if !Panics(t, func() { + _ = YAMLEqT(mock, EDoc(expected), ADoc(actual)) + }) { + croakWantPanic(t, "YAMLEqT") + } + } +} + +func croakWantPanic(t *testing.T, fn string) { + t.Helper() + t.Errorf("expected %q to panic with default settings", fn) +} From 044d258839116ff78c0c51e975e833ca5da5eeef Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Sun, 18 Jan 2026 10:34:41 +0100 Subject: [PATCH 2/6] feat: added support for generics to codegen Signed-off-by: Frederic BIDON --- codegen/go.mod | 1 + codegen/go.sum | 2 + .../generator/domains/domains_test.go | 3 +- .../internal/generator/funcmaps/funcmaps.go | 4 +- .../generator/funcmaps/funcmaps_test.go | 2 +- .../internal/generator/funcmaps/markdown.go | 2 - .../templates/assertion_assertions.gotmpl | 2 +- .../templates/assertion_format.gotmpl | 2 +- .../generator/templates/doc_page.md.gotmpl | 6 ++- .../templates/requirement_assertions.gotmpl | 2 +- .../templates/requirement_format.gotmpl | 2 +- codegen/internal/model/model.go | 43 ++++++++++++++++++- .../internal/scanner/comments/extractor.go | 8 ++-- codegen/internal/scanner/scanner.go | 5 ++- .../internal/scanner/signature/extractor.go | 35 ++++++++++++--- codegen/main.go | 2 +- 16 files changed, 96 insertions(+), 25 deletions(-) diff --git a/codegen/go.mod b/codegen/go.mod index 3a7a506ed..eb4b3f6e7 100644 --- a/codegen/go.mod +++ b/codegen/go.mod @@ -5,6 +5,7 @@ go 1.24.0 toolchain go1.25.0 require ( + github.com/davecgh/go-spew v1.1.1 golang.org/x/text v0.32.0 golang.org/x/tools v0.40.0 ) diff --git a/codegen/go.sum b/codegen/go.sum index efb77b22c..0db39fb38 100644 --- a/codegen/go.sum +++ b/codegen/go.sum @@ -1,3 +1,5 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= diff --git a/codegen/internal/generator/domains/domains_test.go b/codegen/internal/generator/domains/domains_test.go index ed448c1dd..521f91003 100644 --- a/codegen/internal/generator/domains/domains_test.go +++ b/codegen/internal/generator/domains/domains_test.go @@ -75,7 +75,8 @@ func TestDomainIndexSorting(t *testing.T) { index := MakeDomainIndex(c.docs) // Collect domain names in order - var domains []string + const testDomains = 16 + domains := make([]string, 0, testDomains) for domain := range index.Entries() { domains = append(domains, domain) } diff --git a/codegen/internal/generator/funcmaps/funcmaps.go b/codegen/internal/generator/funcmaps/funcmaps.go index 649a5866a..15688ad72 100644 --- a/codegen/internal/generator/funcmaps/funcmaps.go +++ b/codegen/internal/generator/funcmaps/funcmaps.go @@ -203,7 +203,7 @@ func docStringFor(usage, name string) string { case "format": return comment( fmt.Sprintf( - "%sf is the same as [%s], but accepts a format msg string to format arguments like [fmt.Printf].", + "%sf is the same as [%s], but it accepts a format msg string to format arguments like [fmt.Printf].", basename, name, ), @@ -300,6 +300,7 @@ func relocate(values []model.TestValue, pkg string) string { return strings.Join(relocated, ", ") } +// sourceLink recomposes a github URL to the source code. func sourceLink(baseGitHubURL string, pos *token.Position) string { if pos == nil { return "" @@ -326,6 +327,7 @@ func titleize(in string) string { return caser.String(in) } +// godocbadge produces a badge URL from https://pkg.go.dev. func godocbadge(pkggodevURL string) (string, error) { u, err := url.Parse(pkggodevURL) if err != nil { diff --git a/codegen/internal/generator/funcmaps/funcmaps_test.go b/codegen/internal/generator/funcmaps/funcmaps_test.go index fd4d44f7f..5436df892 100644 --- a/codegen/internal/generator/funcmaps/funcmaps_test.go +++ b/codegen/internal/generator/funcmaps/funcmaps_test.go @@ -558,7 +558,7 @@ func docStringForCases() iter.Seq[docStringForCase] { name: "format usage", usage: "format", funcName: "assert.Equal", - expected: "// Equalf is the same as [assert.Equal], but accepts a format msg string to format arguments like [fmt.Printf].", + expected: "// Equalf is the same as [assert.Equal], but it accepts a format msg string to format arguments like [fmt.Printf].", }, { name: "forward usage", diff --git a/codegen/internal/generator/funcmaps/markdown.go b/codegen/internal/generator/funcmaps/markdown.go index c6fa9c96c..1dd417de5 100644 --- a/codegen/internal/generator/funcmaps/markdown.go +++ b/codegen/internal/generator/funcmaps/markdown.go @@ -15,8 +15,6 @@ var refLinkPattern = regexp.MustCompile(`(?m)^\[([^\]]+)\]:\s+(.+)$`) // // 1. Reference-style markdown links: [text]: url // 2. Godoc-style links: [errors.Is], [testing.T], etc. -// -//nolint:gocognit,gocognit // this is temporary accepted extra complexity. Should refactor with steps as functions func FormatMarkdown(in string) string { const sensiblePrealloc = 20 diff --git a/codegen/internal/generator/templates/assertion_assertions.gotmpl b/codegen/internal/generator/templates/assertion_assertions.gotmpl index 082001550..b3c9d2bf3 100644 --- a/codegen/internal/generator/templates/assertion_assertions.gotmpl +++ b/codegen/internal/generator/templates/assertion_assertions.gotmpl @@ -19,7 +19,7 @@ import ( {{ comment .DocString }} // {{ docStringPackage $.Package }} -func {{ .Name }}({{ params .AllParams }}) {{ returns .Returns }} { +func {{ .GenericName }}({{ params .AllParams }}) {{ returns .Returns }} { if h, ok := t.(H); ok { h.Helper() } diff --git a/codegen/internal/generator/templates/assertion_format.gotmpl b/codegen/internal/generator/templates/assertion_format.gotmpl index 6030b7d65..45d120975 100644 --- a/codegen/internal/generator/templates/assertion_format.gotmpl +++ b/codegen/internal/generator/templates/assertion_format.gotmpl @@ -19,7 +19,7 @@ import ( {{ docStringFor "format" .Name }} // {{ docStringPackage $.Package }} -func {{ .Name }}f(t T, {{ params .Params }}, msg string, args ...any) {{ returns .Returns }} { +func {{ .GenericName "f" }}(t T, {{ params .Params }}, msg string, args ...any) {{ returns .Returns }} { if h, ok := t.(H); ok { h.Helper() } return {{ .TargetPackage }}.{{ .Name }}(t, {{ forward .Params }}, forwardArgs(msg, args)) } diff --git a/codegen/internal/generator/templates/doc_page.md.gotmpl b/codegen/internal/generator/templates/doc_page.md.gotmpl index b6bfe30fc..d10043b13 100644 --- a/codegen/internal/generator/templates/doc_page.md.gotmpl +++ b/codegen/internal/generator/templates/doc_page.md.gotmpl @@ -33,8 +33,9 @@ _All links point to <{{ $godoc }}>_ This domain exposes {{ .RefCount }} functionalities. {{- with .Package }} + {{- $enableGenerics := .EnableGenerics }} {{- range .Functions }}{{/* functions in internal/assertions annotated with "domain:" */}} - {{- if or (not .IsGeneric) ($.EnableGenerics) }} + {{- if or (not .IsGeneric) ($enableGenerics) }} {{- if and (not .IsHelper) (not .IsConstructor) }} ### {{ .Name }} @@ -101,10 +102,11 @@ This domain exposes {{ .RefCount }} functionalities. --- {{- with .Package }}{{ if .HasHelpers }} + {{- $enableGenerics := .EnableGenerics }} ## Other helpers {{- range .Functions }}{{/* functions in internal/assertions annotated with "domain:" */}} - {{- if or (not .IsGeneric) ($.EnableGenerics) }} + {{- if or (not .IsGeneric) ($enableGenerics) }} {{- if or .IsHelper .IsConstructor }} ### {{ .Name }} diff --git a/codegen/internal/generator/templates/requirement_assertions.gotmpl b/codegen/internal/generator/templates/requirement_assertions.gotmpl index 7322b9f1e..4b3ec961c 100644 --- a/codegen/internal/generator/templates/requirement_assertions.gotmpl +++ b/codegen/internal/generator/templates/requirement_assertions.gotmpl @@ -19,7 +19,7 @@ import ( {{ comment .DocString }} // {{ docStringPackage $.Package }} -func {{ .Name }}({{ params .AllParams }}) { +func {{ .GenericName }}({{ params .AllParams }}) { if h, ok := t.(H); ok { h.Helper() } {{- if or (eq .Name "Fail") (eq .Name "FailNow") }}{{/* special semantics for these two, which can only fail */}} _ = {{ .TargetPackage }}.{{ .Name }}({{ forward .AllParams }}) diff --git a/codegen/internal/generator/templates/requirement_format.gotmpl b/codegen/internal/generator/templates/requirement_format.gotmpl index 7b9c70db7..8e146809f 100644 --- a/codegen/internal/generator/templates/requirement_format.gotmpl +++ b/codegen/internal/generator/templates/requirement_format.gotmpl @@ -19,7 +19,7 @@ import ( {{ docStringFor "format" .Name }} // {{ docStringPackage $.Package }} -func {{ .Name }}f(t T, {{ params .Params }}, msg string, args ...any) { +func {{ .GenericName "f" }}(t T, {{ params .Params }}, msg string, args ...any) { if h, ok := t.(H); ok { h.Helper() } {{- if or (eq .Name "Fail") (eq .Name "FailNow") }}{{/* special semantics for these two, which can only fail */}} _ = {{ .TargetPackage }}.{{ .Name }}(t, {{ forward .Params }}, forwardArgs(msg, args)) diff --git a/codegen/internal/model/model.go b/codegen/internal/model/model.go index 0380e5fc6..c04237f1b 100644 --- a/codegen/internal/model/model.go +++ b/codegen/internal/model/model.go @@ -8,6 +8,7 @@ import ( "go/token" "maps" "slices" + "strings" ) // AssertionPackage describes the internal/assertions package. @@ -86,18 +87,51 @@ type Function struct { Params Parameters AllParams Parameters Returns Parameters + TypeParams []TypeParam IsGeneric bool IsHelper bool IsDeprecated bool IsConstructor bool Tests []Test - // extraneous information when scanning in collectDoc mode Domain string SourceLink *token.Position ExtraComments []ExtraComment } +// GenericName renders the function name with one or more suffixes, +// accounting for any type parameter for generic functions. +func (f Function) GenericName(suffixes ...string) string { + suffix := strings.Join(suffixes, "") + if !f.IsGeneric { // means len(f.TypeParams) > 0 + return f.Name + suffix + } + + var w strings.Builder + w.WriteString(f.Name) + w.WriteString(suffix) + w.WriteByte('[') + c := f.TypeParams[0] + w.WriteString(c.Name) + if len(f.TypeParams) <= 1 || f.TypeParams[1].Constraint != c.Constraint { + // constraint is elided if next param has the same constraint + w.WriteByte(' ') + w.WriteString(c.Constraint) + } + + for i, p := range f.TypeParams[1:] { + w.WriteString(", ") + w.WriteString(p.Name) + if len(f.TypeParams) <= i+1+1 || f.TypeParams[i+1].Constraint != p.Constraint { + w.WriteByte(' ') + w.WriteString(p.Constraint) + } + } + w.WriteByte(']') + + return w.String() +} + func (f Function) HasTest() bool { return len(f.Tests) > 0 } @@ -116,6 +150,13 @@ type Parameter struct { GoType string Selector string IsVariadic bool + IsGeneric bool +} + +// TypeParam represents a type parameter in a generic function. +type TypeParam struct { + Name string // type parameter name (e.g., "B") + Constraint string // constraint type (e.g., "Boolean", "cmp.Ordered") } // Ident represents an exported identifier (type, constant, or variable) from the source package. diff --git a/codegen/internal/scanner/comments/extractor.go b/codegen/internal/scanner/comments/extractor.go index c680eff5d..5df697dae 100644 --- a/codegen/internal/scanner/comments/extractor.go +++ b/codegen/internal/scanner/comments/extractor.go @@ -235,14 +235,14 @@ func extractCommentFromTypeSpec(typedDeclaration *ast.GenDecl, spec ast.Spec, ob return nil } + if typedSpec.Doc != nil { + return typedSpec.Doc + } + // return Doc, checking both GenDecl.Doc and TypeSpec.Doc if typedDeclaration.Doc != nil { return typedDeclaration.Doc } - - if typedSpec.Doc != nil { - return typedSpec.Doc - } case *ast.ValueSpec: for _, ident := range typedSpec.Names { if ident.Name != object.Name() { diff --git a/codegen/internal/scanner/scanner.go b/codegen/internal/scanner/scanner.go index 4a3150e06..2dd44670d 100644 --- a/codegen/internal/scanner/scanner.go +++ b/codegen/internal/scanner/scanner.go @@ -20,8 +20,6 @@ import ( "github.com/go-openapi/testify/codegen/v2/internal/scanner/signature" ) -const pkgLoadMode = packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps | packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo - // Scanner scans the internal/assertions package and extracts all functions, types, and metadata. type Scanner struct { options @@ -42,6 +40,9 @@ type Scanner struct { func New(opts ...Option) *Scanner { o := optionsWithDefaults(opts) + const pkgLoadMode = packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps | + packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo + cfg := &packages.Config{ Dir: o.dir, Mode: pkgLoadMode, diff --git a/codegen/internal/scanner/signature/extractor.go b/codegen/internal/scanner/signature/extractor.go index e6a54f6f6..c917ce3f7 100644 --- a/codegen/internal/scanner/signature/extractor.go +++ b/codegen/internal/scanner/signature/extractor.go @@ -36,9 +36,12 @@ func (e *Extractor) ExtractFunctionSignature(signature *types.Signature, name st Name: name, } - // check if generic - isGeneric := signature.TypeParams() != nil - function.IsGeneric = isGeneric + // check if generic and extract type parameters + typeParams := signature.TypeParams() + function.IsGeneric = typeParams != nil + if typeParams != nil { + function.TypeParams = e.extractTypeParams(typeParams) + } // extract parameters params := signature.Params() @@ -47,9 +50,10 @@ func (e *Extractor) ExtractFunctionSignature(signature *types.Signature, name st for param := range params.Variables() { p := model.Parameter{ - Name: param.Name(), - GoType: e.ElidedType(param.Type()), - Selector: e.ElidedQualifier(param.Type()), + Name: param.Name(), + GoType: e.ElidedType(param.Type()), + Selector: e.ElidedQualifier(param.Type()), + IsGeneric: isTypeParam(param.Type()), } function.AllParams = append(function.AllParams, p) @@ -136,3 +140,22 @@ func (e *Extractor) ElidedQualifier(t types.Type) string { return "" } + +// isTypeParam checks if a type is a type parameter (generic type variable). +func isTypeParam(t types.Type) bool { + _, ok := t.(*types.TypeParam) + return ok +} + +// extractTypeParams extracts type parameter names and constraints from a TypeParamList. +func (e *Extractor) extractTypeParams(typeParams *types.TypeParamList) []model.TypeParam { + result := make([]model.TypeParam, typeParams.Len()) + for i := range typeParams.Len() { + tp := typeParams.At(i) + result[i] = model.TypeParam{ + Name: tp.Obj().Name(), + Constraint: e.ElidedType(tp.Constraint()), + } + } + return result +} diff --git a/codegen/main.go b/codegen/main.go index a8d510e2a..ae1b20181 100644 --- a/codegen/main.go +++ b/codegen/main.go @@ -55,7 +55,7 @@ func registerFlags(cfg *config) { flag.BoolVar(&cfg.includeFmt, "include-format-funcs", true, "include format functions such as Errorf and Equalf") flag.BoolVar(&cfg.includeFwd, "include-forward-funcs", true, "include forward assertions functions as methods of the Assertions object") flag.BoolVar(&cfg.includeTst, "include-tests", true, "generate the tests in the target package(s)") - flag.BoolVar(&cfg.includeGen, "include-generics", false, "include generic functions") + flag.BoolVar(&cfg.includeGen, "include-generics", true, "include generic functions") flag.BoolVar(&cfg.includeHlp, "include-helpers", true, "include helper functions that are not assertions") flag.BoolVar(&cfg.includeExa, "include-examples", true, "include generated testable examples") flag.BoolVar(&cfg.runExa, "runnable-examples", true, "include Output to generated testable examples, so they are run as tests") From 079cb85986d09762052f0af08be4673681d9811d Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Sun, 18 Jan 2026 10:45:17 +0100 Subject: [PATCH 3/6] refact(number): refactored InDelta, InEpsilon with identical logic to their generic counterpart Signed-off-by: Frederic BIDON --- internal/assertions/compare.go | 12 +- internal/assertions/generics.go | 12 +- internal/assertions/ifaces.go | 2 +- internal/assertions/number.go | 309 +++++++++++++++-------------- internal/assertions/number_test.go | 60 +++++- internal/assertions/yaml.go | 2 +- 6 files changed, 231 insertions(+), 166 deletions(-) diff --git a/internal/assertions/compare.go b/internal/assertions/compare.go index 303882409..8561ebcfe 100644 --- a/internal/assertions/compare.go +++ b/internal/assertions/compare.go @@ -15,6 +15,7 @@ import ( // Greater asserts that the first element is strictly greater than the second. // // Both elements must be of the same type in the [reflect.Kind] sense. +// To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. // // # Usage // @@ -45,7 +46,7 @@ func Greater(t T, e1 any, e2 any, msgAndArgs ...any) bool { // // [GreaterT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, use [Greater] instead. // -// To compare values that need a type conversion (e.g. float32 against float64), you should use [Greater] instead. +// To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. // // # Usage // @@ -74,6 +75,8 @@ func GreaterT[Orderable Ordered](t T, e1, e2 Orderable, msgAndArgs ...any) bool // GreaterOrEqual asserts that the first element is greater than or equal to the second. // +// See also [Greater]. +// // # Usage // // assertions.GreaterOrEqual(t, 2, 1) @@ -105,7 +108,7 @@ func GreaterOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) bool { // [GreaterOrEqualT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, // use [GreaterOrEqual] instead. // -// To compare values that need a type conversion (e.g. float32 against float64), you should use [GreaterOrEqual] instead. +// To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. // // # Usage // @@ -134,6 +137,9 @@ func GreaterOrEqualT[Orderable Ordered](t T, e1, e2 Orderable, msgAndArgs ...any // Less asserts that the first element is strictly less than the second. // +// Both elements must be of the same type in the [reflect.Kind] sense. +// To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. +// // # Usage // // assertions.Less(t, 1, 2) @@ -163,7 +169,7 @@ func Less(t T, e1 any, e2 any, msgAndArgs ...any) bool { // [LessT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, // use [Less] instead. // -// To compare values that need a type conversion (e.g. float32 against float64), you should use [Less] instead. +// To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. // // # Usage // diff --git a/internal/assertions/generics.go b/internal/assertions/generics.go index eb2c73262..14dd82ca7 100644 --- a/internal/assertions/generics.go +++ b/internal/assertions/generics.go @@ -8,7 +8,7 @@ import ( // Type constraint definitions for generic variants of assertions. type ( - // Boolean is a bool or any type that can convert to a bool. + // Boolean is a bool or any type that can be converted to a bool. Boolean interface { ~bool } @@ -22,7 +22,7 @@ type ( ~string | ~[]byte } - // Ordered is a standard ordered type (i.e. types which support "<") plus [time.Time]. + // Ordered is a standard ordered type (i.e. types that support "<": [cmp.Ordered]) plus []byte and [time.Time]. // // This is used by [GreaterT], [GreaterOrEqualT], [LessT], and [LessOrEqualT]. // @@ -31,7 +31,7 @@ type ( cmp.Ordered | []byte | time.Time } - // SignedNumeric is an signed integer or a floating point number. + // SignedNumeric is a signed integer or a floating point number or any type that can be converted to one of these. SignedNumeric interface { ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~float32 | ~float64 @@ -39,12 +39,12 @@ type ( // UnsignedNumeric is an unsigned integer. // - // There a no unsigned floating point numbers. + // NOTE: there are no unsigned floating point numbers. UnsignedNumeric interface { ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 } - // Measurable is any number for which we can compute a delta. + // Measurable is any number for which we can compute a delta (floats or integers). // // This is used by [InDeltaT] and [InEpsilonT]. // @@ -53,7 +53,7 @@ type ( SignedNumeric | UnsignedNumeric | ~float32 | ~float64 } - // RegExp is either a text containing a regular expression to compile, or directly the compiled regexp. + // RegExp is either a text containing a regular expression to compile (string or []byte), or directly the compiled regexp. // // This is used by [RegexpT] and [NotRegexpT]. RegExp interface { diff --git a/internal/assertions/ifaces.go b/internal/assertions/ifaces.go index 3bc5d00d0..0b147587a 100644 --- a/internal/assertions/ifaces.go +++ b/internal/assertions/ifaces.go @@ -11,7 +11,7 @@ type T interface { } // H is an interface for types that implement the Helper method. -// This allows marking functions as test helpers. +// This allows marking functions as test helpers, e.g. [testing.T.Helper]. type H interface { Helper() } diff --git a/internal/assertions/number.go b/internal/assertions/number.go index 5eda842ff..b1436efdc 100644 --- a/internal/assertions/number.go +++ b/internal/assertions/number.go @@ -4,7 +4,6 @@ package assertions import ( - "errors" "fmt" "math" "reflect" @@ -13,6 +12,18 @@ import ( // InDelta asserts that the two numerals are within delta of each other. // +// Delta must be greater than or equal to zero. +// +// Expected and actual values should convert to float64. +// To compare large integers that can't be represented accurately as float64 (eg. uint64), +// prefer [InDeltaT] to preserve the original type. +// +// # Behavior with IEEE floating point arithmetics +// +// - expected NaN is matched only by a NaN, e.g. this works: InDeltaT(math.NaN(), math.Sqrt(-1), 0.0) +// - expected +Inf is matched only by a +Inf +// - expected -Inf is matched only by a -Inf +// // # Usage // // assertions.InDelta(t, math.Pi, 22/7.0, 0.01) @@ -29,21 +40,16 @@ func InDelta(t T, expected, actual any, delta float64, msgAndArgs ...any) bool { af, aok := toFloat(expected) bf, bok := toFloat(actual) - if !aok || !bok { return Fail(t, "Parameters must be numerical", msgAndArgs...) } - if math.IsNaN(af) && math.IsNaN(bf) { - return true + msg, skip, ok := checkDeltaEdgeCases(af, bf, delta) + if !ok { + return Fail(t, msg, msgAndArgs...) } - - if math.IsNaN(af) { - return Fail(t, "Expected must not be NaN", msgAndArgs...) // Proposal for enhancement: wrong message (this is accepted above) - } - - if math.IsNaN(bf) { - return Fail(t, fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), msgAndArgs...) + if skip { + return true } dt := af - bf @@ -82,55 +88,14 @@ func InDeltaT[Number Measurable](t T, expected, actual, delta Number, msgAndArgs h.Helper() } - if delta < 0 { - return Fail(t, "Delta must not be negative", msgAndArgs...) // TODO: add it to the original version - } - - // IEEE float edge cases: NaN, +Inf/-Inf - if isNaN(delta) || isInf(delta, 0) { - return Fail(t, "Delta must not be NaN or Inf", msgAndArgs...) // TODO: add it to the original version - } - - expectedInf := isInf(expected, 0) - actualInf := isInf(actual, 0) - if expectedInf { - // expected -Inf/+Inf - if !actualInf { - return Fail(t, "Expected an Inf value", msgAndArgs...) - } - - if isInf(expected, 1) && !isInf(actual, 1) { - return Fail(t, "Expected a +Inf value but got -Inf", msgAndArgs...) - } - - if isInf(expected, -1) && !isInf(actual, -1) { - return Fail(t, "Expected a -Inf value but got +Inf", msgAndArgs...) - } - - // Both are Inf and match - success - return true - } - - if actualInf { - return Fail(t, "Actual is Inf", msgAndArgs...) + msg, skip, ok := checkDeltaEdgeCases(expected, actual, delta) + if !ok { + return Fail(t, msg, msgAndArgs...) } - - expectedNaN := isNaN(expected) - actualNaN := isNaN(actual) - - if expectedNaN && actualNaN { - // expected NaN + if skip { return true } - if expectedNaN { - return Fail(t, "Expected a NaN value but actual is finite", msgAndArgs...) - } - - if actualNaN { - return Fail(t, fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), msgAndArgs...) - } - var ( dt Number failed bool @@ -152,6 +117,20 @@ func InDeltaT[Number Measurable](t T, expected, actual, delta Number, msgAndArgs // InEpsilon asserts that expected and actual have a relative error less than epsilon. // +// # Behavior with IEEE floating point arithmetics +// +// - expected NaN is matched only by a NaN, e.g. this works: InDeltaT(math.NaN(), math.Sqrt(-1), 0.0) +// - expected +Inf is matched only by a +Inf +// - expected -Inf is matched only by a -Inf +// +// Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer [InDeltaT]. +// +// Formula: +// - If expected == 0: fail if |actual - expected| > epsilon +// - If expected != 0: fail if |actual - expected| > epsilon * |expected| +// +// This allows [InEpsilonT] to work naturally across the full numeric range including zero. +// // # Usage // // assertions.InEpsilon(t, 100.0, 101.0, 0.02) @@ -165,19 +144,23 @@ func InEpsilon(t T, expected, actual any, epsilon float64, msgAndArgs ...any) bo if h, ok := t.(H); ok { h.Helper() } - if math.IsNaN(epsilon) { - return Fail(t, "epsilon must not be NaN", msgAndArgs...) + af, aok := toFloat(expected) + bf, bok := toFloat(actual) + if !aok || !bok { + return Fail(t, "Parameters must be numerical", msgAndArgs...) } - actualEpsilon, err := calcRelativeError(expected, actual) - if err != nil { - return Fail(t, err.Error(), msgAndArgs...) + + msg, skip, ok := checkDeltaEdgeCases(af, bf, epsilon) + if !ok { + return Fail(t, msg, msgAndArgs...) } - if math.IsNaN(actualEpsilon) { - return Fail(t, "relative error is NaN", msgAndArgs...) + if skip { + return true } - if actualEpsilon > epsilon { - return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ - " < %#v (actual)", epsilon, actualEpsilon), msgAndArgs...) + + msg, ok = compareRelativeError(af, bf, epsilon) + if !ok { + return Fail(t, msg, msgAndArgs...) } return true @@ -188,11 +171,22 @@ func InEpsilon(t T, expected, actual any, epsilon float64, msgAndArgs ...any) bo // When expected is zero, epsilon is interpreted as an absolute error threshold, // since relative error is mathematically undefined for zero values. // +// Unlike [InDeltaT], which preserves the original type, [InEpsilonT] converts the expected and actual +// numbers to float64, since the relative error doesn't make sense as an integer. +// +// # Behavior with IEEE floating point arithmetics +// +// - expected NaN is matched only by a NaN, e.g. this works: InDeltaT(math.NaN(), math.Sqrt(-1), 0.0) +// - expected +Inf is matched only by a +Inf +// - expected -Inf is matched only by a -Inf +// +// Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer [InDeltaT]. +// // Formula: // - If expected == 0: fail if |actual - expected| > epsilon // - If expected != 0: fail if |actual - expected| > epsilon * |expected| // -// This allows InEpsilonT to work naturally across the full numeric range including zero. +// This allows [InEpsilonT] to work naturally across the full numeric range including zero. // // # Usage // @@ -208,81 +202,27 @@ func InEpsilonT[Number Measurable](t T, expected, actual Number, epsilon float64 h.Helper() } - if epsilon < 0 { - return Fail(t, "Epsilon must not be negative", msgAndArgs...) - } - - // IEEE float edge cases: NaN, +Inf/-Inf - if isNaN(epsilon) || isInf(epsilon, 0) { - return Fail(t, "Epsilon must not be NaN or Inf", msgAndArgs...) - } - - expectedInf := isInf(expected, 0) - actualInf := isInf(actual, 0) - if expectedInf { - // expected -Inf/+Inf - if !actualInf { - return Fail(t, "Expected an Inf value", msgAndArgs...) - } - - if isInf(expected, 1) && !isInf(actual, 1) { - return Fail(t, "Expected a +Inf value but got -Inf", msgAndArgs...) - } - - if isInf(expected, -1) && !isInf(actual, -1) { - return Fail(t, "Expected a -Inf value but got +Inf", msgAndArgs...) - } - - // Both are Inf and match - success - return true - } - - if actualInf { - return Fail(t, "Actual is Inf", msgAndArgs...) - } - - expectedNaN := isNaN(expected) - actualNaN := isNaN(actual) - - if expectedNaN && actualNaN { - // expected NaN - return true - } - - if expectedNaN { - return Fail(t, "Expected a NaN value but actual is finite", msgAndArgs...) - } - - if actualNaN { - return Fail(t, fmt.Sprintf("Expected %v with epsilon %v, but was NaN", expected, epsilon), msgAndArgs...) - } - af := float64(expected) bf := float64(actual) - - delta := math.Abs(af - bf) - if delta == 0 { - return true + msg, skip, ok := checkDeltaEdgeCases(af, bf, epsilon) + if !ok { + return Fail(t, msg, msgAndArgs...) } - if af == 0 { - if delta > epsilon { - return Fail(t, fmt.Sprintf( - "Expected value is zero, using absolute error comparison.\n"+ - "Absolute difference is too high: %#v (expected)\n"+ - " < %#v (actual)", epsilon, delta), msgAndArgs...) - } + if skip { return true } - if delta > epsilon*math.Abs(af) { - return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ - " < %#v (actual)", epsilon, delta/math.Abs(af)), msgAndArgs...) + msg, ok = compareRelativeError(af, bf, epsilon) + if !ok { + return Fail(t, msg, msgAndArgs...) } return true } -// InDeltaSlice is the same as InDelta, except it compares two slices. +// InDeltaSlice is the same as [InDelta], except it compares two slices. +// +// See [InDelta]. // // # Usage // @@ -316,7 +256,9 @@ func InDeltaSlice(t T, expected, actual any, delta float64, msgAndArgs ...any) b return true } -// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +// InDeltaMapValues is the same as [InDelta], but it compares all values between two maps. Both maps must have exactly the same keys. +// +// See [InDelta]. // // # Usage // @@ -348,10 +290,9 @@ func InDeltaMapValues(t T, expected, actual any, delta float64, msgAndArgs ...an ev := expectedMap.MapIndex(k) av := actualMap.MapIndex(k) - if !ev.IsValid() { - return Fail(t, fmt.Sprintf("missing key %q in expected map", k), msgAndArgs...) - } - + // from [reflect.MapIndex] contract, ev is always a valid [reflect.Value] here + // because we know that the key has been found. + // On the other hand, av may not be there. if !av.IsValid() { return Fail(t, fmt.Sprintf("missing key %q in actual map", k), msgAndArgs...) } @@ -370,7 +311,9 @@ func InDeltaMapValues(t T, expected, actual any, delta float64, msgAndArgs ...an return true } -// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +// InEpsilonSlice is the same as [InEpsilon], except it compares each value from two slices. +// +// See [InEpsilon]. // // # Usage // @@ -411,26 +354,82 @@ func InEpsilonSlice(t T, expected, actual any, epsilon float64, msgAndArgs ...an return true } -func calcRelativeError(expected, actual any) (float64, error) { - af, aok := toFloat(expected) - bf, bok := toFloat(actual) - if !aok || !bok { - return 0, errors.New("parameters must be numerical") +func checkDeltaEdgeCases[Number Measurable](expected, actual, delta Number) (msg string, skip bool, ok bool) { + if delta < 0 { + return "Delta must not be negative", true, false + } + + // IEEE float edge cases: NaN, +Inf/-Inf + if isNaN(delta) || isInf(delta, 0) { + return "Delta must not be NaN or Inf", true, false } - if math.IsNaN(af) && math.IsNaN(bf) { - return 0, nil + + expectedInf := isInf(expected, 0) + actualInf := isInf(actual, 0) + if expectedInf { + // expected -Inf/+Inf + if !actualInf { + return "Expected an Inf value", true, false + } + + if isInf(expected, 1) && !isInf(actual, 1) { + return "Expected a +Inf value but got -Inf", true, false + } + + if isInf(expected, -1) && !isInf(actual, -1) { + return "Expected a -Inf value but got +Inf", true, false + } + + // Both are Inf and match - success + return "", true, true } - if math.IsNaN(af) { - return 0, errors.New("expected value must not be NaN") + + if actualInf { + return "Actual is Inf", true, false } - if af == 0 { - return 0, errors.New("expected value must have a value other than zero to calculate the relative error") + + expectedNaN := isNaN(expected) + actualNaN := isNaN(actual) + + if expectedNaN && actualNaN { + // expected NaN + return "", true, true + } + + if expectedNaN { + return "Expected a NaN value but actual is finite", true, false + } + + if actualNaN { + return fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), true, false + } + + return "", false, true +} + +func compareRelativeError(expected, actual, epsilon float64) (msg string, ok bool) { + delta := math.Abs(expected - actual) + if delta == 0 { + return "", true } - if math.IsNaN(bf) { - return 0, errors.New("actual value must not be NaN") + + if expected == 0 { + if delta > epsilon { + return fmt.Sprintf( + "Expected value is zero, using absolute error comparison.\n"+ + "Absolute difference is too high: %#v (expected)\n"+ + " < %#v (actual)", epsilon, delta), false + } + + return "", true } - return math.Abs(af-bf) / math.Abs(af), nil + if delta > epsilon*math.Abs(expected) { + return fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ + " < %#v (actual)", epsilon, delta/math.Abs(expected)), false + } + + return "", true } func toFloat(x any) (float64, bool) { @@ -465,6 +464,14 @@ func toFloat(x any) (float64, bool) { case time.Duration: xf = float64(xn) default: + // try reflect conversion + val := reflect.ValueOf(xn) + typ := reflect.TypeFor[float64]() + if val.IsValid() && val.CanConvert(typ) { + rxf := val.Convert(typ) + xf = rxf.Float() + break + } xok = false } diff --git a/internal/assertions/number_test.go b/internal/assertions/number_test.go index 8aa766865..ee2a414e5 100644 --- a/internal/assertions/number_test.go +++ b/internal/assertions/number_test.go @@ -160,7 +160,12 @@ func TestNumberInEpsilonSlice(t *testing.T) { []float64{2.1, 2.1}, 0.04), "{2.2, 2.0} is not element-wise close to {2.1, 2.1} in epsilon=0.04") - False(t, InEpsilonSlice(mock, "", nil, 1), "Expected non numeral slices to fail") + False(t, InEpsilonSlice(mock, "", nil, 1), "Expected expected non-slices to fail (nil)") + False(t, InEpsilonSlice(mock, nil, "", 1), "Expected actual non-slices to fail (nil)") + False(t, InEpsilonSlice(mock, 1, []int{}, 1), "Expected expected non-slices to fail") + False(t, InEpsilonSlice(mock, []int{}, 1, 1), "Expected actual non-slices to fail") + False(t, InEpsilonSlice(mock, []string{}, []int{}, 1), "Expected expected non-numeral slices to fail") + False(t, InEpsilonSlice(mock, []int{}, []string{}, 1), "Expected actual non-numeral slices to fail") } type numberInDeltaCase struct { @@ -169,6 +174,8 @@ type numberInDeltaCase struct { } func numberInDeltaCases() iter.Seq[numberInDeltaCase] { + type myFloat float32 + return slices.Values([]numberInDeltaCase{ {uint(2), uint(1), 1}, {uint8(2), uint8(1), 1}, @@ -182,6 +189,7 @@ func numberInDeltaCases() iter.Seq[numberInDeltaCase] { {int64(2), int64(1), 1}, {float32(2), float32(1), 1}, {float64(2), float64(1), 1}, + {myFloat(2), myFloat(1), 1}, }) } @@ -336,6 +344,9 @@ type numberInDeltaMapCase struct { } func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { + keyA := "a" + var iface any + return slices.Values([]numberInDeltaMapCase{ { title: "Within delta", @@ -400,6 +411,46 @@ func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { }, f: False, }, + { + title: "With nil maps", + expect: map[string]float64(nil), + actual: map[string]float64(nil), + f: True, + }, + { + title: "With nil values (not a map)", + expect: map[string]float64(nil), + actual: []float64(nil), + f: False, + }, + { + title: "With nil values (not a map)", + expect: []float64(nil), + actual: map[string]float64(nil), + f: False, + }, + { + title: "With expected nil keys", + expect: map[*string]float64{ + &keyA: 1.00, + (*string)(nil): 2.00, + }, + actual: map[*string]float64{ + &keyA: 1.00, + (*string)(nil): 2.00, + }, + f: True, + }, + { + title: "With expected invalid value", + expect: map[string]any{ + keyA: &iface, + }, + actual: map[string]any{ + keyA: &iface, + }, + f: False, + }, }) } @@ -419,7 +470,10 @@ func numberInEpsilonTrueCases() iter.Seq[numberInEpsilonCase] { {0.1, -0.1, 2}, {0.1, 0, 2}, {math.NaN(), math.NaN(), 1}, + {math.Inf(1), math.Inf(1), 1}, + {math.Inf(-1), math.Inf(-1), 1}, {time.Second, time.Second + time.Millisecond, 0.002}, + {0, 0.1, 2}, // works: fall back to absolute error }) } @@ -432,7 +486,7 @@ func numberInEpsilonFalseCases() iter.Seq[numberInEpsilonCase] { {2.1, -2.2, 1}, {2.1, "bla-bla", 0}, {0.1, -0.1, 1.99}, - {0, 0.1, 2}, // expected must be different to zero + {0, 0.1, 0.01}, // works (and fails): fall back to absolute error {time.Second, time.Second + 10*time.Millisecond, 0.002}, {math.NaN(), 0, 1}, {0, math.NaN(), 1}, @@ -441,10 +495,8 @@ func numberInEpsilonFalseCases() iter.Seq[numberInEpsilonCase] { {math.Inf(-1), 1, 1}, {1, math.Inf(1), 1}, {1, math.Inf(-1), 1}, - {math.Inf(1), math.Inf(1), 1}, {math.Inf(1), math.Inf(-1), 1}, {math.Inf(-1), math.Inf(1), 1}, - {math.Inf(-1), math.Inf(-1), 1}, }) } diff --git a/internal/assertions/yaml.go b/internal/assertions/yaml.go index 898a24a71..1299abfe1 100644 --- a/internal/assertions/yaml.go +++ b/internal/assertions/yaml.go @@ -41,7 +41,7 @@ import ( // // # Examples // -// panic: "key: value", "key: value" +// panic: []byte("key: value"), []byte("key: value") // should panic without the yaml feature enabled. func YAMLEqBytes(t T, expected, actual []byte, msgAndArgs ...any) bool { // Domain: yaml From e12affef24198e72ee13eb6d25018d2c3232629f Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Sun, 18 Jan 2026 12:21:01 +0100 Subject: [PATCH 4/6] feat: added support for generics to doc generation Signed-off-by: Frederic BIDON --- .../internal/generator/funcmaps/funcmaps.go | 18 +++++++++++++ .../generator/templates/doc_page.md.gotmpl | 26 +++++++++++++++---- codegen/internal/model/documentation.go | 14 ++++++++++ internal/assertions/boolean.go | 7 ++--- 4 files changed, 57 insertions(+), 8 deletions(-) diff --git a/codegen/internal/generator/funcmaps/funcmaps.go b/codegen/internal/generator/funcmaps/funcmaps.go index 15688ad72..0a168ae54 100644 --- a/codegen/internal/generator/funcmaps/funcmaps.go +++ b/codegen/internal/generator/funcmaps/funcmaps.go @@ -58,6 +58,7 @@ func FuncMap() template.FuncMap { "returns": PrintReturns, "sourceLink": sourceLink, "titleize": titleize, + "slugize": slugize, } } @@ -345,3 +346,20 @@ func printDebug(in any) string { func printDate() string { return time.Now().Format(time.DateOnly) } + +// slugize converts a name into a markdown ref inside a document. +func slugize(in string) string { + return strings.ToLower( + strings.Map(func(r rune) rune { + switch r { + case '.', '_', ' ', '\t', ':': + return '-' + case '[', ']', ',': + return -1 + default: + return r + } + }, + in, + )) +} diff --git a/codegen/internal/generator/templates/doc_page.md.gotmpl b/codegen/internal/generator/templates/doc_page.md.gotmpl index d10043b13..899604bdd 100644 --- a/codegen/internal/generator/templates/doc_page.md.gotmpl +++ b/codegen/internal/generator/templates/doc_page.md.gotmpl @@ -31,14 +31,30 @@ keywords: _All links point to <{{ $godoc }}>_ This domain exposes {{ .RefCount }} functionalities. +{{- if .HasGenerics }} +Generic assertions are marked with a {{ print "{{" }}% icon icon="star" color=orange %{{ print "}}" }} +{{- end }} +```tree {{- with .Package }} {{- $enableGenerics := .EnableGenerics }} {{- range .Functions }}{{/* functions in internal/assertions annotated with "domain:" */}} {{- if or (not .IsGeneric) ($enableGenerics) }} {{- if and (not .IsHelper) (not .IsConstructor) }} +- [{{ .GenericName }}](#{{ slugize .GenericName }}) | {{ if .IsGeneric}}star | orange {{ else }}angles-right{{ end }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} +``` -### {{ .Name }} +{{- with .Package }} + {{- $enableGenerics := .EnableGenerics }} + {{- range .Functions }}{{/* functions in internal/assertions annotated with "domain:" */}} + {{- if or (not .IsGeneric) ($enableGenerics) }} + {{- if and (not .IsHelper) (not .IsConstructor) }} + +### {{ .GenericName }}{{ if .IsGeneric }} {{ print "{{" }}% icon icon="star" color=orange %{{ print "}}" }}{{ end }}{#{{ slugize .GenericName }}} {{- if .IsDeprecated }} {{ print "{{" }}% expand title="{{ .Name }} [DEPRECATED]" %{{ print "}}" }} @@ -55,14 +71,14 @@ This domain exposes {{ .RefCount }} functionalities. {{ print "{{" }}% tab title="{{ .Package }}" style="secondary" %{{ print "}}" }} | Signature | Usage | |--|--| -| [`{{.Package }}.{{ .Name }}({{ params .AllParams }}) {{ returns .Returns }}`]({{ $godoc }}/{{ .Package }}#{{ .Name }}) | package-level function | +| [`{{.Package }}.{{ .GenericName }}({{ params .AllParams }}) {{ returns .Returns }}`]({{ $godoc }}/{{ .Package }}#{{ .Name }}) | package-level function | {{- if .EnableFormat }} -| [`{{ .Package }}.{{ .Name }}f(t T, {{ params .Params }}, msg string, args ...any) {{ returns .Returns }}`]({{ $godoc }}/{{ .Package }}#{{ .Name }}f) | formatted variant | +| [`{{ .Package }}.{{ .GenericName "f" }}(t T, {{ params .Params }}, msg string, args ...any) {{ returns .Returns }}`]({{ $godoc }}/{{ .Package }}#{{ .Name }}f) | formatted variant | {{- end }} - {{- if .EnableForward }} + {{- if and .EnableForward (not .IsGeneric) }} | [`{{ .Package}}.(*{{ .Receiver }}).{{ .Name }}({{ params .Params }}) {{ returns .Returns }}`]({{ $godoc }}/{{ .Package }}#{{ .Receiver }}.{{ .Name }}) | method variant | {{- end }} - {{- if and .EnableForward .EnableFormat }} + {{- if and .EnableForward .EnableFormat (not .IsGeneric) }} | [`{{ .Package }}.(*{{ .Receiver }}).{{ .Name }}f({{ params .Params }}, msg string, args ..any)`]({{ $godoc }}/{{ .Package }}#{{ .Receiver }}.{{ .Name }}f) | method formatted variant | {{- end }} {{ print "{{" }}% /tab %{{ print "}}" }} diff --git a/codegen/internal/model/documentation.go b/codegen/internal/model/documentation.go index d67c91aab..5e5ed8c42 100644 --- a/codegen/internal/model/documentation.go +++ b/codegen/internal/model/documentation.go @@ -84,6 +84,20 @@ type Document struct { Weight int } +func (d Document) HasGenerics() bool { + if d.Package == nil { + return false + } + + for _, fn := range d.Package.Functions { + if fn.IsGeneric { + return true + } + } + + return false +} + type ExtraPackages []*AssertionPackage func (pkgs ExtraPackages) LookupFunction(name string) []FunctionWithContext { diff --git a/internal/assertions/boolean.go b/internal/assertions/boolean.go index 9830e8c56..30e36dd67 100644 --- a/internal/assertions/boolean.go +++ b/internal/assertions/boolean.go @@ -29,16 +29,17 @@ func True(t T, value bool, msgAndArgs ...any) bool { // // # Usage // -// type B bool -// var b B = true +// type B bool +// var b B = true // -// assertions.True(t, b) +// assertions.True(t, b) // // # Examples // // success: 1 == 1 // failure: 1 == 0 func TrueT[B Boolean](t T, value B, msgAndArgs ...any) bool { + // Domain: boolean if !bool(value) { if h, ok := t.(H); ok { h.Helper() From baf028f2897bae317501478f8434a503aa8daec4 Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Sun, 18 Jan 2026 13:52:37 +0100 Subject: [PATCH 5/6] doc: updated docs for generics and updated roadmap Signed-off-by: Frederic BIDON --- README.md | 13 +- .../generator/templates/doc_index.md.gotmpl | 1 + .../generator/templates/doc_page.md.gotmpl | 2 +- docs/doc-site/_index.md | 38 +- docs/doc-site/api/_index.md | 19 +- docs/doc-site/api/boolean.md | 114 +++++- docs/doc-site/api/collection.md | 162 ++++++-- docs/doc-site/api/common.md | 7 +- docs/doc-site/api/comparison.md | 378 +++++++++++++++++- docs/doc-site/api/condition.md | 19 +- docs/doc-site/api/equality.md | 67 ++-- docs/doc-site/api/error.md | 67 ++-- docs/doc-site/api/file.md | 25 +- docs/doc-site/api/http.md | 25 +- docs/doc-site/api/json.md | 80 +++- docs/doc-site/api/number.md | 195 ++++++++- docs/doc-site/api/ordering.md | 19 +- docs/doc-site/api/panic.md | 19 +- docs/doc-site/api/string.md | 160 ++++++-- docs/doc-site/api/testing.md | 13 +- docs/doc-site/api/time.md | 13 +- docs/doc-site/api/type.md | 31 +- docs/doc-site/api/yaml.md | 138 ++++++- docs/doc-site/keywords/_index.md | 9 - docs/doc-site/project/README.md | 14 +- docs/doc-site/project/maintainers/ROADMAP.md | 119 +++--- hack/doc-site/hugo/testify.yaml | 6 +- 27 files changed, 1423 insertions(+), 330 deletions(-) delete mode 100644 docs/doc-site/keywords/_index.md diff --git a/README.md b/README.md index b487a6dbd..16ef8133d 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Main features: * zero external dependencies * opt-in dependencies for extra features (e.g. asserting YAML, colorized output) +* assertions using generic types (see [a basic example][example-with-generics-url]) * [searchable documentation][doc-url] ## Announcements @@ -43,8 +44,15 @@ Design and exploration phase. Contributions and proposals are welcome. > **Recent news** > Fully refactored how assertions are generated and documented. +> Fixed hangs & panics when using `spew`. Fuzzed `spew`. +> Fixed go routine leaks with `EventuallyWithT` and co. +> Added `Kind` & `NotKind` +> Fix deterministic order of keys in diff +> Fixed edge cases with `InDelta`, `InEpsilon` +> Added opt-in support for colorized output +> Introduced generics (round 1): 16 new type-safe assertions with generic types (added benchmark) > -> Now on our way to apply more fixes, features and adopt generics. See [ROADMAP][roadmap]. +> See our [ROADMAP][roadmap]. ## Import this library in your project @@ -93,7 +101,7 @@ Becomes: ## Usage at go-openapi and go-swagger -This fork now full replaces the original project for all go-openapi projects, +This fork now fully replaces the original project for all go-openapi projects, thus reducing their dependencies footprint. Go-swagger will be adapted over Q1 2026. @@ -161,6 +169,7 @@ Maintainers can cut a new release by either: [doc-badge]: https://img.shields.io/badge/doc-site-blue?link=https%3A%2F%2Fgo-openapi.github.io%2Ftestify%2F [doc-url]: https://go-openapi.github.io/testify +[example-with-generics-url]: https://go-openapi.github.io/testify#usage-with-generics [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify [godoc-url]: http://pkg.go.dev/github.com/go-openapi/testify [slack-logo]: https://a.slack-edge.com/e6a93c1/img/icons/favicon-32.png diff --git a/codegen/internal/generator/templates/doc_index.md.gotmpl b/codegen/internal/generator/templates/doc_index.md.gotmpl index d9accb1b2..0279fdfad 100644 --- a/codegen/internal/generator/templates/doc_index.md.gotmpl +++ b/codegen/internal/generator/templates/doc_index.md.gotmpl @@ -26,6 +26,7 @@ with all documented exported variants documented in a more concise form than the ## Domains The `testify` API is organized in {{ .RefCount }} domains shown below. +Each domain contains assertions regrouped by their use case (e.g. http, json, error). {{ print "{{" }}< children type="card" description="true" >{{ print "}}" }} diff --git a/codegen/internal/generator/templates/doc_page.md.gotmpl b/codegen/internal/generator/templates/doc_page.md.gotmpl index 899604bdd..4ee3540fd 100644 --- a/codegen/internal/generator/templates/doc_page.md.gotmpl +++ b/codegen/internal/generator/templates/doc_page.md.gotmpl @@ -41,7 +41,7 @@ Generic assertions are marked with a {{ print "{{" }}% icon icon="star" color=or {{- range .Functions }}{{/* functions in internal/assertions annotated with "domain:" */}} {{- if or (not .IsGeneric) ($enableGenerics) }} {{- if and (not .IsHelper) (not .IsConstructor) }} -- [{{ .GenericName }}](#{{ slugize .GenericName }}) | {{ if .IsGeneric}}star | orange {{ else }}angles-right{{ end }} +- [{{ .GenericName }}](#{{ slugize .GenericName }}) | {{ if .IsGeneric}}star | orange{{ else }}angles-right{{ end }} {{- end }} {{- end }} {{- end }} diff --git a/docs/doc-site/_index.md b/docs/doc-site/_index.md index 5e2257725..cdbf25322 100644 --- a/docs/doc-site/_index.md +++ b/docs/doc-site/_index.md @@ -1,19 +1,17 @@ --- title: "Testify v2" type: home -description: 'Go testing assertions for the rest of us' +description: 'The v2 our test wanted' weight: 1 --- -**Go testing assertions for the rest of us** - {{% notice info %}} This is the home of `github.com/go-openapi/testify/v2`, an active, opinionated fork of `github.com/stretchr/testify`. {{% /notice %}} ## Testify v2 - The v2 our tests wanted -A set of `go` packages that provide tools for testifying that your code behaves as you intended. +A set of `go` packages that provide tools for _testifying_ (verifying) that your code behaves as you intended. This is the go-openapi fork of the great [testify](https://github.com/stretchr/testify) package. @@ -44,7 +42,9 @@ APIs that became bloated over a decade or so, uncontrolled dependencies, difficu breaking changes, conflicting demands from users etc. {{% /notice %}} -More about our motivations in the project's [README](README.md). +More about our motivations in the project's [README](README.md#motivation). + +You might also be curious about our [ROADMAP](project/maintainers/ROADMAP.md). ### Getting started @@ -86,6 +86,7 @@ To use this package in your projects: ```go import ( "testing" + "github.com/go-openapi/testify/v2/require" ) ... @@ -101,6 +102,33 @@ To use this package in your projects: {{% /card %}} {{< /cards >}} +### Usage with generics + +Assertion functions that support go generic types are suffixed with `T` (for "Type safety"). +A formatted variant suffixed with `Tf` is also exposed. + +Obviously, the `Assertion` type cannot be extended with generic methods, as of `go1.25`. + +{{% card title="InDeltaT" %}} +```go + import ( + "testing" + + "github.com/go-openapi/testify/v2/require" + ) + ... + + const ( + expected = 1.00 + delta = 1E-6 + ) + var input = 1.01 + + result := someComplexComputation(input) + require.InDeltaT(t, expected, input, delta) +``` +{{% /card %}} + ## Licensing `SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers` diff --git a/docs/doc-site/api/_index.md b/docs/doc-site/api/_index.md index b17f59a6f..f01a44070 100644 --- a/docs/doc-site/api/_index.md +++ b/docs/doc-site/api/_index.md @@ -6,7 +6,7 @@ description: | Find the assertion function you need for your data. weight: 1 -modified: 2026-01-11 +modified: 2026-01-18 --- **Go testing assertions for the rest of us** @@ -26,28 +26,29 @@ with all documented exported variants documented in a more concise form than the ## Domains The `testify` API is organized in 18 domains shown below. +Each domain contains assertions regrouped by their use case (e.g. http, json, error). {{< children type="card" description="true" >}} --- -- [Boolean](./boolean.md) - Asserting Boolean Values (2) -- [Collection](./collection.md) - Asserting Slices And Maps (7) -- [Comparison](./comparison.md) - Comparing Ordered Values (6) +- [Boolean](./boolean.md) - Asserting Boolean Values (4) +- [Collection](./collection.md) - Asserting Slices And Maps (9) +- [Comparison](./comparison.md) - Comparing Ordered Values (12) - [Condition](./condition.md) - Expressing Assertions Using Conditions (4) - [Equality](./equality.md) - Asserting Two Things Are Equal (12) - [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 (2) -- [Number](./number.md) - Asserting Numbers (5) +- [Json](./json.md) - Asserting JSON Documents (3) +- [Number](./number.md) - Asserting Numbers (7) - [Ordering](./ordering.md) - Asserting How Collections Are Ordered (4) - [Panic](./panic.md) - Asserting A Panic Behavior (4) -- [String](./string.md) - Asserting Strings (2) +- [String](./string.md) - Asserting Strings (4) - [Testing](./testing.md) - Mimicks Methods From The Testing Standard Library (2) - [Time](./time.md) - Asserting Times And Durations (2) - [Type](./type.md) - Asserting Types Rather Than Values (8) -- [Yaml](./yaml.md) - Asserting Yaml Documents (1) +- [Yaml](./yaml.md) - Asserting Yaml Documents (3) - [Common](./common.md) - Other Uncategorized Helpers (4) --- @@ -66,5 +67,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/boolean.md b/docs/doc-site/api/boolean.md index 522ed390a..01e28abbb 100644 --- a/docs/doc-site/api/boolean.md +++ b/docs/doc-site/api/boolean.md @@ -1,15 +1,19 @@ --- title: "Boolean" description: "Asserting Boolean Values" -modified: 2026-01-11 +modified: 2026-01-18 weight: 1 domains: - "boolean" keywords: - "False" - "Falsef" + - "FalseT" + - "FalseTf" - "True" - "Truef" + - "TrueT" + - "TrueTf" --- Asserting Boolean Values @@ -21,9 +25,17 @@ Asserting Boolean Values _All links point to _ -This domain exposes 2 functionalities. +This domain exposes 4 functionalities. +Generic assertions are marked with a {{% icon icon="star" color=orange %}} -### False +```tree +- [False](#false) | angles-right +- [FalseT[B Boolean]](#falsetb-boolean) | star | orange +- [True](#true) | angles-right +- [TrueT[B Boolean]](#truetb-boolean) | star | orange +``` + +### False{#false} False asserts that the specified value is false. @@ -66,11 +78,56 @@ False asserts that the specified value is false. |--|--| | [`assertions.False(t T, value bool, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#False) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#False](https://github.com/go-openapi/testify/blob/master/internal/assertions/boolean.go#L38) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#False](https://github.com/go-openapi/testify/blob/master/internal/assertions/boolean.go#L63) +{{% /tab %}} +{{< /tabs >}} + +### FalseT[B Boolean] {{% icon icon="star" color=orange %}}{#falsetb-boolean} + +FalseT asserts that the specified value is false. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + type B bool + var b B = true + assertions.FalseT(t, b) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: 1 == 0 + failure: 1 == 1 +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.FalseT[B Boolean](t T, value B, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#FalseT) | package-level function | +| [`assert.FalseTf[B Boolean](t T, value B, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#FalseTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.FalseT[B Boolean](t T, value B, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#FalseT) | package-level function | +| [`require.FalseTf[B Boolean](t T, value B, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#FalseTf) | formatted variant | +{{% /tab %}} + +{{% 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 | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#FalseT](https://github.com/go-openapi/testify/blob/master/internal/assertions/boolean.go#L88) {{% /tab %}} {{< /tabs >}} -### True +### True{#true} True asserts that the specified value is true. @@ -117,6 +174,51 @@ True asserts that the specified value is true. {{% /tab %}} {{< /tabs >}} +### TrueT[B Boolean] {{% icon icon="star" color=orange %}}{#truetb-boolean} + +TrueT asserts that the specified value is true. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + type B bool + var b B = true + assertions.True(t, b) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: 1 == 1 + failure: 1 == 0 +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.TrueT[B Boolean](t T, value B, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#TrueT) | package-level function | +| [`assert.TrueTf[B Boolean](t T, value B, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#TrueTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.TrueT[B Boolean](t T, value B, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#TrueT) | package-level function | +| [`require.TrueTf[B Boolean](t T, value B, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#TrueTf) | formatted variant | +{{% /tab %}} + +{{% 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 | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#TrueT](https://github.com/go-openapi/testify/blob/master/internal/assertions/boolean.go#L41) +{{% /tab %}} +{{< /tabs >}} + --- --- @@ -133,5 +235,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/collection.md b/docs/doc-site/api/collection.md index eecc21bb2..7d3dbae1d 100644 --- a/docs/doc-site/api/collection.md +++ b/docs/doc-site/api/collection.md @@ -1,7 +1,7 @@ --- title: "Collection" description: "Asserting Slices And Maps" -modified: 2026-01-11 +modified: 2026-01-18 weight: 2 domains: - "collection" @@ -10,12 +10,16 @@ keywords: - "Containsf" - "ElementsMatch" - "ElementsMatchf" + - "ElementsMatchT" + - "ElementsMatchTf" - "Len" - "Lenf" - "NotContains" - "NotContainsf" - "NotElementsMatch" - "NotElementsMatchf" + - "NotElementsMatchT" + - "NotElementsMatchTf" - "NotSubset" - "NotSubsetf" - "Subset" @@ -31,9 +35,22 @@ Asserting Slices And Maps _All links point to _ -This domain exposes 7 functionalities. +This domain exposes 9 functionalities. +Generic assertions are marked with a {{% icon icon="star" color=orange %}} + +```tree +- [Contains](#contains) | angles-right +- [ElementsMatch](#elementsmatch) | angles-right +- [ElementsMatchT[E comparable]](#elementsmatchte-comparable) | star | orange +- [Len](#len) | angles-right +- [NotContains](#notcontains) | angles-right +- [NotElementsMatch](#notelementsmatch) | angles-right +- [NotElementsMatchT[E comparable]](#notelementsmatchte-comparable) | star | orange +- [NotSubset](#notsubset) | angles-right +- [Subset](#subset) | angles-right +``` -### Contains +### Contains{#contains} Contains asserts that the specified string, list(array, slice...) or map contains the specified substring or element. @@ -79,11 +96,11 @@ specified substring or element. |--|--| | [`assertions.Contains(t T, s any, contains any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Contains) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Contains](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L64) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Contains](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L63) {{% /tab %}} {{< /tabs >}} -### ElementsMatch +### ElementsMatch{#elementsmatch} ElementsMatch asserts that the specified listA(array, slice...) is equal to specified listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, @@ -93,7 +110,7 @@ the number of appearances of each of them in both lists should match. {{< tabs >}} {{% tab title="Usage" %}} ```go - assertions.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) + assertions.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) ``` {{< /tab >}} {{% tab title="Examples" %}} @@ -128,11 +145,56 @@ the number of appearances of each of them in both lists should match. |--|--| | [`assertions.ElementsMatch(t T, listA any, listB any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#ElementsMatch) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ElementsMatch](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L277) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ElementsMatch](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L276) +{{% /tab %}} +{{< /tabs >}} + +### ElementsMatchT[E comparable] {{% icon icon="star" color=orange %}}{#elementsmatchte-comparable} + +ElementsMatchT asserts that the specified listA(array, slice...) is equal to specified +listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +the number of appearances of each of them in both lists should match. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} + failure: []int{1, 2, 3}, []int{1, 2, 4} +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.ElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ElementsMatchT) | package-level function | +| [`assert.ElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ElementsMatchTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.ElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ElementsMatchT) | package-level function | +| [`require.ElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ElementsMatchTf) | formatted variant | +{{% /tab %}} + +{{% 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 | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ElementsMatchT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L349) {{% /tab %}} {{< /tabs >}} -### Len +### Len{#len} Len asserts that the specified object has specific length. @@ -185,16 +247,12 @@ See also [reflect.Len](https://pkg.go.dev/reflect#Len). **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Len](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L31) -> **Maintainer Note** -> The implementation is based on [reflect.Len]. The potential panic is handled with recover. -A better approach could be to check for the [reflect.Type] before calling [reflect.Len]. - > **Note** > (proposals) this does not currently support iterators, or collection objects that have a Len() method. {{% /tab %}} {{< /tabs >}} -### NotContains +### NotContains{#notcontains} NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the specified substring or element. @@ -240,11 +298,11 @@ specified substring or element. |--|--| | [`assertions.NotContains(t T, s any, contains any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotContains) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotContains](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L94) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotContains](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L93) {{% /tab %}} {{< /tabs >}} -### NotElementsMatch +### NotElementsMatch{#notelementsmatch} NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, @@ -255,9 +313,9 @@ This is an inverse of ElementsMatch. {{< tabs >}} {{% tab title="Usage" %}} ```go - assertions.NotElementsMatch(t, [1, 1, 2, 3], [1, 1, 2, 3]) -> false - assertions.NotElementsMatch(t, [1, 1, 2, 3], [1, 2, 3]) -> true - assertions.NotElementsMatch(t, [1, 2, 3], [1, 2, 4]) -> true + 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 ``` {{< /tab >}} {{% tab title="Examples" %}} @@ -292,11 +350,59 @@ This is an inverse of ElementsMatch. |--|--| | [`assertions.NotElementsMatch(t T, listA any, listB any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatch) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatch](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L314) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatch](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L313) +{{% /tab %}} +{{< /tabs >}} + +### NotElementsMatchT[E comparable] {{% icon icon="star" color=orange %}}{#notelementsmatchte-comparable} + +NotElementsMatchT asserts that the specified listA(array, slice...) is NOT equal to specified +listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +the number of appearances of each of them in both lists should not match. +This is an inverse of ElementsMatch. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + 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 +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: []int{1, 2, 3}, []int{1, 2, 4} + failure: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.NotElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotElementsMatchT) | package-level function | +| [`assert.NotElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotElementsMatchTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.NotElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotElementsMatchT) | package-level function | +| [`require.NotElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotElementsMatchTf) | formatted variant | +{{% /tab %}} + +{{% 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 | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatchT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L385) {{% /tab %}} {{< /tabs >}} -### NotSubset +### NotSubset{#notsubset} NotSubset asserts that the list (array, slice, or map) does NOT contain all elements given in the subset (array, slice, or map). @@ -345,11 +451,11 @@ only the map key is evaluated. |--|--| | [`assertions.NotSubset(t T, list any, subset any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotSubset) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSubset](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L205) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSubset](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L204) {{% /tab %}} {{< /tabs >}} -### Subset +### Subset{#subset} Subset asserts that the list (array, slice, or map) contains all elements given in the subset (array, slice, or map). @@ -361,10 +467,10 @@ only the map key is evaluated. {{< tabs >}} {{% tab title="Usage" %}} ```go - assertions.Subset(t, [1, 2, 3], [1, 2]) - assertions.Subset(t, {"x": 1, "y": 2}, {"x": 1}) - assertions.Subset(t, [1, 2, 3], {1: "one", 2: "two"}) - assertions.Subset(t, {"x": 1, "y": 2}, ["x"]) + assertions.Subset(t, []int{1, 2, 3}, []int{1, 2}) + assertions.Subset(t, []string{"x": 1, "y": 2}, []string{"x": 1}) + assertions.Subset(t, []int{1, 2, 3}, map[int]string{1: "one", 2: "two"}) + assertions.Subset(t, map[string]int{"x": 1, "y": 2}, []string{"x"}) ``` {{< /tab >}} {{% tab title="Examples" %}} @@ -399,7 +505,7 @@ only the map key is evaluated. |--|--| | [`assertions.Subset(t T, list any, subset any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Subset) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Subset](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L128) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Subset](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L127) {{% /tab %}} {{< /tabs >}} @@ -419,5 +525,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/common.md b/docs/doc-site/api/common.md index 625b0bd16..d700fd0e8 100644 --- a/docs/doc-site/api/common.md +++ b/docs/doc-site/api/common.md @@ -1,7 +1,7 @@ --- title: "Common" description: "Other Uncategorized Helpers" -modified: 2026-01-11 +modified: 2026-01-18 weight: 18 domains: - "common" @@ -27,6 +27,9 @@ _All links point to _ This domain exposes 4 functionalities. +```tree +``` + --- ## Other helpers @@ -157,5 +160,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/comparison.md b/docs/doc-site/api/comparison.md index fd118d26b..c6da96545 100644 --- a/docs/doc-site/api/comparison.md +++ b/docs/doc-site/api/comparison.md @@ -1,7 +1,7 @@ --- title: "Comparison" description: "Comparing Ordered Values" -modified: 2026-01-11 +modified: 2026-01-18 weight: 3 domains: - "comparison" @@ -10,14 +10,26 @@ keywords: - "Greaterf" - "GreaterOrEqual" - "GreaterOrEqualf" + - "GreaterOrEqualT" + - "GreaterOrEqualTf" + - "GreaterT" + - "GreaterTf" - "Less" - "Lessf" - "LessOrEqual" - "LessOrEqualf" + - "LessOrEqualT" + - "LessOrEqualTf" + - "LessT" + - "LessTf" - "Negative" - "Negativef" + - "NegativeT" + - "NegativeTf" - "Positive" - "Positivef" + - "PositiveT" + - "PositiveTf" --- Comparing Ordered Values @@ -29,12 +41,31 @@ Comparing Ordered Values _All links point to _ -This domain exposes 6 functionalities. +This domain exposes 12 functionalities. +Generic assertions are marked with a {{% icon icon="star" color=orange %}} + +```tree +- [Greater](#greater) | angles-right +- [GreaterOrEqual](#greaterorequal) | angles-right +- [GreaterOrEqualT[Orderable Ordered]](#greaterorequaltorderable-ordered) | star | orange +- [GreaterT[Orderable Ordered]](#greatertorderable-ordered) | star | orange +- [Less](#less) | angles-right +- [LessOrEqual](#lessorequal) | angles-right +- [LessOrEqualT[Orderable Ordered]](#lessorequaltorderable-ordered) | star | orange +- [LessT[Orderable Ordered]](#lesstorderable-ordered) | star | orange +- [Negative](#negative) | angles-right +- [NegativeT[SignedNumber SignedNumeric]](#negativetsignednumber-signednumeric) | star | orange +- [Positive](#positive) | angles-right +- [PositiveT[SignedNumber SignedNumeric]](#positivetsignednumber-signednumeric) | star | orange +``` -### Greater +### Greater{#greater} Greater asserts that the first element is strictly greater than the second. +Both elements must be of the same type in the [reflect.Kind](https://pkg.go.dev/reflect#Kind) sense. +To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. + {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} @@ -76,14 +107,16 @@ Greater asserts that the first element is strictly greater than the second. |--|--| | [`assertions.Greater(t T, e1 any, e2 any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Greater) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Greater](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L56) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Greater](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L30) {{% /tab %}} {{< /tabs >}} -### GreaterOrEqual +### GreaterOrEqual{#greaterorequal} GreaterOrEqual asserts that the first element is greater than or equal to the second. +See also [Greater]. + {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} @@ -126,14 +159,130 @@ GreaterOrEqual asserts that the first element is greater than or equal to the se |--|--| | [`assertions.GreaterOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#GreaterOrEqual) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#GreaterOrEqual](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L78) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#GreaterOrEqual](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L91) +{{% /tab %}} +{{< /tabs >}} + +### GreaterOrEqualT[Orderable Ordered] {{% icon icon="star" color=orange %}}{#greaterorequaltorderable-ordered} + +GreaterOrEqualT asserts that for two elements of the same type, +the first element is greater than or equal to the second. + +The [Ordered] type can be any of Go's [cmp.Ordered](https://pkg.go.dev/cmp#Ordered) (strings, numeric types), +[]byte (uses [bytes.Compare](https://pkg.go.dev/bytes#Compare)) and [time.Time](https://pkg.go.dev/time#Time) (uses [time.Time.Compare](https://pkg.go.dev/time#Time.Compare). + +Notice that pointers are not [Ordered], but uintptr are. So you can't call [GreaterOrEqualT] with [*time.Time]. + +[GreaterOrEqualT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, +use [GreaterOrEqual] instead. + +To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.GreaterOrEqualT(t, 2, 1) + assertions.GreaterOrEqualT(t, 2, 2) + assertions.GreaterOrEqualT(t, "b", "a") + assertions.GreaterOrEqualT(t, "b", "b") +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: 2, 1 + failure: 1, 2 +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.GreaterOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#GreaterOrEqualT) | package-level function | +| [`assert.GreaterOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#GreaterOrEqualTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.GreaterOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#GreaterOrEqualT) | package-level function | +| [`require.GreaterOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#GreaterOrEqualTf) | formatted variant | +{{% /tab %}} + +{{% 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 | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#GreaterOrEqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L124) +{{% /tab %}} +{{< /tabs >}} + +### GreaterT[Orderable Ordered] {{% icon icon="star" color=orange %}}{#greatertorderable-ordered} + +GreaterT asserts that for two elements of the same type, +the first element is strictly greater than the second. + +The [Ordered] type can be any of Go's [cmp.Ordered](https://pkg.go.dev/cmp#Ordered) (strings, numeric types), +[]byte (uses [bytes.Compare](https://pkg.go.dev/bytes#Compare)) and [time.Time](https://pkg.go.dev/time#Time) (uses [time.Time.Compare](https://pkg.go.dev/time#Time.Compare). + +Notice that pointers are not [Ordered], but uintptr are. So you can't call [GreaterT] with [*time.Time]. + +[GreaterT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, use [Greater] instead. + +To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.GreaterT(t, 2, 1) + 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()) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: 2, 1 + failure: 1, 2 +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.GreaterT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#GreaterT) | package-level function | +| [`assert.GreaterTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#GreaterTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.GreaterT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#GreaterT) | package-level function | +| [`require.GreaterTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#GreaterTf) | formatted variant | +{{% /tab %}} + +{{% 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 | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#GreaterT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L62) {{% /tab %}} {{< /tabs >}} -### Less +### Less{#less} Less asserts that the first element is strictly less than the second. +Both elements must be of the same type in the [reflect.Kind](https://pkg.go.dev/reflect#Kind) sense. +To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. + {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} @@ -175,11 +324,11 @@ Less asserts that the first element is strictly less than the second. |--|--| | [`assertions.Less(t T, e1 any, e2 any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Less) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Less](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L99) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Less](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L153) {{% /tab %}} {{< /tabs >}} -### LessOrEqual +### LessOrEqual{#lessorequal} LessOrEqual asserts that the first element is less than or equal to the second. @@ -225,11 +374,122 @@ LessOrEqual asserts that the first element is less than or equal to the second. |--|--| | [`assertions.LessOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#LessOrEqual) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#LessOrEqual](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L121) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#LessOrEqual](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L211) +{{% /tab %}} +{{< /tabs >}} + +### LessOrEqualT[Orderable Ordered] {{% icon icon="star" color=orange %}}{#lessorequaltorderable-ordered} + +LessOrEqualT asserts that for two elements of the same type, the first element is less than or equal to the second. + +The [Ordered] type can be any of Go's [cmp.Ordered](https://pkg.go.dev/cmp#Ordered) (strings, numeric types), +[]byte (uses [bytes.Compare](https://pkg.go.dev/bytes#Compare)) and [time.Time](https://pkg.go.dev/time#Time) (uses [time.Time.Compare](https://pkg.go.dev/time#Time.Compare). + +Notice that pointers are not [Ordered], but uintptr are. So you can't call [LessOrEqualT] with [*time.Time]. + +[LessOrEqualT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, +use [LessOrEqual] instead. + +To compare values that need a type conversion (e.g. float32 against float64), you should use [LessOrEqual] instead. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.LessOrEqualT(t, 1, 2) + assertions.LessOrEqualT(t, 2, 2) + assertions.LessOrEqualT(t, "a", "b") + assertions.LessOrEqualT(t, "b", "b") +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: 1, 2 + failure: 2, 1 +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.LessOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#LessOrEqualT) | package-level function | +| [`assert.LessOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#LessOrEqualTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.LessOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#LessOrEqualT) | package-level function | +| [`require.LessOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#LessOrEqualTf) | formatted variant | +{{% /tab %}} + +{{% 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 | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#LessOrEqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L243) +{{% /tab %}} +{{< /tabs >}} + +### LessT[Orderable Ordered] {{% icon icon="star" color=orange %}}{#lesstorderable-ordered} + +LessT asserts that for two elements of the same type, the first element is strictly less than the second. + +The [Ordered] type can be any of Go's [cmp.Ordered](https://pkg.go.dev/cmp#Ordered) (strings, numeric types), +[]byte (uses [bytes.Compare](https://pkg.go.dev/bytes#Compare)) and [time.Time](https://pkg.go.dev/time#Time) (uses [time.Time.Compare](https://pkg.go.dev/time#Time.Compare). + +Notice that pointers are not [Ordered], but uintptr are. So you can't call [LessT] with [*time.Time]. + +[LessT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, +use [Less] instead. + +To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.LessT(t, 1, 2) + assertions.LessT(t, float64(1), float64(2)) + assertions.LessT(t, "a", "b") +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: 1, 2 + failure: 2, 1 +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.LessT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#LessT) | package-level function | +| [`assert.LessTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#LessTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.LessT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#LessT) | package-level function | +| [`require.LessTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#LessTf) | formatted variant | +{{% /tab %}} + +{{% 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 | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#LessT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L184) {{% /tab %}} {{< /tabs >}} -### Negative +### Negative{#negative} Negative asserts that the specified element is strictly negative. @@ -273,11 +533,55 @@ Negative asserts that the specified element is strictly negative. |--|--| | [`assertions.Negative(t T, e any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Negative) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Negative](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L162) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Negative](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L314) +{{% /tab %}} +{{< /tabs >}} + +### NegativeT[SignedNumber SignedNumeric] {{% icon icon="star" color=orange %}}{#negativetsignednumber-signednumeric} + +NegativeT asserts that the specified element of a signed numeric type is strictly negative. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.NegativeT(t, -1) + assertions.NegativeT(t, -1.23) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: -1 + failure: 1 +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.NegativeT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NegativeT) | package-level function | +| [`assert.NegativeTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NegativeTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.NegativeT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NegativeT) | package-level function | +| [`require.NegativeTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NegativeTf) | formatted variant | +{{% /tab %}} + +{{% 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 | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NegativeT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L335) {{% /tab %}} {{< /tabs >}} -### Positive +### Positive{#positive} Positive asserts that the specified element is strictly positive. @@ -321,7 +625,51 @@ Positive asserts that the specified element is strictly positive. |--|--| | [`assertions.Positive(t T, e any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Positive) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Positive](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L141) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Positive](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L268) +{{% /tab %}} +{{< /tabs >}} + +### PositiveT[SignedNumber SignedNumeric] {{% icon icon="star" color=orange %}}{#positivetsignednumber-signednumeric} + +PositiveT asserts that the specified element of a signed numeric type is strictly positive. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.PositiveT(t, 1) + assertions.PositiveT(t, 1.23) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: 1 + failure: -1 +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.PositiveT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#PositiveT) | package-level function | +| [`assert.PositiveTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#PositiveTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.PositiveT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#PositiveT) | package-level function | +| [`require.PositiveTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#PositiveTf) | formatted variant | +{{% /tab %}} + +{{% 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 | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#PositiveT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L289) {{% /tab %}} {{< /tabs >}} @@ -341,5 +689,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/condition.md b/docs/doc-site/api/condition.md index 9073bdbe2..4faddbaac 100644 --- a/docs/doc-site/api/condition.md +++ b/docs/doc-site/api/condition.md @@ -1,7 +1,7 @@ --- title: "Condition" description: "Expressing Assertions Using Conditions" -modified: 2026-01-11 +modified: 2026-01-18 weight: 4 domains: - "condition" @@ -27,7 +27,14 @@ _All links point to _ This domain exposes 4 functionalities. -### Condition +```tree +- [Condition](#condition) | angles-right +- [Eventually](#eventually) | angles-right +- [EventuallyWithT](#eventuallywitht) | angles-right +- [Never](#never) | angles-right +``` + +### Condition{#condition} Condition uses a [Comparison] to assert a complex condition. @@ -74,7 +81,7 @@ Condition uses a [Comparison] to assert a complex condition. {{% /tab %}} {{< /tabs >}} -### Eventually +### Eventually{#eventually} Eventually asserts that the given condition will be met in waitFor time, periodically checking the target function on each tick. @@ -138,7 +145,7 @@ A blocking condition will cause [Eventually] to hang until it returns. {{% /tab %}} {{< /tabs >}} -### EventuallyWithT +### EventuallyWithT{#eventuallywitht} EventuallyWithT asserts that the given condition will be met in waitFor time, periodically checking the target function at each tick. @@ -211,7 +218,7 @@ It may write to variables outside its scope without triggering race conditions. {{% /tab %}} {{< /tabs >}} -### Never +### Never{#never} Never asserts that the given condition is never satisfied within waitFor time, periodically checking the target function at each tick. @@ -287,5 +294,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/equality.md b/docs/doc-site/api/equality.md index 21365307d..76b5715aa 100644 --- a/docs/doc-site/api/equality.md +++ b/docs/doc-site/api/equality.md @@ -1,7 +1,7 @@ --- title: "Equality" description: "Asserting Two Things Are Equal" -modified: 2026-01-11 +modified: 2026-01-18 weight: 5 domains: - "equality" @@ -43,7 +43,22 @@ _All links point to _ This domain exposes 12 functionalities. -### Empty +```tree +- [Empty](#empty) | angles-right +- [Equal](#equal) | angles-right +- [EqualExportedValues](#equalexportedvalues) | angles-right +- [EqualValues](#equalvalues) | angles-right +- [Exactly](#exactly) | angles-right +- [Nil](#nil) | angles-right +- [NotEmpty](#notempty) | angles-right +- [NotEqual](#notequal) | angles-right +- [NotEqualValues](#notequalvalues) | angles-right +- [NotNil](#notnil) | angles-right +- [NotSame](#notsame) | angles-right +- [Same](#same) | angles-right +``` + +### Empty{#empty} Empty asserts that the given value is "empty". @@ -96,11 +111,11 @@ Pointer values are "empty" if the pointer is nil or if the pointed value is "emp |--|--| | [`assertions.Empty(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Empty) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Empty](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L276) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Empty](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L289) {{% /tab %}} {{< /tabs >}} -### Equal +### Equal{#equal} Equal asserts that two objects are equal. @@ -148,11 +163,11 @@ Function equality cannot be determined and will always fail. |--|--| | [`assertions.Equal(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Equal) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Equal](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L28) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Equal](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L30) {{% /tab %}} {{< /tabs >}} -### EqualExportedValues +### EqualExportedValues{#equalexportedvalues} EqualExportedValues asserts that the types of two objects are equal and their public fields are also equal. This is useful for comparing structs that have private fields @@ -202,11 +217,11 @@ that could potentially differ. |--|--| | [`assertions.EqualExportedValues(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#EqualExportedValues) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#EqualExportedValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L161) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#EqualExportedValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L174) {{% /tab %}} {{< /tabs >}} -### EqualValues +### EqualValues{#equalvalues} EqualValues asserts that two objects are equal or convertible to the larger type and equal. @@ -250,11 +265,11 @@ type and equal. |--|--| | [`assertions.EqualValues(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#EqualValues) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#EqualValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L127) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#EqualValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L140) {{% /tab %}} {{< /tabs >}} -### Exactly +### Exactly{#exactly} Exactly asserts that two objects are equal in value and type. @@ -297,11 +312,11 @@ Exactly asserts that two objects are equal in value and type. |--|--| | [`assertions.Exactly(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Exactly) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Exactly](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L198) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Exactly](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L211) {{% /tab %}} {{< /tabs >}} -### Nil +### Nil{#nil} Nil asserts that the specified object is nil. @@ -344,11 +359,11 @@ Nil asserts that the specified object is nil. |--|--| | [`assertions.Nil(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Nil) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Nil](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L245) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Nil](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L258) {{% /tab %}} {{< /tabs >}} -### NotEmpty +### NotEmpty{#notempty} NotEmpty asserts that the specified object is NOT [Empty]. @@ -393,11 +408,11 @@ NotEmpty asserts that the specified object is NOT [Empty]. |--|--| | [`assertions.NotEmpty(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotEmpty) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotEmpty](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L301) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotEmpty](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L314) {{% /tab %}} {{< /tabs >}} -### NotEqual +### NotEqual{#notequal} NotEqual asserts that the specified values are NOT equal. @@ -442,11 +457,11 @@ referenced values (as opposed to the memory addresses). |--|--| | [`assertions.NotEqual(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotEqual) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotEqual](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L327) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotEqual](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L340) {{% /tab %}} {{< /tabs >}} -### NotEqualValues +### NotEqualValues{#notequalvalues} NotEqualValues asserts that two objects are not equal even when converted to the same type. @@ -489,11 +504,11 @@ NotEqualValues asserts that two objects are not equal even when converted to the |--|--| | [`assertions.NotEqualValues(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotEqualValues) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotEqualValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L354) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotEqualValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L367) {{% /tab %}} {{< /tabs >}} -### NotNil +### NotNil{#notnil} NotNil asserts that the specified object is not nil. @@ -536,11 +551,11 @@ assertions.NotNil(t, err) |--|--| | [`assertions.NotNil(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotNil) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotNil](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L224) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotNil](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L237) {{% /tab %}} {{< /tabs >}} -### NotSame +### NotSame{#notsame} NotSame asserts that two pointers do not reference the same object. @@ -586,11 +601,11 @@ determined based on the equality of both type and value. |--|--| | [`assertions.NotSame(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotSame) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSame](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L96) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSame](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L109) {{% /tab %}} {{< /tabs >}} -### Same +### Same{#same} Same asserts that two pointers reference the same object. @@ -636,7 +651,7 @@ determined based on the equality of both type and value. |--|--| | [`assertions.Same(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Same) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Same](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L62) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Same](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L75) {{% /tab %}} {{< /tabs >}} @@ -656,5 +671,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/error.md b/docs/doc-site/api/error.md index d9b1b1b40..c4ab3eaf4 100644 --- a/docs/doc-site/api/error.md +++ b/docs/doc-site/api/error.md @@ -1,7 +1,7 @@ --- title: "Error" description: "Asserting Errors" -modified: 2026-01-11 +modified: 2026-01-18 weight: 6 domains: - "error" @@ -35,7 +35,18 @@ _All links point to _ This domain exposes 8 functionalities. -### EqualError +```tree +- [EqualError](#equalerror) | angles-right +- [Error](#error) | angles-right +- [ErrorAs](#erroras) | angles-right +- [ErrorContains](#errorcontains) | angles-right +- [ErrorIs](#erroris) | angles-right +- [NoError](#noerror) | angles-right +- [NotErrorAs](#noterroras) | angles-right +- [NotErrorIs](#noterroris) | angles-right +``` + +### EqualError{#equalerror} EqualError asserts that a function returned a non-nil error (i.e. an error) and that it is equal to the provided error. @@ -61,30 +72,30 @@ and that it is equal to the provided error. {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| -| [`assert.EqualError(t T, theError error, errString string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualError) | package-level function | -| [`assert.EqualErrorf(t T, theError error, errString string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualErrorf) | formatted variant | -| [`assert.(*Assertions).EqualError(theError error, errString string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.EqualError) | method variant | -| [`assert.(*Assertions).EqualErrorf(theError error, errString string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.EqualErrorf) | method formatted variant | +| [`assert.EqualError(t T, err error, errString string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualError) | package-level function | +| [`assert.EqualErrorf(t T, err error, errString string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualErrorf) | formatted variant | +| [`assert.(*Assertions).EqualError(err error, errString string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.EqualError) | method variant | +| [`assert.(*Assertions).EqualErrorf(err error, errString string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.EqualErrorf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| -| [`require.EqualError(t T, theError error, errString string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EqualError) | package-level function | -| [`require.EqualErrorf(t T, theError error, errString string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EqualErrorf) | formatted variant | -| [`require.(*Assertions).EqualError(theError error, errString string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.EqualError) | method variant | -| [`require.(*Assertions).EqualErrorf(theError error, errString string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.EqualErrorf) | method formatted variant | +| [`require.EqualError(t T, err error, errString string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EqualError) | package-level function | +| [`require.EqualErrorf(t T, err error, errString string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EqualErrorf) | formatted variant | +| [`require.(*Assertions).EqualError(err error, errString string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.EqualError) | method variant | +| [`require.(*Assertions).EqualErrorf(err error, errString string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.EqualErrorf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.EqualError(t T, theError error, errString string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#EqualError) | internal implementation | +| [`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#L80) {{% /tab %}} {{< /tabs >}} -### Error +### Error{#error} Error asserts that a function returned a non-nil error (ie. an error). @@ -132,7 +143,7 @@ Error asserts that a function returned a non-nil error (ie. an error). {{% /tab %}} {{< /tabs >}} -### ErrorAs +### ErrorAs{#erroras} ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. @@ -181,7 +192,7 @@ This is a wrapper for [errors.As](https://pkg.go.dev/errors#As). {{% /tab %}} {{< /tabs >}} -### ErrorContains +### ErrorContains{#errorcontains} ErrorContains asserts that a function returned a non-nil error (i.e. an error) and that the error contains the specified substring. @@ -207,30 +218,30 @@ error) and that the error contains the specified substring. {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| -| [`assert.ErrorContains(t T, theError error, contains string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ErrorContains) | package-level function | -| [`assert.ErrorContainsf(t T, theError error, contains string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ErrorContainsf) | formatted variant | -| [`assert.(*Assertions).ErrorContains(theError error, contains string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.ErrorContains) | method variant | -| [`assert.(*Assertions).ErrorContainsf(theError error, contains string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.ErrorContainsf) | method formatted variant | +| [`assert.ErrorContains(t T, err error, contains string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ErrorContains) | package-level function | +| [`assert.ErrorContainsf(t T, err error, contains string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ErrorContainsf) | formatted variant | +| [`assert.(*Assertions).ErrorContains(err error, contains string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.ErrorContains) | method variant | +| [`assert.(*Assertions).ErrorContainsf(err error, contains string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.ErrorContainsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| -| [`require.ErrorContains(t T, theError error, contains string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ErrorContains) | package-level function | -| [`require.ErrorContainsf(t T, theError error, contains string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ErrorContainsf) | formatted variant | -| [`require.(*Assertions).ErrorContains(theError error, contains string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.ErrorContains) | method variant | -| [`require.(*Assertions).ErrorContainsf(theError error, contains string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.ErrorContainsf) | method formatted variant | +| [`require.ErrorContains(t T, err error, contains string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ErrorContains) | package-level function | +| [`require.ErrorContainsf(t T, err error, contains string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ErrorContainsf) | formatted variant | +| [`require.(*Assertions).ErrorContains(err error, contains string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.ErrorContains) | method variant | +| [`require.(*Assertions).ErrorContainsf(err error, contains string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.ErrorContainsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.ErrorContains(t T, theError error, contains string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#ErrorContains) | internal implementation | +| [`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#L111) {{% /tab %}} {{< /tabs >}} -### ErrorIs +### ErrorIs{#erroris} ErrorIs asserts that at least one of the errors in err's chain matches target. @@ -279,7 +290,7 @@ This is a wrapper for [errors.Is](https://pkg.go.dev/errors#Is). {{% /tab %}} {{< /tabs >}} -### NoError +### NoError{#noerror} NoError asserts that a function returned a nil error (ie. no error). @@ -329,7 +340,7 @@ NoError asserts that a function returned a nil error (ie. no error). {{% /tab %}} {{< /tabs >}} -### NotErrorAs +### NotErrorAs{#noterroras} NotErrorAs asserts that none of the errors in err's chain matches target, but if so, sets target to that error value. @@ -377,7 +388,7 @@ but if so, sets target to that error value. {{% /tab %}} {{< /tabs >}} -### NotErrorIs +### NotErrorIs{#noterroris} NotErrorIs asserts that none of the errors in err's chain matches target. @@ -442,5 +453,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/file.md b/docs/doc-site/api/file.md index 31aaadb1f..100930ad8 100644 --- a/docs/doc-site/api/file.md +++ b/docs/doc-site/api/file.md @@ -1,7 +1,7 @@ --- title: "File" description: "Asserting OS Files" -modified: 2026-01-11 +modified: 2026-01-18 weight: 7 domains: - "file" @@ -31,7 +31,16 @@ _All links point to _ This domain exposes 6 functionalities. -### DirExists +```tree +- [DirExists](#direxists) | angles-right +- [FileEmpty](#fileempty) | angles-right +- [FileExists](#fileexists) | angles-right +- [FileNotEmpty](#filenotempty) | angles-right +- [NoDirExists](#nodirexists) | angles-right +- [NoFileExists](#nofileexists) | angles-right +``` + +### DirExists{#direxists} DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. @@ -79,7 +88,7 @@ if the path is a file rather a directory or there is an error checking whether i {{% /tab %}} {{< /tabs >}} -### FileEmpty +### FileEmpty{#fileempty} FileEmpty checks whether a file exists in the given path and is empty. It fails if the file is not empty, if the path points to a directory or there is an error when trying to check the file. @@ -127,7 +136,7 @@ It fails if the file is not empty, if the path points to a directory or there is {{% /tab %}} {{< /tabs >}} -### FileExists +### FileExists{#fileexists} FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. @@ -175,7 +184,7 @@ the path points to a directory or there is an error when trying to check the fil {{% /tab %}} {{< /tabs >}} -### FileNotEmpty +### FileNotEmpty{#filenotempty} FileNotEmpty checks whether a file exists in the given path and is not empty. It fails if the file is empty, if the path points to a directory or there is an error when trying to check the file. @@ -223,7 +232,7 @@ It fails if the file is empty, if the path points to a directory or there is an {{% /tab %}} {{< /tabs >}} -### NoDirExists +### NoDirExists{#nodirexists} NoDirExists checks whether a directory does not exist in the given path. It fails if the path points to an existing _directory_ only. @@ -271,7 +280,7 @@ It fails if the path points to an existing _directory_ only. {{% /tab %}} {{< /tabs >}} -### NoFileExists +### NoFileExists{#nofileexists} NoFileExists checks whether a file does not exist in a given path. It fails if the path points to an existing _file_ only. @@ -335,5 +344,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/http.md b/docs/doc-site/api/http.md index 625c37798..cb54d08af 100644 --- a/docs/doc-site/api/http.md +++ b/docs/doc-site/api/http.md @@ -1,7 +1,7 @@ --- title: "Http" description: "Asserting HTTP Response And Body" -modified: 2026-01-11 +modified: 2026-01-18 weight: 8 domains: - "http" @@ -33,7 +33,16 @@ _All links point to _ This domain exposes 7 functionalities. -### HTTPBodyContains +```tree +- [HTTPBodyContains](#httpbodycontains) | angles-right +- [HTTPBodyNotContains](#httpbodynotcontains) | angles-right +- [HTTPError](#httperror) | angles-right +- [HTTPRedirect](#httpredirect) | angles-right +- [HTTPStatusCode](#httpstatuscode) | angles-right +- [HTTPSuccess](#httpsuccess) | angles-right +``` + +### HTTPBodyContains{#httpbodycontains} HTTPBodyContains asserts that a specified handler returns a body that contains a string. @@ -82,7 +91,7 @@ Returns whether the assertion was successful (true) or not (false). {{% /tab %}} {{< /tabs >}} -### HTTPBodyNotContains +### HTTPBodyNotContains{#httpbodynotcontains} HTTPBodyNotContains asserts that a specified handler returns a body that does not contain a string. @@ -132,7 +141,7 @@ Returns whether the assertion was successful (true) or not (false). {{% /tab %}} {{< /tabs >}} -### HTTPError +### HTTPError{#httperror} HTTPError asserts that a specified handler returns an error status code. @@ -181,7 +190,7 @@ Returns whether the assertion was successful (true) or not (false). {{% /tab %}} {{< /tabs >}} -### HTTPRedirect +### HTTPRedirect{#httpredirect} HTTPRedirect asserts that a specified handler returns a redirect status code. @@ -230,7 +239,7 @@ Returns whether the assertion was successful (true) or not (false). {{% /tab %}} {{< /tabs >}} -### HTTPStatusCode +### HTTPStatusCode{#httpstatuscode} HTTPStatusCode asserts that a specified handler returns a specified status code. @@ -279,7 +288,7 @@ Returns whether the assertion was successful (true) or not (false). {{% /tab %}} {{< /tabs >}} -### HTTPSuccess +### HTTPSuccess{#httpsuccess} HTTPSuccess asserts that a specified handler returns a success status code. @@ -373,5 +382,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/json.md b/docs/doc-site/api/json.md index 41b245104..420a38712 100644 --- a/docs/doc-site/api/json.md +++ b/docs/doc-site/api/json.md @@ -1,7 +1,7 @@ --- title: "Json" description: "Asserting JSON Documents" -modified: 2026-01-11 +modified: 2026-01-18 weight: 9 domains: - "json" @@ -10,6 +10,8 @@ keywords: - "JSONEqf" - "JSONEqBytes" - "JSONEqBytesf" + - "JSONEqT" + - "JSONEqTf" --- Asserting JSON Documents @@ -21,12 +23,21 @@ Asserting JSON Documents _All links point to _ -This domain exposes 2 functionalities. +This domain exposes 3 functionalities. +Generic assertions are marked with a {{% icon icon="star" color=orange %}} -### JSONEq +```tree +- [JSONEq](#jsoneq) | angles-right +- [JSONEqBytes](#jsoneqbytes) | angles-right +- [JSONEqT[EDoc, ADoc Text]](#jsoneqtedoc-adoc-text) | star | orange +``` + +### JSONEq{#jsoneq} JSONEq asserts that two JSON strings are equivalent. +Expected and actual must be valid JSON. + {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} @@ -66,13 +77,15 @@ JSONEq asserts that two JSON strings are equivalent. |--|--| | [`assertions.JSONEq(t T, expected string, actual string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#JSONEq) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#JSONEq](https://github.com/go-openapi/testify/blob/master/internal/assertions/json.go#L55) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#JSONEq](https://github.com/go-openapi/testify/blob/master/internal/assertions/json.go#L62) {{% /tab %}} {{< /tabs >}} -### JSONEqBytes +### JSONEqBytes{#jsoneqbytes} -JSONEqBytes asserts that two JSON byte slices are equivalent. +JSONEqBytes asserts that two JSON slices of bytes are equivalent. + +Expected and actual must be valid JSON. {{% expand title="Examples" %}} {{< tabs >}} @@ -113,7 +126,58 @@ JSONEqBytes asserts that two JSON byte slices are equivalent. |--|--| | [`assertions.JSONEqBytes(t T, expected []byte, actual []byte, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#JSONEqBytes) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#JSONEqBytes](https://github.com/go-openapi/testify/blob/master/internal/assertions/json.go#L22) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#JSONEqBytes](https://github.com/go-openapi/testify/blob/master/internal/assertions/json.go#L25) + +> **Maintainer Note** +> proposal for enhancement. We could use and indirection for users to inject their favorite JSON +library like we do for YAML. +{{% /tab %}} +{{< /tabs >}} + +### JSONEqT[EDoc, ADoc Text] {{% icon icon="star" color=orange %}}{#jsoneqtedoc-adoc-text} + +JSONEqT asserts that two JSON documents are equivalent. + +The expected and actual arguments may be string or []byte. They do not need to be of the same type. + +Expected and actual must be valid JSON. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`) + failure: `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]` +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.JSONEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONEqT) | package-level function | +| [`assert.JSONEqTf[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONEqTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.JSONEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#JSONEqT) | package-level function | +| [`require.JSONEqTf[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#JSONEqTf) | formatted variant | +{{% /tab %}} + +{{% 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 | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#JSONEqT](https://github.com/go-openapi/testify/blob/master/internal/assertions/json.go#L85) {{% /tab %}} {{< /tabs >}} @@ -133,5 +197,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/number.md b/docs/doc-site/api/number.md index a3d2cd378..608708ffb 100644 --- a/docs/doc-site/api/number.md +++ b/docs/doc-site/api/number.md @@ -1,7 +1,7 @@ --- title: "Number" description: "Asserting Numbers" -modified: 2026-01-11 +modified: 2026-01-18 weight: 10 domains: - "number" @@ -12,10 +12,14 @@ keywords: - "InDeltaMapValuesf" - "InDeltaSlice" - "InDeltaSlicef" + - "InDeltaT" + - "InDeltaTf" - "InEpsilon" - "InEpsilonf" - "InEpsilonSlice" - "InEpsilonSlicef" + - "InEpsilonT" + - "InEpsilonTf" --- Asserting Numbers @@ -27,14 +31,38 @@ Asserting Numbers _All links point to _ -This domain exposes 5 functionalities. +This domain exposes 7 functionalities. +Generic assertions are marked with a {{% icon icon="star" color=orange %}} + +```tree +- [InDelta](#indelta) | angles-right +- [InDeltaMapValues](#indeltamapvalues) | angles-right +- [InDeltaSlice](#indeltaslice) | angles-right +- [InDeltaT[Number Measurable]](#indeltatnumber-measurable) | star | orange +- [InEpsilon](#inepsilon) | angles-right +- [InEpsilonSlice](#inepsilonslice) | angles-right +- [InEpsilonT[Number Measurable]](#inepsilontnumber-measurable) | star | orange +``` -### InDelta +### InDelta{#indelta} InDelta asserts that the two numerals are within delta of each other. +Delta must be greater than or equal to zero. + +Expected and actual values should convert to float64. +To compare large integers that can't be represented accurately as float64 (eg. uint64), +prefer [InDeltaT] to preserve the original type. + {{% expand title="Examples" %}} {{< tabs >}} +{{% tab title="Behavior With IEEE Floating Point Arithmetics" %}} +```go + - expected NaN is matched only by a NaN, e.g. this works: InDeltaT(math.NaN(), math.Sqrt(-1), 0.0) + - expected +Inf is matched only by a +Inf + - expected -Inf is matched only by a -Inf +``` +{{< /tab >}} {{% tab title="Usage" %}} ```go assertions.InDelta(t, math.Pi, 22/7.0, 0.01) @@ -72,13 +100,15 @@ assertions.InDelta(t, math.Pi, 22/7.0, 0.01) |--|--| | [`assertions.InDelta(t T, expected any, actual any, delta float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InDelta) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InDelta](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L24) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InDelta](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L35) {{% /tab %}} {{< /tabs >}} -### InDeltaMapValues +### InDeltaMapValues{#indeltamapvalues} -InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +InDeltaMapValues is the same as [InDelta], but it compares all values between two maps. Both maps must have exactly the same keys. + +See [InDelta]. {{% expand title="Examples" %}} {{< tabs >}} @@ -119,13 +149,15 @@ InDeltaMapValues is the same as InDelta, but it compares all values between two |--|--| | [`assertions.InDeltaMapValues(t T, expected any, actual any, delta float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InDeltaMapValues) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InDeltaMapValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L101) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InDeltaMapValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L271) {{% /tab %}} {{< /tabs >}} -### InDeltaSlice +### InDeltaSlice{#indeltaslice} + +InDeltaSlice is the same as [InDelta], except it compares two slices. -InDeltaSlice is the same as InDelta, except it compares two slices. +See [InDelta]. {{% expand title="Examples" %}} {{< tabs >}} @@ -166,16 +198,84 @@ InDeltaSlice is the same as InDelta, except it compares two slices. |--|--| | [`assertions.InDeltaSlice(t T, expected any, actual any, delta float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InDeltaSlice) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InDeltaSlice](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L67) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InDeltaSlice](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L235) +{{% /tab %}} +{{< /tabs >}} + +### InDeltaT[Number Measurable] {{% icon icon="star" color=orange %}}{#indeltatnumber-measurable} + +InDeltaT asserts that the two numerals of the same type numerical type are within delta of each other. + +[InDeltaT] accepts any go numeric type, including integer types. + +The main difference with [InDelta] is that the delta is expressed with the same type as the values, not necessarily a float64. + +Delta must be greater than or equal to zero. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Behavior With IEEE Floating Point Arithmetics" %}} +```go + - expected NaN is matched only by a NaN, e.g. this works: InDeltaT(math.NaN(), math.Sqrt(-1), 0.0) + - expected +Inf is matched only by a +Inf + - expected -Inf is matched only by a -Inf +``` +{{< /tab >}} +{{% tab title="Usage" %}} +```go +assertions.InDeltaT(t, math.Pi, 22/7.0, 0.01) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: 1.0, 1.01, 0.02 + failure: 1.0, 1.1, 0.05 +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.InDeltaT[Number Measurable](t T, expected Number, actual Number, delta Number, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDeltaT) | package-level function | +| [`assert.InDeltaTf[Number Measurable](t T, expected Number, actual Number, delta Number, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDeltaTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.InDeltaT[Number Measurable](t T, expected Number, actual Number, delta Number, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InDeltaT) | package-level function | +| [`require.InDeltaTf[Number Measurable](t T, expected Number, actual Number, delta Number, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InDeltaTf) | formatted variant | +{{% /tab %}} + +{{% 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 | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InDeltaT](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L85) {{% /tab %}} {{< /tabs >}} -### InEpsilon +### InEpsilon{#inepsilon} InEpsilon asserts that expected and actual have a relative error less than epsilon. {{% expand title="Examples" %}} {{< tabs >}} +{{% tab title="Behavior With IEEE Floating Point Arithmetics" %}} +```go + - expected NaN is matched only by a NaN, e.g. this works: InDeltaT(math.NaN(), math.Sqrt(-1), 0.0) + - expected +Inf is matched only by a +Inf + - expected -Inf is matched only by a -Inf +Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer [InDeltaT]. +Formula: + - If expected == 0: fail if |actual - expected| > epsilon + - If expected != 0: fail if |actual - expected| > epsilon * |expected| +This allows [InEpsilonT] to work naturally across the full numeric range including zero. +``` +{{< /tab >}} {{% tab title="Usage" %}} ```go assertions.InEpsilon(t, 100.0, 101.0, 0.02) @@ -213,13 +313,15 @@ InEpsilon asserts that expected and actual have a relative error less than epsil |--|--| | [`assertions.InEpsilon(t T, expected any, actual any, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InEpsilon) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InEpsilon](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L155) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InEpsilon](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L142) {{% /tab %}} {{< /tabs >}} -### InEpsilonSlice +### InEpsilonSlice{#inepsilonslice} + +InEpsilonSlice is the same as [InEpsilon], except it compares each value from two slices. -InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +See [InEpsilon]. {{% expand title="Examples" %}} {{< tabs >}} @@ -260,7 +362,68 @@ InEpsilonSlice is the same as InEpsilon, except it compares each value from two |--|--| | [`assertions.InEpsilonSlice(t T, expected any, actual any, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InEpsilonSlice) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InEpsilonSlice](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L188) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InEpsilonSlice](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L326) +{{% /tab %}} +{{< /tabs >}} + +### InEpsilonT[Number Measurable] {{% icon icon="star" color=orange %}}{#inepsilontnumber-measurable} + +InEpsilonT asserts that expected and actual have a relative error less than epsilon. + +When expected is zero, epsilon is interpreted as an absolute error threshold, +since relative error is mathematically undefined for zero values. + +Unlike [InDeltaT], which preserves the original type, [InEpsilonT] converts the expected and actual +numbers to float64, since the relative error doesn't make sense as an integer. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Behavior With IEEE Floating Point Arithmetics" %}} +```go + - expected NaN is matched only by a NaN, e.g. this works: InDeltaT(math.NaN(), math.Sqrt(-1), 0.0) + - expected +Inf is matched only by a +Inf + - expected -Inf is matched only by a -Inf +Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer [InDeltaT]. +Formula: + - If expected == 0: fail if |actual - expected| > epsilon + - If expected != 0: fail if |actual - expected| > epsilon * |expected| +This allows [InEpsilonT] to work naturally across the full numeric range including zero. +``` +{{< /tab >}} +{{% tab title="Usage" %}} +```go + assertions.InEpsilon(t, 100.0, 101.0, 0.02) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: 100.0, 101.0, 0.02 + failure: 100.0, 110.0, 0.05 +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.InEpsilonT[Number Measurable](t T, expected Number, actual Number, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilonT) | package-level function | +| [`assert.InEpsilonTf[Number Measurable](t T, expected Number, actual Number, epsilon float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilonTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.InEpsilonT[Number Measurable](t T, expected Number, actual Number, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InEpsilonT) | package-level function | +| [`require.InEpsilonTf[Number Measurable](t T, expected Number, actual Number, epsilon float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InEpsilonTf) | formatted variant | +{{% /tab %}} + +{{% 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 | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InEpsilonT](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L199) {{% /tab %}} {{< /tabs >}} @@ -280,5 +443,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/ordering.md b/docs/doc-site/api/ordering.md index a44719b01..a14f974a1 100644 --- a/docs/doc-site/api/ordering.md +++ b/docs/doc-site/api/ordering.md @@ -1,7 +1,7 @@ --- title: "Ordering" description: "Asserting How Collections Are Ordered" -modified: 2026-01-11 +modified: 2026-01-18 weight: 11 domains: - "ordering" @@ -27,7 +27,14 @@ _All links point to _ This domain exposes 4 functionalities. -### IsDecreasing +```tree +- [IsDecreasing](#isdecreasing) | angles-right +- [IsIncreasing](#isincreasing) | angles-right +- [IsNonDecreasing](#isnondecreasing) | angles-right +- [IsNonIncreasing](#isnonincreasing) | angles-right +``` + +### IsDecreasing{#isdecreasing} IsDecreasing asserts that the collection is decreasing. @@ -76,7 +83,7 @@ IsDecreasing asserts that the collection is decreasing. {{% /tab %}} {{< /tabs >}} -### IsIncreasing +### IsIncreasing{#isincreasing} IsIncreasing asserts that the collection is increasing. @@ -125,7 +132,7 @@ IsIncreasing asserts that the collection is increasing. {{% /tab %}} {{< /tabs >}} -### IsNonDecreasing +### IsNonDecreasing{#isnondecreasing} IsNonDecreasing asserts that the collection is not decreasing. @@ -174,7 +181,7 @@ IsNonDecreasing asserts that the collection is not decreasing. {{% /tab %}} {{< /tabs >}} -### IsNonIncreasing +### IsNonIncreasing{#isnonincreasing} IsNonIncreasing asserts that the collection is not increasing. @@ -239,5 +246,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/panic.md b/docs/doc-site/api/panic.md index 98dd90e21..88dd74bfa 100644 --- a/docs/doc-site/api/panic.md +++ b/docs/doc-site/api/panic.md @@ -1,7 +1,7 @@ --- title: "Panic" description: "Asserting A Panic Behavior" -modified: 2026-01-11 +modified: 2026-01-18 weight: 12 domains: - "panic" @@ -27,7 +27,14 @@ _All links point to _ This domain exposes 4 functionalities. -### NotPanics +```tree +- [NotPanics](#notpanics) | angles-right +- [Panics](#panics) | angles-right +- [PanicsWithError](#panicswitherror) | angles-right +- [PanicsWithValue](#panicswithvalue) | angles-right +``` + +### NotPanics{#notpanics} NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. @@ -74,7 +81,7 @@ NotPanics asserts that the code inside the specified PanicTestFunc does NOT pani {{% /tab %}} {{< /tabs >}} -### Panics +### Panics{#panics} Panics asserts that the code inside the specified PanicTestFunc panics. @@ -121,7 +128,7 @@ Panics asserts that the code inside the specified PanicTestFunc panics. {{% /tab %}} {{< /tabs >}} -### PanicsWithError +### PanicsWithError{#panicswitherror} PanicsWithError asserts that the code inside the specified PanicTestFunc panics, and that the recovered panic value is an error that satisfies the @@ -170,7 +177,7 @@ EqualError comparison. {{% /tab %}} {{< /tabs >}} -### PanicsWithValue +### PanicsWithValue{#panicswithvalue} PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that the recovered panic value equals the expected panic value. @@ -234,5 +241,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/string.md b/docs/doc-site/api/string.md index 3fcf66201..0a98d70f9 100644 --- a/docs/doc-site/api/string.md +++ b/docs/doc-site/api/string.md @@ -1,15 +1,19 @@ --- title: "String" description: "Asserting Strings" -modified: 2026-01-11 +modified: 2026-01-18 weight: 13 domains: - "string" keywords: - "NotRegexp" - "NotRegexpf" + - "NotRegexpT" + - "NotRegexpTf" - "Regexp" - "Regexpf" + - "RegexpT" + - "RegexpTf" --- Asserting Strings @@ -21,11 +25,71 @@ Asserting Strings _All links point to _ -This domain exposes 2 functionalities. +This domain exposes 4 functionalities. +Generic assertions are marked with a {{% icon icon="star" color=orange %}} -### NotRegexp +```tree +- [NotRegexp](#notregexp) | angles-right +- [NotRegexpT[Rex RegExp, ADoc Text]](#notregexptrex-regexp-adoc-text) | star | orange +- [Regexp](#regexp) | angles-right +- [RegexpT[Rex RegExp, ADoc Text]](#regexptrex-regexp-adoc-text) | star | orange +``` + +### NotRegexp{#notregexp} + +NotRegexp asserts that a specified regular expression does not match a string. + +See [Regexp]. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") + assertions.NotRegexp(t, "^start", "it's not starting") +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: "^start", "not starting" + failure: "^start", "starting" +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.NotRegexp(t T, rx any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotRegexp) | package-level function | +| [`assert.NotRegexpf(t T, rx any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotRegexpf) | formatted variant | +| [`assert.(*Assertions).NotRegexp(rx any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotRegexp) | method variant | +| [`assert.(*Assertions).NotRegexpf(rx any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotRegexpf) | method formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.NotRegexp(t T, rx any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotRegexp) | package-level function | +| [`require.NotRegexpf(t T, rx any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotRegexpf) | formatted variant | +| [`require.(*Assertions).NotRegexp(rx any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotRegexp) | method variant | +| [`require.(*Assertions).NotRegexpf(rx any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotRegexpf) | method formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.NotRegexp(t T, rx any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotRegexp) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotRegexp](https://github.com/go-openapi/testify/blob/master/internal/assertions/string.go#L90) +{{% /tab %}} +{{< /tabs >}} + +### NotRegexpT[Rex RegExp, ADoc Text] {{% icon icon="star" color=orange %}}{#notregexptrex-regexp-adoc-text} + +NotRegexpT asserts that a specified regular expression does not match a string. -NotRegexp asserts that a specified regexp does not match a string. +See [RegexpT]. {{% expand title="Examples" %}} {{< tabs >}} @@ -48,32 +112,32 @@ NotRegexp asserts that a specified regexp does not match a string. {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| -| [`assert.NotRegexp(t T, rx any, str any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotRegexp) | package-level function | -| [`assert.NotRegexpf(t T, rx any, str any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotRegexpf) | formatted variant | -| [`assert.(*Assertions).NotRegexp(rx any, str any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotRegexp) | method variant | -| [`assert.(*Assertions).NotRegexpf(rx any, str any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotRegexpf) | method formatted variant | +| [`assert.NotRegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotRegexpT) | package-level function | +| [`assert.NotRegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotRegexpTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| -| [`require.NotRegexp(t T, rx any, str any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotRegexp) | package-level function | -| [`require.NotRegexpf(t T, rx any, str any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotRegexpf) | formatted variant | -| [`require.(*Assertions).NotRegexp(rx any, str any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotRegexp) | method variant | -| [`require.(*Assertions).NotRegexpf(rx any, str any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotRegexpf) | method formatted variant | +| [`require.NotRegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotRegexpT) | package-level function | +| [`require.NotRegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotRegexpTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.NotRegexp(t T, rx any, str any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotRegexp) | internal implementation | +| [`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 | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotRegexp](https://github.com/go-openapi/testify/blob/master/internal/assertions/string.go#L53) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotRegexpT](https://github.com/go-openapi/testify/blob/master/internal/assertions/string.go#L131) {{% /tab %}} {{< /tabs >}} -### Regexp +### Regexp{#regexp} + +Regexp asserts that a specified regular expression matches a string. + +The regular expression may be passed as a [regexp.Regexp](https://pkg.go.dev/regexp#Regexp), a string or a []byte and will be compiled. -Regexp asserts that a specified regexp matches a string. +The actual argument to be matched may be a string, []byte or anything that prints as a string with [fmt.Sprint](https://pkg.go.dev/fmt#Sprint). {{% expand title="Examples" %}} {{< tabs >}} @@ -96,26 +160,68 @@ Regexp asserts that a specified regexp matches a string. {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| -| [`assert.Regexp(t T, rx any, str any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Regexp) | package-level function | -| [`assert.Regexpf(t T, rx any, str any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Regexpf) | formatted variant | -| [`assert.(*Assertions).Regexp(rx any, str any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Regexp) | method variant | -| [`assert.(*Assertions).Regexpf(rx any, str any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Regexpf) | method formatted variant | +| [`assert.Regexp(t T, rx any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Regexp) | package-level function | +| [`assert.Regexpf(t T, rx any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Regexpf) | formatted variant | +| [`assert.(*Assertions).Regexp(rx any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Regexp) | method variant | +| [`assert.(*Assertions).Regexpf(rx any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Regexpf) | method formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.Regexp(t T, rx any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Regexp) | package-level function | +| [`require.Regexpf(t T, rx any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Regexpf) | formatted variant | +| [`require.(*Assertions).Regexp(rx any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Regexp) | method variant | +| [`require.(*Assertions).Regexpf(rx any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Regexpf) | method formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.Regexp(t T, rx any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Regexp) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Regexp](https://github.com/go-openapi/testify/blob/master/internal/assertions/string.go#L27) +{{% /tab %}} +{{< /tabs >}} + +### RegexpT[Rex RegExp, ADoc Text] {{% icon icon="star" color=orange %}}{#regexptrex-regexp-adoc-text} + +RegexpT asserts that a specified regular expression matches a string. + +The actual argument to be matched may be a string or []byte. + +See [Regexp]. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Examples" %}} +```go + success: "^start", "starting" + failure: "^start", "not starting" +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.RegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#RegexpT) | package-level function | +| [`assert.RegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#RegexpTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| -| [`require.Regexp(t T, rx any, str any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Regexp) | package-level function | -| [`require.Regexpf(t T, rx any, str any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Regexpf) | formatted variant | -| [`require.(*Assertions).Regexp(rx any, str any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Regexp) | method variant | -| [`require.(*Assertions).Regexpf(rx any, str any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Regexpf) | method formatted variant | +| [`require.RegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#RegexpT) | package-level function | +| [`require.RegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#RegexpTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.Regexp(t T, rx any, str any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Regexp) | internal implementation | +| [`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 | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Regexp](https://github.com/go-openapi/testify/blob/master/internal/assertions/string.go#L22) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#RegexpT](https://github.com/go-openapi/testify/blob/master/internal/assertions/string.go#L63) {{% /tab %}} {{< /tabs >}} @@ -135,5 +241,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/testing.md b/docs/doc-site/api/testing.md index 358f129fa..8edd73bcd 100644 --- a/docs/doc-site/api/testing.md +++ b/docs/doc-site/api/testing.md @@ -1,7 +1,7 @@ --- title: "Testing" description: "Mimicks Methods From The Testing Standard Library" -modified: 2026-01-11 +modified: 2026-01-18 weight: 14 domains: - "testing" @@ -23,7 +23,12 @@ _All links point to _ This domain exposes 2 functionalities. -### Fail +```tree +- [Fail](#fail) | angles-right +- [FailNow](#failnow) | angles-right +``` + +### Fail{#fail} Fail reports a failure through. @@ -69,7 +74,7 @@ Fail reports a failure through. {{% /tab %}} {{< /tabs >}} -### FailNow +### FailNow{#failnow} FailNow fails test. @@ -131,5 +136,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/time.md b/docs/doc-site/api/time.md index 05d928a0a..4c0f8fca9 100644 --- a/docs/doc-site/api/time.md +++ b/docs/doc-site/api/time.md @@ -1,7 +1,7 @@ --- title: "Time" description: "Asserting Times And Durations" -modified: 2026-01-11 +modified: 2026-01-18 weight: 15 domains: - "time" @@ -23,7 +23,12 @@ _All links point to _ This domain exposes 2 functionalities. -### WithinDuration +```tree +- [WithinDuration](#withinduration) | angles-right +- [WithinRange](#withinrange) | angles-right +``` + +### WithinDuration{#withinduration} WithinDuration asserts that the two times are within duration delta of each other. @@ -70,7 +75,7 @@ WithinDuration asserts that the two times are within duration delta of each othe {{% /tab %}} {{< /tabs >}} -### WithinRange +### WithinRange{#withinrange} WithinRange asserts that a time is within a time range (inclusive). @@ -133,5 +138,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/type.md b/docs/doc-site/api/type.md index 4a51b46e2..56fbddbc1 100644 --- a/docs/doc-site/api/type.md +++ b/docs/doc-site/api/type.md @@ -1,7 +1,7 @@ --- title: "Type" description: "Asserting Types Rather Than Values" -modified: 2026-01-11 +modified: 2026-01-18 weight: 16 domains: - "type" @@ -35,7 +35,18 @@ _All links point to _ This domain exposes 8 functionalities. -### Implements +```tree +- [Implements](#implements) | angles-right +- [IsNotType](#isnottype) | angles-right +- [IsType](#istype) | angles-right +- [Kind](#kind) | angles-right +- [NotImplements](#notimplements) | angles-right +- [NotKind](#notkind) | angles-right +- [NotZero](#notzero) | angles-right +- [Zero](#zero) | angles-right +``` + +### Implements{#implements} Implements asserts that an object is implemented by the specified interface. @@ -82,7 +93,7 @@ Implements asserts that an object is implemented by the specified interface. {{% /tab %}} {{< /tabs >}} -### IsNotType +### IsNotType{#isnottype} IsNotType asserts that the specified objects are not of the same type. @@ -129,7 +140,7 @@ IsNotType asserts that the specified objects are not of the same type. {{% /tab %}} {{< /tabs >}} -### IsType +### IsType{#istype} IsType asserts that the specified objects are of the same type. @@ -176,7 +187,7 @@ IsType asserts that the specified objects are of the same type. {{% /tab %}} {{< /tabs >}} -### Kind +### Kind{#kind} Kind asserts that the [reflect.Kind](https://pkg.go.dev/reflect#Kind) of a given object matches the expected [reflect.Kind](https://pkg.go.dev/reflect#Kind). @@ -226,7 +237,7 @@ are comparable to [reflect.Invalid](https://pkg.go.dev/reflect#Invalid). See als {{% /tab %}} {{< /tabs >}} -### NotImplements +### NotImplements{#notimplements} NotImplements asserts that an object does not implement the specified interface. @@ -273,7 +284,7 @@ NotImplements asserts that an object does not implement the specified interface. {{% /tab %}} {{< /tabs >}} -### NotKind +### NotKind{#notkind} NotKind asserts that the [reflect.Kind](https://pkg.go.dev/reflect#Kind) of a given object does not match the expected [reflect.Kind](https://pkg.go.dev/reflect#Kind). @@ -323,7 +334,7 @@ are comparable to [reflect.Invalid](https://pkg.go.dev/reflect#Invalid). See als {{% /tab %}} {{< /tabs >}} -### NotZero +### NotZero{#notzero} NotZero asserts that i is not the zero value for its type. @@ -370,7 +381,7 @@ NotZero asserts that i is not the zero value for its type. {{% /tab %}} {{< /tabs >}} -### Zero +### Zero{#zero} Zero asserts that i is the zero value for its type. @@ -433,5 +444,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/api/yaml.md b/docs/doc-site/api/yaml.md index 88338c969..1fe014f07 100644 --- a/docs/doc-site/api/yaml.md +++ b/docs/doc-site/api/yaml.md @@ -1,13 +1,17 @@ --- title: "Yaml" description: "Asserting Yaml Documents" -modified: 2026-01-11 +modified: 2026-01-18 weight: 17 domains: - "yaml" keywords: - "YAMLEq" - "YAMLEqf" + - "YAMLEqBytes" + - "YAMLEqBytesf" + - "YAMLEqT" + - "YAMLEqTf" --- Asserting Yaml Documents @@ -19,14 +23,76 @@ Asserting Yaml Documents _All links point to _ -This domain exposes 1 functionalities. +This domain exposes 3 functionalities. +Generic assertions are marked with a {{% icon icon="star" color=orange %}} -### YAMLEq +```tree +- [YAMLEq](#yamleq) | angles-right +- [YAMLEqBytes](#yamleqbytes) | angles-right +- [YAMLEqT[EDoc, ADoc Text]](#yamleqtedoc-adoc-text) | star | orange +``` + +### YAMLEq{#yamleq} + +YAMLEq asserts that two YAML strings are equivalent. + +See [YAMLEqBytes]. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% 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.YAMLEq(t T, expected string, actual string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEq) | package-level function | +| [`assert.YAMLEqf(t T, expected string, actual string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEqf) | formatted variant | +| [`assert.(*Assertions).YAMLEq(expected string, actual string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.YAMLEq) | method variant | +| [`assert.(*Assertions).YAMLEqf(expected string, actual string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.YAMLEqf) | method formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.YAMLEq(t T, expected string, actual string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLEq) | package-level function | +| [`require.YAMLEqf(t T, expected string, actual string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLEqf) | formatted variant | +| [`require.(*Assertions).YAMLEq(expected string, actual string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.YAMLEq) | method variant | +| [`require.(*Assertions).YAMLEqf(expected string, actual string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.YAMLEqf) | method formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.YAMLEq(t T, expected string, actual string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#YAMLEq) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#YAMLEq](https://github.com/go-openapi/testify/blob/master/internal/assertions/yaml.go#L77) +{{% /tab %}} +{{< /tabs >}} + +### YAMLEqBytes{#yamleqbytes} + +YAMLEqBytes asserts that two YAML slices of bytes are equivalent. -YAMLEq asserts that the first documents in the two YAML strings are equivalent. +Expected and actual must be valid YAML. {{% expand title="Examples" %}} {{< tabs >}} +{{% tab title="Important" %}} +```go +By default, this function is disabled and will panic. +To enable it, you should add a blank import like so: + import( + "github.com/go-openapi/testify/enable/yaml/v2" + ) +``` +{{< /tab >}} {{% tab title="Usage" %}} ```go expected := `--- @@ -43,6 +109,52 @@ YAMLEq asserts that the first documents in the two YAML strings are equivalent. ``` {{< /tab >}} {{% tab title="Examples" %}} +```go + panic: []byte("key: value"), []byte("key: value") + should panic without the yaml feature enabled. +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.YAMLEqBytes(t T, expected []byte, actual []byte, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEqBytes) | package-level function | +| [`assert.YAMLEqBytesf(t T, expected []byte, actual []byte, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEqBytesf) | formatted variant | +| [`assert.(*Assertions).YAMLEqBytes(expected []byte, actual []byte) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.YAMLEqBytes) | method variant | +| [`assert.(*Assertions).YAMLEqBytesf(expected []byte, actual []byte, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.YAMLEqBytesf) | method formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.YAMLEqBytes(t T, expected []byte, actual []byte, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLEqBytes) | package-level function | +| [`require.YAMLEqBytesf(t T, expected []byte, actual []byte, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLEqBytesf) | formatted variant | +| [`require.(*Assertions).YAMLEqBytes(expected []byte, actual []byte) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.YAMLEqBytes) | method variant | +| [`require.(*Assertions).YAMLEqBytesf(expected []byte, actual []byte, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.YAMLEqBytesf) | method formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.YAMLEqBytes(t T, expected []byte, actual []byte, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#YAMLEqBytes) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#YAMLEqBytes](https://github.com/go-openapi/testify/blob/master/internal/assertions/yaml.go#L46) +{{% /tab %}} +{{< /tabs >}} + +### YAMLEqT[EDoc, ADoc Text] {{% icon icon="star" color=orange %}}{#yamleqtedoc-adoc-text} + +YAMLEqT asserts that two YAML documents are equivalent. + +The expected and actual arguments may be string or []byte. They do not need to be of the same type. + +See [YAMLEqBytes]. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Examples" %}} ```go panic: "key: value", "key: value" should panic without the yaml feature enabled. @@ -55,26 +167,22 @@ YAMLEq asserts that the first documents in the two YAML strings are equivalent. {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| -| [`assert.YAMLEq(t T, expected string, actual string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEq) | package-level function | -| [`assert.YAMLEqf(t T, expected string, actual string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEqf) | formatted variant | -| [`assert.(*Assertions).YAMLEq(expected string, actual string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.YAMLEq) | method variant | -| [`assert.(*Assertions).YAMLEqf(expected string, actual string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.YAMLEqf) | method formatted variant | +| [`assert.YAMLEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEqT) | package-level function | +| [`assert.YAMLEqTf[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEqTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| -| [`require.YAMLEq(t T, expected string, actual string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLEq) | package-level function | -| [`require.YAMLEqf(t T, expected string, actual string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLEqf) | formatted variant | -| [`require.(*Assertions).YAMLEq(expected string, actual string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.YAMLEq) | method variant | -| [`require.(*Assertions).YAMLEqf(expected string, actual string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.YAMLEqf) | method formatted variant | +| [`require.YAMLEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLEqT) | package-level function | +| [`require.YAMLEqTf[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLEqTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.YAMLEq(t T, expected string, actual string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#YAMLEq) | internal implementation | +| [`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 | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#YAMLEq](https://github.com/go-openapi/testify/blob/master/internal/assertions/yaml.go#L32) +**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 >}} @@ -94,5 +202,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] --> diff --git a/docs/doc-site/keywords/_index.md b/docs/doc-site/keywords/_index.md deleted file mode 100644 index 0e748fefe..000000000 --- a/docs/doc-site/keywords/_index.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -#type: taxonomy -title: Methods -params: - children: - type: card ---- -Example -{{% children sort="title" %}} diff --git a/docs/doc-site/project/README.md b/docs/doc-site/project/README.md index cf2b5dad6..130cd4f51 100644 --- a/docs/doc-site/project/README.md +++ b/docs/doc-site/project/README.md @@ -9,18 +9,10 @@ description: | weight: 1 --- -**The testify/v2 we wanted** +**Go testing assertions for the rest of us** ## Motivation -From the maintainers of `testify`, it looks like a v2 is coming up, but they'll do it at their own pace. - -We like all the principles they put forward to build this v2. [See discussion about v2](https://github.com/stretchr/testify/discussions/1560) - -However, at `go-openapi` we would like to address the well-known issues in `testify` with different priorities. - ---- - 1. We want first to remove all external dependencies. > For all our libraries and generated test code we don't want test dependencies @@ -63,7 +55,7 @@ This fork targets **go1.24**. * [x] **simplified** maintenance * [x] can add or remove assertions with ease * [x] **mitigated API bloat** with comprehensive domain-indexed documentation -* [ ] can leverage generics without backward compatibility concerns +* [x] can leverage generics without backward compatibility concerns The approach will be **selective and pragmatic** rather than comprehensive: @@ -85,6 +77,7 @@ while maintaining the flexibility that makes testify useful for real-world testi ### Other (non-breaking) changes * added `JSONEqBytes` +* added `YAMLEqBytes` * adapted & merged fixes: see [ROADMAP](./maintainers/ROADMAP.md). ## Usage at go-openapi @@ -126,7 +119,6 @@ YAMLEq,YAMLEqf Zero,Zerof ``` - ## [The original README](./maintainers/ORIGINAL.md) diff --git a/docs/doc-site/project/maintainers/ROADMAP.md b/docs/doc-site/project/maintainers/ROADMAP.md index d7e2c15f4..aa9db261d 100644 --- a/docs/doc-site/project/maintainers/ROADMAP.md +++ b/docs/doc-site/project/maintainers/ROADMAP.md @@ -1,5 +1,5 @@ --- -title: "Testify v2 roadmap" +title: "Roadmap" description: "Let's share our plans." weight: 4 --- @@ -20,19 +20,31 @@ timeline : documentation site : panic handling fixes : removed deprecated - v2.2 (Fev 2026) : extension w/ generics : optional dependencies (colorized) - v2.3 (Mar 2026) : other extensions + : upstream PRs: Kind/NotKind + : generics + v2.2 (Fev 2026) : generics (cont.) + : JSON assertions. JSONMarshalsAs... + : IsSorted, IsSortedFunc + : more benchmarks. Perf improvements + v2.3 (Mar 2026) : other extensions (TBD) + : more documentation and examples + : export internal tools (spew, difflib, benchviz) section Q2 2026 - 2006 : Twitter + v2.4 (Apr 2026) : Stabilize API + : export internal tools (blackbox) {{< /mermaid >}} 1. [x] The first release comes with zero dependencies and an unstable API (see below [our use case](#usage-at-go-openapi)) 2. [x] This project is going to be injected as the main and sole test dependency of the `go-openapi` libraries -2. [ ] ... and the `go-swagger` tool -3. [x] Valuable pending pull requests from the original project could be merged (e.g. `JSONEqBytes`) or transformed as "enable" modules (e.g. colorized output) -4. [ ] Unclear assertions may be provided an alternative verb (e.g. `InDelta`) -5. [ ] Since we have leveled the go requirements to the rest of the go-openapi (currently go1.24) there is quite a bit of relinting lying ahead. +3. [x] Since we have leveled the go requirements to the rest of the go-openapi (currently go1.24) there is quite a bit of relinting lying ahead. +4. [x] Valuable pending pull requests from the original project could be merged (e.g. `JSONEqBytes`) or transformed as "enable" modules (e.g. colorized output) +5. [x] More testing and bug fixes (from upstream or detected during our testing) +6. [x] Introduces colorization (opt-in) +7. [x] Introduces generics +8. [ ] New features following test simplification effort in go-openapi repos (e.g. JSONMarshalsAs ...) +9. [ ] Unclear assertions may be provided an alternative verb (e.g. `InDelta`) +10. [ ] Inject this test dependency into the `go-swagger` tool ### What won't come anytime soon @@ -40,18 +52,42 @@ timeline testify-style mocks are thus not going to be supported anytime soon. * extra convoluted stuff in the like of `InDeltaSlice` (more likely to be removed) -## Generics adoption +## PRs and issues from the original repo -### Context from the original repository +We monitor github.com/stretchr/testify (upstream) for updates, new issues and proposals. -Several attempts have been made to introduce generics in the original stretchr/testify repository: +### Already merged or incorporated / adapted -* **github.com/stretchr/testify#1308** - Comprehensive refactor replacing `interface{}` with generic type parameters across assertions (Draft, v2.0.0 milestone) -* **github.com/stretchr/testify#1805** - Proposal for generic `IsOfType[T]()` to avoid dummy value instantiation in type checks -* **github.com/stretchr/testify#1685** - Iterator support (`iter.Seq`) for Contains/ElementsMatch assertions (Go 1.23+) -* **github.com/stretchr/testify#1147** - General discussion about generics adoption (marked "Not Planned") +The following proposed contributions to the original repo have been merged or incorporated with +some adaptations into this fork: -### Challenges identified +* [x] github.com/stretchr/testify#1147 - General discussion about generics adoption (marked "Not Planned") +* [x] github.com/stretchr/testify#1223 - Display uint values in decimal instead of hex in diffs [merged] +* [x] github.com/stretchr/testify#1232 - Colorized output for expected/actual/errors +* [x] github.com/stretchr/testify#1308 - Comprehensive refactor replacing `interface{}` with generic type parameters across assertions (Draft, v2.0.0 milestone) +* [x] github.com/stretchr/testify#1356 - panic(nil) handling for Go 1.21+ +* [x] github.com/stretchr/testify#1467 - Colorized output with terminal detection (most mature implementation) +* [x] github.com/stretchr/testify#1480 - Colorized diffs via TESTIFY_COLORED_DIFF env var +* [x] github.com/stretchr/testify#1513 - JSONEqBytes for byte slice JSON comparison +* [x] github.com/stretchr/testify#1772 - YAML library migration to maintained fork (go.yaml.in/yaml) +* [x] github.com/stretchr/testify#1797 - Codegen package consolidation and licensing +* [x] github.com/stretchr/testify#1816 - Fix panic on unexported struct key in map (internalized go-spew - may need deeper fix) +* [x] github.com/stretchr/testify#1818 - Fix panic on invalid regex in Regexp/NotRegexp assertions [merged] +* [x] github.com/stretchr/testify#1822 - Deterministic map ordering in diffs (internalized go-spew) +* [x] github.com/stretchr/testify#1825 - Fix panic when using EqualValues with uncomparable types [merged] +* [x] github.com/stretchr/testify#1829 - Fix time.Time rendering in diffs (internalized go-spew) +* [x] github.com/stretchr/testify#994 - Colorize expected vs actual values +* [x] github.com/stretchr/testify/issues/1611 - Go routine leak +* [x] github.com/stretchr/testify/issues/1813 +* [x] github.com/stretchr/testify/issues/1826 - type safety with spew +* â›” https://github.com/stretchr/testify/pull/1824 - No longer relevant in our context + +### Merges from upstream under consideration + +* [ ] **github.com/stretchr/testify#1685** - Iterator support (`iter.Seq`) for Contains/ElementsMatch assertions (Go 1.23+) +* [ ] **github.com/stretchr/testify#1805** - Proposal for generic `IsOfType[T]()` to avoid dummy value instantiation in type checks + +## Generics adoption The original repository's exploration of generics revealed several design challenges: @@ -64,54 +100,3 @@ The original repository's exploration of generics revealed several design challe 4. **Breaking changes**: Any comprehensive generics adoption requires a major version bump and Go 1.18+ minimum version 5. **Inconsistent design patterns**: Different assertions would need different constraint strategies, making a uniform approach difficult - -## PRs from the original repo - -### Already merged or incorporated - -The following proposed contributions to the original repo have been merged or incorporated with -some adaptations into this fork: - -* github.com/stretchr/testify#1513 - JSONEqBytes for byte slice JSON comparison -* github.com/stretchr/testify#1772 - YAML library migration to maintained fork (go.yaml.in/yaml) -* github.com/stretchr/testify#1797 - Codegen package consolidation and licensing -* github.com/stretchr/testify#1356 - panic(nil) handling for Go 1.21+ -* github.com/stretchr/testify#1825 - Fix panic when using EqualValues with uncomparable types [merged] -* github.com/stretchr/testify#1818 - Fix panic on invalid regex in Regexp/NotRegexp assertions [merged] -* github.com/stretchr/testify#1223 - Display uint values in decimal instead of hex in diffs [merged] - -### Planned merges - -#### Critical safety fixes (high priority) - -* Follow / adapt https://github.com/stretchr/testify/pull/1824 - -Not PRs, but reported issues in the original repo (need to investigate): - -* https://github.com/stretchr/testify/issues/1826 -* https://github.com/stretchr/testify/issues/1611 -* https://github.com/stretchr/testify/issues/1813 - -#### Leveraging internalized dependencies (go-spew, difflib) - -These improvements apply to the internalized and modernized copies of dependencies in this fork: - -* github.com/stretchr/testify#1829 - Fix time.Time rendering in diffs (internalized go-spew) -* github.com/stretchr/testify#1822 - Deterministic map ordering in diffs (internalized go-spew) -* github.com/stretchr/testify#1816 - Fix panic on unexported struct key in map (internalized go-spew - may need deeper fix) - -#### UX improvements - -* diff rendering - -### Under consideration - -#### Colorized output - -Several PRs propose colorized terminal output with different approaches and dependencies. -If implemented, this would be provided as an optional `enable/color` module: - -* github.com/stretchr/testify#1467 - Colorized output with terminal detection (most mature implementation) -* github.com/stretchr/testify#1480 - Colorized diffs via TESTIFY_COLORED_DIFF env var -* github.com/stretchr/testify#1232 - Colorized output for expected/actual/errors -* github.com/stretchr/testify#994 - Colorize expected vs actual values diff --git a/hack/doc-site/hugo/testify.yaml b/hack/doc-site/hugo/testify.yaml index 9f0652863..47bd1a33f 100644 --- a/hack/doc-site/hugo/testify.yaml +++ b/hack/doc-site/hugo/testify.yaml @@ -7,10 +7,10 @@ params: goVersion: '1.24.0' # Latest release tag (from git tags) - latestRelease: 'v2.0.2' + latestRelease: 'v2.1.8' # Version message for the documentation set - versionMessage: 'Documentation for testify v2.0.2' + versionMessage: 'Documentation test for latest master' # Build timestamp - buildTime: '2026-01-02T14:19:49Z' + buildTime: '2026-01-18T09:58:55Z' From 310ad63f75ba798f2b97fb810dddc4df21f861bd Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Sun, 18 Jan 2026 13:53:52 +0100 Subject: [PATCH 6/6] chore: regenerated packages and doc Signed-off-by: Frederic BIDON --- assert/assert_assertions.go | 541 +++++++++++++++++++++++-- assert/assert_assertions_test.go | 384 +++++++++++++++++- assert/assert_examples_test.go | 130 +++++- assert/assert_format.go | 350 ++++++++++++----- assert/assert_format_test.go | 384 +++++++++++++++++- assert/assert_forward.go | 216 +++++----- assert/assert_forward_test.go | 26 +- assert/assert_helpers.go | 2 +- assert/assert_helpers_test.go | 2 +- assert/assert_types.go | 41 +- require/require_assertions.go | 609 +++++++++++++++++++++++++++-- require/require_assertions_test.go | 324 ++++++++++++++- require/require_examples_test.go | 130 +++++- require/require_format.go | 418 +++++++++++++++----- require/require_format_test.go | 324 ++++++++++++++- require/require_forward.go | 224 ++++++----- require/require_forward_test.go | 26 +- require/require_helpers.go | 2 +- require/require_helpers_test.go | 2 +- require/require_types.go | 41 +- 20 files changed, 3732 insertions(+), 444 deletions(-) diff --git a/assert/assert_assertions.go b/assert/assert_assertions.go index e87343f96..1b8325f24 100644 --- a/assert/assert_assertions.go +++ b/assert/assert_assertions.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package assert @@ -82,7 +82,7 @@ func DirExists(t T, path string, msgAndArgs ...any) bool { // // # Usage // -// assertions.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) +// assertions.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) // // # Examples // @@ -97,6 +97,27 @@ func ElementsMatch(t T, listA any, listB any, msgAndArgs ...any) (ok bool) { return assertions.ElementsMatch(t, listA, listB, msgAndArgs...) } +// ElementsMatchT asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// # Usage +// +// assertions.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) +// +// # Examples +// +// success: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} +// failure: []int{1, 2, 3}, []int{1, 2, 4} +// +// Upon failure, the test [T] is marked as failed and continues execution. +func ElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.ElementsMatchT(t, listA, listB, msgAndArgs...) +} + // Empty asserts that the given value is "empty". // // Zero values are "empty". @@ -164,11 +185,11 @@ func Equal(t T, expected any, actual any, msgAndArgs ...any) bool { // failure: ErrTest, "wrong error message" // // Upon failure, the test [T] is marked as failed and continues execution. -func EqualError(t T, theError error, errString string, msgAndArgs ...any) bool { +func EqualError(t T, err error, errString string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.EqualError(t, theError, errString, msgAndArgs...) + return assertions.EqualError(t, err, errString, msgAndArgs...) } // EqualExportedValues asserts that the types of two objects are equal and their public @@ -272,11 +293,11 @@ func ErrorAs(t T, err error, target any, msgAndArgs ...any) bool { // failure: ErrTest, "not in message" // // Upon failure, the test [T] is marked as failed and continues execution. -func ErrorContains(t T, theError error, contains string, msgAndArgs ...any) bool { +func ErrorContains(t T, err error, contains string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.ErrorContains(t, theError, contains, msgAndArgs...) + return assertions.ErrorContains(t, err, contains, msgAndArgs...) } // ErrorIs asserts that at least one of the errors in err's chain matches target. @@ -455,6 +476,28 @@ func False(t T, value bool, msgAndArgs ...any) bool { return assertions.False(t, value, msgAndArgs...) } +// FalseT asserts that the specified value is false. +// +// # Usage +// +// type B bool +// var b B = true +// +// assertions.FalseT(t, b) +// +// # Examples +// +// success: 1 == 0 +// failure: 1 == 1 +// +// Upon failure, the test [T] is marked as failed and continues execution. +func FalseT[B Boolean](t T, value B, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.FalseT(t, value, msgAndArgs...) +} + // FileEmpty checks whether a file exists in the given path and is empty. // It fails if the file is not empty, if the path points to a directory or there is an error when trying to check the file. // @@ -517,6 +560,9 @@ func FileNotEmpty(t T, path string, msgAndArgs ...any) bool { // Greater asserts that the first element is strictly greater than the second. // +// Both elements must be of the same type in the [reflect.Kind] sense. +// To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. +// // # Usage // // assertions.Greater(t, 2, 1) @@ -538,6 +584,8 @@ func Greater(t T, e1 any, e2 any, msgAndArgs ...any) bool { // GreaterOrEqual asserts that the first element is greater than or equal to the second. // +// See also [Greater]. +// // # Usage // // assertions.GreaterOrEqual(t, 2, 1) @@ -558,6 +606,71 @@ func GreaterOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) bool { return assertions.GreaterOrEqual(t, e1, e2, msgAndArgs...) } +// GreaterOrEqualT asserts that for two elements of the same type, +// the first element is greater than or equal to the second. +// +// The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), +// []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. +// +// Notice that pointers are not [Ordered], but uintptr are. So you can't call [GreaterOrEqualT] with [*time.Time]. +// +// [GreaterOrEqualT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, +// use [GreaterOrEqual] instead. +// +// To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. +// +// # Usage +// +// assertions.GreaterOrEqualT(t, 2, 1) +// assertions.GreaterOrEqualT(t, 2, 2) +// assertions.GreaterOrEqualT(t, "b", "a") +// assertions.GreaterOrEqualT(t, "b", "b") +// +// # Examples +// +// success: 2, 1 +// failure: 1, 2 +// +// Upon failure, the test [T] is marked as failed and continues execution. +func GreaterOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.GreaterOrEqualT(t, e1, e2, msgAndArgs...) +} + +// GreaterT asserts that for two elements of the same type, +// the first element is strictly greater than the second. +// +// The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), +// []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. +// +// Notice that pointers are not [Ordered], but uintptr are. So you can't call [GreaterT] with [*time.Time]. +// +// [GreaterT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, use [Greater] instead. +// +// To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. +// +// # Usage +// +// assertions.GreaterT(t, 2, 1) +// 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()) +// +// # Examples +// +// success: 2, 1 +// failure: 1, 2 +// +// Upon failure, the test [T] is marked as failed and continues execution. +func GreaterT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.GreaterT(t, e1, e2, msgAndArgs...) +} + // HTTPBodyContains asserts that a specified handler returns a body that contains a string. // // Returns whether the assertion was successful (true) or not (false). @@ -706,6 +819,18 @@ func Implements(t T, interfaceObject any, object any, msgAndArgs ...any) bool { // InDelta asserts that the two numerals are within delta of each other. // +// Delta must be greater than or equal to zero. +// +// Expected and actual values should convert to float64. +// To compare large integers that can't be represented accurately as float64 (eg. uint64), +// prefer [InDeltaT] to preserve the original type. +// +// # Behavior with IEEE floating point arithmetics +// +// - expected NaN is matched only by a NaN, e.g. this works: InDeltaT(math.NaN(), math.Sqrt(-1), 0.0) +// - expected +Inf is matched only by a +Inf +// - expected -Inf is matched only by a -Inf +// // # Usage // // assertions.InDelta(t, math.Pi, 22/7.0, 0.01) @@ -723,7 +848,9 @@ func InDelta(t T, expected any, actual any, delta float64, msgAndArgs ...any) bo return assertions.InDelta(t, expected, actual, delta, msgAndArgs...) } -// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +// InDeltaMapValues is the same as [InDelta], but it compares all values between two maps. Both maps must have exactly the same keys. +// +// See [InDelta]. // // # Usage // @@ -742,7 +869,9 @@ func InDeltaMapValues(t T, expected any, actual any, delta float64, msgAndArgs . return assertions.InDeltaMapValues(t, expected, actual, delta, msgAndArgs...) } -// InDeltaSlice is the same as InDelta, except it compares two slices. +// InDeltaSlice is the same as [InDelta], except it compares two slices. +// +// See [InDelta]. // // # Usage // @@ -761,8 +890,53 @@ func InDeltaSlice(t T, expected any, actual any, delta float64, msgAndArgs ...an return assertions.InDeltaSlice(t, expected, actual, delta, msgAndArgs...) } +// InDeltaT asserts that the two numerals of the same type numerical type are within delta of each other. +// +// [InDeltaT] accepts any go numeric type, including integer types. +// +// The main difference with [InDelta] is that the delta is expressed with the same type as the values, not necessarily a float64. +// +// Delta must be greater than or equal to zero. +// +// # Behavior with IEEE floating point arithmetics +// +// - expected NaN is matched only by a NaN, e.g. this works: InDeltaT(math.NaN(), math.Sqrt(-1), 0.0) +// - expected +Inf is matched only by a +Inf +// - expected -Inf is matched only by a -Inf +// +// # Usage +// +// assertions.InDeltaT(t, math.Pi, 22/7.0, 0.01) +// +// # Examples +// +// success: 1.0, 1.01, 0.02 +// failure: 1.0, 1.1, 0.05 +// +// Upon failure, the test [T] is marked as failed and continues execution. +func InDeltaT[Number Measurable](t T, expected Number, actual Number, delta Number, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.InDeltaT(t, expected, actual, delta, msgAndArgs...) +} + // InEpsilon asserts that expected and actual have a relative error less than epsilon. // +// # Behavior with IEEE floating point arithmetics +// +// - expected NaN is matched only by a NaN, e.g. this works: InDeltaT(math.NaN(), math.Sqrt(-1), 0.0) +// - expected +Inf is matched only by a +Inf +// - expected -Inf is matched only by a -Inf +// +// Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer [InDeltaT]. +// +// Formula: +// - If expected == 0: fail if |actual - expected| > epsilon +// - If expected != 0: fail if |actual - expected| > epsilon * |expected| +// +// This allows [InEpsilonT] to work naturally across the full numeric range including zero. +// // # Usage // // assertions.InEpsilon(t, 100.0, 101.0, 0.02) @@ -780,7 +954,9 @@ func InEpsilon(t T, expected any, actual any, epsilon float64, msgAndArgs ...any return assertions.InEpsilon(t, expected, actual, epsilon, msgAndArgs...) } -// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +// InEpsilonSlice is the same as [InEpsilon], except it compares each value from two slices. +// +// See [InEpsilon]. // // # Usage // @@ -799,6 +975,45 @@ func InEpsilonSlice(t T, expected any, actual any, epsilon float64, msgAndArgs . return assertions.InEpsilonSlice(t, expected, actual, epsilon, msgAndArgs...) } +// InEpsilonT asserts that expected and actual have a relative error less than epsilon. +// +// When expected is zero, epsilon is interpreted as an absolute error threshold, +// since relative error is mathematically undefined for zero values. +// +// Unlike [InDeltaT], which preserves the original type, [InEpsilonT] converts the expected and actual +// numbers to float64, since the relative error doesn't make sense as an integer. +// +// # Behavior with IEEE floating point arithmetics +// +// - expected NaN is matched only by a NaN, e.g. this works: InDeltaT(math.NaN(), math.Sqrt(-1), 0.0) +// - expected +Inf is matched only by a +Inf +// - expected -Inf is matched only by a -Inf +// +// Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer [InDeltaT]. +// +// Formula: +// - If expected == 0: fail if |actual - expected| > epsilon +// - If expected != 0: fail if |actual - expected| > epsilon * |expected| +// +// This allows [InEpsilonT] to work naturally across the full numeric range including zero. +// +// # Usage +// +// assertions.InEpsilon(t, 100.0, 101.0, 0.02) +// +// # Examples +// +// success: 100.0, 101.0, 0.02 +// failure: 100.0, 110.0, 0.05 +// +// Upon failure, the test [T] is marked as failed and continues execution. +func InEpsilonT[Number Measurable](t T, expected Number, actual Number, epsilon float64, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.InEpsilonT(t, expected, actual, epsilon, msgAndArgs...) +} + // IsDecreasing asserts that the collection is decreasing. // // # Usage @@ -923,6 +1138,8 @@ func IsType(t T, expectedType any, object any, msgAndArgs ...any) bool { // JSONEq asserts that two JSON strings are equivalent. // +// Expected and actual must be valid JSON. +// // # Usage // // assertions.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) @@ -940,7 +1157,9 @@ func JSONEq(t T, expected string, actual string, msgAndArgs ...any) bool { return assertions.JSONEq(t, expected, actual, msgAndArgs...) } -// JSONEqBytes asserts that two JSON byte slices are equivalent. +// JSONEqBytes asserts that two JSON slices of bytes are equivalent. +// +// Expected and actual must be valid JSON. // // # Usage // @@ -959,6 +1178,29 @@ func JSONEqBytes(t T, expected []byte, actual []byte, msgAndArgs ...any) bool { return assertions.JSONEqBytes(t, expected, actual, msgAndArgs...) } +// JSONEqT asserts that two JSON documents are equivalent. +// +// The expected and actual arguments may be string or []byte. They do not need to be of the same type. +// +// Expected and actual must be valid JSON. +// +// # Usage +// +// assertions.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) +// +// # Examples +// +// success: `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`) +// failure: `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]` +// +// Upon failure, the test [T] is marked as failed and continues execution. +func JSONEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.JSONEqT(t, expected, actual, 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) @@ -1010,6 +1252,9 @@ func Len(t T, object any, length int, msgAndArgs ...any) bool { // Less asserts that the first element is strictly less than the second. // +// Both elements must be of the same type in the [reflect.Kind] sense. +// To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. +// // # Usage // // assertions.Less(t, 1, 2) @@ -1051,6 +1296,69 @@ func LessOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) bool { return assertions.LessOrEqual(t, e1, e2, msgAndArgs...) } +// LessOrEqualT asserts that for two elements of the same type, the first element is less than or equal to the second. +// +// The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), +// []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. +// +// Notice that pointers are not [Ordered], but uintptr are. So you can't call [LessOrEqualT] with [*time.Time]. +// +// [LessOrEqualT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, +// use [LessOrEqual] instead. +// +// To compare values that need a type conversion (e.g. float32 against float64), you should use [LessOrEqual] instead. +// +// # Usage +// +// assertions.LessOrEqualT(t, 1, 2) +// assertions.LessOrEqualT(t, 2, 2) +// assertions.LessOrEqualT(t, "a", "b") +// assertions.LessOrEqualT(t, "b", "b") +// +// # Examples +// +// success: 1, 2 +// failure: 2, 1 +// +// Upon failure, the test [T] is marked as failed and continues execution. +func LessOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.LessOrEqualT(t, e1, e2, msgAndArgs...) +} + +// LessT asserts that for two elements of the same type, the first element is strictly less than the second. +// +// The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), +// []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. +// +// Notice that pointers are not [Ordered], but uintptr are. So you can't call [LessT] with [*time.Time]. +// +// [LessT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, +// use [Less] instead. +// +// To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. +// +// # Usage +// +// assertions.LessT(t, 1, 2) +// assertions.LessT(t, float64(1), float64(2)) +// assertions.LessT(t, "a", "b") +// +// # Examples +// +// success: 1, 2 +// failure: 2, 1 +// +// Upon failure, the test [T] is marked as failed and continues execution. +func LessT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.LessT(t, e1, e2, msgAndArgs...) +} + // Negative asserts that the specified element is strictly negative. // // # Usage @@ -1071,6 +1379,26 @@ func Negative(t T, e any, msgAndArgs ...any) bool { return assertions.Negative(t, e, msgAndArgs...) } +// NegativeT asserts that the specified element of a signed numeric type is strictly negative. +// +// # Usage +// +// assertions.NegativeT(t, -1) +// assertions.NegativeT(t, -1.23) +// +// # Examples +// +// success: -1 +// failure: 1 +// +// Upon failure, the test [T] is marked as failed and continues execution. +func NegativeT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.NegativeT(t, e, msgAndArgs...) +} + // Never asserts that the given condition is never satisfied within waitFor time, // periodically checking the target function at each tick. // @@ -1213,9 +1541,9 @@ func NotContains(t T, s any, contains any, msgAndArgs ...any) bool { // // # Usage // -// assertions.NotElementsMatch(t, [1, 1, 2, 3], [1, 1, 2, 3]) -> false -// assertions.NotElementsMatch(t, [1, 1, 2, 3], [1, 2, 3]) -> true -// assertions.NotElementsMatch(t, [1, 2, 3], [1, 2, 4]) -> true +// 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 // // # Examples // @@ -1230,6 +1558,30 @@ func NotElementsMatch(t T, listA any, listB any, msgAndArgs ...any) (ok bool) { return assertions.NotElementsMatch(t, listA, listB, msgAndArgs...) } +// NotElementsMatchT asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// # Usage +// +// 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 +// +// # Examples +// +// success: []int{1, 2, 3}, []int{1, 2, 4} +// failure: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} +// +// Upon failure, the test [T] is marked as failed and continues execution. +func NotElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool) { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.NotElementsMatchT(t, listA, listB, msgAndArgs...) +} + // NotEmpty asserts that the specified object is NOT [Empty]. // // # Usage @@ -1412,7 +1764,31 @@ func NotPanics(t T, f assertions.PanicTestFunc, msgAndArgs ...any) bool { return assertions.NotPanics(t, f, msgAndArgs...) } -// NotRegexp asserts that a specified regexp does not match a string. +// NotRegexp asserts that a specified regular expression does not match a string. +// +// See [Regexp]. +// +// # Usage +// +// assertions.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assertions.NotRegexp(t, "^start", "it's not starting") +// +// # Examples +// +// success: "^start", "not starting" +// failure: "^start", "starting" +// +// Upon failure, the test [T] is marked as failed and continues execution. +func NotRegexp(t T, rx any, actual any, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.NotRegexp(t, rx, actual, msgAndArgs...) +} + +// NotRegexpT asserts that a specified regular expression does not match a string. +// +// See [RegexpT]. // // # Usage // @@ -1425,11 +1801,11 @@ func NotPanics(t T, f assertions.PanicTestFunc, msgAndArgs ...any) bool { // failure: "^start", "starting" // // Upon failure, the test [T] is marked as failed and continues execution. -func NotRegexp(t T, rx any, str any, msgAndArgs ...any) bool { +func NotRegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.NotRegexp(t, rx, str, msgAndArgs...) + return assertions.NotRegexpT(t, rx, actual, msgAndArgs...) } // NotSame asserts that two pointers do not reference the same object. @@ -1578,7 +1954,31 @@ func Positive(t T, e any, msgAndArgs ...any) bool { return assertions.Positive(t, e, msgAndArgs...) } -// Regexp asserts that a specified regexp matches a string. +// PositiveT asserts that the specified element of a signed numeric type is strictly positive. +// +// # Usage +// +// assertions.PositiveT(t, 1) +// assertions.PositiveT(t, 1.23) +// +// # Examples +// +// success: 1 +// failure: -1 +// +// Upon failure, the test [T] is marked as failed and continues execution. +func PositiveT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.PositiveT(t, e, msgAndArgs...) +} + +// Regexp asserts that a specified regular expression matches a string. +// +// The regular expression may be passed as a [regexp.Regexp], a string or a []byte and will be compiled. +// +// The actual argument to be matched may be a string, []byte or anything that prints as a string with [fmt.Sprint]. // // # Usage // @@ -1591,11 +1991,30 @@ func Positive(t T, e any, msgAndArgs ...any) bool { // failure: "^start", "not starting" // // Upon failure, the test [T] is marked as failed and continues execution. -func Regexp(t T, rx any, str any, msgAndArgs ...any) bool { +func Regexp(t T, rx any, actual any, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.Regexp(t, rx, actual, msgAndArgs...) +} + +// RegexpT asserts that a specified regular expression matches a string. +// +// The actual argument to be matched may be a string or []byte. +// +// See [Regexp]. +// +// # Examples +// +// success: "^start", "starting" +// failure: "^start", "not starting" +// +// Upon failure, the test [T] is marked as failed and continues execution. +func RegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.Regexp(t, rx, str, msgAndArgs...) + return assertions.RegexpT(t, rx, actual, msgAndArgs...) } // Same asserts that two pointers reference the same object. @@ -1628,10 +2047,10 @@ func Same(t T, expected any, actual any, msgAndArgs ...any) bool { // // # Usage // -// assertions.Subset(t, [1, 2, 3], [1, 2]) -// assertions.Subset(t, {"x": 1, "y": 2}, {"x": 1}) -// assertions.Subset(t, [1, 2, 3], {1: "one", 2: "two"}) -// assertions.Subset(t, {"x": 1, "y": 2}, ["x"]) +// assertions.Subset(t, []int{1, 2, 3}, []int{1, 2}) +// assertions.Subset(t, []string{"x": 1, "y": 2}, []string{"x": 1}) +// assertions.Subset(t, []int{1, 2, 3}, map[int]string{1: "one", 2: "two"}) +// assertions.Subset(t, map[string]int{"x": 1, "y": 2}, []string{"x"}) // // # Examples // @@ -1665,6 +2084,28 @@ func True(t T, value bool, msgAndArgs ...any) bool { return assertions.True(t, value, msgAndArgs...) } +// TrueT asserts that the specified value is true. +// +// # Usage +// +// type B bool +// var b B = true +// +// assertions.True(t, b) +// +// # Examples +// +// success: 1 == 1 +// failure: 1 == 0 +// +// Upon failure, the test [T] is marked as failed and continues execution. +func TrueT[B Boolean](t T, value B, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.TrueT(t, value, msgAndArgs...) +} + // WithinDuration asserts that the two times are within duration delta of each other. // // # Usage @@ -1703,7 +2144,36 @@ func WithinRange(t T, actual time.Time, start time.Time, end time.Time, msgAndAr return assertions.WithinRange(t, actual, start, end, msgAndArgs...) } -// YAMLEq asserts that the first documents in the two YAML strings are equivalent. +// YAMLEq asserts that two YAML strings are equivalent. +// +// See [YAMLEqBytes]. +// +// # 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 YAMLEq(t T, expected string, actual string, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.YAMLEq(t, expected, actual, msgAndArgs...) +} + +// YAMLEqBytes asserts that two YAML slices of bytes are equivalent. +// +// Expected and actual must be valid YAML. +// +// # Important +// +// By default, this function is disabled and will panic. +// +// To enable it, you should add a blank import like so: +// +// import( +// "github.com/go-openapi/testify/enable/yaml/v2" +// ) // // # Usage // @@ -1721,15 +2191,34 @@ func WithinRange(t T, actual time.Time, start time.Time, end time.Time, msgAndAr // // # Examples // +// panic: []byte("key: value"), []byte("key: value") +// should panic without the yaml feature enabled. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func YAMLEqBytes(t T, expected []byte, actual []byte, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.YAMLEqBytes(t, expected, actual, msgAndArgs...) +} + +// YAMLEqT asserts that two YAML documents are equivalent. +// +// The expected and actual arguments may be string or []byte. They do not need to be of the same type. +// +// See [YAMLEqBytes]. +// +// # 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 YAMLEq(t T, expected string, actual string, msgAndArgs ...any) bool { +func YAMLEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.YAMLEq(t, expected, actual, msgAndArgs...) + return assertions.YAMLEqT(t, expected, actual, msgAndArgs...) } // Zero asserts that i is the zero value for its type. diff --git a/assert/assert_assertions_test.go b/assert/assert_assertions_test.go index 8d8377f77..c0a723785 100644 --- a/assert/assert_assertions_test.go +++ b/assert/assert_assertions_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package assert @@ -113,6 +113,30 @@ func TestElementsMatch(t *testing.T) { }) } +func TestElementsMatchT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) + if !result { + t.Error("ElementsMatchT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := ElementsMatchT(mock, []int{1, 2, 3}, []int{1, 2, 4}) + if result { + t.Error("ElementsMatchT should return false on failure") + } + if !mock.failed { + t.Error("ElementsMatchT should mark test as failed") + } + }) +} + func TestEmpty(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -457,6 +481,30 @@ func TestFalse(t *testing.T) { }) } +func TestFalseT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := FalseT(t, 1 == 0) + if !result { + t.Error("FalseT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := FalseT(mock, 1 == 1) + if result { + t.Error("FalseT should return false on failure") + } + if !mock.failed { + t.Error("FalseT should mark test as failed") + } + }) +} + func TestFileEmpty(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -577,6 +625,54 @@ func TestGreaterOrEqual(t *testing.T) { }) } +func TestGreaterOrEqualT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := GreaterOrEqualT(t, 2, 1) + if !result { + t.Error("GreaterOrEqualT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := GreaterOrEqualT(mock, 1, 2) + if result { + t.Error("GreaterOrEqualT should return false on failure") + } + if !mock.failed { + t.Error("GreaterOrEqualT should mark test as failed") + } + }) +} + +func TestGreaterT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := GreaterT(t, 2, 1) + if !result { + t.Error("GreaterT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := GreaterT(mock, 1, 2) + if result { + t.Error("GreaterT should return false on failure") + } + if !mock.failed { + t.Error("GreaterT should mark test as failed") + } + }) +} + func TestHTTPBodyContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -817,6 +913,30 @@ func TestInDeltaSlice(t *testing.T) { }) } +func TestInDeltaT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := InDeltaT(t, 1.0, 1.01, 0.02) + if !result { + t.Error("InDeltaT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := InDeltaT(mock, 1.0, 1.1, 0.05) + if result { + t.Error("InDeltaT should return false on failure") + } + if !mock.failed { + t.Error("InDeltaT should mark test as failed") + } + }) +} + func TestInEpsilon(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -865,6 +985,30 @@ func TestInEpsilonSlice(t *testing.T) { }) } +func TestInEpsilonT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := InEpsilonT(t, 100.0, 101.0, 0.02) + if !result { + t.Error("InEpsilonT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := InEpsilonT(mock, 100.0, 110.0, 0.05) + if result { + t.Error("InEpsilonT should return false on failure") + } + if !mock.failed { + t.Error("InEpsilonT should mark test as failed") + } + }) +} + func TestIsDecreasing(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1057,6 +1201,30 @@ func TestJSONEqBytes(t *testing.T) { }) } +func TestJSONEqT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) + if !result { + t.Error("JSONEqT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := JSONEqT(mock, `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]`) + if result { + t.Error("JSONEqT should return false on failure") + } + if !mock.failed { + t.Error("JSONEqT should mark test as failed") + } + }) +} + func TestKind(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1153,6 +1321,54 @@ func TestLessOrEqual(t *testing.T) { }) } +func TestLessOrEqualT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := LessOrEqualT(t, 1, 2) + if !result { + t.Error("LessOrEqualT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := LessOrEqualT(mock, 2, 1) + if result { + t.Error("LessOrEqualT should return false on failure") + } + if !mock.failed { + t.Error("LessOrEqualT should mark test as failed") + } + }) +} + +func TestLessT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := LessT(t, 1, 2) + if !result { + t.Error("LessT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := LessT(mock, 2, 1) + if result { + t.Error("LessT should return false on failure") + } + if !mock.failed { + t.Error("LessT should mark test as failed") + } + }) +} + func TestNegative(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1177,6 +1393,30 @@ func TestNegative(t *testing.T) { }) } +func TestNegativeT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := NegativeT(t, -1) + if !result { + t.Error("NegativeT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := NegativeT(mock, 1) + if result { + t.Error("NegativeT should return false on failure") + } + if !mock.failed { + t.Error("NegativeT should mark test as failed") + } + }) +} + func TestNever(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1345,6 +1585,30 @@ func TestNotElementsMatch(t *testing.T) { }) } +func TestNotElementsMatchT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4}) + if !result { + t.Error("NotElementsMatchT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := NotElementsMatchT(mock, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) + if result { + t.Error("NotElementsMatchT should return false on failure") + } + if !mock.failed { + t.Error("NotElementsMatchT should mark test as failed") + } + }) +} + func TestNotEmpty(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1585,6 +1849,30 @@ func TestNotRegexp(t *testing.T) { }) } +func TestNotRegexpT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := NotRegexpT(t, "^start", "not starting") + if !result { + t.Error("NotRegexpT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := NotRegexpT(mock, "^start", "starting") + if result { + t.Error("NotRegexpT should return false on failure") + } + if !mock.failed { + t.Error("NotRegexpT should mark test as failed") + } + }) +} + func TestNotSame(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1753,6 +2041,30 @@ func TestPositive(t *testing.T) { }) } +func TestPositiveT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := PositiveT(t, 1) + if !result { + t.Error("PositiveT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := PositiveT(mock, -1) + if result { + t.Error("PositiveT should return false on failure") + } + if !mock.failed { + t.Error("PositiveT should mark test as failed") + } + }) +} + func TestRegexp(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1777,6 +2089,30 @@ func TestRegexp(t *testing.T) { }) } +func TestRegexpT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := RegexpT(t, "^start", "starting") + if !result { + t.Error("RegexpT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := RegexpT(mock, "^start", "not starting") + if result { + t.Error("RegexpT should return false on failure") + } + if !mock.failed { + t.Error("RegexpT should mark test as failed") + } + }) +} + func TestSame(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1849,6 +2185,30 @@ func TestTrue(t *testing.T) { }) } +func TestTrueT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := TrueT(t, 1 == 1) + if !result { + t.Error("TrueT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := TrueT(mock, 1 == 0) + if result { + t.Error("TrueT should return false on failure") + } + if !mock.failed { + t.Error("TrueT should mark test as failed") + } + }) +} + func TestWithinDuration(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1908,6 +2268,28 @@ func TestYAMLEq(t *testing.T) { }) } +func TestYAMLEqBytes(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + Panics(t, func() { + YAMLEqBytes(t, []byte("key: value"), []byte("key: value")) + }, "should panic without the yaml feature enabled.") + }) +} + +func TestYAMLEqT(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + Panics(t, func() { + YAMLEqT(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 da17b371b..203648f5b 100644 --- a/assert/assert_examples_test.go +++ b/assert/assert_examples_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package assert_test @@ -53,6 +53,14 @@ func ExampleElementsMatch() { // Output: success: true } +func ExampleElementsMatchT() { + t := new(testing.T) + success := assert.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleEmpty() { t := new(testing.T) success := assert.Empty(t, "") @@ -169,6 +177,14 @@ func ExampleFalse() { // Output: success: true } +func ExampleFalseT() { + t := new(testing.T) + success := assert.FalseT(t, 1 == 0) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleFileEmpty() { t := new(testing.T) success := assert.FileEmpty(t, filepath.Join(testDataPath(), "empty_file")) @@ -209,6 +225,22 @@ func ExampleGreaterOrEqual() { // Output: success: true } +func ExampleGreaterOrEqualT() { + t := new(testing.T) + success := assert.GreaterOrEqualT(t, 2, 1) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + +func ExampleGreaterT() { + t := new(testing.T) + success := assert.GreaterT(t, 2, 1) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleHTTPBodyContains() { t := new(testing.T) success := assert.HTTPBodyContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!") @@ -289,6 +321,14 @@ func ExampleInDeltaSlice() { // Output: success: true } +func ExampleInDeltaT() { + t := new(testing.T) + success := assert.InDeltaT(t, 1.0, 1.01, 0.02) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleInEpsilon() { t := new(testing.T) success := assert.InEpsilon(t, 100.0, 101.0, 0.02) @@ -305,6 +345,14 @@ func ExampleInEpsilonSlice() { // Output: success: true } +func ExampleInEpsilonT() { + t := new(testing.T) + success := assert.InEpsilonT(t, 100.0, 101.0, 0.02) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleIsDecreasing() { t := new(testing.T) success := assert.IsDecreasing(t, []int{3, 2, 1}) @@ -369,6 +417,14 @@ func ExampleJSONEqBytes() { // Output: success: true } +func ExampleJSONEqT() { + t := new(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 ExampleKind() { t := new(testing.T) success := assert.Kind(t, reflect.String, "hello") @@ -401,6 +457,22 @@ func ExampleLessOrEqual() { // Output: success: true } +func ExampleLessOrEqualT() { + t := new(testing.T) + success := assert.LessOrEqualT(t, 1, 2) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + +func ExampleLessT() { + t := new(testing.T) + success := assert.LessT(t, 1, 2) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleNegative() { t := new(testing.T) success := assert.Negative(t, -1) @@ -409,6 +481,14 @@ func ExampleNegative() { // Output: success: true } +func ExampleNegativeT() { + t := new(testing.T) + success := assert.NegativeT(t, -1) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleNever() { t := new(testing.T) success := assert.Never(t, func() bool { @@ -467,6 +547,14 @@ func ExampleNotElementsMatch() { // Output: success: true } +func ExampleNotElementsMatchT() { + t := new(testing.T) + success := assert.NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4}) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleNotEmpty() { t := new(testing.T) success := assert.NotEmpty(t, "not empty") @@ -548,6 +636,14 @@ func ExampleNotRegexp() { // Output: success: true } +func ExampleNotRegexpT() { + t := new(testing.T) + success := assert.NotRegexpT(t, "^start", "not starting") + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleNotSame() { t := new(testing.T) success := assert.NotSame(t, &staticVar, ptr("static string")) @@ -610,6 +706,14 @@ func ExamplePositive() { // Output: success: true } +func ExamplePositiveT() { + t := new(testing.T) + success := assert.PositiveT(t, 1) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleRegexp() { t := new(testing.T) success := assert.Regexp(t, "^start", "starting") @@ -618,6 +722,14 @@ func ExampleRegexp() { // Output: success: true } +func ExampleRegexpT() { + t := new(testing.T) + success := assert.RegexpT(t, "^start", "starting") + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleSame() { t := new(testing.T) success := assert.Same(t, &staticVar, staticVarPtr) @@ -642,6 +754,14 @@ func ExampleTrue() { // Output: success: true } +func ExampleTrueT() { + t := new(testing.T) + success := assert.TrueT(t, 1 == 1) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleWithinDuration() { t := new(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) @@ -662,6 +782,14 @@ func ExampleWithinRange() { // no success example available. Please add some examples to produce a testable example. // } +// func ExampleYAMLEqBytes() { +// no success example available. Please add some examples to produce a testable example. +// } + +// func ExampleYAMLEqT() { +// no success example available. Please add some examples to produce a testable example. +// } + func ExampleZero() { t := new(testing.T) success := assert.Zero(t, 0) diff --git a/assert/assert_format.go b/assert/assert_format.go index e7a88613e..d5ca7852b 100644 --- a/assert/assert_format.go +++ b/assert/assert_format.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package assert @@ -15,7 +15,7 @@ import ( "github.com/go-openapi/testify/v2/internal/assertions" ) -// Conditionf is the same as [Condition], but accepts a format msg string to format arguments like [fmt.Printf]. +// Conditionf is the same as [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 Conditionf(t T, comp Comparison, msg string, args ...any) bool { @@ -25,7 +25,7 @@ func Conditionf(t T, comp Comparison, msg string, args ...any) bool { return assertions.Condition(t, comp, forwardArgs(msg, args)) } -// Containsf is the same as [Contains], but accepts a format msg string to format arguments like [fmt.Printf]. +// Containsf is the same as [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 Containsf(t T, s any, contains any, msg string, args ...any) bool { @@ -35,7 +35,7 @@ func Containsf(t T, s any, contains any, msg string, args ...any) bool { return assertions.Contains(t, s, contains, forwardArgs(msg, args)) } -// DirExistsf is the same as [DirExists], but accepts a format msg string to format arguments like [fmt.Printf]. +// DirExistsf is the same as [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 DirExistsf(t T, path string, msg string, args ...any) bool { @@ -45,7 +45,7 @@ func DirExistsf(t T, path string, msg string, args ...any) bool { return assertions.DirExists(t, path, forwardArgs(msg, args)) } -// ElementsMatchf is the same as [ElementsMatch], but accepts a format msg string to format arguments like [fmt.Printf]. +// ElementsMatchf is the same as [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 ElementsMatchf(t T, listA any, listB any, msg string, args ...any) (ok bool) { @@ -55,7 +55,17 @@ func ElementsMatchf(t T, listA any, listB any, msg string, args ...any) (ok bool return assertions.ElementsMatch(t, listA, listB, forwardArgs(msg, args)) } -// Emptyf is the same as [Empty], but accepts a format msg string to format arguments like [fmt.Printf]. +// ElementsMatchTf is the same as [ElementsMatchT], 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 ElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.ElementsMatchT(t, listA, listB, forwardArgs(msg, args)) +} + +// Emptyf is the same as [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 Emptyf(t T, object any, msg string, args ...any) bool { @@ -65,7 +75,7 @@ func Emptyf(t T, object any, msg string, args ...any) bool { return assertions.Empty(t, object, forwardArgs(msg, args)) } -// Equalf is the same as [Equal], but accepts a format msg string to format arguments like [fmt.Printf]. +// Equalf is the same as [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 Equalf(t T, expected any, actual any, msg string, args ...any) bool { @@ -75,17 +85,17 @@ func Equalf(t T, expected any, actual any, msg string, args ...any) bool { return assertions.Equal(t, expected, actual, forwardArgs(msg, args)) } -// EqualErrorf is the same as [EqualError], but accepts a format msg string to format arguments like [fmt.Printf]. +// EqualErrorf is the same as [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 EqualErrorf(t T, theError error, errString string, msg string, args ...any) bool { +func EqualErrorf(t T, err error, errString string, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.EqualError(t, theError, errString, forwardArgs(msg, args)) + return assertions.EqualError(t, err, errString, forwardArgs(msg, args)) } -// EqualExportedValuesf is the same as [EqualExportedValues], but accepts a format msg string to format arguments like [fmt.Printf]. +// EqualExportedValuesf is the same as [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 EqualExportedValuesf(t T, expected any, actual any, msg string, args ...any) bool { @@ -95,7 +105,7 @@ func EqualExportedValuesf(t T, expected any, actual any, msg string, args ...any return assertions.EqualExportedValues(t, expected, actual, forwardArgs(msg, args)) } -// EqualValuesf is the same as [EqualValues], but accepts a format msg string to format arguments like [fmt.Printf]. +// EqualValuesf is the same as [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 EqualValuesf(t T, expected any, actual any, msg string, args ...any) bool { @@ -105,7 +115,7 @@ func EqualValuesf(t T, expected any, actual any, msg string, args ...any) bool { return assertions.EqualValues(t, expected, actual, forwardArgs(msg, args)) } -// Errorf is the same as [Error], but accepts a format msg string to format arguments like [fmt.Printf]. +// Errorf is the same as [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 Errorf(t T, err error, msg string, args ...any) bool { @@ -115,7 +125,7 @@ func Errorf(t T, err error, msg string, args ...any) bool { return assertions.Error(t, err, forwardArgs(msg, args)) } -// ErrorAsf is the same as [ErrorAs], but accepts a format msg string to format arguments like [fmt.Printf]. +// ErrorAsf is the same as [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 ErrorAsf(t T, err error, target any, msg string, args ...any) bool { @@ -125,17 +135,17 @@ func ErrorAsf(t T, err error, target any, msg string, args ...any) bool { return assertions.ErrorAs(t, err, target, forwardArgs(msg, args)) } -// ErrorContainsf is the same as [ErrorContains], but accepts a format msg string to format arguments like [fmt.Printf]. +// ErrorContainsf is the same as [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 ErrorContainsf(t T, theError error, contains string, msg string, args ...any) bool { +func ErrorContainsf(t T, err error, contains string, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.ErrorContains(t, theError, contains, forwardArgs(msg, args)) + return assertions.ErrorContains(t, err, contains, forwardArgs(msg, args)) } -// ErrorIsf is the same as [ErrorIs], but accepts a format msg string to format arguments like [fmt.Printf]. +// ErrorIsf is the same as [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 ErrorIsf(t T, err error, target error, msg string, args ...any) bool { @@ -145,7 +155,7 @@ func ErrorIsf(t T, err error, target error, msg string, args ...any) bool { return assertions.ErrorIs(t, err, target, forwardArgs(msg, args)) } -// Eventuallyf is the same as [Eventually], but accepts a format msg string to format arguments like [fmt.Printf]. +// Eventuallyf is the same as [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 Eventuallyf(t T, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...any) bool { @@ -155,7 +165,7 @@ 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 accepts a format msg string to format arguments like [fmt.Printf]. +// EventuallyWithTf is the same as [EventuallyWithT], 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 { @@ -165,7 +175,7 @@ func EventuallyWithTf(t T, condition func(collect *CollectT), waitFor time.Durat return assertions.EventuallyWithT(t, condition, waitFor, tick, forwardArgs(msg, args)) } -// Exactlyf is the same as [Exactly], but accepts a format msg string to format arguments like [fmt.Printf]. +// Exactlyf is the same as [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 Exactlyf(t T, expected any, actual any, msg string, args ...any) bool { @@ -175,7 +185,7 @@ func Exactlyf(t T, expected any, actual any, msg string, args ...any) bool { return assertions.Exactly(t, expected, actual, forwardArgs(msg, args)) } -// Failf is the same as [Fail], but accepts a format msg string to format arguments like [fmt.Printf]. +// Failf is the same as [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 Failf(t T, failureMessage string, msg string, args ...any) bool { @@ -185,7 +195,7 @@ func Failf(t T, failureMessage string, msg string, args ...any) bool { return assertions.Fail(t, failureMessage, forwardArgs(msg, args)) } -// FailNowf is the same as [FailNow], but accepts a format msg string to format arguments like [fmt.Printf]. +// FailNowf is the same as [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 FailNowf(t T, failureMessage string, msg string, args ...any) bool { @@ -195,7 +205,7 @@ func FailNowf(t T, failureMessage string, msg string, args ...any) bool { return assertions.FailNow(t, failureMessage, forwardArgs(msg, args)) } -// Falsef is the same as [False], but accepts a format msg string to format arguments like [fmt.Printf]. +// Falsef is the same as [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 Falsef(t T, value bool, msg string, args ...any) bool { @@ -205,7 +215,17 @@ func Falsef(t T, value bool, msg string, args ...any) bool { return assertions.False(t, value, forwardArgs(msg, args)) } -// FileEmptyf is the same as [FileEmpty], but accepts a format msg string to format arguments like [fmt.Printf]. +// FalseTf is the same as [FalseT], 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 FalseTf[B Boolean](t T, value B, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.FalseT(t, value, forwardArgs(msg, args)) +} + +// FileEmptyf is the same as [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 FileEmptyf(t T, path string, msg string, args ...any) bool { @@ -215,7 +235,7 @@ func FileEmptyf(t T, path string, msg string, args ...any) bool { return assertions.FileEmpty(t, path, forwardArgs(msg, args)) } -// FileExistsf is the same as [FileExists], but accepts a format msg string to format arguments like [fmt.Printf]. +// FileExistsf is the same as [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 FileExistsf(t T, path string, msg string, args ...any) bool { @@ -225,7 +245,7 @@ func FileExistsf(t T, path string, msg string, args ...any) bool { return assertions.FileExists(t, path, forwardArgs(msg, args)) } -// FileNotEmptyf is the same as [FileNotEmpty], but accepts a format msg string to format arguments like [fmt.Printf]. +// FileNotEmptyf is the same as [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 FileNotEmptyf(t T, path string, msg string, args ...any) bool { @@ -235,7 +255,7 @@ func FileNotEmptyf(t T, path string, msg string, args ...any) bool { return assertions.FileNotEmpty(t, path, forwardArgs(msg, args)) } -// Greaterf is the same as [Greater], but accepts a format msg string to format arguments like [fmt.Printf]. +// Greaterf is the same as [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 Greaterf(t T, e1 any, e2 any, msg string, args ...any) bool { @@ -245,7 +265,7 @@ func Greaterf(t T, e1 any, e2 any, msg string, args ...any) bool { return assertions.Greater(t, e1, e2, forwardArgs(msg, args)) } -// GreaterOrEqualf is the same as [GreaterOrEqual], but accepts a format msg string to format arguments like [fmt.Printf]. +// GreaterOrEqualf is the same as [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 GreaterOrEqualf(t T, e1 any, e2 any, msg string, args ...any) bool { @@ -255,7 +275,27 @@ func GreaterOrEqualf(t T, e1 any, e2 any, msg string, args ...any) bool { return assertions.GreaterOrEqual(t, e1, e2, forwardArgs(msg, args)) } -// HTTPBodyContainsf is the same as [HTTPBodyContains], but accepts a format msg string to format arguments like [fmt.Printf]. +// GreaterOrEqualTf is the same as [GreaterOrEqualT], 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 GreaterOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.GreaterOrEqualT(t, e1, e2, forwardArgs(msg, args)) +} + +// GreaterTf is the same as [GreaterT], 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 GreaterTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.GreaterT(t, e1, e2, forwardArgs(msg, args)) +} + +// HTTPBodyContainsf is the same as [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 HTTPBodyContainsf(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ...any) bool { @@ -265,7 +305,7 @@ func HTTPBodyContainsf(t T, handler http.HandlerFunc, method string, url string, return assertions.HTTPBodyContains(t, handler, method, url, values, str, forwardArgs(msg, args)) } -// HTTPBodyNotContainsf is the same as [HTTPBodyNotContains], but accepts a format msg string to format arguments like [fmt.Printf]. +// HTTPBodyNotContainsf is the same as [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 HTTPBodyNotContainsf(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ...any) bool { @@ -275,7 +315,7 @@ func HTTPBodyNotContainsf(t T, handler http.HandlerFunc, method string, url stri return assertions.HTTPBodyNotContains(t, handler, method, url, values, str, forwardArgs(msg, args)) } -// HTTPErrorf is the same as [HTTPError], but accepts a format msg string to format arguments like [fmt.Printf]. +// HTTPErrorf is the same as [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 HTTPErrorf(t T, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool { @@ -285,7 +325,7 @@ func HTTPErrorf(t T, handler http.HandlerFunc, method string, url string, values return assertions.HTTPError(t, handler, method, url, values, forwardArgs(msg, args)) } -// HTTPRedirectf is the same as [HTTPRedirect], but accepts a format msg string to format arguments like [fmt.Printf]. +// HTTPRedirectf is the same as [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 HTTPRedirectf(t T, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool { @@ -295,7 +335,7 @@ func HTTPRedirectf(t T, handler http.HandlerFunc, method string, url string, val return assertions.HTTPRedirect(t, handler, method, url, values, forwardArgs(msg, args)) } -// HTTPStatusCodef is the same as [HTTPStatusCode], but accepts a format msg string to format arguments like [fmt.Printf]. +// HTTPStatusCodef is the same as [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 HTTPStatusCodef(t T, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...any) bool { @@ -305,7 +345,7 @@ func HTTPStatusCodef(t T, handler http.HandlerFunc, method string, url string, v return assertions.HTTPStatusCode(t, handler, method, url, values, statuscode, forwardArgs(msg, args)) } -// HTTPSuccessf is the same as [HTTPSuccess], but accepts a format msg string to format arguments like [fmt.Printf]. +// HTTPSuccessf is the same as [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 HTTPSuccessf(t T, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool { @@ -315,7 +355,7 @@ func HTTPSuccessf(t T, handler http.HandlerFunc, method string, url string, valu return assertions.HTTPSuccess(t, handler, method, url, values, forwardArgs(msg, args)) } -// Implementsf is the same as [Implements], but accepts a format msg string to format arguments like [fmt.Printf]. +// Implementsf is the same as [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 Implementsf(t T, interfaceObject any, object any, msg string, args ...any) bool { @@ -325,7 +365,7 @@ func Implementsf(t T, interfaceObject any, object any, msg string, args ...any) return assertions.Implements(t, interfaceObject, object, forwardArgs(msg, args)) } -// InDeltaf is the same as [InDelta], but accepts a format msg string to format arguments like [fmt.Printf]. +// InDeltaf is the same as [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 InDeltaf(t T, expected any, actual any, delta float64, msg string, args ...any) bool { @@ -335,7 +375,7 @@ func InDeltaf(t T, expected any, actual any, delta float64, msg string, args ... return assertions.InDelta(t, expected, actual, delta, forwardArgs(msg, args)) } -// InDeltaMapValuesf is the same as [InDeltaMapValues], but accepts a format msg string to format arguments like [fmt.Printf]. +// InDeltaMapValuesf is the same as [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 InDeltaMapValuesf(t T, expected any, actual any, delta float64, msg string, args ...any) bool { @@ -345,7 +385,7 @@ func InDeltaMapValuesf(t T, expected any, actual any, delta float64, msg string, return assertions.InDeltaMapValues(t, expected, actual, delta, forwardArgs(msg, args)) } -// InDeltaSlicef is the same as [InDeltaSlice], but accepts a format msg string to format arguments like [fmt.Printf]. +// InDeltaSlicef is the same as [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 InDeltaSlicef(t T, expected any, actual any, delta float64, msg string, args ...any) bool { @@ -355,7 +395,17 @@ func InDeltaSlicef(t T, expected any, actual any, delta float64, msg string, arg return assertions.InDeltaSlice(t, expected, actual, delta, forwardArgs(msg, args)) } -// InEpsilonf is the same as [InEpsilon], but accepts a format msg string to format arguments like [fmt.Printf]. +// InDeltaTf is the same as [InDeltaT], 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 InDeltaTf[Number Measurable](t T, expected Number, actual Number, delta Number, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.InDeltaT(t, expected, actual, delta, forwardArgs(msg, args)) +} + +// InEpsilonf is the same as [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 InEpsilonf(t T, expected any, actual any, epsilon float64, msg string, args ...any) bool { @@ -365,7 +415,7 @@ func InEpsilonf(t T, expected any, actual any, epsilon float64, msg string, args return assertions.InEpsilon(t, expected, actual, epsilon, forwardArgs(msg, args)) } -// InEpsilonSlicef is the same as [InEpsilonSlice], but accepts a format msg string to format arguments like [fmt.Printf]. +// InEpsilonSlicef is the same as [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 InEpsilonSlicef(t T, expected any, actual any, epsilon float64, msg string, args ...any) bool { @@ -375,7 +425,17 @@ func InEpsilonSlicef(t T, expected any, actual any, epsilon float64, msg string, return assertions.InEpsilonSlice(t, expected, actual, epsilon, forwardArgs(msg, args)) } -// IsDecreasingf is the same as [IsDecreasing], but accepts a format msg string to format arguments like [fmt.Printf]. +// InEpsilonTf is the same as [InEpsilonT], 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 InEpsilonTf[Number Measurable](t T, expected Number, actual Number, epsilon float64, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.InEpsilonT(t, expected, actual, epsilon, forwardArgs(msg, args)) +} + +// IsDecreasingf is the same as [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 IsDecreasingf(t T, object any, msg string, args ...any) bool { @@ -385,7 +445,7 @@ func IsDecreasingf(t T, object any, msg string, args ...any) bool { return assertions.IsDecreasing(t, object, forwardArgs(msg, args)) } -// IsIncreasingf is the same as [IsIncreasing], but accepts a format msg string to format arguments like [fmt.Printf]. +// IsIncreasingf is the same as [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 IsIncreasingf(t T, object any, msg string, args ...any) bool { @@ -395,7 +455,7 @@ func IsIncreasingf(t T, object any, msg string, args ...any) bool { return assertions.IsIncreasing(t, object, forwardArgs(msg, args)) } -// IsNonDecreasingf is the same as [IsNonDecreasing], but accepts a format msg string to format arguments like [fmt.Printf]. +// IsNonDecreasingf is the same as [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 IsNonDecreasingf(t T, object any, msg string, args ...any) bool { @@ -405,7 +465,7 @@ func IsNonDecreasingf(t T, object any, msg string, args ...any) bool { return assertions.IsNonDecreasing(t, object, forwardArgs(msg, args)) } -// IsNonIncreasingf is the same as [IsNonIncreasing], but accepts a format msg string to format arguments like [fmt.Printf]. +// IsNonIncreasingf is the same as [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 IsNonIncreasingf(t T, object any, msg string, args ...any) bool { @@ -415,7 +475,7 @@ func IsNonIncreasingf(t T, object any, msg string, args ...any) bool { return assertions.IsNonIncreasing(t, object, forwardArgs(msg, args)) } -// IsNotTypef is the same as [IsNotType], but accepts a format msg string to format arguments like [fmt.Printf]. +// IsNotTypef is the same as [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 IsNotTypef(t T, theType any, object any, msg string, args ...any) bool { @@ -425,7 +485,7 @@ func IsNotTypef(t T, theType any, object any, msg string, args ...any) bool { return assertions.IsNotType(t, theType, object, forwardArgs(msg, args)) } -// IsTypef is the same as [IsType], but accepts a format msg string to format arguments like [fmt.Printf]. +// IsTypef is the same as [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 IsTypef(t T, expectedType any, object any, msg string, args ...any) bool { @@ -435,7 +495,7 @@ func IsTypef(t T, expectedType any, object any, msg string, args ...any) bool { return assertions.IsType(t, expectedType, object, forwardArgs(msg, args)) } -// JSONEqf is the same as [JSONEq], but accepts a format msg string to format arguments like [fmt.Printf]. +// JSONEqf is the same as [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 JSONEqf(t T, expected string, actual string, msg string, args ...any) bool { @@ -445,7 +505,7 @@ func JSONEqf(t T, expected string, actual string, msg string, args ...any) bool return assertions.JSONEq(t, expected, actual, forwardArgs(msg, args)) } -// JSONEqBytesf is the same as [JSONEqBytes], but accepts a format msg string to format arguments like [fmt.Printf]. +// JSONEqBytesf is the same as [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 JSONEqBytesf(t T, expected []byte, actual []byte, msg string, args ...any) bool { @@ -455,7 +515,17 @@ func JSONEqBytesf(t T, expected []byte, actual []byte, msg string, args ...any) return assertions.JSONEqBytes(t, expected, actual, forwardArgs(msg, args)) } -// Kindf is the same as [Kind], but accepts a format msg string to format arguments like [fmt.Printf]. +// JSONEqTf is the same as [JSONEqT], 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 JSONEqTf[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.JSONEqT(t, expected, actual, 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. func Kindf(t T, expectedKind reflect.Kind, object any, msg string, args ...any) bool { @@ -465,7 +535,7 @@ func Kindf(t T, expectedKind reflect.Kind, object any, msg string, args ...any) return assertions.Kind(t, expectedKind, object, forwardArgs(msg, args)) } -// Lenf is the same as [Len], but accepts a format msg string to format arguments like [fmt.Printf]. +// Lenf is the same as [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 Lenf(t T, object any, length int, msg string, args ...any) bool { @@ -475,7 +545,7 @@ func Lenf(t T, object any, length int, msg string, args ...any) bool { return assertions.Len(t, object, length, forwardArgs(msg, args)) } -// Lessf is the same as [Less], but accepts a format msg string to format arguments like [fmt.Printf]. +// Lessf is the same as [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 Lessf(t T, e1 any, e2 any, msg string, args ...any) bool { @@ -485,7 +555,7 @@ func Lessf(t T, e1 any, e2 any, msg string, args ...any) bool { return assertions.Less(t, e1, e2, forwardArgs(msg, args)) } -// LessOrEqualf is the same as [LessOrEqual], but accepts a format msg string to format arguments like [fmt.Printf]. +// LessOrEqualf is the same as [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 LessOrEqualf(t T, e1 any, e2 any, msg string, args ...any) bool { @@ -495,7 +565,27 @@ func LessOrEqualf(t T, e1 any, e2 any, msg string, args ...any) bool { return assertions.LessOrEqual(t, e1, e2, forwardArgs(msg, args)) } -// Negativef is the same as [Negative], but accepts a format msg string to format arguments like [fmt.Printf]. +// LessOrEqualTf is the same as [LessOrEqualT], 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 LessOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.LessOrEqualT(t, e1, e2, forwardArgs(msg, args)) +} + +// LessTf is the same as [LessT], 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 LessTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.LessT(t, e1, e2, forwardArgs(msg, args)) +} + +// Negativef is the same as [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 Negativef(t T, e any, msg string, args ...any) bool { @@ -505,7 +595,17 @@ func Negativef(t T, e any, msg string, args ...any) bool { return assertions.Negative(t, e, forwardArgs(msg, args)) } -// Neverf is the same as [Never], but accepts a format msg string to format arguments like [fmt.Printf]. +// NegativeTf is the same as [NegativeT], 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 NegativeTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.NegativeT(t, e, forwardArgs(msg, args)) +} + +// Neverf is the same as [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 Neverf(t T, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...any) bool { @@ -515,7 +615,7 @@ func Neverf(t T, condition func() bool, waitFor time.Duration, tick time.Duratio return assertions.Never(t, condition, waitFor, tick, forwardArgs(msg, args)) } -// Nilf is the same as [Nil], but accepts a format msg string to format arguments like [fmt.Printf]. +// Nilf is the same as [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 Nilf(t T, object any, msg string, args ...any) bool { @@ -525,7 +625,7 @@ func Nilf(t T, object any, msg string, args ...any) bool { return assertions.Nil(t, object, forwardArgs(msg, args)) } -// NoDirExistsf is the same as [NoDirExists], but accepts a format msg string to format arguments like [fmt.Printf]. +// NoDirExistsf is the same as [NoDirExists], 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 NoDirExistsf(t T, path string, msg string, args ...any) bool { @@ -535,7 +635,7 @@ func NoDirExistsf(t T, path string, msg string, args ...any) bool { return assertions.NoDirExists(t, path, forwardArgs(msg, args)) } -// NoErrorf is the same as [NoError], but accepts a format msg string to format arguments like [fmt.Printf]. +// NoErrorf is the same as [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 NoErrorf(t T, err error, msg string, args ...any) bool { @@ -545,7 +645,7 @@ func NoErrorf(t T, err error, msg string, args ...any) bool { return assertions.NoError(t, err, forwardArgs(msg, args)) } -// NoFileExistsf is the same as [NoFileExists], but accepts a format msg string to format arguments like [fmt.Printf]. +// NoFileExistsf is the same as [NoFileExists], 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 NoFileExistsf(t T, path string, msg string, args ...any) bool { @@ -555,7 +655,7 @@ func NoFileExistsf(t T, path string, msg string, args ...any) bool { return assertions.NoFileExists(t, path, forwardArgs(msg, args)) } -// NotContainsf is the same as [NotContains], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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. func NotContainsf(t T, s any, contains any, msg string, args ...any) bool { @@ -565,7 +665,7 @@ func NotContainsf(t T, s any, contains any, msg string, args ...any) bool { return assertions.NotContains(t, s, contains, forwardArgs(msg, args)) } -// NotElementsMatchf is the same as [NotElementsMatch], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotElementsMatchf is the same as [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 NotElementsMatchf(t T, listA any, listB any, msg string, args ...any) (ok bool) { @@ -575,7 +675,17 @@ func NotElementsMatchf(t T, listA any, listB any, msg string, args ...any) (ok b return assertions.NotElementsMatch(t, listA, listB, forwardArgs(msg, args)) } -// NotEmptyf is the same as [NotEmpty], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotElementsMatchTf is the same as [NotElementsMatchT], 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 NotElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool) { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.NotElementsMatchT(t, listA, listB, forwardArgs(msg, args)) +} + +// NotEmptyf is the same as [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 NotEmptyf(t T, object any, msg string, args ...any) bool { @@ -585,7 +695,7 @@ func NotEmptyf(t T, object any, msg string, args ...any) bool { return assertions.NotEmpty(t, object, forwardArgs(msg, args)) } -// NotEqualf is the same as [NotEqual], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotEqualf is the same as [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 NotEqualf(t T, expected any, actual any, msg string, args ...any) bool { @@ -595,7 +705,7 @@ func NotEqualf(t T, expected any, actual any, msg string, args ...any) bool { return assertions.NotEqual(t, expected, actual, forwardArgs(msg, args)) } -// NotEqualValuesf is the same as [NotEqualValues], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotEqualValuesf is the same as [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 NotEqualValuesf(t T, expected any, actual any, msg string, args ...any) bool { @@ -605,7 +715,7 @@ func NotEqualValuesf(t T, expected any, actual any, msg string, args ...any) boo return assertions.NotEqualValues(t, expected, actual, forwardArgs(msg, args)) } -// NotErrorAsf is the same as [NotErrorAs], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotErrorAsf is the same as [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 NotErrorAsf(t T, err error, target any, msg string, args ...any) bool { @@ -615,7 +725,7 @@ func NotErrorAsf(t T, err error, target any, msg string, args ...any) bool { return assertions.NotErrorAs(t, err, target, forwardArgs(msg, args)) } -// NotErrorIsf is the same as [NotErrorIs], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotErrorIsf is the same as [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 NotErrorIsf(t T, err error, target error, msg string, args ...any) bool { @@ -625,7 +735,7 @@ func NotErrorIsf(t T, err error, target error, msg string, args ...any) bool { return assertions.NotErrorIs(t, err, target, forwardArgs(msg, args)) } -// NotImplementsf is the same as [NotImplements], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotImplementsf is the same as [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 NotImplementsf(t T, interfaceObject any, object any, msg string, args ...any) bool { @@ -635,7 +745,7 @@ func NotImplementsf(t T, interfaceObject any, object any, msg string, args ...an return assertions.NotImplements(t, interfaceObject, object, forwardArgs(msg, args)) } -// NotKindf is the same as [NotKind], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotKindf is the same as [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 NotKindf(t T, expectedKind reflect.Kind, object any, msg string, args ...any) bool { @@ -645,7 +755,7 @@ func NotKindf(t T, expectedKind reflect.Kind, object any, msg string, args ...an return assertions.NotKind(t, expectedKind, object, forwardArgs(msg, args)) } -// NotNilf is the same as [NotNil], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotNilf is the same as [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 NotNilf(t T, object any, msg string, args ...any) bool { @@ -655,7 +765,7 @@ func NotNilf(t T, object any, msg string, args ...any) bool { return assertions.NotNil(t, object, forwardArgs(msg, args)) } -// NotPanicsf is the same as [NotPanics], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotPanicsf is the same as [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 NotPanicsf(t T, f assertions.PanicTestFunc, msg string, args ...any) bool { @@ -665,17 +775,27 @@ func NotPanicsf(t T, f assertions.PanicTestFunc, msg string, args ...any) bool { return assertions.NotPanics(t, f, forwardArgs(msg, args)) } -// NotRegexpf is the same as [NotRegexp], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotRegexpf is the same as [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 NotRegexpf(t T, rx any, str any, msg string, args ...any) bool { +func NotRegexpf(t T, rx any, actual any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.NotRegexp(t, rx, str, forwardArgs(msg, args)) + return assertions.NotRegexp(t, rx, actual, forwardArgs(msg, args)) } -// NotSamef is the same as [NotSame], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotRegexpTf is the same as [NotRegexpT], 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 NotRegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.NotRegexpT(t, rx, actual, forwardArgs(msg, args)) +} + +// NotSamef is the same as [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 NotSamef(t T, expected any, actual any, msg string, args ...any) bool { @@ -685,7 +805,7 @@ func NotSamef(t T, expected any, actual any, msg string, args ...any) bool { return assertions.NotSame(t, expected, actual, forwardArgs(msg, args)) } -// NotSubsetf is the same as [NotSubset], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotSubsetf is the same as [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 NotSubsetf(t T, list any, subset any, msg string, args ...any) (ok bool) { @@ -695,7 +815,7 @@ func NotSubsetf(t T, list any, subset any, msg string, args ...any) (ok bool) { return assertions.NotSubset(t, list, subset, forwardArgs(msg, args)) } -// NotZerof is the same as [NotZero], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotZerof is the same as [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 NotZerof(t T, i any, msg string, args ...any) bool { @@ -705,7 +825,7 @@ func NotZerof(t T, i any, msg string, args ...any) bool { return assertions.NotZero(t, i, forwardArgs(msg, args)) } -// Panicsf is the same as [Panics], but accepts a format msg string to format arguments like [fmt.Printf]. +// Panicsf is the same as [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 Panicsf(t T, f assertions.PanicTestFunc, msg string, args ...any) bool { @@ -715,7 +835,7 @@ func Panicsf(t T, f assertions.PanicTestFunc, msg string, args ...any) bool { return assertions.Panics(t, f, forwardArgs(msg, args)) } -// PanicsWithErrorf is the same as [PanicsWithError], but accepts a format msg string to format arguments like [fmt.Printf]. +// PanicsWithErrorf is the same as [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 PanicsWithErrorf(t T, errString string, f assertions.PanicTestFunc, msg string, args ...any) bool { @@ -725,7 +845,7 @@ func PanicsWithErrorf(t T, errString string, f assertions.PanicTestFunc, msg str return assertions.PanicsWithError(t, errString, f, forwardArgs(msg, args)) } -// PanicsWithValuef is the same as [PanicsWithValue], but accepts a format msg string to format arguments like [fmt.Printf]. +// PanicsWithValuef is the same as [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 PanicsWithValuef(t T, expected any, f assertions.PanicTestFunc, msg string, args ...any) bool { @@ -735,7 +855,7 @@ func PanicsWithValuef(t T, expected any, f assertions.PanicTestFunc, msg string, return assertions.PanicsWithValue(t, expected, f, forwardArgs(msg, args)) } -// Positivef is the same as [Positive], but accepts a format msg string to format arguments like [fmt.Printf]. +// Positivef is the same as [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 Positivef(t T, e any, msg string, args ...any) bool { @@ -745,17 +865,37 @@ func Positivef(t T, e any, msg string, args ...any) bool { return assertions.Positive(t, e, forwardArgs(msg, args)) } -// Regexpf is the same as [Regexp], but accepts a format msg string to format arguments like [fmt.Printf]. +// PositiveTf is the same as [PositiveT], 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 Regexpf(t T, rx any, str any, msg string, args ...any) bool { +func PositiveTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.Regexp(t, rx, str, forwardArgs(msg, args)) + return assertions.PositiveT(t, e, forwardArgs(msg, args)) } -// Samef is the same as [Same], but accepts a format msg string to format arguments like [fmt.Printf]. +// Regexpf is the same as [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 Regexpf(t T, rx any, actual any, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.Regexp(t, rx, actual, forwardArgs(msg, args)) +} + +// RegexpTf is the same as [RegexpT], 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 RegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.RegexpT(t, rx, actual, forwardArgs(msg, args)) +} + +// Samef is the same as [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 Samef(t T, expected any, actual any, msg string, args ...any) bool { @@ -765,7 +905,7 @@ func Samef(t T, expected any, actual any, msg string, args ...any) bool { return assertions.Same(t, expected, actual, forwardArgs(msg, args)) } -// Subsetf is the same as [Subset], but accepts a format msg string to format arguments like [fmt.Printf]. +// Subsetf is the same as [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 Subsetf(t T, list any, subset any, msg string, args ...any) (ok bool) { @@ -775,7 +915,7 @@ func Subsetf(t T, list any, subset any, msg string, args ...any) (ok bool) { return assertions.Subset(t, list, subset, forwardArgs(msg, args)) } -// Truef is the same as [True], but accepts a format msg string to format arguments like [fmt.Printf]. +// Truef is the same as [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 Truef(t T, value bool, msg string, args ...any) bool { @@ -785,7 +925,17 @@ func Truef(t T, value bool, msg string, args ...any) bool { return assertions.True(t, value, forwardArgs(msg, args)) } -// WithinDurationf is the same as [WithinDuration], but accepts a format msg string to format arguments like [fmt.Printf]. +// TrueTf is the same as [TrueT], 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 TrueTf[B Boolean](t T, value B, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.TrueT(t, value, forwardArgs(msg, args)) +} + +// WithinDurationf is the same as [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 WithinDurationf(t T, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...any) bool { @@ -795,7 +945,7 @@ func WithinDurationf(t T, expected time.Time, actual time.Time, delta time.Durat return assertions.WithinDuration(t, expected, actual, delta, forwardArgs(msg, args)) } -// WithinRangef is the same as [WithinRange], but accepts a format msg string to format arguments like [fmt.Printf]. +// WithinRangef is the same as [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 WithinRangef(t T, actual time.Time, start time.Time, end time.Time, msg string, args ...any) bool { @@ -805,7 +955,7 @@ func WithinRangef(t T, actual time.Time, start time.Time, end time.Time, msg str return assertions.WithinRange(t, actual, start, end, forwardArgs(msg, args)) } -// YAMLEqf is the same as [YAMLEq], but accepts a format msg string to format arguments like [fmt.Printf]. +// YAMLEqf is the same as [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 YAMLEqf(t T, expected string, actual string, msg string, args ...any) bool { @@ -815,7 +965,27 @@ func YAMLEqf(t T, expected string, actual string, msg string, args ...any) bool return assertions.YAMLEq(t, expected, actual, forwardArgs(msg, args)) } -// Zerof is the same as [Zero], but accepts a format msg string to format arguments like [fmt.Printf]. +// YAMLEqBytesf is the same as [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 YAMLEqBytesf(t T, expected []byte, actual []byte, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.YAMLEqBytes(t, expected, actual, forwardArgs(msg, args)) +} + +// YAMLEqTf is the same as [YAMLEqT], 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 YAMLEqTf[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.YAMLEqT(t, expected, actual, 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. func Zerof(t T, i any, msg string, args ...any) bool { diff --git a/assert/assert_format_test.go b/assert/assert_format_test.go index c682dd4e7..c9fdfcb89 100644 --- a/assert/assert_format_test.go +++ b/assert/assert_format_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package assert @@ -113,6 +113,30 @@ func TestElementsMatchf(t *testing.T) { }) } +func TestElementsMatchTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := ElementsMatchTf(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}, "test message") + if !result { + t.Error("ElementsMatchTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := ElementsMatchTf(mock, []int{1, 2, 3}, []int{1, 2, 4}, "test message") + if result { + t.Error("ElementsMatchTf should return false on failure") + } + if !mock.failed { + t.Error("ElementsMatchT should mark test as failed") + } + }) +} + func TestEmptyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -457,6 +481,30 @@ func TestFalsef(t *testing.T) { }) } +func TestFalseTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := FalseTf(t, 1 == 0, "test message") + if !result { + t.Error("FalseTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := FalseTf(mock, 1 == 1, "test message") + if result { + t.Error("FalseTf should return false on failure") + } + if !mock.failed { + t.Error("FalseT should mark test as failed") + } + }) +} + func TestFileEmptyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -577,6 +625,54 @@ func TestGreaterOrEqualf(t *testing.T) { }) } +func TestGreaterOrEqualTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := GreaterOrEqualTf(t, 2, 1, "test message") + if !result { + t.Error("GreaterOrEqualTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := GreaterOrEqualTf(mock, 1, 2, "test message") + if result { + t.Error("GreaterOrEqualTf should return false on failure") + } + if !mock.failed { + t.Error("GreaterOrEqualT should mark test as failed") + } + }) +} + +func TestGreaterTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := GreaterTf(t, 2, 1, "test message") + if !result { + t.Error("GreaterTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := GreaterTf(mock, 1, 2, "test message") + if result { + t.Error("GreaterTf should return false on failure") + } + if !mock.failed { + t.Error("GreaterT should mark test as failed") + } + }) +} + func TestHTTPBodyContainsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -817,6 +913,30 @@ func TestInDeltaSlicef(t *testing.T) { }) } +func TestInDeltaTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := InDeltaTf(t, 1.0, 1.01, 0.02, "test message") + if !result { + t.Error("InDeltaTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := InDeltaTf(mock, 1.0, 1.1, 0.05, "test message") + if result { + t.Error("InDeltaTf should return false on failure") + } + if !mock.failed { + t.Error("InDeltaT should mark test as failed") + } + }) +} + func TestInEpsilonf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -865,6 +985,30 @@ func TestInEpsilonSlicef(t *testing.T) { }) } +func TestInEpsilonTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := InEpsilonTf(t, 100.0, 101.0, 0.02, "test message") + if !result { + t.Error("InEpsilonTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := InEpsilonTf(mock, 100.0, 110.0, 0.05, "test message") + if result { + t.Error("InEpsilonTf should return false on failure") + } + if !mock.failed { + t.Error("InEpsilonT should mark test as failed") + } + }) +} + func TestIsDecreasingf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1057,6 +1201,30 @@ func TestJSONEqBytesf(t *testing.T) { }) } +func TestJSONEqTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := JSONEqTf(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`), "test message") + if !result { + t.Error("JSONEqTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := JSONEqTf(mock, `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]`, "test message") + if result { + t.Error("JSONEqTf should return false on failure") + } + if !mock.failed { + t.Error("JSONEqT should mark test as failed") + } + }) +} + func TestKindf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1153,6 +1321,54 @@ func TestLessOrEqualf(t *testing.T) { }) } +func TestLessOrEqualTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := LessOrEqualTf(t, 1, 2, "test message") + if !result { + t.Error("LessOrEqualTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := LessOrEqualTf(mock, 2, 1, "test message") + if result { + t.Error("LessOrEqualTf should return false on failure") + } + if !mock.failed { + t.Error("LessOrEqualT should mark test as failed") + } + }) +} + +func TestLessTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := LessTf(t, 1, 2, "test message") + if !result { + t.Error("LessTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := LessTf(mock, 2, 1, "test message") + if result { + t.Error("LessTf should return false on failure") + } + if !mock.failed { + t.Error("LessT should mark test as failed") + } + }) +} + func TestNegativef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1177,6 +1393,30 @@ func TestNegativef(t *testing.T) { }) } +func TestNegativeTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := NegativeTf(t, -1, "test message") + if !result { + t.Error("NegativeTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := NegativeTf(mock, 1, "test message") + if result { + t.Error("NegativeTf should return false on failure") + } + if !mock.failed { + t.Error("NegativeT should mark test as failed") + } + }) +} + func TestNeverf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1345,6 +1585,30 @@ func TestNotElementsMatchf(t *testing.T) { }) } +func TestNotElementsMatchTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := NotElementsMatchTf(t, []int{1, 2, 3}, []int{1, 2, 4}, "test message") + if !result { + t.Error("NotElementsMatchTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := NotElementsMatchTf(mock, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}, "test message") + if result { + t.Error("NotElementsMatchTf should return false on failure") + } + if !mock.failed { + t.Error("NotElementsMatchT should mark test as failed") + } + }) +} + func TestNotEmptyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1585,6 +1849,30 @@ func TestNotRegexpf(t *testing.T) { }) } +func TestNotRegexpTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := NotRegexpTf(t, "^start", "not starting", "test message") + if !result { + t.Error("NotRegexpTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := NotRegexpTf(mock, "^start", "starting", "test message") + if result { + t.Error("NotRegexpTf should return false on failure") + } + if !mock.failed { + t.Error("NotRegexpT should mark test as failed") + } + }) +} + func TestNotSamef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1753,6 +2041,30 @@ func TestPositivef(t *testing.T) { }) } +func TestPositiveTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := PositiveTf(t, 1, "test message") + if !result { + t.Error("PositiveTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := PositiveTf(mock, -1, "test message") + if result { + t.Error("PositiveTf should return false on failure") + } + if !mock.failed { + t.Error("PositiveT should mark test as failed") + } + }) +} + func TestRegexpf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1777,6 +2089,30 @@ func TestRegexpf(t *testing.T) { }) } +func TestRegexpTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := RegexpTf(t, "^start", "starting", "test message") + if !result { + t.Error("RegexpTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := RegexpTf(mock, "^start", "not starting", "test message") + if result { + t.Error("RegexpTf should return false on failure") + } + if !mock.failed { + t.Error("RegexpT should mark test as failed") + } + }) +} + func TestSamef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1849,6 +2185,30 @@ func TestTruef(t *testing.T) { }) } +func TestTrueTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := TrueTf(t, 1 == 1, "test message") + if !result { + t.Error("TrueTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := TrueTf(mock, 1 == 0, "test message") + if result { + t.Error("TrueTf should return false on failure") + } + if !mock.failed { + t.Error("TrueT should mark test as failed") + } + }) +} + func TestWithinDurationf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1908,6 +2268,28 @@ func TestYAMLEqf(t *testing.T) { }) } +func TestYAMLEqBytesf(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + Panicsf(t, func() { + YAMLEqBytesf(t, []byte("key: value"), []byte("key: value"), "test message") + }, "should panic without the yaml feature enabled.") + }) +} + +func TestYAMLEqTf(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + Panicsf(t, func() { + YAMLEqTf(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 2f139fe11..5a5edeed9 100644 --- a/assert/assert_forward.go +++ b/assert/assert_forward.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package assert @@ -41,7 +41,7 @@ func (a *Assertions) Condition(comp Comparison, msgAndArgs ...any) bool { return assertions.Condition(a.t, comp, msgAndArgs...) } -// Conditionf is the same as [Assertions.Condition], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -61,7 +61,7 @@ func (a *Assertions) Contains(s any, contains any, msgAndArgs ...any) bool { return assertions.Contains(a.t, s, contains, msgAndArgs...) } -// Containsf is the same as [Assertions.Contains], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -81,7 +81,7 @@ func (a *Assertions) DirExists(path string, msgAndArgs ...any) bool { return assertions.DirExists(a.t, path, msgAndArgs...) } -// DirExistsf is the same as [Assertions.DirExists], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -101,7 +101,7 @@ func (a *Assertions) ElementsMatch(listA any, listB any, msgAndArgs ...any) (ok return assertions.ElementsMatch(a.t, listA, listB, msgAndArgs...) } -// ElementsMatchf is the same as [Assertions.ElementsMatch], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -121,7 +121,7 @@ func (a *Assertions) Empty(object any, msgAndArgs ...any) bool { return assertions.Empty(a.t, object, msgAndArgs...) } -// Emptyf is the same as [Assertions.Empty], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -141,7 +141,7 @@ func (a *Assertions) Equal(expected any, actual any, msgAndArgs ...any) bool { return assertions.Equal(a.t, expected, actual, msgAndArgs...) } -// Equalf is the same as [Assertions.Equal], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -154,21 +154,21 @@ func (a *Assertions) Equalf(expected any, actual any, msg string, args ...any) b // 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(theError error, errString string, msgAndArgs ...any) bool { +func (a *Assertions) EqualError(err error, errString string, msgAndArgs ...any) bool { if h, ok := a.t.(H); ok { h.Helper() } - return assertions.EqualError(a.t, theError, errString, msgAndArgs...) + return assertions.EqualError(a.t, err, errString, msgAndArgs...) } -// EqualErrorf is the same as [Assertions.EqualError], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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(theError error, errString string, msg string, args ...any) bool { +func (a *Assertions) EqualErrorf(err error, errString string, msg string, args ...any) bool { if h, ok := a.t.(H); ok { h.Helper() } - return assertions.EqualError(a.t, theError, 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. @@ -181,7 +181,7 @@ func (a *Assertions) EqualExportedValues(expected any, actual any, msgAndArgs .. return assertions.EqualExportedValues(a.t, expected, actual, msgAndArgs...) } -// EqualExportedValuesf is the same as [Assertions.EqualExportedValues], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -201,7 +201,7 @@ func (a *Assertions) EqualValues(expected any, actual any, msgAndArgs ...any) bo return assertions.EqualValues(a.t, expected, actual, msgAndArgs...) } -// EqualValuesf is the same as [Assertions.EqualValues], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -221,7 +221,7 @@ func (a *Assertions) Error(err error, msgAndArgs ...any) bool { return assertions.Error(a.t, err, msgAndArgs...) } -// Errorf is the same as [Assertions.Error], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -241,7 +241,7 @@ func (a *Assertions) ErrorAs(err error, target any, msgAndArgs ...any) bool { return assertions.ErrorAs(a.t, err, target, msgAndArgs...) } -// ErrorAsf is the same as [Assertions.ErrorAs], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -254,21 +254,21 @@ func (a *Assertions) ErrorAsf(err error, target any, msg string, args ...any) bo // 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(theError error, contains string, msgAndArgs ...any) bool { +func (a *Assertions) ErrorContains(err error, contains string, msgAndArgs ...any) bool { if h, ok := a.t.(H); ok { h.Helper() } - return assertions.ErrorContains(a.t, theError, contains, msgAndArgs...) + return assertions.ErrorContains(a.t, err, contains, msgAndArgs...) } -// ErrorContainsf is the same as [Assertions.ErrorContains], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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(theError error, contains string, msg string, args ...any) bool { +func (a *Assertions) ErrorContainsf(err error, contains string, msg string, args ...any) bool { if h, ok := a.t.(H); ok { h.Helper() } - return assertions.ErrorContains(a.t, theError, 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. @@ -281,7 +281,7 @@ func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...any) bool { return assertions.ErrorIs(a.t, err, target, msgAndArgs...) } -// ErrorIsf is the same as [Assertions.ErrorIs], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -301,7 +301,7 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti return assertions.Eventually(a.t, condition, waitFor, tick, msgAndArgs...) } -// Eventuallyf is the same as [Assertions.Eventually], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -321,7 +321,7 @@ func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor return assertions.EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) } -// EventuallyWithTf is the same as [Assertions.EventuallyWithT], but accepts a format msg string to format arguments like [fmt.Printf]. +// EventuallyWithTf is the same as [Assertions.EventuallyWithT], 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 { @@ -341,7 +341,7 @@ func (a *Assertions) Exactly(expected any, actual any, msgAndArgs ...any) bool { return assertions.Exactly(a.t, expected, actual, msgAndArgs...) } -// Exactlyf is the same as [Assertions.Exactly], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -361,7 +361,7 @@ func (a *Assertions) Fail(failureMessage string, msgAndArgs ...any) bool { return assertions.Fail(a.t, failureMessage, msgAndArgs...) } -// Failf is the same as [Assertions.Fail], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -381,7 +381,7 @@ func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...any) bool { return assertions.FailNow(a.t, failureMessage, msgAndArgs...) } -// FailNowf is the same as [Assertions.FailNow], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -401,7 +401,7 @@ func (a *Assertions) False(value bool, msgAndArgs ...any) bool { return assertions.False(a.t, value, msgAndArgs...) } -// Falsef is the same as [Assertions.False], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -421,7 +421,7 @@ func (a *Assertions) FileEmpty(path string, msgAndArgs ...any) bool { return assertions.FileEmpty(a.t, path, msgAndArgs...) } -// FileEmptyf is the same as [Assertions.FileEmpty], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -441,7 +441,7 @@ func (a *Assertions) FileExists(path string, msgAndArgs ...any) bool { return assertions.FileExists(a.t, path, msgAndArgs...) } -// FileExistsf is the same as [Assertions.FileExists], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -461,7 +461,7 @@ func (a *Assertions) FileNotEmpty(path string, msgAndArgs ...any) bool { return assertions.FileNotEmpty(a.t, path, msgAndArgs...) } -// FileNotEmptyf is the same as [Assertions.FileNotEmpty], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -481,7 +481,7 @@ func (a *Assertions) Greater(e1 any, e2 any, msgAndArgs ...any) bool { return assertions.Greater(a.t, e1, e2, msgAndArgs...) } -// Greaterf is the same as [Assertions.Greater], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -501,7 +501,7 @@ func (a *Assertions) GreaterOrEqual(e1 any, e2 any, msgAndArgs ...any) bool { return assertions.GreaterOrEqual(a.t, e1, e2, msgAndArgs...) } -// GreaterOrEqualf is the same as [Assertions.GreaterOrEqual], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -521,7 +521,7 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, u return assertions.HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...) } -// HTTPBodyContainsf is the same as [Assertions.HTTPBodyContains], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -541,7 +541,7 @@ func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string return assertions.HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...) } -// HTTPBodyNotContainsf is the same as [Assertions.HTTPBodyNotContains], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -561,7 +561,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri return assertions.HTTPError(a.t, handler, method, url, values, msgAndArgs...) } -// HTTPErrorf is the same as [Assertions.HTTPError], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -581,7 +581,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s return assertions.HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...) } -// HTTPRedirectf is the same as [Assertions.HTTPRedirect], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -601,7 +601,7 @@ func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url return assertions.HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...) } -// HTTPStatusCodef is the same as [Assertions.HTTPStatusCode], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -621,7 +621,7 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url st return assertions.HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...) } -// HTTPSuccessf is the same as [Assertions.HTTPSuccess], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -641,7 +641,7 @@ func (a *Assertions) Implements(interfaceObject any, object any, msgAndArgs ...a return assertions.Implements(a.t, interfaceObject, object, msgAndArgs...) } -// Implementsf is the same as [Assertions.Implements], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -661,7 +661,7 @@ func (a *Assertions) InDelta(expected any, actual any, delta float64, msgAndArgs return assertions.InDelta(a.t, expected, actual, delta, msgAndArgs...) } -// InDeltaf is the same as [Assertions.InDelta], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -681,7 +681,7 @@ func (a *Assertions) InDeltaMapValues(expected any, actual any, delta float64, m return assertions.InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...) } -// InDeltaMapValuesf is the same as [Assertions.InDeltaMapValues], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -701,7 +701,7 @@ func (a *Assertions) InDeltaSlice(expected any, actual any, delta float64, msgAn return assertions.InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) } -// InDeltaSlicef is the same as [Assertions.InDeltaSlice], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -721,7 +721,7 @@ func (a *Assertions) InEpsilon(expected any, actual any, epsilon float64, msgAnd return assertions.InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) } -// InEpsilonf is the same as [Assertions.InEpsilon], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -741,7 +741,7 @@ func (a *Assertions) InEpsilonSlice(expected any, actual any, epsilon float64, m return assertions.InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) } -// InEpsilonSlicef is the same as [Assertions.InEpsilonSlice], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -761,7 +761,7 @@ func (a *Assertions) IsDecreasing(object any, msgAndArgs ...any) bool { return assertions.IsDecreasing(a.t, object, msgAndArgs...) } -// IsDecreasingf is the same as [Assertions.IsDecreasing], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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(object any, msg string, args ...any) bool { @@ -781,7 +781,7 @@ func (a *Assertions) IsIncreasing(object any, msgAndArgs ...any) bool { return assertions.IsIncreasing(a.t, object, msgAndArgs...) } -// IsIncreasingf is the same as [Assertions.IsIncreasing], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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(object any, msg string, args ...any) bool { @@ -801,7 +801,7 @@ func (a *Assertions) IsNonDecreasing(object any, msgAndArgs ...any) bool { return assertions.IsNonDecreasing(a.t, object, msgAndArgs...) } -// IsNonDecreasingf is the same as [Assertions.IsNonDecreasing], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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(object any, msg string, args ...any) bool { @@ -821,7 +821,7 @@ func (a *Assertions) IsNonIncreasing(object any, msgAndArgs ...any) bool { return assertions.IsNonIncreasing(a.t, object, msgAndArgs...) } -// IsNonIncreasingf is the same as [Assertions.IsNonIncreasing], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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(object any, msg string, args ...any) bool { @@ -841,7 +841,7 @@ func (a *Assertions) IsNotType(theType any, object any, msgAndArgs ...any) bool return assertions.IsNotType(a.t, theType, object, msgAndArgs...) } -// IsNotTypef is the same as [Assertions.IsNotType], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -861,7 +861,7 @@ func (a *Assertions) IsType(expectedType any, object any, msgAndArgs ...any) boo return assertions.IsType(a.t, expectedType, object, msgAndArgs...) } -// IsTypef is the same as [Assertions.IsType], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -881,7 +881,7 @@ func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...any) b return assertions.JSONEq(a.t, expected, actual, msgAndArgs...) } -// JSONEqf is the same as [Assertions.JSONEq], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -901,7 +901,7 @@ func (a *Assertions) JSONEqBytes(expected []byte, actual []byte, msgAndArgs ...a return assertions.JSONEqBytes(a.t, expected, actual, msgAndArgs...) } -// JSONEqBytesf is the same as [Assertions.JSONEqBytes], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -921,7 +921,7 @@ func (a *Assertions) Kind(expectedKind reflect.Kind, object any, msgAndArgs ...a return assertions.Kind(a.t, expectedKind, object, msgAndArgs...) } -// Kindf is the same as [Assertions.Kind], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -941,7 +941,7 @@ func (a *Assertions) Len(object any, length int, msgAndArgs ...any) bool { return assertions.Len(a.t, object, length, msgAndArgs...) } -// Lenf is the same as [Assertions.Len], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -961,7 +961,7 @@ func (a *Assertions) Less(e1 any, e2 any, msgAndArgs ...any) bool { return assertions.Less(a.t, e1, e2, msgAndArgs...) } -// Lessf is the same as [Assertions.Less], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -981,7 +981,7 @@ func (a *Assertions) LessOrEqual(e1 any, e2 any, msgAndArgs ...any) bool { return assertions.LessOrEqual(a.t, e1, e2, msgAndArgs...) } -// LessOrEqualf is the same as [Assertions.LessOrEqual], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1001,7 +1001,7 @@ func (a *Assertions) Negative(e any, msgAndArgs ...any) bool { return assertions.Negative(a.t, e, msgAndArgs...) } -// Negativef is the same as [Assertions.Negative], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1021,7 +1021,7 @@ func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick ti return assertions.Never(a.t, condition, waitFor, tick, msgAndArgs...) } -// Neverf is the same as [Assertions.Never], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1041,7 +1041,7 @@ func (a *Assertions) Nil(object any, msgAndArgs ...any) bool { return assertions.Nil(a.t, object, msgAndArgs...) } -// Nilf is the same as [Assertions.Nil], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1061,7 +1061,7 @@ func (a *Assertions) NoDirExists(path string, msgAndArgs ...any) bool { return assertions.NoDirExists(a.t, path, msgAndArgs...) } -// NoDirExistsf is the same as [Assertions.NoDirExists], but accepts a format msg string to format arguments like [fmt.Printf]. +// NoDirExistsf is the same as [Assertions.NoDirExists], 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) NoDirExistsf(path string, msg string, args ...any) bool { @@ -1081,7 +1081,7 @@ func (a *Assertions) NoError(err error, msgAndArgs ...any) bool { return assertions.NoError(a.t, err, msgAndArgs...) } -// NoErrorf is the same as [Assertions.NoError], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1101,7 +1101,7 @@ func (a *Assertions) NoFileExists(path string, msgAndArgs ...any) bool { return assertions.NoFileExists(a.t, path, msgAndArgs...) } -// NoFileExistsf is the same as [Assertions.NoFileExists], but accepts a format msg string to format arguments like [fmt.Printf]. +// NoFileExistsf is the same as [Assertions.NoFileExists], 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) NoFileExistsf(path string, msg string, args ...any) bool { @@ -1121,7 +1121,7 @@ func (a *Assertions) NotContains(s any, contains any, msgAndArgs ...any) bool { return assertions.NotContains(a.t, s, contains, msgAndArgs...) } -// NotContainsf is the same as [Assertions.NotContains], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1141,7 +1141,7 @@ func (a *Assertions) NotElementsMatch(listA any, listB any, msgAndArgs ...any) ( return assertions.NotElementsMatch(a.t, listA, listB, msgAndArgs...) } -// NotElementsMatchf is the same as [Assertions.NotElementsMatch], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1161,7 +1161,7 @@ func (a *Assertions) NotEmpty(object any, msgAndArgs ...any) bool { return assertions.NotEmpty(a.t, object, msgAndArgs...) } -// NotEmptyf is the same as [Assertions.NotEmpty], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1181,7 +1181,7 @@ func (a *Assertions) NotEqual(expected any, actual any, msgAndArgs ...any) bool return assertions.NotEqual(a.t, expected, actual, msgAndArgs...) } -// NotEqualf is the same as [Assertions.NotEqual], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1201,7 +1201,7 @@ func (a *Assertions) NotEqualValues(expected any, actual any, msgAndArgs ...any) return assertions.NotEqualValues(a.t, expected, actual, msgAndArgs...) } -// NotEqualValuesf is the same as [Assertions.NotEqualValues], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1221,7 +1221,7 @@ func (a *Assertions) NotErrorAs(err error, target any, msgAndArgs ...any) bool { return assertions.NotErrorAs(a.t, err, target, msgAndArgs...) } -// NotErrorAsf is the same as [Assertions.NotErrorAs], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1241,7 +1241,7 @@ func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...any) bool return assertions.NotErrorIs(a.t, err, target, msgAndArgs...) } -// NotErrorIsf is the same as [Assertions.NotErrorIs], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1261,7 +1261,7 @@ func (a *Assertions) NotImplements(interfaceObject any, object any, msgAndArgs . return assertions.NotImplements(a.t, interfaceObject, object, msgAndArgs...) } -// NotImplementsf is the same as [Assertions.NotImplements], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1281,7 +1281,7 @@ func (a *Assertions) NotKind(expectedKind reflect.Kind, object any, msgAndArgs . return assertions.NotKind(a.t, expectedKind, object, msgAndArgs...) } -// NotKindf is the same as [Assertions.NotKind], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1301,7 +1301,7 @@ func (a *Assertions) NotNil(object any, msgAndArgs ...any) bool { return assertions.NotNil(a.t, object, msgAndArgs...) } -// NotNilf is the same as [Assertions.NotNil], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1321,7 +1321,7 @@ func (a *Assertions) NotPanics(f assertions.PanicTestFunc, msgAndArgs ...any) bo return assertions.NotPanics(a.t, f, msgAndArgs...) } -// NotPanicsf is the same as [Assertions.NotPanics], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 assertions.PanicTestFunc, msg string, args ...any) bool { @@ -1334,21 +1334,21 @@ func (a *Assertions) NotPanicsf(f assertions.PanicTestFunc, msg string, 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, str any, msgAndArgs ...any) bool { +func (a *Assertions) NotRegexp(rx any, actual any, msgAndArgs ...any) bool { if h, ok := a.t.(H); ok { h.Helper() } - return assertions.NotRegexp(a.t, rx, str, msgAndArgs...) + return assertions.NotRegexp(a.t, rx, actual, msgAndArgs...) } -// NotRegexpf is the same as [Assertions.NotRegexp], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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, str any, msg string, args ...any) bool { +func (a *Assertions) NotRegexpf(rx any, actual any, msg string, args ...any) bool { if h, ok := a.t.(H); ok { h.Helper() } - return assertions.NotRegexp(a.t, rx, str, 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. @@ -1361,7 +1361,7 @@ func (a *Assertions) NotSame(expected any, actual any, msgAndArgs ...any) bool { return assertions.NotSame(a.t, expected, actual, msgAndArgs...) } -// NotSamef is the same as [Assertions.NotSame], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1381,7 +1381,7 @@ func (a *Assertions) NotSubset(list any, subset any, msgAndArgs ...any) (ok bool return assertions.NotSubset(a.t, list, subset, msgAndArgs...) } -// NotSubsetf is the same as [Assertions.NotSubset], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1401,7 +1401,7 @@ func (a *Assertions) NotZero(i any, msgAndArgs ...any) bool { return assertions.NotZero(a.t, i, msgAndArgs...) } -// NotZerof is the same as [Assertions.NotZero], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1421,7 +1421,7 @@ func (a *Assertions) Panics(f assertions.PanicTestFunc, msgAndArgs ...any) bool return assertions.Panics(a.t, f, msgAndArgs...) } -// Panicsf is the same as [Assertions.Panics], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 assertions.PanicTestFunc, msg string, args ...any) bool { @@ -1441,7 +1441,7 @@ func (a *Assertions) PanicsWithError(errString string, f assertions.PanicTestFun return assertions.PanicsWithError(a.t, errString, f, msgAndArgs...) } -// PanicsWithErrorf is the same as [Assertions.PanicsWithError], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 assertions.PanicTestFunc, msg string, args ...any) bool { @@ -1461,7 +1461,7 @@ func (a *Assertions) PanicsWithValue(expected any, f assertions.PanicTestFunc, m return assertions.PanicsWithValue(a.t, expected, f, msgAndArgs...) } -// PanicsWithValuef is the same as [Assertions.PanicsWithValue], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 assertions.PanicTestFunc, msg string, args ...any) bool { @@ -1481,7 +1481,7 @@ func (a *Assertions) Positive(e any, msgAndArgs ...any) bool { return assertions.Positive(a.t, e, msgAndArgs...) } -// Positivef is the same as [Assertions.Positive], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1494,21 +1494,21 @@ func (a *Assertions) Positivef(e any, msg string, args ...any) bool { // 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, str any, msgAndArgs ...any) bool { +func (a *Assertions) Regexp(rx any, actual any, msgAndArgs ...any) bool { if h, ok := a.t.(H); ok { h.Helper() } - return assertions.Regexp(a.t, rx, str, msgAndArgs...) + return assertions.Regexp(a.t, rx, actual, msgAndArgs...) } -// Regexpf is the same as [Assertions.Regexp], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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, str any, msg string, args ...any) bool { +func (a *Assertions) Regexpf(rx any, actual any, msg string, args ...any) bool { if h, ok := a.t.(H); ok { h.Helper() } - return assertions.Regexp(a.t, rx, str, 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. @@ -1521,7 +1521,7 @@ func (a *Assertions) Same(expected any, actual any, msgAndArgs ...any) bool { return assertions.Same(a.t, expected, actual, msgAndArgs...) } -// Samef is the same as [Assertions.Same], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1541,7 +1541,7 @@ func (a *Assertions) Subset(list any, subset any, msgAndArgs ...any) (ok bool) { return assertions.Subset(a.t, list, subset, msgAndArgs...) } -// Subsetf is the same as [Assertions.Subset], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1561,7 +1561,7 @@ func (a *Assertions) True(value bool, msgAndArgs ...any) bool { return assertions.True(a.t, value, msgAndArgs...) } -// Truef is the same as [Assertions.True], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1581,7 +1581,7 @@ func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta return assertions.WithinDuration(a.t, expected, actual, delta, msgAndArgs...) } -// WithinDurationf is the same as [Assertions.WithinDuration], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1601,7 +1601,7 @@ func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Tim return assertions.WithinRange(a.t, actual, start, end, msgAndArgs...) } -// WithinRangef is the same as [Assertions.WithinRange], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1621,7 +1621,7 @@ func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...any) b return assertions.YAMLEq(a.t, expected, actual, msgAndArgs...) } -// YAMLEqf is the same as [Assertions.YAMLEq], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { @@ -1631,6 +1631,26 @@ func (a *Assertions) YAMLEqf(expected string, actual string, msg string, 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 { + h.Helper() + } + 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 { + h.Helper() + } + 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. @@ -1641,7 +1661,7 @@ func (a *Assertions) Zero(i any, msgAndArgs ...any) bool { return assertions.Zero(a.t, i, msgAndArgs...) } -// Zerof is the same as [Assertions.Zero], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 { diff --git a/assert/assert_forward_test.go b/assert/assert_forward_test.go index 7b5d9a072..c52c70a98 100644 --- a/assert/assert_forward_test.go +++ b/assert/assert_forward_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package assert @@ -4270,6 +4270,30 @@ func TestAssertionsYAMLEqf(t *testing.T) { }) } +func TestAssertionsYAMLEqBytes(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + a := New(t) + a.Panics(func() { + a.YAMLEqBytes([]byte("key: value"), []byte("key: value")) + }, "should panic without the yaml feature enabled.") + }) +} + +func TestAssertionsYAMLEqBytesf(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + a := New(t) + a.Panics(func() { + a.YAMLEqBytesf([]byte("key: value"), []byte("key: value"), "test message") + }, "should panic without the yaml feature enabled.") + }) +} + func TestAssertionsZero(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 b825eaa4a..b4216c990 100644 --- a/assert/assert_helpers.go +++ b/assert/assert_helpers.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package assert diff --git a/assert/assert_helpers_test.go b/assert/assert_helpers_test.go index f33263283..e687cc1ce 100644 --- a/assert/assert_helpers_test.go +++ b/assert/assert_helpers_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package assert diff --git a/assert/assert_types.go b/assert/assert_types.go index bd89020a1..038328fe6 100644 --- a/assert/assert_types.go +++ b/assert/assert_types.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package assert @@ -24,6 +24,9 @@ type ( // for table driven tests. BoolAssertionFunc = assertions.BoolAssertionFunc + // Boolean is a bool or any type that can be converted to a bool. + Boolean = assertions.Boolean + // CollectT implements the [T] interface and collects all errors. // // [CollectT] is specifically intended to be used with [EventuallyWithT] and @@ -42,9 +45,23 @@ type ( ErrorAssertionFunc = assertions.ErrorAssertionFunc // H is an interface for types that implement the Helper method. - // This allows marking functions as test helpers. + // This allows marking functions as test helpers, e.g. [testing.T.Helper]. H = assertions.H + // Measurable is any number for which we can compute a delta (floats or integers). + // + // This is used by [InDeltaT] and [InEpsilonT]. + // + // NOTE: unfortunately complex64 and complex128 are not supported. + Measurable = assertions.Measurable + + // Ordered is a standard ordered type (i.e. types that support "<": [cmp.Ordered]) plus []byte and [time.Time]. + // + // This is used by [GreaterT], [GreaterOrEqualT], [LessT], and [LessOrEqualT]. + // + // NOTE: since [time.Time] is a struct, custom types which redeclare [time.Time] are not supported. + Ordered = assertions.Ordered + // PanicAssertionFunc is a common function prototype when validating a panic value. Can be useful // for table driven tests. PanicAssertionFunc = assertions.PanicAssertionFunc @@ -53,9 +70,29 @@ type ( // methods, and represents a simple func that takes no arguments, and returns nothing. PanicTestFunc = assertions.PanicTestFunc + // RegExp is either a text containing a regular expression to compile (string or []byte), or directly the compiled regexp. + // + // This is used by [RegexpT] and [NotRegexpT]. + RegExp = assertions.RegExp + + // SignedNumeric is a signed integer or a floating point number or any type that can be converted to one of these. + SignedNumeric = assertions.SignedNumeric + // T is an interface wrapper around [testing.T]. T = assertions.T + // Text is any type of underlying type string or []byte. + // + // This is used by [RegexpT], [NotRegexpT], [JSONEqT], and [YAMLEqT]. + // + // NOTE: unfortunately, []rune is not supported. + Text = assertions.Text + + // UnsignedNumeric is an unsigned integer. + // + // NOTE: there are no unsigned floating point numbers. + UnsignedNumeric = assertions.UnsignedNumeric + // ValueAssertionFunc is a common function prototype when validating a single value. Can be useful // for table driven tests. ValueAssertionFunc = assertions.ValueAssertionFunc diff --git a/require/require_assertions.go b/require/require_assertions.go index d3a0c90eb..6e7036f77 100644 --- a/require/require_assertions.go +++ b/require/require_assertions.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package require @@ -94,7 +94,7 @@ func DirExists(t T, path string, msgAndArgs ...any) { // // # Usage // -// assertions.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) +// assertions.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) // // # Examples // @@ -113,6 +113,31 @@ func ElementsMatch(t T, listA any, listB any, msgAndArgs ...any) { t.FailNow() } +// ElementsMatchT asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// # Usage +// +// assertions.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) +// +// # Examples +// +// success: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} +// failure: []int{1, 2, 3}, []int{1, 2, 4} +// +// Upon failure, the test [T] is marked as failed and stops execution. +func ElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.ElementsMatchT(t, listA, listB, msgAndArgs...) { + return + } + + t.FailNow() +} + // Empty asserts that the given value is "empty". // // Zero values are "empty". @@ -188,11 +213,11 @@ func Equal(t T, expected any, actual any, msgAndArgs ...any) { // failure: ErrTest, "wrong error message" // // Upon failure, the test [T] is marked as failed and stops execution. -func EqualError(t T, theError error, errString string, msgAndArgs ...any) { +func EqualError(t T, err error, errString string, msgAndArgs ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.EqualError(t, theError, errString, msgAndArgs...) { + if assertions.EqualError(t, err, errString, msgAndArgs...) { return } @@ -316,11 +341,11 @@ func ErrorAs(t T, err error, target any, msgAndArgs ...any) { // failure: ErrTest, "not in message" // // Upon failure, the test [T] is marked as failed and stops execution. -func ErrorContains(t T, theError error, contains string, msgAndArgs ...any) { +func ErrorContains(t T, err error, contains string, msgAndArgs ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.ErrorContains(t, theError, contains, msgAndArgs...) { + if assertions.ErrorContains(t, err, contains, msgAndArgs...) { return } @@ -527,6 +552,32 @@ func False(t T, value bool, msgAndArgs ...any) { t.FailNow() } +// FalseT asserts that the specified value is false. +// +// # Usage +// +// type B bool +// var b B = true +// +// assertions.FalseT(t, b) +// +// # Examples +// +// success: 1 == 0 +// failure: 1 == 1 +// +// Upon failure, the test [T] is marked as failed and stops execution. +func FalseT[B Boolean](t T, value B, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.FalseT(t, value, msgAndArgs...) { + return + } + + t.FailNow() +} + // FileEmpty checks whether a file exists in the given path and is empty. // It fails if the file is not empty, if the path points to a directory or there is an error when trying to check the file. // @@ -601,6 +652,9 @@ func FileNotEmpty(t T, path string, msgAndArgs ...any) { // Greater asserts that the first element is strictly greater than the second. // +// Both elements must be of the same type in the [reflect.Kind] sense. +// To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. +// // # Usage // // assertions.Greater(t, 2, 1) @@ -626,6 +680,8 @@ func Greater(t T, e1 any, e2 any, msgAndArgs ...any) { // GreaterOrEqual asserts that the first element is greater than or equal to the second. // +// See also [Greater]. +// // # Usage // // assertions.GreaterOrEqual(t, 2, 1) @@ -650,6 +706,79 @@ func GreaterOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) { t.FailNow() } +// GreaterOrEqualT asserts that for two elements of the same type, +// the first element is greater than or equal to the second. +// +// The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), +// []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. +// +// Notice that pointers are not [Ordered], but uintptr are. So you can't call [GreaterOrEqualT] with [*time.Time]. +// +// [GreaterOrEqualT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, +// use [GreaterOrEqual] instead. +// +// To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. +// +// # Usage +// +// assertions.GreaterOrEqualT(t, 2, 1) +// assertions.GreaterOrEqualT(t, 2, 2) +// assertions.GreaterOrEqualT(t, "b", "a") +// assertions.GreaterOrEqualT(t, "b", "b") +// +// # Examples +// +// success: 2, 1 +// failure: 1, 2 +// +// Upon failure, the test [T] is marked as failed and stops execution. +func GreaterOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.GreaterOrEqualT(t, e1, e2, msgAndArgs...) { + return + } + + t.FailNow() +} + +// GreaterT asserts that for two elements of the same type, +// the first element is strictly greater than the second. +// +// The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), +// []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. +// +// Notice that pointers are not [Ordered], but uintptr are. So you can't call [GreaterT] with [*time.Time]. +// +// [GreaterT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, use [Greater] instead. +// +// To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. +// +// # Usage +// +// assertions.GreaterT(t, 2, 1) +// 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()) +// +// # Examples +// +// success: 2, 1 +// failure: 1, 2 +// +// Upon failure, the test [T] is marked as failed and stops execution. +func GreaterT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.GreaterT(t, e1, e2, msgAndArgs...) { + return + } + + t.FailNow() +} + // HTTPBodyContains asserts that a specified handler returns a body that contains a string. // // Returns whether the assertion was successful (true) or not (false). @@ -826,6 +955,18 @@ func Implements(t T, interfaceObject any, object any, msgAndArgs ...any) { // InDelta asserts that the two numerals are within delta of each other. // +// Delta must be greater than or equal to zero. +// +// Expected and actual values should convert to float64. +// To compare large integers that can't be represented accurately as float64 (eg. uint64), +// prefer [InDeltaT] to preserve the original type. +// +// # Behavior with IEEE floating point arithmetics +// +// - expected NaN is matched only by a NaN, e.g. this works: InDeltaT(math.NaN(), math.Sqrt(-1), 0.0) +// - expected +Inf is matched only by a +Inf +// - expected -Inf is matched only by a -Inf +// // # Usage // // assertions.InDelta(t, math.Pi, 22/7.0, 0.01) @@ -847,7 +988,9 @@ func InDelta(t T, expected any, actual any, delta float64, msgAndArgs ...any) { t.FailNow() } -// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +// InDeltaMapValues is the same as [InDelta], but it compares all values between two maps. Both maps must have exactly the same keys. +// +// See [InDelta]. // // # Usage // @@ -870,7 +1013,9 @@ func InDeltaMapValues(t T, expected any, actual any, delta float64, msgAndArgs . t.FailNow() } -// InDeltaSlice is the same as InDelta, except it compares two slices. +// InDeltaSlice is the same as [InDelta], except it compares two slices. +// +// See [InDelta]. // // # Usage // @@ -893,8 +1038,57 @@ func InDeltaSlice(t T, expected any, actual any, delta float64, msgAndArgs ...an t.FailNow() } +// InDeltaT asserts that the two numerals of the same type numerical type are within delta of each other. +// +// [InDeltaT] accepts any go numeric type, including integer types. +// +// The main difference with [InDelta] is that the delta is expressed with the same type as the values, not necessarily a float64. +// +// Delta must be greater than or equal to zero. +// +// # Behavior with IEEE floating point arithmetics +// +// - expected NaN is matched only by a NaN, e.g. this works: InDeltaT(math.NaN(), math.Sqrt(-1), 0.0) +// - expected +Inf is matched only by a +Inf +// - expected -Inf is matched only by a -Inf +// +// # Usage +// +// assertions.InDeltaT(t, math.Pi, 22/7.0, 0.01) +// +// # Examples +// +// success: 1.0, 1.01, 0.02 +// failure: 1.0, 1.1, 0.05 +// +// Upon failure, the test [T] is marked as failed and stops execution. +func InDeltaT[Number Measurable](t T, expected Number, actual Number, delta Number, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.InDeltaT(t, expected, actual, delta, msgAndArgs...) { + return + } + + t.FailNow() +} + // InEpsilon asserts that expected and actual have a relative error less than epsilon. // +// # Behavior with IEEE floating point arithmetics +// +// - expected NaN is matched only by a NaN, e.g. this works: InDeltaT(math.NaN(), math.Sqrt(-1), 0.0) +// - expected +Inf is matched only by a +Inf +// - expected -Inf is matched only by a -Inf +// +// Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer [InDeltaT]. +// +// Formula: +// - If expected == 0: fail if |actual - expected| > epsilon +// - If expected != 0: fail if |actual - expected| > epsilon * |expected| +// +// This allows [InEpsilonT] to work naturally across the full numeric range including zero. +// // # Usage // // assertions.InEpsilon(t, 100.0, 101.0, 0.02) @@ -916,7 +1110,9 @@ func InEpsilon(t T, expected any, actual any, epsilon float64, msgAndArgs ...any t.FailNow() } -// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +// InEpsilonSlice is the same as [InEpsilon], except it compares each value from two slices. +// +// See [InEpsilon]. // // # Usage // @@ -939,6 +1135,49 @@ func InEpsilonSlice(t T, expected any, actual any, epsilon float64, msgAndArgs . t.FailNow() } +// InEpsilonT asserts that expected and actual have a relative error less than epsilon. +// +// When expected is zero, epsilon is interpreted as an absolute error threshold, +// since relative error is mathematically undefined for zero values. +// +// Unlike [InDeltaT], which preserves the original type, [InEpsilonT] converts the expected and actual +// numbers to float64, since the relative error doesn't make sense as an integer. +// +// # Behavior with IEEE floating point arithmetics +// +// - expected NaN is matched only by a NaN, e.g. this works: InDeltaT(math.NaN(), math.Sqrt(-1), 0.0) +// - expected +Inf is matched only by a +Inf +// - expected -Inf is matched only by a -Inf +// +// Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer [InDeltaT]. +// +// Formula: +// - If expected == 0: fail if |actual - expected| > epsilon +// - If expected != 0: fail if |actual - expected| > epsilon * |expected| +// +// This allows [InEpsilonT] to work naturally across the full numeric range including zero. +// +// # Usage +// +// assertions.InEpsilon(t, 100.0, 101.0, 0.02) +// +// # Examples +// +// success: 100.0, 101.0, 0.02 +// failure: 100.0, 110.0, 0.05 +// +// Upon failure, the test [T] is marked as failed and stops execution. +func InEpsilonT[Number Measurable](t T, expected Number, actual Number, epsilon float64, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.InEpsilonT(t, expected, actual, epsilon, msgAndArgs...) { + return + } + + t.FailNow() +} + // IsDecreasing asserts that the collection is decreasing. // // # Usage @@ -1087,6 +1326,8 @@ func IsType(t T, expectedType any, object any, msgAndArgs ...any) { // JSONEq asserts that two JSON strings are equivalent. // +// Expected and actual must be valid JSON. +// // # Usage // // assertions.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) @@ -1108,7 +1349,9 @@ func JSONEq(t T, expected string, actual string, msgAndArgs ...any) { t.FailNow() } -// JSONEqBytes asserts that two JSON byte slices are equivalent. +// JSONEqBytes asserts that two JSON slices of bytes are equivalent. +// +// Expected and actual must be valid JSON. // // # Usage // @@ -1131,6 +1374,33 @@ func JSONEqBytes(t T, expected []byte, actual []byte, msgAndArgs ...any) { t.FailNow() } +// JSONEqT asserts that two JSON documents are equivalent. +// +// The expected and actual arguments may be string or []byte. They do not need to be of the same type. +// +// Expected and actual must be valid JSON. +// +// # Usage +// +// assertions.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) +// +// # Examples +// +// success: `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`) +// failure: `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]` +// +// Upon failure, the test [T] is marked as failed and stops execution. +func JSONEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.JSONEqT(t, expected, actual, 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) @@ -1190,6 +1460,9 @@ func Len(t T, object any, length int, msgAndArgs ...any) { // Less asserts that the first element is strictly less than the second. // +// Both elements must be of the same type in the [reflect.Kind] sense. +// To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. +// // # Usage // // assertions.Less(t, 1, 2) @@ -1239,6 +1512,77 @@ func LessOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) { t.FailNow() } +// LessOrEqualT asserts that for two elements of the same type, the first element is less than or equal to the second. +// +// The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), +// []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. +// +// Notice that pointers are not [Ordered], but uintptr are. So you can't call [LessOrEqualT] with [*time.Time]. +// +// [LessOrEqualT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, +// use [LessOrEqual] instead. +// +// To compare values that need a type conversion (e.g. float32 against float64), you should use [LessOrEqual] instead. +// +// # Usage +// +// assertions.LessOrEqualT(t, 1, 2) +// assertions.LessOrEqualT(t, 2, 2) +// assertions.LessOrEqualT(t, "a", "b") +// assertions.LessOrEqualT(t, "b", "b") +// +// # Examples +// +// success: 1, 2 +// failure: 2, 1 +// +// Upon failure, the test [T] is marked as failed and stops execution. +func LessOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.LessOrEqualT(t, e1, e2, msgAndArgs...) { + return + } + + t.FailNow() +} + +// LessT asserts that for two elements of the same type, the first element is strictly less than the second. +// +// The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), +// []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. +// +// Notice that pointers are not [Ordered], but uintptr are. So you can't call [LessT] with [*time.Time]. +// +// [LessT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, +// use [Less] instead. +// +// To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. +// +// # Usage +// +// assertions.LessT(t, 1, 2) +// assertions.LessT(t, float64(1), float64(2)) +// assertions.LessT(t, "a", "b") +// +// # Examples +// +// success: 1, 2 +// failure: 2, 1 +// +// Upon failure, the test [T] is marked as failed and stops execution. +func LessT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.LessT(t, e1, e2, msgAndArgs...) { + return + } + + t.FailNow() +} + // Negative asserts that the specified element is strictly negative. // // # Usage @@ -1263,6 +1607,30 @@ func Negative(t T, e any, msgAndArgs ...any) { t.FailNow() } +// NegativeT asserts that the specified element of a signed numeric type is strictly negative. +// +// # Usage +// +// assertions.NegativeT(t, -1) +// assertions.NegativeT(t, -1.23) +// +// # Examples +// +// success: -1 +// failure: 1 +// +// Upon failure, the test [T] is marked as failed and stops execution. +func NegativeT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.NegativeT(t, e, msgAndArgs...) { + return + } + + t.FailNow() +} + // Never asserts that the given condition is never satisfied within waitFor time, // periodically checking the target function at each tick. // @@ -1429,9 +1797,9 @@ func NotContains(t T, s any, contains any, msgAndArgs ...any) { // // # Usage // -// assertions.NotElementsMatch(t, [1, 1, 2, 3], [1, 1, 2, 3]) -> false -// assertions.NotElementsMatch(t, [1, 1, 2, 3], [1, 2, 3]) -> true -// assertions.NotElementsMatch(t, [1, 2, 3], [1, 2, 4]) -> true +// 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 // // # Examples // @@ -1450,6 +1818,34 @@ func NotElementsMatch(t T, listA any, listB any, msgAndArgs ...any) { t.FailNow() } +// NotElementsMatchT asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// # Usage +// +// 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 +// +// # Examples +// +// success: []int{1, 2, 3}, []int{1, 2, 4} +// failure: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} +// +// Upon failure, the test [T] is marked as failed and stops execution. +func NotElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.NotElementsMatchT(t, listA, listB, msgAndArgs...) { + return + } + + t.FailNow() +} + // NotEmpty asserts that the specified object is NOT [Empty]. // // # Usage @@ -1668,7 +2064,35 @@ func NotPanics(t T, f assertions.PanicTestFunc, msgAndArgs ...any) { t.FailNow() } -// NotRegexp asserts that a specified regexp does not match a string. +// NotRegexp asserts that a specified regular expression does not match a string. +// +// See [Regexp]. +// +// # Usage +// +// assertions.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assertions.NotRegexp(t, "^start", "it's not starting") +// +// # Examples +// +// success: "^start", "not starting" +// failure: "^start", "starting" +// +// Upon failure, the test [T] is marked as failed and stops execution. +func NotRegexp(t T, rx any, actual any, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.NotRegexp(t, rx, actual, msgAndArgs...) { + return + } + + t.FailNow() +} + +// NotRegexpT asserts that a specified regular expression does not match a string. +// +// See [RegexpT]. // // # Usage // @@ -1681,11 +2105,11 @@ func NotPanics(t T, f assertions.PanicTestFunc, msgAndArgs ...any) { // failure: "^start", "starting" // // Upon failure, the test [T] is marked as failed and stops execution. -func NotRegexp(t T, rx any, str any, msgAndArgs ...any) { +func NotRegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.NotRegexp(t, rx, str, msgAndArgs...) { + if assertions.NotRegexpT(t, rx, actual, msgAndArgs...) { return } @@ -1866,7 +2290,35 @@ func Positive(t T, e any, msgAndArgs ...any) { t.FailNow() } -// Regexp asserts that a specified regexp matches a string. +// PositiveT asserts that the specified element of a signed numeric type is strictly positive. +// +// # Usage +// +// assertions.PositiveT(t, 1) +// assertions.PositiveT(t, 1.23) +// +// # Examples +// +// success: 1 +// failure: -1 +// +// Upon failure, the test [T] is marked as failed and stops execution. +func PositiveT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.PositiveT(t, e, msgAndArgs...) { + return + } + + t.FailNow() +} + +// Regexp asserts that a specified regular expression matches a string. +// +// The regular expression may be passed as a [regexp.Regexp], a string or a []byte and will be compiled. +// +// The actual argument to be matched may be a string, []byte or anything that prints as a string with [fmt.Sprint]. // // # Usage // @@ -1879,11 +2331,34 @@ func Positive(t T, e any, msgAndArgs ...any) { // failure: "^start", "not starting" // // Upon failure, the test [T] is marked as failed and stops execution. -func Regexp(t T, rx any, str any, msgAndArgs ...any) { +func Regexp(t T, rx any, actual any, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.Regexp(t, rx, actual, msgAndArgs...) { + return + } + + t.FailNow() +} + +// RegexpT asserts that a specified regular expression matches a string. +// +// The actual argument to be matched may be a string or []byte. +// +// See [Regexp]. +// +// # Examples +// +// success: "^start", "starting" +// failure: "^start", "not starting" +// +// Upon failure, the test [T] is marked as failed and stops execution. +func RegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.Regexp(t, rx, str, msgAndArgs...) { + if assertions.RegexpT(t, rx, actual, msgAndArgs...) { return } @@ -1924,10 +2399,10 @@ func Same(t T, expected any, actual any, msgAndArgs ...any) { // // # Usage // -// assertions.Subset(t, [1, 2, 3], [1, 2]) -// assertions.Subset(t, {"x": 1, "y": 2}, {"x": 1}) -// assertions.Subset(t, [1, 2, 3], {1: "one", 2: "two"}) -// assertions.Subset(t, {"x": 1, "y": 2}, ["x"]) +// assertions.Subset(t, []int{1, 2, 3}, []int{1, 2}) +// assertions.Subset(t, []string{"x": 1, "y": 2}, []string{"x": 1}) +// assertions.Subset(t, []int{1, 2, 3}, map[int]string{1: "one", 2: "two"}) +// assertions.Subset(t, map[string]int{"x": 1, "y": 2}, []string{"x"}) // // # Examples // @@ -1969,6 +2444,32 @@ func True(t T, value bool, msgAndArgs ...any) { t.FailNow() } +// TrueT asserts that the specified value is true. +// +// # Usage +// +// type B bool +// var b B = true +// +// assertions.True(t, b) +// +// # Examples +// +// success: 1 == 1 +// failure: 1 == 0 +// +// Upon failure, the test [T] is marked as failed and stops execution. +func TrueT[B Boolean](t T, value B, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.TrueT(t, value, msgAndArgs...) { + return + } + + t.FailNow() +} + // WithinDuration asserts that the two times are within duration delta of each other. // // # Usage @@ -2015,7 +2516,40 @@ func WithinRange(t T, actual time.Time, start time.Time, end time.Time, msgAndAr t.FailNow() } -// YAMLEq asserts that the first documents in the two YAML strings are equivalent. +// YAMLEq asserts that two YAML strings are equivalent. +// +// See [YAMLEqBytes]. +// +// # 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 YAMLEq(t T, expected string, actual string, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.YAMLEq(t, expected, actual, msgAndArgs...) { + return + } + + t.FailNow() +} + +// YAMLEqBytes asserts that two YAML slices of bytes are equivalent. +// +// Expected and actual must be valid YAML. +// +// # Important +// +// By default, this function is disabled and will panic. +// +// To enable it, you should add a blank import like so: +// +// import( +// "github.com/go-openapi/testify/enable/yaml/v2" +// ) // // # Usage // @@ -2033,15 +2567,38 @@ func WithinRange(t T, actual time.Time, start time.Time, end time.Time, msgAndAr // // # Examples // +// panic: []byte("key: value"), []byte("key: value") +// should panic without the yaml feature enabled. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func YAMLEqBytes(t T, expected []byte, actual []byte, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.YAMLEqBytes(t, expected, actual, msgAndArgs...) { + return + } + + t.FailNow() +} + +// YAMLEqT asserts that two YAML documents are equivalent. +// +// The expected and actual arguments may be string or []byte. They do not need to be of the same type. +// +// See [YAMLEqBytes]. +// +// # 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 YAMLEq(t T, expected string, actual string, msgAndArgs ...any) { +func YAMLEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.YAMLEq(t, expected, actual, msgAndArgs...) { + if assertions.YAMLEqT(t, expected, actual, msgAndArgs...) { return } diff --git a/require/require_assertions_test.go b/require/require_assertions_test.go index 534c572d2..5436ee3fd 100644 --- a/require/require_assertions_test.go +++ b/require/require_assertions_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package require @@ -97,6 +97,26 @@ func TestElementsMatch(t *testing.T) { }) } +func TestElementsMatchT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + ElementsMatchT(mock, []int{1, 2, 3}, []int{1, 2, 4}) + // require functions don't return a value + if !mock.failed { + t.Error("ElementsMatchT should call FailNow()") + } + }) +} + func TestEmpty(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -385,6 +405,26 @@ func TestFalse(t *testing.T) { }) } +func TestFalseT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + FalseT(t, 1 == 0) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + FalseT(mock, 1 == 1) + // require functions don't return a value + if !mock.failed { + t.Error("FalseT should call FailNow()") + } + }) +} + func TestFileEmpty(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -485,6 +525,46 @@ func TestGreaterOrEqual(t *testing.T) { }) } +func TestGreaterOrEqualT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + GreaterOrEqualT(t, 2, 1) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + GreaterOrEqualT(mock, 1, 2) + // require functions don't return a value + if !mock.failed { + t.Error("GreaterOrEqualT should call FailNow()") + } + }) +} + +func TestGreaterT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + GreaterT(t, 2, 1) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + GreaterT(mock, 1, 2) + // require functions don't return a value + if !mock.failed { + t.Error("GreaterT should call FailNow()") + } + }) +} + func TestHTTPBodyContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -685,6 +765,26 @@ func TestInDeltaSlice(t *testing.T) { }) } +func TestInDeltaT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + InDeltaT(t, 1.0, 1.01, 0.02) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + InDeltaT(mock, 1.0, 1.1, 0.05) + // require functions don't return a value + if !mock.failed { + t.Error("InDeltaT should call FailNow()") + } + }) +} + func TestInEpsilon(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -725,6 +825,26 @@ func TestInEpsilonSlice(t *testing.T) { }) } +func TestInEpsilonT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + InEpsilonT(t, 100.0, 101.0, 0.02) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + InEpsilonT(mock, 100.0, 110.0, 0.05) + // require functions don't return a value + if !mock.failed { + t.Error("InEpsilonT should call FailNow()") + } + }) +} + func TestIsDecreasing(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -885,6 +1005,26 @@ func TestJSONEqBytes(t *testing.T) { }) } +func TestJSONEqT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + JSONEqT(mock, `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]`) + // require functions don't return a value + if !mock.failed { + t.Error("JSONEqT should call FailNow()") + } + }) +} + func TestKind(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -965,6 +1105,46 @@ func TestLessOrEqual(t *testing.T) { }) } +func TestLessOrEqualT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + LessOrEqualT(t, 1, 2) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + LessOrEqualT(mock, 2, 1) + // require functions don't return a value + if !mock.failed { + t.Error("LessOrEqualT should call FailNow()") + } + }) +} + +func TestLessT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + LessT(t, 1, 2) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + LessT(mock, 2, 1) + // require functions don't return a value + if !mock.failed { + t.Error("LessT should call FailNow()") + } + }) +} + func TestNegative(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -985,6 +1165,26 @@ func TestNegative(t *testing.T) { }) } +func TestNegativeT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + NegativeT(t, -1) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + NegativeT(mock, 1) + // require functions don't return a value + if !mock.failed { + t.Error("NegativeT should call FailNow()") + } + }) +} + func TestNever(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1125,6 +1325,26 @@ func TestNotElementsMatch(t *testing.T) { }) } +func TestNotElementsMatchT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4}) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + NotElementsMatchT(mock, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) + // require functions don't return a value + if !mock.failed { + t.Error("NotElementsMatchT should call FailNow()") + } + }) +} + func TestNotEmpty(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1325,6 +1545,26 @@ func TestNotRegexp(t *testing.T) { }) } +func TestNotRegexpT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + NotRegexpT(t, "^start", "not starting") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + NotRegexpT(mock, "^start", "starting") + // require functions don't return a value + if !mock.failed { + t.Error("NotRegexpT should call FailNow()") + } + }) +} + func TestNotSame(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1465,6 +1705,26 @@ func TestPositive(t *testing.T) { }) } +func TestPositiveT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + PositiveT(t, 1) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + PositiveT(mock, -1) + // require functions don't return a value + if !mock.failed { + t.Error("PositiveT should call FailNow()") + } + }) +} + func TestRegexp(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1485,6 +1745,26 @@ func TestRegexp(t *testing.T) { }) } +func TestRegexpT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + RegexpT(t, "^start", "starting") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + RegexpT(mock, "^start", "not starting") + // require functions don't return a value + if !mock.failed { + t.Error("RegexpT should call FailNow()") + } + }) +} + func TestSame(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1545,6 +1825,26 @@ func TestTrue(t *testing.T) { }) } +func TestTrueT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + TrueT(t, 1 == 1) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + TrueT(mock, 1 == 0) + // require functions don't return a value + if !mock.failed { + t.Error("TrueT should call FailNow()") + } + }) +} + func TestWithinDuration(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1596,6 +1896,28 @@ func TestYAMLEq(t *testing.T) { }) } +func TestYAMLEqBytes(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + Panics(t, func() { + YAMLEqBytes(t, []byte("key: value"), []byte("key: value")) + }, "should panic without the yaml feature enabled.") + }) +} + +func TestYAMLEqT(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + Panics(t, func() { + YAMLEqT(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 0dd3d16ad..083d4860f 100644 --- a/require/require_examples_test.go +++ b/require/require_examples_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package require_test @@ -54,6 +54,14 @@ func ExampleElementsMatch() { // Output: passed } +func ExampleElementsMatchT() { + t := new(testing.T) + require.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) + fmt.Println("passed") + + // Output: passed +} + func ExampleEmpty() { t := new(testing.T) require.Empty(t, "") @@ -170,6 +178,14 @@ func ExampleFalse() { // Output: passed } +func ExampleFalseT() { + t := new(testing.T) + require.FalseT(t, 1 == 0) + fmt.Println("passed") + + // Output: passed +} + func ExampleFileEmpty() { t := new(testing.T) require.FileEmpty(t, filepath.Join(testDataPath(), "empty_file")) @@ -210,6 +226,22 @@ func ExampleGreaterOrEqual() { // Output: passed } +func ExampleGreaterOrEqualT() { + t := new(testing.T) + require.GreaterOrEqualT(t, 2, 1) + fmt.Println("passed") + + // Output: passed +} + +func ExampleGreaterT() { + t := new(testing.T) + require.GreaterT(t, 2, 1) + fmt.Println("passed") + + // Output: passed +} + func ExampleHTTPBodyContains() { t := new(testing.T) require.HTTPBodyContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!") @@ -290,6 +322,14 @@ func ExampleInDeltaSlice() { // Output: passed } +func ExampleInDeltaT() { + t := new(testing.T) + require.InDeltaT(t, 1.0, 1.01, 0.02) + fmt.Println("passed") + + // Output: passed +} + func ExampleInEpsilon() { t := new(testing.T) require.InEpsilon(t, 100.0, 101.0, 0.02) @@ -306,6 +346,14 @@ func ExampleInEpsilonSlice() { // Output: passed } +func ExampleInEpsilonT() { + t := new(testing.T) + require.InEpsilonT(t, 100.0, 101.0, 0.02) + fmt.Println("passed") + + // Output: passed +} + func ExampleIsDecreasing() { t := new(testing.T) require.IsDecreasing(t, []int{3, 2, 1}) @@ -370,6 +418,14 @@ func ExampleJSONEqBytes() { // Output: passed } +func ExampleJSONEqT() { + t := new(testing.T) + require.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) + fmt.Println("passed") + + // Output: passed +} + func ExampleKind() { t := new(testing.T) require.Kind(t, reflect.String, "hello") @@ -402,6 +458,22 @@ func ExampleLessOrEqual() { // Output: passed } +func ExampleLessOrEqualT() { + t := new(testing.T) + require.LessOrEqualT(t, 1, 2) + fmt.Println("passed") + + // Output: passed +} + +func ExampleLessT() { + t := new(testing.T) + require.LessT(t, 1, 2) + fmt.Println("passed") + + // Output: passed +} + func ExampleNegative() { t := new(testing.T) require.Negative(t, -1) @@ -410,6 +482,14 @@ func ExampleNegative() { // Output: passed } +func ExampleNegativeT() { + t := new(testing.T) + require.NegativeT(t, -1) + fmt.Println("passed") + + // Output: passed +} + func ExampleNever() { t := new(testing.T) require.Never(t, func() bool { @@ -468,6 +548,14 @@ func ExampleNotElementsMatch() { // Output: passed } +func ExampleNotElementsMatchT() { + t := new(testing.T) + require.NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4}) + fmt.Println("passed") + + // Output: passed +} + func ExampleNotEmpty() { t := new(testing.T) require.NotEmpty(t, "not empty") @@ -549,6 +637,14 @@ func ExampleNotRegexp() { // Output: passed } +func ExampleNotRegexpT() { + t := new(testing.T) + require.NotRegexpT(t, "^start", "not starting") + fmt.Println("passed") + + // Output: passed +} + func ExampleNotSame() { t := new(testing.T) require.NotSame(t, &staticVar, ptr("static string")) @@ -611,6 +707,14 @@ func ExamplePositive() { // Output: passed } +func ExamplePositiveT() { + t := new(testing.T) + require.PositiveT(t, 1) + fmt.Println("passed") + + // Output: passed +} + func ExampleRegexp() { t := new(testing.T) require.Regexp(t, "^start", "starting") @@ -619,6 +723,14 @@ func ExampleRegexp() { // Output: passed } +func ExampleRegexpT() { + t := new(testing.T) + require.RegexpT(t, "^start", "starting") + fmt.Println("passed") + + // Output: passed +} + func ExampleSame() { t := new(testing.T) require.Same(t, &staticVar, staticVarPtr) @@ -643,6 +755,14 @@ func ExampleTrue() { // Output: passed } +func ExampleTrueT() { + t := new(testing.T) + require.TrueT(t, 1 == 1) + fmt.Println("passed") + + // Output: passed +} + func ExampleWithinDuration() { 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) @@ -663,6 +783,14 @@ func ExampleWithinRange() { // no success example available. Please add some examples to produce a testable example. // } +// func ExampleYAMLEqBytes() { +// no success example available. Please add some examples to produce a testable example. +// } + +// func ExampleYAMLEqT() { +// no success example available. Please add some examples to produce a testable example. +// } + func ExampleZero() { t := new(testing.T) require.Zero(t, 0) diff --git a/require/require_format.go b/require/require_format.go index 43a939a6f..cbf3cfe93 100644 --- a/require/require_format.go +++ b/require/require_format.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package require @@ -15,7 +15,7 @@ import ( "github.com/go-openapi/testify/v2/internal/assertions" ) -// Conditionf is the same as [Condition], but accepts a format msg string to format arguments like [fmt.Printf]. +// Conditionf is the same as [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 Conditionf(t T, comp Comparison, msg string, args ...any) { @@ -29,7 +29,7 @@ func Conditionf(t T, comp Comparison, msg string, args ...any) { t.FailNow() } -// Containsf is the same as [Contains], but accepts a format msg string to format arguments like [fmt.Printf]. +// Containsf is the same as [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 Containsf(t T, s any, contains any, msg string, args ...any) { @@ -43,7 +43,7 @@ func Containsf(t T, s any, contains any, msg string, args ...any) { t.FailNow() } -// DirExistsf is the same as [DirExists], but accepts a format msg string to format arguments like [fmt.Printf]. +// DirExistsf is the same as [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 DirExistsf(t T, path string, msg string, args ...any) { @@ -57,7 +57,7 @@ func DirExistsf(t T, path string, msg string, args ...any) { t.FailNow() } -// ElementsMatchf is the same as [ElementsMatch], but accepts a format msg string to format arguments like [fmt.Printf]. +// ElementsMatchf is the same as [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 ElementsMatchf(t T, listA any, listB any, msg string, args ...any) { @@ -71,7 +71,21 @@ func ElementsMatchf(t T, listA any, listB any, msg string, args ...any) { t.FailNow() } -// Emptyf is the same as [Empty], but accepts a format msg string to format arguments like [fmt.Printf]. +// ElementsMatchTf is the same as [ElementsMatchT], 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 ElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.ElementsMatchT(t, listA, listB, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// Emptyf is the same as [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 Emptyf(t T, object any, msg string, args ...any) { @@ -85,7 +99,7 @@ func Emptyf(t T, object any, msg string, args ...any) { t.FailNow() } -// Equalf is the same as [Equal], but accepts a format msg string to format arguments like [fmt.Printf]. +// Equalf is the same as [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 Equalf(t T, expected any, actual any, msg string, args ...any) { @@ -99,21 +113,21 @@ func Equalf(t T, expected any, actual any, msg string, args ...any) { t.FailNow() } -// EqualErrorf is the same as [EqualError], but accepts a format msg string to format arguments like [fmt.Printf]. +// EqualErrorf is the same as [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 EqualErrorf(t T, theError error, errString string, msg string, args ...any) { +func EqualErrorf(t T, err error, errString string, msg string, args ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.EqualError(t, theError, errString, forwardArgs(msg, args)) { + if assertions.EqualError(t, err, errString, forwardArgs(msg, args)) { return } t.FailNow() } -// EqualExportedValuesf is the same as [EqualExportedValues], but accepts a format msg string to format arguments like [fmt.Printf]. +// EqualExportedValuesf is the same as [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 EqualExportedValuesf(t T, expected any, actual any, msg string, args ...any) { @@ -127,7 +141,7 @@ func EqualExportedValuesf(t T, expected any, actual any, msg string, args ...any t.FailNow() } -// EqualValuesf is the same as [EqualValues], but accepts a format msg string to format arguments like [fmt.Printf]. +// EqualValuesf is the same as [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 EqualValuesf(t T, expected any, actual any, msg string, args ...any) { @@ -141,7 +155,7 @@ func EqualValuesf(t T, expected any, actual any, msg string, args ...any) { t.FailNow() } -// Errorf is the same as [Error], but accepts a format msg string to format arguments like [fmt.Printf]. +// Errorf is the same as [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 Errorf(t T, err error, msg string, args ...any) { @@ -155,7 +169,7 @@ func Errorf(t T, err error, msg string, args ...any) { t.FailNow() } -// ErrorAsf is the same as [ErrorAs], but accepts a format msg string to format arguments like [fmt.Printf]. +// ErrorAsf is the same as [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 ErrorAsf(t T, err error, target any, msg string, args ...any) { @@ -169,21 +183,21 @@ func ErrorAsf(t T, err error, target any, msg string, args ...any) { t.FailNow() } -// ErrorContainsf is the same as [ErrorContains], but accepts a format msg string to format arguments like [fmt.Printf]. +// ErrorContainsf is the same as [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 ErrorContainsf(t T, theError error, contains string, msg string, args ...any) { +func ErrorContainsf(t T, err error, contains string, msg string, args ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.ErrorContains(t, theError, contains, forwardArgs(msg, args)) { + if assertions.ErrorContains(t, err, contains, forwardArgs(msg, args)) { return } t.FailNow() } -// ErrorIsf is the same as [ErrorIs], but accepts a format msg string to format arguments like [fmt.Printf]. +// ErrorIsf is the same as [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 ErrorIsf(t T, err error, target error, msg string, args ...any) { @@ -197,7 +211,7 @@ func ErrorIsf(t T, err error, target error, msg string, args ...any) { t.FailNow() } -// Eventuallyf is the same as [Eventually], but accepts a format msg string to format arguments like [fmt.Printf]. +// Eventuallyf is the same as [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 Eventuallyf(t T, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...any) { @@ -211,7 +225,7 @@ func Eventuallyf(t T, condition func() bool, waitFor time.Duration, tick time.Du t.FailNow() } -// EventuallyWithTf is the same as [EventuallyWithT], but accepts a format msg string to format arguments like [fmt.Printf]. +// EventuallyWithTf is the same as [EventuallyWithT], 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) { @@ -225,7 +239,7 @@ func EventuallyWithTf(t T, condition func(collect *CollectT), waitFor time.Durat t.FailNow() } -// Exactlyf is the same as [Exactly], but accepts a format msg string to format arguments like [fmt.Printf]. +// Exactlyf is the same as [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 Exactlyf(t T, expected any, actual any, msg string, args ...any) { @@ -239,7 +253,7 @@ func Exactlyf(t T, expected any, actual any, msg string, args ...any) { t.FailNow() } -// Failf is the same as [Fail], but accepts a format msg string to format arguments like [fmt.Printf]. +// Failf is the same as [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 Failf(t T, failureMessage string, msg string, args ...any) { @@ -251,7 +265,7 @@ func Failf(t T, failureMessage string, msg string, args ...any) { t.FailNow() } -// FailNowf is the same as [FailNow], but accepts a format msg string to format arguments like [fmt.Printf]. +// FailNowf is the same as [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 FailNowf(t T, failureMessage string, msg string, args ...any) { @@ -263,7 +277,7 @@ func FailNowf(t T, failureMessage string, msg string, args ...any) { t.FailNow() } -// Falsef is the same as [False], but accepts a format msg string to format arguments like [fmt.Printf]. +// Falsef is the same as [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 Falsef(t T, value bool, msg string, args ...any) { @@ -277,7 +291,21 @@ func Falsef(t T, value bool, msg string, args ...any) { t.FailNow() } -// FileEmptyf is the same as [FileEmpty], but accepts a format msg string to format arguments like [fmt.Printf]. +// FalseTf is the same as [FalseT], 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 FalseTf[B Boolean](t T, value B, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.FalseT(t, value, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// FileEmptyf is the same as [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 FileEmptyf(t T, path string, msg string, args ...any) { @@ -291,7 +319,7 @@ func FileEmptyf(t T, path string, msg string, args ...any) { t.FailNow() } -// FileExistsf is the same as [FileExists], but accepts a format msg string to format arguments like [fmt.Printf]. +// FileExistsf is the same as [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 FileExistsf(t T, path string, msg string, args ...any) { @@ -305,7 +333,7 @@ func FileExistsf(t T, path string, msg string, args ...any) { t.FailNow() } -// FileNotEmptyf is the same as [FileNotEmpty], but accepts a format msg string to format arguments like [fmt.Printf]. +// FileNotEmptyf is the same as [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 FileNotEmptyf(t T, path string, msg string, args ...any) { @@ -319,7 +347,7 @@ func FileNotEmptyf(t T, path string, msg string, args ...any) { t.FailNow() } -// Greaterf is the same as [Greater], but accepts a format msg string to format arguments like [fmt.Printf]. +// Greaterf is the same as [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 Greaterf(t T, e1 any, e2 any, msg string, args ...any) { @@ -333,7 +361,7 @@ func Greaterf(t T, e1 any, e2 any, msg string, args ...any) { t.FailNow() } -// GreaterOrEqualf is the same as [GreaterOrEqual], but accepts a format msg string to format arguments like [fmt.Printf]. +// GreaterOrEqualf is the same as [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 GreaterOrEqualf(t T, e1 any, e2 any, msg string, args ...any) { @@ -347,7 +375,35 @@ func GreaterOrEqualf(t T, e1 any, e2 any, msg string, args ...any) { t.FailNow() } -// HTTPBodyContainsf is the same as [HTTPBodyContains], but accepts a format msg string to format arguments like [fmt.Printf]. +// GreaterOrEqualTf is the same as [GreaterOrEqualT], 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 GreaterOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.GreaterOrEqualT(t, e1, e2, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// GreaterTf is the same as [GreaterT], 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 GreaterTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.GreaterT(t, e1, e2, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// HTTPBodyContainsf is the same as [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 HTTPBodyContainsf(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ...any) { @@ -361,7 +417,7 @@ func HTTPBodyContainsf(t T, handler http.HandlerFunc, method string, url string, t.FailNow() } -// HTTPBodyNotContainsf is the same as [HTTPBodyNotContains], but accepts a format msg string to format arguments like [fmt.Printf]. +// HTTPBodyNotContainsf is the same as [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 HTTPBodyNotContainsf(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ...any) { @@ -375,7 +431,7 @@ func HTTPBodyNotContainsf(t T, handler http.HandlerFunc, method string, url stri t.FailNow() } -// HTTPErrorf is the same as [HTTPError], but accepts a format msg string to format arguments like [fmt.Printf]. +// HTTPErrorf is the same as [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 HTTPErrorf(t T, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) { @@ -389,7 +445,7 @@ func HTTPErrorf(t T, handler http.HandlerFunc, method string, url string, values t.FailNow() } -// HTTPRedirectf is the same as [HTTPRedirect], but accepts a format msg string to format arguments like [fmt.Printf]. +// HTTPRedirectf is the same as [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 HTTPRedirectf(t T, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) { @@ -403,7 +459,7 @@ func HTTPRedirectf(t T, handler http.HandlerFunc, method string, url string, val t.FailNow() } -// HTTPStatusCodef is the same as [HTTPStatusCode], but accepts a format msg string to format arguments like [fmt.Printf]. +// HTTPStatusCodef is the same as [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 HTTPStatusCodef(t T, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...any) { @@ -417,7 +473,7 @@ func HTTPStatusCodef(t T, handler http.HandlerFunc, method string, url string, v t.FailNow() } -// HTTPSuccessf is the same as [HTTPSuccess], but accepts a format msg string to format arguments like [fmt.Printf]. +// HTTPSuccessf is the same as [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 HTTPSuccessf(t T, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) { @@ -431,7 +487,7 @@ func HTTPSuccessf(t T, handler http.HandlerFunc, method string, url string, valu t.FailNow() } -// Implementsf is the same as [Implements], but accepts a format msg string to format arguments like [fmt.Printf]. +// Implementsf is the same as [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 Implementsf(t T, interfaceObject any, object any, msg string, args ...any) { @@ -445,7 +501,7 @@ func Implementsf(t T, interfaceObject any, object any, msg string, args ...any) t.FailNow() } -// InDeltaf is the same as [InDelta], but accepts a format msg string to format arguments like [fmt.Printf]. +// InDeltaf is the same as [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 InDeltaf(t T, expected any, actual any, delta float64, msg string, args ...any) { @@ -459,7 +515,7 @@ func InDeltaf(t T, expected any, actual any, delta float64, msg string, args ... t.FailNow() } -// InDeltaMapValuesf is the same as [InDeltaMapValues], but accepts a format msg string to format arguments like [fmt.Printf]. +// InDeltaMapValuesf is the same as [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 InDeltaMapValuesf(t T, expected any, actual any, delta float64, msg string, args ...any) { @@ -473,7 +529,7 @@ func InDeltaMapValuesf(t T, expected any, actual any, delta float64, msg string, t.FailNow() } -// InDeltaSlicef is the same as [InDeltaSlice], but accepts a format msg string to format arguments like [fmt.Printf]. +// InDeltaSlicef is the same as [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 InDeltaSlicef(t T, expected any, actual any, delta float64, msg string, args ...any) { @@ -487,7 +543,21 @@ func InDeltaSlicef(t T, expected any, actual any, delta float64, msg string, arg t.FailNow() } -// InEpsilonf is the same as [InEpsilon], but accepts a format msg string to format arguments like [fmt.Printf]. +// InDeltaTf is the same as [InDeltaT], 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 InDeltaTf[Number Measurable](t T, expected Number, actual Number, delta Number, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.InDeltaT(t, expected, actual, delta, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// InEpsilonf is the same as [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 InEpsilonf(t T, expected any, actual any, epsilon float64, msg string, args ...any) { @@ -501,7 +571,7 @@ func InEpsilonf(t T, expected any, actual any, epsilon float64, msg string, args t.FailNow() } -// InEpsilonSlicef is the same as [InEpsilonSlice], but accepts a format msg string to format arguments like [fmt.Printf]. +// InEpsilonSlicef is the same as [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 InEpsilonSlicef(t T, expected any, actual any, epsilon float64, msg string, args ...any) { @@ -515,7 +585,21 @@ func InEpsilonSlicef(t T, expected any, actual any, epsilon float64, msg string, t.FailNow() } -// IsDecreasingf is the same as [IsDecreasing], but accepts a format msg string to format arguments like [fmt.Printf]. +// InEpsilonTf is the same as [InEpsilonT], 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 InEpsilonTf[Number Measurable](t T, expected Number, actual Number, epsilon float64, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.InEpsilonT(t, expected, actual, epsilon, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// IsDecreasingf is the same as [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 IsDecreasingf(t T, object any, msg string, args ...any) { @@ -529,7 +613,7 @@ func IsDecreasingf(t T, object any, msg string, args ...any) { t.FailNow() } -// IsIncreasingf is the same as [IsIncreasing], but accepts a format msg string to format arguments like [fmt.Printf]. +// IsIncreasingf is the same as [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 IsIncreasingf(t T, object any, msg string, args ...any) { @@ -543,7 +627,7 @@ func IsIncreasingf(t T, object any, msg string, args ...any) { t.FailNow() } -// IsNonDecreasingf is the same as [IsNonDecreasing], but accepts a format msg string to format arguments like [fmt.Printf]. +// IsNonDecreasingf is the same as [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 IsNonDecreasingf(t T, object any, msg string, args ...any) { @@ -557,7 +641,7 @@ func IsNonDecreasingf(t T, object any, msg string, args ...any) { t.FailNow() } -// IsNonIncreasingf is the same as [IsNonIncreasing], but accepts a format msg string to format arguments like [fmt.Printf]. +// IsNonIncreasingf is the same as [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 IsNonIncreasingf(t T, object any, msg string, args ...any) { @@ -571,7 +655,7 @@ func IsNonIncreasingf(t T, object any, msg string, args ...any) { t.FailNow() } -// IsNotTypef is the same as [IsNotType], but accepts a format msg string to format arguments like [fmt.Printf]. +// IsNotTypef is the same as [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 IsNotTypef(t T, theType any, object any, msg string, args ...any) { @@ -585,7 +669,7 @@ func IsNotTypef(t T, theType any, object any, msg string, args ...any) { t.FailNow() } -// IsTypef is the same as [IsType], but accepts a format msg string to format arguments like [fmt.Printf]. +// IsTypef is the same as [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 IsTypef(t T, expectedType any, object any, msg string, args ...any) { @@ -599,7 +683,7 @@ func IsTypef(t T, expectedType any, object any, msg string, args ...any) { t.FailNow() } -// JSONEqf is the same as [JSONEq], but accepts a format msg string to format arguments like [fmt.Printf]. +// JSONEqf is the same as [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 JSONEqf(t T, expected string, actual string, msg string, args ...any) { @@ -613,7 +697,7 @@ func JSONEqf(t T, expected string, actual string, msg string, args ...any) { t.FailNow() } -// JSONEqBytesf is the same as [JSONEqBytes], but accepts a format msg string to format arguments like [fmt.Printf]. +// JSONEqBytesf is the same as [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 JSONEqBytesf(t T, expected []byte, actual []byte, msg string, args ...any) { @@ -627,7 +711,21 @@ func JSONEqBytesf(t T, expected []byte, actual []byte, msg string, args ...any) t.FailNow() } -// Kindf is the same as [Kind], but accepts a format msg string to format arguments like [fmt.Printf]. +// JSONEqTf is the same as [JSONEqT], 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 JSONEqTf[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.JSONEqT(t, expected, actual, 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. func Kindf(t T, expectedKind reflect.Kind, object any, msg string, args ...any) { @@ -641,7 +739,7 @@ func Kindf(t T, expectedKind reflect.Kind, object any, msg string, args ...any) t.FailNow() } -// Lenf is the same as [Len], but accepts a format msg string to format arguments like [fmt.Printf]. +// Lenf is the same as [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 Lenf(t T, object any, length int, msg string, args ...any) { @@ -655,7 +753,7 @@ func Lenf(t T, object any, length int, msg string, args ...any) { t.FailNow() } -// Lessf is the same as [Less], but accepts a format msg string to format arguments like [fmt.Printf]. +// Lessf is the same as [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 Lessf(t T, e1 any, e2 any, msg string, args ...any) { @@ -669,7 +767,7 @@ func Lessf(t T, e1 any, e2 any, msg string, args ...any) { t.FailNow() } -// LessOrEqualf is the same as [LessOrEqual], but accepts a format msg string to format arguments like [fmt.Printf]. +// LessOrEqualf is the same as [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 LessOrEqualf(t T, e1 any, e2 any, msg string, args ...any) { @@ -683,7 +781,35 @@ func LessOrEqualf(t T, e1 any, e2 any, msg string, args ...any) { t.FailNow() } -// Negativef is the same as [Negative], but accepts a format msg string to format arguments like [fmt.Printf]. +// LessOrEqualTf is the same as [LessOrEqualT], 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 LessOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.LessOrEqualT(t, e1, e2, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// LessTf is the same as [LessT], 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 LessTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.LessT(t, e1, e2, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// Negativef is the same as [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 Negativef(t T, e any, msg string, args ...any) { @@ -697,7 +823,21 @@ func Negativef(t T, e any, msg string, args ...any) { t.FailNow() } -// Neverf is the same as [Never], but accepts a format msg string to format arguments like [fmt.Printf]. +// NegativeTf is the same as [NegativeT], 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 NegativeTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.NegativeT(t, e, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// Neverf is the same as [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 Neverf(t T, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...any) { @@ -711,7 +851,7 @@ func Neverf(t T, condition func() bool, waitFor time.Duration, tick time.Duratio t.FailNow() } -// Nilf is the same as [Nil], but accepts a format msg string to format arguments like [fmt.Printf]. +// Nilf is the same as [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 Nilf(t T, object any, msg string, args ...any) { @@ -725,7 +865,7 @@ func Nilf(t T, object any, msg string, args ...any) { t.FailNow() } -// NoDirExistsf is the same as [NoDirExists], but accepts a format msg string to format arguments like [fmt.Printf]. +// NoDirExistsf is the same as [NoDirExists], 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 NoDirExistsf(t T, path string, msg string, args ...any) { @@ -739,7 +879,7 @@ func NoDirExistsf(t T, path string, msg string, args ...any) { t.FailNow() } -// NoErrorf is the same as [NoError], but accepts a format msg string to format arguments like [fmt.Printf]. +// NoErrorf is the same as [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 NoErrorf(t T, err error, msg string, args ...any) { @@ -753,7 +893,7 @@ func NoErrorf(t T, err error, msg string, args ...any) { t.FailNow() } -// NoFileExistsf is the same as [NoFileExists], but accepts a format msg string to format arguments like [fmt.Printf]. +// NoFileExistsf is the same as [NoFileExists], 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 NoFileExistsf(t T, path string, msg string, args ...any) { @@ -767,7 +907,7 @@ func NoFileExistsf(t T, path string, msg string, args ...any) { t.FailNow() } -// NotContainsf is the same as [NotContains], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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. func NotContainsf(t T, s any, contains any, msg string, args ...any) { @@ -781,7 +921,7 @@ func NotContainsf(t T, s any, contains any, msg string, args ...any) { t.FailNow() } -// NotElementsMatchf is the same as [NotElementsMatch], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotElementsMatchf is the same as [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 NotElementsMatchf(t T, listA any, listB any, msg string, args ...any) { @@ -795,7 +935,21 @@ func NotElementsMatchf(t T, listA any, listB any, msg string, args ...any) { t.FailNow() } -// NotEmptyf is the same as [NotEmpty], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotElementsMatchTf is the same as [NotElementsMatchT], 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 NotElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.NotElementsMatchT(t, listA, listB, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// NotEmptyf is the same as [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 NotEmptyf(t T, object any, msg string, args ...any) { @@ -809,7 +963,7 @@ func NotEmptyf(t T, object any, msg string, args ...any) { t.FailNow() } -// NotEqualf is the same as [NotEqual], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotEqualf is the same as [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 NotEqualf(t T, expected any, actual any, msg string, args ...any) { @@ -823,7 +977,7 @@ func NotEqualf(t T, expected any, actual any, msg string, args ...any) { t.FailNow() } -// NotEqualValuesf is the same as [NotEqualValues], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotEqualValuesf is the same as [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 NotEqualValuesf(t T, expected any, actual any, msg string, args ...any) { @@ -837,7 +991,7 @@ func NotEqualValuesf(t T, expected any, actual any, msg string, args ...any) { t.FailNow() } -// NotErrorAsf is the same as [NotErrorAs], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotErrorAsf is the same as [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 NotErrorAsf(t T, err error, target any, msg string, args ...any) { @@ -851,7 +1005,7 @@ func NotErrorAsf(t T, err error, target any, msg string, args ...any) { t.FailNow() } -// NotErrorIsf is the same as [NotErrorIs], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotErrorIsf is the same as [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 NotErrorIsf(t T, err error, target error, msg string, args ...any) { @@ -865,7 +1019,7 @@ func NotErrorIsf(t T, err error, target error, msg string, args ...any) { t.FailNow() } -// NotImplementsf is the same as [NotImplements], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotImplementsf is the same as [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 NotImplementsf(t T, interfaceObject any, object any, msg string, args ...any) { @@ -879,7 +1033,7 @@ func NotImplementsf(t T, interfaceObject any, object any, msg string, args ...an t.FailNow() } -// NotKindf is the same as [NotKind], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotKindf is the same as [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 NotKindf(t T, expectedKind reflect.Kind, object any, msg string, args ...any) { @@ -893,7 +1047,7 @@ func NotKindf(t T, expectedKind reflect.Kind, object any, msg string, args ...an t.FailNow() } -// NotNilf is the same as [NotNil], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotNilf is the same as [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 NotNilf(t T, object any, msg string, args ...any) { @@ -907,7 +1061,7 @@ func NotNilf(t T, object any, msg string, args ...any) { t.FailNow() } -// NotPanicsf is the same as [NotPanics], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotPanicsf is the same as [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 NotPanicsf(t T, f assertions.PanicTestFunc, msg string, args ...any) { @@ -921,21 +1075,35 @@ func NotPanicsf(t T, f assertions.PanicTestFunc, msg string, args ...any) { t.FailNow() } -// NotRegexpf is the same as [NotRegexp], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotRegexpf is the same as [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 NotRegexpf(t T, rx any, str any, msg string, args ...any) { +func NotRegexpf(t T, rx any, actual any, msg string, args ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.NotRegexp(t, rx, str, forwardArgs(msg, args)) { + if assertions.NotRegexp(t, rx, actual, forwardArgs(msg, args)) { return } t.FailNow() } -// NotSamef is the same as [NotSame], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotRegexpTf is the same as [NotRegexpT], 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 NotRegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.NotRegexpT(t, rx, actual, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// NotSamef is the same as [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 NotSamef(t T, expected any, actual any, msg string, args ...any) { @@ -949,7 +1117,7 @@ func NotSamef(t T, expected any, actual any, msg string, args ...any) { t.FailNow() } -// NotSubsetf is the same as [NotSubset], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotSubsetf is the same as [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 NotSubsetf(t T, list any, subset any, msg string, args ...any) { @@ -963,7 +1131,7 @@ func NotSubsetf(t T, list any, subset any, msg string, args ...any) { t.FailNow() } -// NotZerof is the same as [NotZero], but accepts a format msg string to format arguments like [fmt.Printf]. +// NotZerof is the same as [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 NotZerof(t T, i any, msg string, args ...any) { @@ -977,7 +1145,7 @@ func NotZerof(t T, i any, msg string, args ...any) { t.FailNow() } -// Panicsf is the same as [Panics], but accepts a format msg string to format arguments like [fmt.Printf]. +// Panicsf is the same as [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 Panicsf(t T, f assertions.PanicTestFunc, msg string, args ...any) { @@ -991,7 +1159,7 @@ func Panicsf(t T, f assertions.PanicTestFunc, msg string, args ...any) { t.FailNow() } -// PanicsWithErrorf is the same as [PanicsWithError], but accepts a format msg string to format arguments like [fmt.Printf]. +// PanicsWithErrorf is the same as [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 PanicsWithErrorf(t T, errString string, f assertions.PanicTestFunc, msg string, args ...any) { @@ -1005,7 +1173,7 @@ func PanicsWithErrorf(t T, errString string, f assertions.PanicTestFunc, msg str t.FailNow() } -// PanicsWithValuef is the same as [PanicsWithValue], but accepts a format msg string to format arguments like [fmt.Printf]. +// PanicsWithValuef is the same as [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 PanicsWithValuef(t T, expected any, f assertions.PanicTestFunc, msg string, args ...any) { @@ -1019,7 +1187,7 @@ func PanicsWithValuef(t T, expected any, f assertions.PanicTestFunc, msg string, t.FailNow() } -// Positivef is the same as [Positive], but accepts a format msg string to format arguments like [fmt.Printf]. +// Positivef is the same as [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 Positivef(t T, e any, msg string, args ...any) { @@ -1033,21 +1201,49 @@ func Positivef(t T, e any, msg string, args ...any) { t.FailNow() } -// Regexpf is the same as [Regexp], but accepts a format msg string to format arguments like [fmt.Printf]. +// PositiveTf is the same as [PositiveT], 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 Regexpf(t T, rx any, str any, msg string, args ...any) { +func PositiveTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, args ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.Regexp(t, rx, str, forwardArgs(msg, args)) { + if assertions.PositiveT(t, e, forwardArgs(msg, args)) { return } t.FailNow() } -// Samef is the same as [Same], but accepts a format msg string to format arguments like [fmt.Printf]. +// Regexpf is the same as [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 Regexpf(t T, rx any, actual any, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.Regexp(t, rx, actual, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// RegexpTf is the same as [RegexpT], 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 RegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.RegexpT(t, rx, actual, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// Samef is the same as [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 Samef(t T, expected any, actual any, msg string, args ...any) { @@ -1061,7 +1257,7 @@ func Samef(t T, expected any, actual any, msg string, args ...any) { t.FailNow() } -// Subsetf is the same as [Subset], but accepts a format msg string to format arguments like [fmt.Printf]. +// Subsetf is the same as [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 Subsetf(t T, list any, subset any, msg string, args ...any) { @@ -1075,7 +1271,7 @@ func Subsetf(t T, list any, subset any, msg string, args ...any) { t.FailNow() } -// Truef is the same as [True], but accepts a format msg string to format arguments like [fmt.Printf]. +// Truef is the same as [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 Truef(t T, value bool, msg string, args ...any) { @@ -1089,7 +1285,21 @@ func Truef(t T, value bool, msg string, args ...any) { t.FailNow() } -// WithinDurationf is the same as [WithinDuration], but accepts a format msg string to format arguments like [fmt.Printf]. +// TrueTf is the same as [TrueT], 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 TrueTf[B Boolean](t T, value B, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.TrueT(t, value, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// WithinDurationf is the same as [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 WithinDurationf(t T, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...any) { @@ -1103,7 +1313,7 @@ func WithinDurationf(t T, expected time.Time, actual time.Time, delta time.Durat t.FailNow() } -// WithinRangef is the same as [WithinRange], but accepts a format msg string to format arguments like [fmt.Printf]. +// WithinRangef is the same as [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 WithinRangef(t T, actual time.Time, start time.Time, end time.Time, msg string, args ...any) { @@ -1117,7 +1327,7 @@ func WithinRangef(t T, actual time.Time, start time.Time, end time.Time, msg str t.FailNow() } -// YAMLEqf is the same as [YAMLEq], but accepts a format msg string to format arguments like [fmt.Printf]. +// YAMLEqf is the same as [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 YAMLEqf(t T, expected string, actual string, msg string, args ...any) { @@ -1131,7 +1341,35 @@ func YAMLEqf(t T, expected string, actual string, msg string, args ...any) { t.FailNow() } -// Zerof is the same as [Zero], but accepts a format msg string to format arguments like [fmt.Printf]. +// YAMLEqBytesf is the same as [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 YAMLEqBytesf(t T, expected []byte, actual []byte, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.YAMLEqBytes(t, expected, actual, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// YAMLEqTf is the same as [YAMLEqT], 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 YAMLEqTf[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.YAMLEqT(t, expected, actual, 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. func Zerof(t T, i any, msg string, args ...any) { diff --git a/require/require_format_test.go b/require/require_format_test.go index 2dff348ed..d9874bcc5 100644 --- a/require/require_format_test.go +++ b/require/require_format_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package require @@ -97,6 +97,26 @@ func TestElementsMatchf(t *testing.T) { }) } +func TestElementsMatchTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + ElementsMatchTf(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + ElementsMatchTf(mock, []int{1, 2, 3}, []int{1, 2, 4}, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("ElementsMatchT should call FailNow()") + } + }) +} + func TestEmptyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -385,6 +405,26 @@ func TestFalsef(t *testing.T) { }) } +func TestFalseTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + FalseTf(t, 1 == 0, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + FalseTf(mock, 1 == 1, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("FalseT should call FailNow()") + } + }) +} + func TestFileEmptyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -485,6 +525,46 @@ func TestGreaterOrEqualf(t *testing.T) { }) } +func TestGreaterOrEqualTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + GreaterOrEqualTf(t, 2, 1, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + GreaterOrEqualTf(mock, 1, 2, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("GreaterOrEqualT should call FailNow()") + } + }) +} + +func TestGreaterTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + GreaterTf(t, 2, 1, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + GreaterTf(mock, 1, 2, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("GreaterT should call FailNow()") + } + }) +} + func TestHTTPBodyContainsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -685,6 +765,26 @@ func TestInDeltaSlicef(t *testing.T) { }) } +func TestInDeltaTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + InDeltaTf(t, 1.0, 1.01, 0.02, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + InDeltaTf(mock, 1.0, 1.1, 0.05, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("InDeltaT should call FailNow()") + } + }) +} + func TestInEpsilonf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -725,6 +825,26 @@ func TestInEpsilonSlicef(t *testing.T) { }) } +func TestInEpsilonTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + InEpsilonTf(t, 100.0, 101.0, 0.02, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + InEpsilonTf(mock, 100.0, 110.0, 0.05, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("InEpsilonT should call FailNow()") + } + }) +} + func TestIsDecreasingf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -885,6 +1005,26 @@ func TestJSONEqBytesf(t *testing.T) { }) } +func TestJSONEqTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + JSONEqTf(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`), "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + JSONEqTf(mock, `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]`, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("JSONEqT should call FailNow()") + } + }) +} + func TestKindf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -965,6 +1105,46 @@ func TestLessOrEqualf(t *testing.T) { }) } +func TestLessOrEqualTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + LessOrEqualTf(t, 1, 2, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + LessOrEqualTf(mock, 2, 1, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("LessOrEqualT should call FailNow()") + } + }) +} + +func TestLessTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + LessTf(t, 1, 2, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + LessTf(mock, 2, 1, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("LessT should call FailNow()") + } + }) +} + func TestNegativef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -985,6 +1165,26 @@ func TestNegativef(t *testing.T) { }) } +func TestNegativeTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + NegativeTf(t, -1, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + NegativeTf(mock, 1, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("NegativeT should call FailNow()") + } + }) +} + func TestNeverf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1125,6 +1325,26 @@ func TestNotElementsMatchf(t *testing.T) { }) } +func TestNotElementsMatchTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + NotElementsMatchTf(t, []int{1, 2, 3}, []int{1, 2, 4}, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + NotElementsMatchTf(mock, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("NotElementsMatchT should call FailNow()") + } + }) +} + func TestNotEmptyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1325,6 +1545,26 @@ func TestNotRegexpf(t *testing.T) { }) } +func TestNotRegexpTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + NotRegexpTf(t, "^start", "not starting", "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + NotRegexpTf(mock, "^start", "starting", "test message") + // require functions don't return a value + if !mock.failed { + t.Error("NotRegexpT should call FailNow()") + } + }) +} + func TestNotSamef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1465,6 +1705,26 @@ func TestPositivef(t *testing.T) { }) } +func TestPositiveTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + PositiveTf(t, 1, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + PositiveTf(mock, -1, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("PositiveT should call FailNow()") + } + }) +} + func TestRegexpf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1485,6 +1745,26 @@ func TestRegexpf(t *testing.T) { }) } +func TestRegexpTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + RegexpTf(t, "^start", "starting", "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + RegexpTf(mock, "^start", "not starting", "test message") + // require functions don't return a value + if !mock.failed { + t.Error("RegexpT should call FailNow()") + } + }) +} + func TestSamef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1545,6 +1825,26 @@ func TestTruef(t *testing.T) { }) } +func TestTrueTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + TrueTf(t, 1 == 1, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + TrueTf(mock, 1 == 0, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("TrueT should call FailNow()") + } + }) +} + func TestWithinDurationf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1596,6 +1896,28 @@ func TestYAMLEqf(t *testing.T) { }) } +func TestYAMLEqBytesf(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + Panicsf(t, func() { + YAMLEqBytesf(t, []byte("key: value"), []byte("key: value"), "test message") + }, "should panic without the yaml feature enabled.") + }) +} + +func TestYAMLEqTf(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + Panicsf(t, func() { + YAMLEqTf(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 ef46dee5f..63b71a553 100644 --- a/require/require_forward.go +++ b/require/require_forward.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package require @@ -45,7 +45,7 @@ func (a *Assertions) Condition(comp Comparison, msgAndArgs ...any) { a.t.FailNow() } -// Conditionf is the same as [Assertions.Condition], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -73,7 +73,7 @@ func (a *Assertions) Contains(s any, contains any, msgAndArgs ...any) { a.t.FailNow() } -// Containsf is the same as [Assertions.Contains], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -101,7 +101,7 @@ func (a *Assertions) DirExists(path string, msgAndArgs ...any) { a.t.FailNow() } -// DirExistsf is the same as [Assertions.DirExists], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -129,7 +129,7 @@ func (a *Assertions) ElementsMatch(listA any, listB any, msgAndArgs ...any) { a.t.FailNow() } -// ElementsMatchf is the same as [Assertions.ElementsMatch], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -157,7 +157,7 @@ func (a *Assertions) Empty(object any, msgAndArgs ...any) { a.t.FailNow() } -// Emptyf is the same as [Assertions.Empty], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -185,7 +185,7 @@ func (a *Assertions) Equal(expected any, actual any, msgAndArgs ...any) { a.t.FailNow() } -// Equalf is the same as [Assertions.Equal], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -202,25 +202,25 @@ func (a *Assertions) Equalf(expected any, actual any, msg string, args ...any) { // 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(theError error, errString string, msgAndArgs ...any) { +func (a *Assertions) EqualError(err error, errString string, msgAndArgs ...any) { if h, ok := a.t.(H); ok { h.Helper() } - if assertions.EqualError(a.t, theError, errString, msgAndArgs...) { + if assertions.EqualError(a.t, err, errString, msgAndArgs...) { return } a.t.FailNow() } -// EqualErrorf is the same as [Assertions.EqualError], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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(theError error, errString string, msg string, args ...any) { +func (a *Assertions) EqualErrorf(err error, errString string, msg string, args ...any) { if h, ok := a.t.(H); ok { h.Helper() } - if assertions.EqualError(a.t, theError, errString, forwardArgs(msg, args)) { + if assertions.EqualError(a.t, err, errString, forwardArgs(msg, args)) { return } @@ -241,7 +241,7 @@ func (a *Assertions) EqualExportedValues(expected any, actual any, msgAndArgs .. a.t.FailNow() } -// EqualExportedValuesf is the same as [Assertions.EqualExportedValues], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -269,7 +269,7 @@ func (a *Assertions) EqualValues(expected any, actual any, msgAndArgs ...any) { a.t.FailNow() } -// EqualValuesf is the same as [Assertions.EqualValues], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -297,7 +297,7 @@ func (a *Assertions) Error(err error, msgAndArgs ...any) { a.t.FailNow() } -// Errorf is the same as [Assertions.Error], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -325,7 +325,7 @@ func (a *Assertions) ErrorAs(err error, target any, msgAndArgs ...any) { a.t.FailNow() } -// ErrorAsf is the same as [Assertions.ErrorAs], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -342,25 +342,25 @@ func (a *Assertions) ErrorAsf(err error, target any, msg string, args ...any) { // 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(theError error, contains string, msgAndArgs ...any) { +func (a *Assertions) ErrorContains(err error, contains string, msgAndArgs ...any) { if h, ok := a.t.(H); ok { h.Helper() } - if assertions.ErrorContains(a.t, theError, contains, msgAndArgs...) { + if assertions.ErrorContains(a.t, err, contains, msgAndArgs...) { return } a.t.FailNow() } -// ErrorContainsf is the same as [Assertions.ErrorContains], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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(theError error, contains string, msg string, args ...any) { +func (a *Assertions) ErrorContainsf(err error, contains string, msg string, args ...any) { if h, ok := a.t.(H); ok { h.Helper() } - if assertions.ErrorContains(a.t, theError, contains, forwardArgs(msg, args)) { + if assertions.ErrorContains(a.t, err, contains, forwardArgs(msg, args)) { return } @@ -381,7 +381,7 @@ func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...any) { a.t.FailNow() } -// ErrorIsf is the same as [Assertions.ErrorIs], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -409,7 +409,7 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti a.t.FailNow() } -// Eventuallyf is the same as [Assertions.Eventually], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -437,7 +437,7 @@ func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor a.t.FailNow() } -// EventuallyWithTf is the same as [Assertions.EventuallyWithT], but accepts a format msg string to format arguments like [fmt.Printf]. +// EventuallyWithTf is the same as [Assertions.EventuallyWithT], 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) { @@ -465,7 +465,7 @@ func (a *Assertions) Exactly(expected any, actual any, msgAndArgs ...any) { a.t.FailNow() } -// Exactlyf is the same as [Assertions.Exactly], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -491,7 +491,7 @@ func (a *Assertions) Fail(failureMessage string, msgAndArgs ...any) { a.t.FailNow() } -// Failf is the same as [Assertions.Fail], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -515,7 +515,7 @@ func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...any) { a.t.FailNow() } -// FailNowf is the same as [Assertions.FailNow], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -541,7 +541,7 @@ func (a *Assertions) False(value bool, msgAndArgs ...any) { a.t.FailNow() } -// Falsef is the same as [Assertions.False], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -569,7 +569,7 @@ func (a *Assertions) FileEmpty(path string, msgAndArgs ...any) { a.t.FailNow() } -// FileEmptyf is the same as [Assertions.FileEmpty], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -597,7 +597,7 @@ func (a *Assertions) FileExists(path string, msgAndArgs ...any) { a.t.FailNow() } -// FileExistsf is the same as [Assertions.FileExists], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -625,7 +625,7 @@ func (a *Assertions) FileNotEmpty(path string, msgAndArgs ...any) { a.t.FailNow() } -// FileNotEmptyf is the same as [Assertions.FileNotEmpty], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -653,7 +653,7 @@ func (a *Assertions) Greater(e1 any, e2 any, msgAndArgs ...any) { a.t.FailNow() } -// Greaterf is the same as [Assertions.Greater], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -681,7 +681,7 @@ func (a *Assertions) GreaterOrEqual(e1 any, e2 any, msgAndArgs ...any) { a.t.FailNow() } -// GreaterOrEqualf is the same as [Assertions.GreaterOrEqual], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -709,7 +709,7 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, u a.t.FailNow() } -// HTTPBodyContainsf is the same as [Assertions.HTTPBodyContains], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -737,7 +737,7 @@ func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string a.t.FailNow() } -// HTTPBodyNotContainsf is the same as [Assertions.HTTPBodyNotContains], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -765,7 +765,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri a.t.FailNow() } -// HTTPErrorf is the same as [Assertions.HTTPError], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -793,7 +793,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s a.t.FailNow() } -// HTTPRedirectf is the same as [Assertions.HTTPRedirect], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -821,7 +821,7 @@ func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url a.t.FailNow() } -// HTTPStatusCodef is the same as [Assertions.HTTPStatusCode], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -849,7 +849,7 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url st a.t.FailNow() } -// HTTPSuccessf is the same as [Assertions.HTTPSuccess], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -877,7 +877,7 @@ func (a *Assertions) Implements(interfaceObject any, object any, msgAndArgs ...a a.t.FailNow() } -// Implementsf is the same as [Assertions.Implements], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -905,7 +905,7 @@ func (a *Assertions) InDelta(expected any, actual any, delta float64, msgAndArgs a.t.FailNow() } -// InDeltaf is the same as [Assertions.InDelta], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -933,7 +933,7 @@ func (a *Assertions) InDeltaMapValues(expected any, actual any, delta float64, m a.t.FailNow() } -// InDeltaMapValuesf is the same as [Assertions.InDeltaMapValues], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -961,7 +961,7 @@ func (a *Assertions) InDeltaSlice(expected any, actual any, delta float64, msgAn a.t.FailNow() } -// InDeltaSlicef is the same as [Assertions.InDeltaSlice], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -989,7 +989,7 @@ func (a *Assertions) InEpsilon(expected any, actual any, epsilon float64, msgAnd a.t.FailNow() } -// InEpsilonf is the same as [Assertions.InEpsilon], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1017,7 +1017,7 @@ func (a *Assertions) InEpsilonSlice(expected any, actual any, epsilon float64, m a.t.FailNow() } -// InEpsilonSlicef is the same as [Assertions.InEpsilonSlice], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1045,7 +1045,7 @@ func (a *Assertions) IsDecreasing(object any, msgAndArgs ...any) { a.t.FailNow() } -// IsDecreasingf is the same as [Assertions.IsDecreasing], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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(object any, msg string, args ...any) { @@ -1073,7 +1073,7 @@ func (a *Assertions) IsIncreasing(object any, msgAndArgs ...any) { a.t.FailNow() } -// IsIncreasingf is the same as [Assertions.IsIncreasing], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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(object any, msg string, args ...any) { @@ -1101,7 +1101,7 @@ func (a *Assertions) IsNonDecreasing(object any, msgAndArgs ...any) { a.t.FailNow() } -// IsNonDecreasingf is the same as [Assertions.IsNonDecreasing], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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(object any, msg string, args ...any) { @@ -1129,7 +1129,7 @@ func (a *Assertions) IsNonIncreasing(object any, msgAndArgs ...any) { a.t.FailNow() } -// IsNonIncreasingf is the same as [Assertions.IsNonIncreasing], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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(object any, msg string, args ...any) { @@ -1157,7 +1157,7 @@ func (a *Assertions) IsNotType(theType any, object any, msgAndArgs ...any) { a.t.FailNow() } -// IsNotTypef is the same as [Assertions.IsNotType], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1185,7 +1185,7 @@ func (a *Assertions) IsType(expectedType any, object any, msgAndArgs ...any) { a.t.FailNow() } -// IsTypef is the same as [Assertions.IsType], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1213,7 +1213,7 @@ func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...any) { a.t.FailNow() } -// JSONEqf is the same as [Assertions.JSONEq], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1241,7 +1241,7 @@ func (a *Assertions) JSONEqBytes(expected []byte, actual []byte, msgAndArgs ...a a.t.FailNow() } -// JSONEqBytesf is the same as [Assertions.JSONEqBytes], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1269,7 +1269,7 @@ func (a *Assertions) Kind(expectedKind reflect.Kind, object any, msgAndArgs ...a a.t.FailNow() } -// Kindf is the same as [Assertions.Kind], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1297,7 +1297,7 @@ func (a *Assertions) Len(object any, length int, msgAndArgs ...any) { a.t.FailNow() } -// Lenf is the same as [Assertions.Len], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1325,7 +1325,7 @@ func (a *Assertions) Less(e1 any, e2 any, msgAndArgs ...any) { a.t.FailNow() } -// Lessf is the same as [Assertions.Less], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1353,7 +1353,7 @@ func (a *Assertions) LessOrEqual(e1 any, e2 any, msgAndArgs ...any) { a.t.FailNow() } -// LessOrEqualf is the same as [Assertions.LessOrEqual], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1381,7 +1381,7 @@ func (a *Assertions) Negative(e any, msgAndArgs ...any) { a.t.FailNow() } -// Negativef is the same as [Assertions.Negative], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1409,7 +1409,7 @@ func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick ti a.t.FailNow() } -// Neverf is the same as [Assertions.Never], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1437,7 +1437,7 @@ func (a *Assertions) Nil(object any, msgAndArgs ...any) { a.t.FailNow() } -// Nilf is the same as [Assertions.Nil], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1465,7 +1465,7 @@ func (a *Assertions) NoDirExists(path string, msgAndArgs ...any) { a.t.FailNow() } -// NoDirExistsf is the same as [Assertions.NoDirExists], but accepts a format msg string to format arguments like [fmt.Printf]. +// NoDirExistsf is the same as [Assertions.NoDirExists], 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) NoDirExistsf(path string, msg string, args ...any) { @@ -1493,7 +1493,7 @@ func (a *Assertions) NoError(err error, msgAndArgs ...any) { a.t.FailNow() } -// NoErrorf is the same as [Assertions.NoError], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1521,7 +1521,7 @@ func (a *Assertions) NoFileExists(path string, msgAndArgs ...any) { a.t.FailNow() } -// NoFileExistsf is the same as [Assertions.NoFileExists], but accepts a format msg string to format arguments like [fmt.Printf]. +// NoFileExistsf is the same as [Assertions.NoFileExists], 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) NoFileExistsf(path string, msg string, args ...any) { @@ -1549,7 +1549,7 @@ func (a *Assertions) NotContains(s any, contains any, msgAndArgs ...any) { a.t.FailNow() } -// NotContainsf is the same as [Assertions.NotContains], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1577,7 +1577,7 @@ func (a *Assertions) NotElementsMatch(listA any, listB any, msgAndArgs ...any) { a.t.FailNow() } -// NotElementsMatchf is the same as [Assertions.NotElementsMatch], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1605,7 +1605,7 @@ func (a *Assertions) NotEmpty(object any, msgAndArgs ...any) { a.t.FailNow() } -// NotEmptyf is the same as [Assertions.NotEmpty], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1633,7 +1633,7 @@ func (a *Assertions) NotEqual(expected any, actual any, msgAndArgs ...any) { a.t.FailNow() } -// NotEqualf is the same as [Assertions.NotEqual], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1661,7 +1661,7 @@ func (a *Assertions) NotEqualValues(expected any, actual any, msgAndArgs ...any) a.t.FailNow() } -// NotEqualValuesf is the same as [Assertions.NotEqualValues], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1689,7 +1689,7 @@ func (a *Assertions) NotErrorAs(err error, target any, msgAndArgs ...any) { a.t.FailNow() } -// NotErrorAsf is the same as [Assertions.NotErrorAs], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1717,7 +1717,7 @@ func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...any) { a.t.FailNow() } -// NotErrorIsf is the same as [Assertions.NotErrorIs], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1745,7 +1745,7 @@ func (a *Assertions) NotImplements(interfaceObject any, object any, msgAndArgs . a.t.FailNow() } -// NotImplementsf is the same as [Assertions.NotImplements], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1773,7 +1773,7 @@ func (a *Assertions) NotKind(expectedKind reflect.Kind, object any, msgAndArgs . a.t.FailNow() } -// NotKindf is the same as [Assertions.NotKind], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1801,7 +1801,7 @@ func (a *Assertions) NotNil(object any, msgAndArgs ...any) { a.t.FailNow() } -// NotNilf is the same as [Assertions.NotNil], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1829,7 +1829,7 @@ func (a *Assertions) NotPanics(f assertions.PanicTestFunc, msgAndArgs ...any) { a.t.FailNow() } -// NotPanicsf is the same as [Assertions.NotPanics], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 assertions.PanicTestFunc, msg string, args ...any) { @@ -1846,25 +1846,25 @@ func (a *Assertions) NotPanicsf(f assertions.PanicTestFunc, msg string, 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 stops execution. -func (a *Assertions) NotRegexp(rx any, str any, msgAndArgs ...any) { +func (a *Assertions) NotRegexp(rx any, actual any, msgAndArgs ...any) { if h, ok := a.t.(H); ok { h.Helper() } - if assertions.NotRegexp(a.t, rx, str, msgAndArgs...) { + if assertions.NotRegexp(a.t, rx, actual, msgAndArgs...) { return } a.t.FailNow() } -// NotRegexpf is the same as [Assertions.NotRegexp], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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, str any, msg string, args ...any) { +func (a *Assertions) NotRegexpf(rx any, actual any, msg string, args ...any) { if h, ok := a.t.(H); ok { h.Helper() } - if assertions.NotRegexp(a.t, rx, str, forwardArgs(msg, args)) { + if assertions.NotRegexp(a.t, rx, actual, forwardArgs(msg, args)) { return } @@ -1885,7 +1885,7 @@ func (a *Assertions) NotSame(expected any, actual any, msgAndArgs ...any) { a.t.FailNow() } -// NotSamef is the same as [Assertions.NotSame], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1913,7 +1913,7 @@ func (a *Assertions) NotSubset(list any, subset any, msgAndArgs ...any) { a.t.FailNow() } -// NotSubsetf is the same as [Assertions.NotSubset], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1941,7 +1941,7 @@ func (a *Assertions) NotZero(i any, msgAndArgs ...any) { a.t.FailNow() } -// NotZerof is the same as [Assertions.NotZero], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -1969,7 +1969,7 @@ func (a *Assertions) Panics(f assertions.PanicTestFunc, msgAndArgs ...any) { a.t.FailNow() } -// Panicsf is the same as [Assertions.Panics], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 assertions.PanicTestFunc, msg string, args ...any) { @@ -1997,7 +1997,7 @@ func (a *Assertions) PanicsWithError(errString string, f assertions.PanicTestFun a.t.FailNow() } -// PanicsWithErrorf is the same as [Assertions.PanicsWithError], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 assertions.PanicTestFunc, msg string, args ...any) { @@ -2025,7 +2025,7 @@ func (a *Assertions) PanicsWithValue(expected any, f assertions.PanicTestFunc, m a.t.FailNow() } -// PanicsWithValuef is the same as [Assertions.PanicsWithValue], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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 assertions.PanicTestFunc, msg string, args ...any) { @@ -2053,7 +2053,7 @@ func (a *Assertions) Positive(e any, msgAndArgs ...any) { a.t.FailNow() } -// Positivef is the same as [Assertions.Positive], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -2070,25 +2070,25 @@ func (a *Assertions) Positivef(e any, msg string, args ...any) { // 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, str any, msgAndArgs ...any) { +func (a *Assertions) Regexp(rx any, actual any, msgAndArgs ...any) { if h, ok := a.t.(H); ok { h.Helper() } - if assertions.Regexp(a.t, rx, str, msgAndArgs...) { + if assertions.Regexp(a.t, rx, actual, msgAndArgs...) { return } a.t.FailNow() } -// Regexpf is the same as [Assertions.Regexp], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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, str any, msg string, args ...any) { +func (a *Assertions) Regexpf(rx any, actual any, msg string, args ...any) { if h, ok := a.t.(H); ok { h.Helper() } - if assertions.Regexp(a.t, rx, str, forwardArgs(msg, args)) { + if assertions.Regexp(a.t, rx, actual, forwardArgs(msg, args)) { return } @@ -2109,7 +2109,7 @@ func (a *Assertions) Same(expected any, actual any, msgAndArgs ...any) { a.t.FailNow() } -// Samef is the same as [Assertions.Same], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -2137,7 +2137,7 @@ func (a *Assertions) Subset(list any, subset any, msgAndArgs ...any) { a.t.FailNow() } -// Subsetf is the same as [Assertions.Subset], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -2165,7 +2165,7 @@ func (a *Assertions) True(value bool, msgAndArgs ...any) { a.t.FailNow() } -// Truef is the same as [Assertions.True], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -2193,7 +2193,7 @@ func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta a.t.FailNow() } -// WithinDurationf is the same as [Assertions.WithinDuration], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -2221,7 +2221,7 @@ func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Tim a.t.FailNow() } -// WithinRangef is the same as [Assertions.WithinRange], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -2249,7 +2249,7 @@ func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...any) { a.t.FailNow() } -// YAMLEqf is the same as [Assertions.YAMLEq], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { @@ -2263,6 +2263,34 @@ func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args .. 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 { + h.Helper() + } + if assertions.YAMLEqBytes(a.t, expected, actual, msgAndArgs...) { + return + } + + 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 { + h.Helper() + } + if assertions.YAMLEqBytes(a.t, expected, actual, forwardArgs(msg, args)) { + return + } + + 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. @@ -2277,7 +2305,7 @@ func (a *Assertions) Zero(i any, msgAndArgs ...any) { a.t.FailNow() } -// Zerof is the same as [Assertions.Zero], but accepts a format msg string to format arguments like [fmt.Printf]. +// 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) { diff --git a/require/require_forward_test.go b/require/require_forward_test.go index f809f75a1..8727e84a1 100644 --- a/require/require_forward_test.go +++ b/require/require_forward_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package require @@ -3880,6 +3880,30 @@ func TestAssertionsYAMLEqf(t *testing.T) { }) } +func TestAssertionsYAMLEqBytes(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + a := New(t) + a.Panics(func() { + a.YAMLEqBytes([]byte("key: value"), []byte("key: value")) + }, "should panic without the yaml feature enabled.") + }) +} + +func TestAssertionsYAMLEqBytesf(t *testing.T) { + t.Parallel() + t.Run("panic", func(t *testing.T) { + t.Parallel() + + a := New(t) + a.Panics(func() { + a.YAMLEqBytesf([]byte("key: value"), []byte("key: value"), "test message") + }, "should panic without the yaml feature enabled.") + }) +} + func TestAssertionsZero(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 bbbbbc772..3c81e56c2 100644 --- a/require/require_helpers.go +++ b/require/require_helpers.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package require diff --git a/require/require_helpers_test.go b/require/require_helpers_test.go index f4f8ea917..432ad7258 100644 --- a/require/require_helpers_test.go +++ b/require/require_helpers_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package require diff --git a/require/require_types.go b/require/require_types.go index 54ef701d8..073f5bdf0 100644 --- a/require/require_types.go +++ b/require/require_types.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-11 (version ca82e58) using codegen version v2.1.9-0.20260111184010-ca82e58db12c+dirty [sha: ca82e58db12cbb61bfcae58c3684b3add9599d10] +// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] package require @@ -24,6 +24,9 @@ type ( // for table driven tests. BoolAssertionFunc func(T, bool, ...any) + // Boolean is a bool or any type that can be converted to a bool. + Boolean = assertions.Boolean + // CollectT implements the [T] interface and collects all errors. // // [CollectT] is specifically intended to be used with [EventuallyWithT] and @@ -42,9 +45,23 @@ type ( ErrorAssertionFunc func(T, error, ...any) // H is an interface for types that implement the Helper method. - // This allows marking functions as test helpers. + // This allows marking functions as test helpers, e.g. [testing.T.Helper]. H = assertions.H + // Measurable is any number for which we can compute a delta (floats or integers). + // + // This is used by [InDeltaT] and [InEpsilonT]. + // + // NOTE: unfortunately complex64 and complex128 are not supported. + Measurable = assertions.Measurable + + // Ordered is a standard ordered type (i.e. types that support "<": [cmp.Ordered]) plus []byte and [time.Time]. + // + // This is used by [GreaterT], [GreaterOrEqualT], [LessT], and [LessOrEqualT]. + // + // NOTE: since [time.Time] is a struct, custom types which redeclare [time.Time] are not supported. + Ordered = assertions.Ordered + // PanicAssertionFunc is a common function prototype when validating a panic value. Can be useful // for table driven tests. PanicAssertionFunc func(t T, f assertions.PanicTestFunc, msgAndArgs ...any) @@ -53,12 +70,32 @@ type ( // methods, and represents a simple func that takes no arguments, and returns nothing. PanicTestFunc = assertions.PanicTestFunc + // RegExp is either a text containing a regular expression to compile (string or []byte), or directly the compiled regexp. + // + // This is used by [RegexpT] and [NotRegexpT]. + RegExp = assertions.RegExp + + // SignedNumeric is a signed integer or a floating point number or any type that can be converted to one of these. + SignedNumeric = assertions.SignedNumeric + // T is an interface wrapper around [testing.T]. T interface { assertions.T FailNow() } + // Text is any type of underlying type string or []byte. + // + // This is used by [RegexpT], [NotRegexpT], [JSONEqT], and [YAMLEqT]. + // + // NOTE: unfortunately, []rune is not supported. + Text = assertions.Text + + // UnsignedNumeric is an unsigned integer. + // + // NOTE: there are no unsigned floating point numbers. + UnsignedNumeric = assertions.UnsignedNumeric + // ValueAssertionFunc is a common function prototype when validating a single value. Can be useful // for table driven tests. ValueAssertionFunc func(T, any, ...any)