diff --git a/assert/assert_assertions.go b/assert/assert_assertions.go index 1b8325f24..e8ae550d8 100644 --- a/assert/assert_assertions.go +++ b/assert/assert_assertions.go @@ -2,11 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package assert import ( + "iter" "net/http" "net/url" "reflect" @@ -76,6 +77,26 @@ func DirExists(t T, path string, msgAndArgs ...any) bool { return assertions.DirExists(t, path, msgAndArgs...) } +// DirNotExists checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +// +// # Usage +// +// assertions.DirNotExists(t, "path/to/directory") +// +// # Examples +// +// success: filepath.Join(testDataPath(),"non_existing_dir") +// failure: filepath.Join(testDataPath(),"existing_dir") +// +// Upon failure, the test [T] is marked as failed and continues execution. +func DirNotExists(t T, path string, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.DirNotExists(t, path, msgAndArgs...) +} + // 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, // the number of appearances of each of them in both lists should match. @@ -115,7 +136,7 @@ func ElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) if h, ok := t.(H); ok { h.Helper() } - return assertions.ElementsMatchT(t, listA, listB, msgAndArgs...) + return assertions.ElementsMatchT[E](t, listA, listB, msgAndArgs...) } // Empty asserts that the given value is "empty". @@ -218,6 +239,34 @@ func EqualExportedValues(t T, expected any, actual any, msgAndArgs ...any) bool return assertions.EqualExportedValues(t, expected, actual, msgAndArgs...) } +// EqualT asserts that two objects of the same comparable type are equal. +// +// Pointer variable equality is determined based on the equality of the memory addresses (unlike [Equal], but like [Same]). +// +// Functions, slices and maps are not comparable. See also [ComparisonOperators]. +// +// If you need to compare values of non-comparable types, or compare pointers by the value they point to, +// use [Equal] instead. +// +// # Usage +// +// assertions.EqualT(t, 123, 123) +// +// # Examples +// +// success: 123, 123 +// failure: 123, 456 +// +// Upon failure, the test [T] is marked as failed and continues execution. +// +// [ComparisonOperators]: https://go.dev/ref/spec#Comparison_operators. +func EqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.EqualT[V](t, expected, actual, msgAndArgs...) +} + // EqualValues asserts that two objects are equal or convertible to the larger // type and equal. // @@ -495,7 +544,7 @@ 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...) + return assertions.FalseT[B](t, value, msgAndArgs...) } // FileEmpty checks whether a file exists in the given path and is empty. @@ -558,6 +607,26 @@ func FileNotEmpty(t T, path string, msgAndArgs ...any) bool { return assertions.FileNotEmpty(t, path, msgAndArgs...) } +// FileNotExists checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +// +// # Usage +// +// assertions.FileNotExists(t, "path/to/file") +// +// # Examples +// +// success: filepath.Join(testDataPath(),"non_existing_file") +// failure: filepath.Join(testDataPath(),"existing_file") +// +// Upon failure, the test [T] is marked as failed and continues execution. +func FileNotExists(t T, path string, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.FileNotExists(t, path, msgAndArgs...) +} + // 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. @@ -636,7 +705,7 @@ func GreaterOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndA if h, ok := t.(H); ok { h.Helper() } - return assertions.GreaterOrEqualT(t, e1, e2, msgAndArgs...) + return assertions.GreaterOrEqualT[Orderable](t, e1, e2, msgAndArgs...) } // GreaterT asserts that for two elements of the same type, @@ -668,7 +737,7 @@ func GreaterT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ... if h, ok := t.(H); ok { h.Helper() } - return assertions.GreaterT(t, e1, e2, msgAndArgs...) + return assertions.GreaterT[Orderable](t, e1, e2, msgAndArgs...) } // HTTPBodyContains asserts that a specified handler returns a body that contains a string. @@ -918,7 +987,7 @@ func InDeltaT[Number Measurable](t T, expected Number, actual Number, delta Numb if h, ok := t.(H); ok { h.Helper() } - return assertions.InDeltaT(t, expected, actual, delta, msgAndArgs...) + return assertions.InDeltaT[Number](t, expected, actual, delta, msgAndArgs...) } // InEpsilon asserts that expected and actual have a relative error less than epsilon. @@ -1011,10 +1080,10 @@ func InEpsilonT[Number Measurable](t T, expected Number, actual Number, epsilon if h, ok := t.(H); ok { h.Helper() } - return assertions.InEpsilonT(t, expected, actual, epsilon, msgAndArgs...) + return assertions.InEpsilonT[Number](t, expected, actual, epsilon, msgAndArgs...) } -// IsDecreasing asserts that the collection is decreasing. +// IsDecreasing asserts that the collection is strictly decreasing. // // # Usage // @@ -1035,7 +1104,28 @@ func IsDecreasing(t T, object any, msgAndArgs ...any) bool { return assertions.IsDecreasing(t, object, msgAndArgs...) } -// IsIncreasing asserts that the collection is increasing. +// IsDecreasingT asserts that a slice of [Ordered] is strictly decreasing. +// +// # Usage +// +// assertions.IsDecreasingT(t, []int{2, 1, 0}) +// assertions.IsDecreasingT(t, []float{2, 1}) +// assertions.IsDecreasingT(t, []string{"b", "a"}) +// +// # Examples +// +// success: []int{3, 2, 1} +// failure: []int{1, 2, 3} +// +// Upon failure, the test [T] is marked as failed and continues execution. +func IsDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.IsDecreasingT[OrderedSlice, E](t, collection, msgAndArgs...) +} + +// IsIncreasing asserts that the collection is strictly increasing. // // # Usage // @@ -1056,7 +1146,28 @@ func IsIncreasing(t T, object any, msgAndArgs ...any) bool { return assertions.IsIncreasing(t, object, msgAndArgs...) } -// IsNonDecreasing asserts that the collection is not decreasing. +// IsIncreasingT asserts that a slice of [Ordered] is strictly increasing. +// +// # Usage +// +// assertions.IsIncreasingT(t, []int{1, 2, 3}) +// assertions.IsIncreasingT(t, []float{1, 2}) +// assertions.IsIncreasingT(t, []string{"a", "b"}) +// +// # Examples +// +// success: []int{1, 2, 3} +// failure: []int{1, 1, 2} +// +// Upon failure, the test [T] is marked as failed and continues execution. +func IsIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.IsIncreasingT[OrderedSlice, E](t, collection, msgAndArgs...) +} + +// IsNonDecreasing asserts that the collection is not strictly decreasing. // // # Usage // @@ -1067,7 +1178,7 @@ func IsIncreasing(t T, object any, msgAndArgs ...any) bool { // # Examples // // success: []int{1, 1, 2} -// failure: []int{2, 1, 1} +// failure: []int{2, 1, 0} // // Upon failure, the test [T] is marked as failed and continues execution. func IsNonDecreasing(t T, object any, msgAndArgs ...any) bool { @@ -1077,6 +1188,27 @@ func IsNonDecreasing(t T, object any, msgAndArgs ...any) bool { return assertions.IsNonDecreasing(t, object, msgAndArgs...) } +// IsNonDecreasingT asserts that a slice of [Ordered] is not decreasing. +// +// # Usage +// +// assertions.IsNonDecreasingT(t, []int{1, 1, 2}) +// assertions.IsNonDecreasingT(t, []float{1, 2}) +// assertions.IsNonDecreasingT(t, []string{"a", "b"}) +// +// # Examples +// +// success: []int{1, 1, 2} +// failure: []int{2, 1, 0} +// +// Upon failure, the test [T] is marked as failed and continues execution. +func IsNonDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.IsNonDecreasingT[OrderedSlice, E](t, collection, msgAndArgs...) +} + // IsNonIncreasing asserts that the collection is not increasing. // // # Usage @@ -1098,6 +1230,46 @@ func IsNonIncreasing(t T, object any, msgAndArgs ...any) bool { return assertions.IsNonIncreasing(t, object, msgAndArgs...) } +// IsNonIncreasingT asserts that a slice of [Ordered] is NOT strictly increasing. +// +// # Usage +// +// assertions.IsNonIncreasing(t, []int{2, 1, 1}) +// assertions.IsNonIncreasing(t, []float{2, 1}) +// assertions.IsNonIncreasing(t, []string{"b", "a"}) +// +// # Examples +// +// success: []int{2, 1, 1} +// failure: []int{1, 2, 3} +// +// Upon failure, the test [T] is marked as failed and continues execution. +func IsNonIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.IsNonIncreasingT[OrderedSlice, E](t, collection, msgAndArgs...) +} + +// IsNotOfTypeT asserts that an object is of a given type. +// +// # Usage +// +// assertions.IsOfType[MyType](t,myVar) +// +// # Examples +// +// success: 123.123 +// failure: myType(123.123) +// +// Upon failure, the test [T] is marked as failed and continues execution. +func IsNotOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.IsNotOfTypeT[EType](t, object, msgAndArgs...) +} + // IsNotType asserts that the specified objects are not of the same type. // // # Usage @@ -1117,6 +1289,25 @@ func IsNotType(t T, theType any, object any, msgAndArgs ...any) bool { return assertions.IsNotType(t, theType, object, msgAndArgs...) } +// IsOfTypeT asserts that an object is of a given type. +// +// # Usage +// +// assertions.IsOfTypeT[MyType](t,myVar) +// +// # Examples +// +// success: myType(123.123) +// failure: 123.123 +// +// Upon failure, the test [T] is marked as failed and continues execution. +func IsOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.IsOfTypeT[EType](t, object, msgAndArgs...) +} + // IsType asserts that the specified objects are of the same type. // // # Usage @@ -1198,7 +1389,7 @@ func JSONEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any if h, ok := t.(H); ok { h.Helper() } - return assertions.JSONEqT(t, expected, actual, msgAndArgs...) + return assertions.JSONEqT[EDoc, ADoc](t, expected, actual, msgAndArgs...) } // Kind asserts that the [reflect.Kind] of a given object matches the expected [reflect.Kind]. @@ -1325,7 +1516,7 @@ func LessOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs if h, ok := t.(H); ok { h.Helper() } - return assertions.LessOrEqualT(t, e1, e2, msgAndArgs...) + return assertions.LessOrEqualT[Orderable](t, e1, e2, msgAndArgs...) } // LessT asserts that for two elements of the same type, the first element is strictly less than the second. @@ -1356,7 +1547,45 @@ func LessT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any if h, ok := t.(H); ok { h.Helper() } - return assertions.LessT(t, e1, e2, msgAndArgs...) + return assertions.LessT[Orderable](t, e1, e2, msgAndArgs...) +} + +// MapContainsT asserts that the specified map contains a key. +// +// # Usage +// +// assertions.MapContainsT(t, map[string]string{"Hello": "x","World": "y"}, "World") +// +// # Examples +// +// success: map[string]string{"A": "B"}, "A" +// failure: map[string]string{"A": "B"}, "C" +// +// Upon failure, the test [T] is marked as failed and continues execution. +func MapContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.MapContainsT[Map, K, V](t, m, key, msgAndArgs...) +} + +// MapNotContainsT asserts that the specified map does not contain a key. +// +// # Usage +// +// assertions.MapNotContainsT(t, map[string]string{"Hello": "x","World": "y"}, "hi") +// +// # Examples +// +// success: map[string]string{"A": "B"}, "C" +// failure: map[string]string{"A": "B"}, "A" +// +// Upon failure, the test [T] is marked as failed and continues execution. +func MapNotContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.MapNotContainsT[Map, K, V](t, m, key, msgAndArgs...) } // Negative asserts that the specified element is strictly negative. @@ -1396,7 +1625,7 @@ func NegativeT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...an if h, ok := t.(H); ok { h.Helper() } - return assertions.NegativeT(t, e, msgAndArgs...) + return assertions.NegativeT[SignedNumber](t, e, msgAndArgs...) } // Never asserts that the given condition is never satisfied within waitFor time, @@ -1450,26 +1679,6 @@ func Nil(t T, object any, msgAndArgs ...any) bool { return assertions.Nil(t, object, msgAndArgs...) } -// NoDirExists checks whether a directory does not exist in the given path. -// It fails if the path points to an existing _directory_ only. -// -// # Usage -// -// assertions.NoDirExists(t, "path/to/directory") -// -// # Examples -// -// success: filepath.Join(testDataPath(),"non_existing_dir") -// failure: filepath.Join(testDataPath(),"existing_dir") -// -// Upon failure, the test [T] is marked as failed and continues execution. -func NoDirExists(t T, path string, msgAndArgs ...any) bool { - if h, ok := t.(H); ok { - h.Helper() - } - return assertions.NoDirExists(t, path, msgAndArgs...) -} - // NoError asserts that a function returned a nil error (ie. no error). // // # Usage @@ -1492,26 +1701,6 @@ func NoError(t T, err error, msgAndArgs ...any) bool { return assertions.NoError(t, err, msgAndArgs...) } -// NoFileExists checks whether a file does not exist in a given path. It fails -// if the path points to an existing _file_ only. -// -// # Usage -// -// assertions.NoFileExists(t, "path/to/file") -// -// # Examples -// -// success: filepath.Join(testDataPath(),"non_existing_file") -// failure: filepath.Join(testDataPath(),"existing_file") -// -// Upon failure, the test [T] is marked as failed and continues execution. -func NoFileExists(t T, path string, msgAndArgs ...any) bool { - if h, ok := t.(H); ok { - h.Helper() - } - return assertions.NoFileExists(t, path, msgAndArgs...) -} - // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // @@ -1579,7 +1768,7 @@ func NotElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...an if h, ok := t.(H); ok { h.Helper() } - return assertions.NotElementsMatchT(t, listA, listB, msgAndArgs...) + return assertions.NotElementsMatchT[E](t, listA, listB, msgAndArgs...) } // NotEmpty asserts that the specified object is NOT [Empty]. @@ -1625,6 +1814,27 @@ func NotEqual(t T, expected any, actual any, msgAndArgs ...any) bool { return assertions.NotEqual(t, expected, actual, msgAndArgs...) } +// NotEqualT asserts that the specified values of the same comparable type are NOT equal. +// +// See [EqualT]. +// +// # Usage +// +// assertions.NotEqualT(t, obj1, obj2) +// +// # Examples +// +// success: 123, 456 +// failure: 123, 123 +// +// Upon failure, the test [T] is marked as failed and continues execution. +func NotEqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.NotEqualT[V](t, expected, actual, msgAndArgs...) +} + // NotEqualValues asserts that two objects are not equal even when converted to the same type. // // # Usage @@ -1805,7 +2015,7 @@ func NotRegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...a if h, ok := t.(H); ok { h.Helper() } - return assertions.NotRegexpT(t, rx, actual, msgAndArgs...) + return assertions.NotRegexpT[Rex, ADoc](t, rx, actual, msgAndArgs...) } // NotSame asserts that two pointers do not reference the same object. @@ -1830,6 +2040,50 @@ func NotSame(t T, expected any, actual any, msgAndArgs ...any) bool { return assertions.NotSame(t, expected, actual, msgAndArgs...) } +// NotSameT asserts that two pointers do not reference the same object. +// +// See [SameT] +// +// # Usage +// +// assertions.NotSameT(t, ptr1, ptr2) +// +// # Examples +// +// success: &staticVar, ptr("static string") +// failure: &staticVar, staticVarPtr +// +// Upon failure, the test [T] is marked as failed and continues execution. +func NotSameT[P any](t T, expected *P, actual *P, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.NotSameT[P](t, expected, actual, msgAndArgs...) +} + +// NotSortedT asserts that the slice of [Ordered] is NOT sorted (i.e. non-strictly increasing). +// +// Unlike [IsDecreasingT], it accepts slices that are neither increasing nor decreasing. +// +// # Usage +// +// assertions.NotSortedT(t, []int{3, 2, 3}) +// assertions.NotSortedT(t, []float{2, 1}) +// assertions.NotSortedT(t, []string{"b", "a"}) +// +// # Examples +// +// success: []int{3, 1, 3} +// failure: []int{1, 4, 8} +// +// Upon failure, the test [T] is marked as failed and continues execution. +func NotSortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.NotSortedT[OrderedSlice, E](t, collection, msgAndArgs...) +} + // NotSubset asserts that the list (array, slice, or map) does NOT contain all // elements given in the subset (array, slice, or map). // Map elements are key-value pairs unless compared with an array or slice where @@ -1971,7 +2225,7 @@ func PositiveT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...an if h, ok := t.(H); ok { h.Helper() } - return assertions.PositiveT(t, e, msgAndArgs...) + return assertions.PositiveT[SignedNumber](t, e, msgAndArgs...) } // Regexp asserts that a specified regular expression matches a string. @@ -2014,7 +2268,7 @@ func RegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) if h, ok := t.(H); ok { h.Helper() } - return assertions.RegexpT(t, rx, actual, msgAndArgs...) + return assertions.RegexpT[Rex, ADoc](t, rx, actual, msgAndArgs...) } // Same asserts that two pointers reference the same object. @@ -2039,6 +2293,204 @@ func Same(t T, expected any, actual any, msgAndArgs ...any) bool { return assertions.Same(t, expected, actual, msgAndArgs...) } +// SameT asserts that two pointers of the same type reference the same object. +// +// # Usage +// +// assertions.SameT(t, ptr1, ptr2) +// +// # Examples +// +// success: &staticVar, staticVarPtr +// failure: &staticVar, ptr("static string") +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SameT[P any](t T, expected *P, actual *P, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SameT[P](t, expected, actual, msgAndArgs...) +} + +// SeqContainsT asserts that the specified iterator contains a comparable element. +// +// # Usage +// +// assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World") +// +// # Examples +// +// success: slices.Values([]string{"A","B"}), "A" +// failure: slices.Values([]string{"A","B"}), "C" +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SeqContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SeqContainsT[E](t, iter, element, msgAndArgs...) +} + +// SeqNotContainsT asserts that the specified iterator does not contain a comparable element. +// +// # Usage +// +// assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World") +// +// # Examples +// +// success: slices.Values([]string{"A","B"}), "C" +// failure: slices.Values([]string{"A","B"}), "A" +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SeqNotContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SeqNotContainsT[E](t, iter, element, msgAndArgs...) +} + +// SliceContainsT asserts that the specified slice contains a comparable element. +// +// # Usage +// +// assertions.SliceContainsT(t, []{"Hello","World"}, "World") +// +// # Examples +// +// success: []string{"A","B"}, "A" +// failure: []string{"A","B"}, "C" +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SliceContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SliceContainsT[Slice, E](t, s, element, msgAndArgs...) +} + +// SliceNotContainsT asserts that the specified slice does not contain a comparable element. +// +// # Usage +// +// assertions.SliceNotContainsT(t, []{"Hello","World"}, "hi") +// +// # Examples +// +// success: []string{"A","B"}, "C" +// failure: []string{"A","B"}, "A" +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SliceNotContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SliceNotContainsT[Slice, E](t, s, element, msgAndArgs...) +} + +// SliceNotSubsetT asserts that a slice of comparable elements does not contain all the elements given in the subset. +// +// # Usage +// +// assertions.SliceNotSubsetT(t, []int{1, 2, 3}, []int{1, 4}) +// +// # Examples +// +// success: []int{1, 2, 3}, []int{4, 5} +// failure: []int{1, 2, 3}, []int{1, 2} +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SliceNotSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool) { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SliceNotSubsetT[Slice, E](t, list, subset, msgAndArgs...) +} + +// SliceSubsetT asserts that a slice of comparable elements contains all the elements given in the subset. +// +// # Usage +// +// assertions.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) +// +// # Examples +// +// success: []int{1, 2, 3}, []int{1, 2} +// failure: []int{1, 2, 3}, []int{4, 5} +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SliceSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool) { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SliceSubsetT[Slice, E](t, list, subset, msgAndArgs...) +} + +// SortedT asserts that the slice of [Ordered] is sorted (i.e. non-strictly increasing). +// +// Unlike [IsIncreasingT], it accepts elements to be equal. +// +// # Usage +// +// assertions.SortedT(t, []int{1, 2, 3}) +// assertions.SortedT(t, []float{1, 2}) +// assertions.SortedT(t, []string{"a", "b"}) +// +// # Examples +// +// success: []int{1, 1, 3} +// failure: []int{1, 4, 2} +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SortedT[OrderedSlice, E](t, collection, msgAndArgs...) +} + +// StringContainsT asserts that a string contains the specified substring. +// +// Strings may be go strings or []byte. +// +// # Usage +// +// assertions.StringContainsT(t, "Hello World", "World") +// +// # Examples +// +// success: "AB", "A" +// failure: "AB", "C" +// +// Upon failure, the test [T] is marked as failed and continues execution. +func StringContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.StringContainsT[ADoc, EDoc](t, str, substring, msgAndArgs...) +} + +// StringNotContainsT asserts that a string does not contain the specified substring. +// +// Strings may be go strings or []byte. +// +// # Usage +// +// assertions.StringNotContainsT(t, "Hello World", "hi") +// +// # Examples +// +// success: "AB", "C" +// failure: "AB", "A" +// +// Upon failure, the test [T] is marked as failed and continues execution. +func StringNotContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.StringNotContainsT[ADoc, EDoc](t, str, substring, msgAndArgs...) +} + // Subset asserts that the list (array, slice, or map) contains all elements // given in the subset (array, slice, or map). // @@ -2103,7 +2555,7 @@ 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...) + return assertions.TrueT[B](t, value, msgAndArgs...) } // WithinDuration asserts that the two times are within duration delta of each other. @@ -2218,7 +2670,7 @@ func YAMLEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any if h, ok := t.(H); ok { h.Helper() } - return assertions.YAMLEqT(t, expected, actual, msgAndArgs...) + return assertions.YAMLEqT[EDoc, ADoc](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 c0a723785..837bae922 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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package assert @@ -13,6 +13,7 @@ import ( "net/url" "path/filepath" "reflect" + "slices" "testing" "time" ) @@ -89,6 +90,30 @@ func TestDirExists(t *testing.T) { }) } +func TestDirNotExists(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := DirNotExists(t, filepath.Join(testDataPath(), "non_existing_dir")) + if !result { + t.Error("DirNotExists should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := DirNotExists(mock, filepath.Join(testDataPath(), "existing_dir")) + if result { + t.Error("DirNotExists should return false on failure") + } + if !mock.failed { + t.Error("DirNotExists should mark test as failed") + } + }) +} + func TestElementsMatch(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -233,6 +258,30 @@ func TestEqualExportedValues(t *testing.T) { }) } +func TestEqualT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := EqualT(t, 123, 123) + if !result { + t.Error("EqualT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := EqualT(mock, 123, 456) + if result { + t.Error("EqualT should return false on failure") + } + if !mock.failed { + t.Error("EqualT should mark test as failed") + } + }) +} + func TestEqualValues(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -577,6 +626,30 @@ func TestFileNotEmpty(t *testing.T) { }) } +func TestFileNotExists(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := FileNotExists(t, filepath.Join(testDataPath(), "non_existing_file")) + if !result { + t.Error("FileNotExists should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := FileNotExists(mock, filepath.Join(testDataPath(), "existing_file")) + if result { + t.Error("FileNotExists should return false on failure") + } + if !mock.failed { + t.Error("FileNotExists should mark test as failed") + } + }) +} + func TestGreater(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1033,6 +1106,30 @@ func TestIsDecreasing(t *testing.T) { }) } +func TestIsDecreasingT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := IsDecreasingT(t, []int{3, 2, 1}) + if !result { + t.Error("IsDecreasingT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := IsDecreasingT(mock, []int{1, 2, 3}) + if result { + t.Error("IsDecreasingT should return false on failure") + } + if !mock.failed { + t.Error("IsDecreasingT should mark test as failed") + } + }) +} + func TestIsIncreasing(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1057,6 +1154,30 @@ func TestIsIncreasing(t *testing.T) { }) } +func TestIsIncreasingT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := IsIncreasingT(t, []int{1, 2, 3}) + if !result { + t.Error("IsIncreasingT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := IsIncreasingT(mock, []int{1, 1, 2}) + if result { + t.Error("IsIncreasingT should return false on failure") + } + if !mock.failed { + t.Error("IsIncreasingT should mark test as failed") + } + }) +} + func TestIsNonDecreasing(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1071,7 +1192,7 @@ func TestIsNonDecreasing(t *testing.T) { t.Parallel() mock := new(mockT) - result := IsNonDecreasing(mock, []int{2, 1, 1}) + result := IsNonDecreasing(mock, []int{2, 1, 0}) if result { t.Error("IsNonDecreasing should return false on failure") } @@ -1081,6 +1202,30 @@ func TestIsNonDecreasing(t *testing.T) { }) } +func TestIsNonDecreasingT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := IsNonDecreasingT(t, []int{1, 1, 2}) + if !result { + t.Error("IsNonDecreasingT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := IsNonDecreasingT(mock, []int{2, 1, 0}) + if result { + t.Error("IsNonDecreasingT should return false on failure") + } + if !mock.failed { + t.Error("IsNonDecreasingT should mark test as failed") + } + }) +} + func TestIsNonIncreasing(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1105,6 +1250,54 @@ func TestIsNonIncreasing(t *testing.T) { }) } +func TestIsNonIncreasingT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := IsNonIncreasingT(t, []int{2, 1, 1}) + if !result { + t.Error("IsNonIncreasingT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := IsNonIncreasingT(mock, []int{1, 2, 3}) + if result { + t.Error("IsNonIncreasingT should return false on failure") + } + if !mock.failed { + t.Error("IsNonIncreasingT should mark test as failed") + } + }) +} + +func TestIsNotOfTypeT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := IsNotOfTypeT[myType](t, 123.123) + if !result { + t.Error("IsNotOfTypeT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := IsNotOfTypeT[myType](mock, myType(123.123)) + if result { + t.Error("IsNotOfTypeT should return false on failure") + } + if !mock.failed { + t.Error("IsNotOfTypeT should mark test as failed") + } + }) +} + func TestIsNotType(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1129,6 +1322,30 @@ func TestIsNotType(t *testing.T) { }) } +func TestIsOfTypeT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := IsOfTypeT[myType](t, myType(123.123)) + if !result { + t.Error("IsOfTypeT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := IsOfTypeT[myType](mock, 123.123) + if result { + t.Error("IsOfTypeT should return false on failure") + } + if !mock.failed { + t.Error("IsOfTypeT should mark test as failed") + } + }) +} + func TestIsType(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1369,13 +1586,13 @@ func TestLessT(t *testing.T) { }) } -func TestNegative(t *testing.T) { +func TestMapContainsT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - result := Negative(t, -1) + result := MapContainsT(t, map[string]string{"A": "B"}, "A") if !result { - t.Error("Negative should return true on success") + t.Error("MapContainsT should return true on success") } }) @@ -1383,23 +1600,23 @@ func TestNegative(t *testing.T) { t.Parallel() mock := new(mockT) - result := Negative(mock, 1) + result := MapContainsT(mock, map[string]string{"A": "B"}, "C") if result { - t.Error("Negative should return false on failure") + t.Error("MapContainsT should return false on failure") } if !mock.failed { - t.Error("Negative should mark test as failed") + t.Error("MapContainsT should mark test as failed") } }) } -func TestNegativeT(t *testing.T) { +func TestMapNotContainsT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - result := NegativeT(t, -1) + result := MapNotContainsT(t, map[string]string{"A": "B"}, "C") if !result { - t.Error("NegativeT should return true on success") + t.Error("MapNotContainsT should return true on success") } }) @@ -1407,23 +1624,23 @@ func TestNegativeT(t *testing.T) { t.Parallel() mock := new(mockT) - result := NegativeT(mock, 1) + result := MapNotContainsT(mock, map[string]string{"A": "B"}, "A") if result { - t.Error("NegativeT should return false on failure") + t.Error("MapNotContainsT should return false on failure") } if !mock.failed { - t.Error("NegativeT should mark test as failed") + t.Error("MapNotContainsT should mark test as failed") } }) } -func TestNever(t *testing.T) { +func TestNegative(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - result := Never(t, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond) + result := Negative(t, -1) if !result { - t.Error("Never should return true on success") + t.Error("Negative should return true on success") } }) @@ -1431,23 +1648,23 @@ func TestNever(t *testing.T) { t.Parallel() mock := new(mockT) - result := Never(mock, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond) + result := Negative(mock, 1) if result { - t.Error("Never should return false on failure") + t.Error("Negative should return false on failure") } if !mock.failed { - t.Error("Never should mark test as failed") + t.Error("Negative should mark test as failed") } }) } -func TestNil(t *testing.T) { +func TestNegativeT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - result := Nil(t, nil) + result := NegativeT(t, -1) if !result { - t.Error("Nil should return true on success") + t.Error("NegativeT should return true on success") } }) @@ -1455,23 +1672,23 @@ func TestNil(t *testing.T) { t.Parallel() mock := new(mockT) - result := Nil(mock, "not nil") + result := NegativeT(mock, 1) if result { - t.Error("Nil should return false on failure") + t.Error("NegativeT should return false on failure") } if !mock.failed { - t.Error("Nil should mark test as failed") + t.Error("NegativeT should mark test as failed") } }) } -func TestNoDirExists(t *testing.T) { +func TestNever(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - result := NoDirExists(t, filepath.Join(testDataPath(), "non_existing_dir")) + result := Never(t, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond) if !result { - t.Error("NoDirExists should return true on success") + t.Error("Never should return true on success") } }) @@ -1479,23 +1696,23 @@ func TestNoDirExists(t *testing.T) { t.Parallel() mock := new(mockT) - result := NoDirExists(mock, filepath.Join(testDataPath(), "existing_dir")) + result := Never(mock, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond) if result { - t.Error("NoDirExists should return false on failure") + t.Error("Never should return false on failure") } if !mock.failed { - t.Error("NoDirExists should mark test as failed") + t.Error("Never should mark test as failed") } }) } -func TestNoError(t *testing.T) { +func TestNil(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - result := NoError(t, nil) + result := Nil(t, nil) if !result { - t.Error("NoError should return true on success") + t.Error("Nil should return true on success") } }) @@ -1503,23 +1720,23 @@ func TestNoError(t *testing.T) { t.Parallel() mock := new(mockT) - result := NoError(mock, ErrTest) + result := Nil(mock, "not nil") if result { - t.Error("NoError should return false on failure") + t.Error("Nil should return false on failure") } if !mock.failed { - t.Error("NoError should mark test as failed") + t.Error("Nil should mark test as failed") } }) } -func TestNoFileExists(t *testing.T) { +func TestNoError(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - result := NoFileExists(t, filepath.Join(testDataPath(), "non_existing_file")) + result := NoError(t, nil) if !result { - t.Error("NoFileExists should return true on success") + t.Error("NoError should return true on success") } }) @@ -1527,12 +1744,12 @@ func TestNoFileExists(t *testing.T) { t.Parallel() mock := new(mockT) - result := NoFileExists(mock, filepath.Join(testDataPath(), "existing_file")) + result := NoError(mock, ErrTest) if result { - t.Error("NoFileExists should return false on failure") + t.Error("NoError should return false on failure") } if !mock.failed { - t.Error("NoFileExists should mark test as failed") + t.Error("NoError should mark test as failed") } }) } @@ -1657,6 +1874,30 @@ func TestNotEqual(t *testing.T) { }) } +func TestNotEqualT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := NotEqualT(t, 123, 456) + if !result { + t.Error("NotEqualT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := NotEqualT(mock, 123, 123) + if result { + t.Error("NotEqualT should return false on failure") + } + if !mock.failed { + t.Error("NotEqualT should mark test as failed") + } + }) +} + func TestNotEqualValues(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1897,6 +2138,54 @@ func TestNotSame(t *testing.T) { }) } +func TestNotSameT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := NotSameT(t, &staticVar, ptr("static string")) + if !result { + t.Error("NotSameT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := NotSameT(mock, &staticVar, staticVarPtr) + if result { + t.Error("NotSameT should return false on failure") + } + if !mock.failed { + t.Error("NotSameT should mark test as failed") + } + }) +} + +func TestNotSortedT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := NotSortedT(t, []int{3, 1, 3}) + if !result { + t.Error("NotSortedT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := NotSortedT(mock, []int{1, 4, 8}) + if result { + t.Error("NotSortedT should return false on failure") + } + if !mock.failed { + t.Error("NotSortedT should mark test as failed") + } + }) +} + func TestNotSubset(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -2137,6 +2426,246 @@ func TestSame(t *testing.T) { }) } +func TestSameT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := SameT(t, &staticVar, staticVarPtr) + if !result { + t.Error("SameT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SameT(mock, &staticVar, ptr("static string")) + if result { + t.Error("SameT should return false on failure") + } + if !mock.failed { + t.Error("SameT should mark test as failed") + } + }) +} + +func TestSeqContainsT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := SeqContainsT(t, slices.Values([]string{"A", "B"}), "A") + if !result { + t.Error("SeqContainsT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SeqContainsT(mock, slices.Values([]string{"A", "B"}), "C") + if result { + t.Error("SeqContainsT should return false on failure") + } + if !mock.failed { + t.Error("SeqContainsT should mark test as failed") + } + }) +} + +func TestSeqNotContainsT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := SeqNotContainsT(t, slices.Values([]string{"A", "B"}), "C") + if !result { + t.Error("SeqNotContainsT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SeqNotContainsT(mock, slices.Values([]string{"A", "B"}), "A") + if result { + t.Error("SeqNotContainsT should return false on failure") + } + if !mock.failed { + t.Error("SeqNotContainsT should mark test as failed") + } + }) +} + +func TestSliceContainsT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := SliceContainsT(t, []string{"A", "B"}, "A") + if !result { + t.Error("SliceContainsT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SliceContainsT(mock, []string{"A", "B"}, "C") + if result { + t.Error("SliceContainsT should return false on failure") + } + if !mock.failed { + t.Error("SliceContainsT should mark test as failed") + } + }) +} + +func TestSliceNotContainsT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := SliceNotContainsT(t, []string{"A", "B"}, "C") + if !result { + t.Error("SliceNotContainsT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SliceNotContainsT(mock, []string{"A", "B"}, "A") + if result { + t.Error("SliceNotContainsT should return false on failure") + } + if !mock.failed { + t.Error("SliceNotContainsT should mark test as failed") + } + }) +} + +func TestSliceNotSubsetT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := SliceNotSubsetT(t, []int{1, 2, 3}, []int{4, 5}) + if !result { + t.Error("SliceNotSubsetT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SliceNotSubsetT(mock, []int{1, 2, 3}, []int{1, 2}) + if result { + t.Error("SliceNotSubsetT should return false on failure") + } + if !mock.failed { + t.Error("SliceNotSubsetT should mark test as failed") + } + }) +} + +func TestSliceSubsetT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) + if !result { + t.Error("SliceSubsetT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SliceSubsetT(mock, []int{1, 2, 3}, []int{4, 5}) + if result { + t.Error("SliceSubsetT should return false on failure") + } + if !mock.failed { + t.Error("SliceSubsetT should mark test as failed") + } + }) +} + +func TestSortedT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := SortedT(t, []int{1, 1, 3}) + if !result { + t.Error("SortedT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SortedT(mock, []int{1, 4, 2}) + if result { + t.Error("SortedT should return false on failure") + } + if !mock.failed { + t.Error("SortedT should mark test as failed") + } + }) +} + +func TestStringContainsT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := StringContainsT(t, "AB", "A") + if !result { + t.Error("StringContainsT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := StringContainsT(mock, "AB", "C") + if result { + t.Error("StringContainsT should return false on failure") + } + if !mock.failed { + t.Error("StringContainsT should mark test as failed") + } + }) +} + +func TestStringNotContainsT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := StringNotContainsT(t, "AB", "C") + if !result { + t.Error("StringNotContainsT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := StringNotContainsT(mock, "AB", "A") + if result { + t.Error("StringNotContainsT should return false on failure") + } + if !mock.failed { + t.Error("StringNotContainsT should mark test as failed") + } + }) +} + func TestSubset(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -2386,3 +2915,5 @@ type dummyError struct { func (d *dummyError) Error() string { return "dummy error" } + +type myType float64 diff --git a/assert/assert_examples_test.go b/assert/assert_examples_test.go index 203648f5b..01b2b842c 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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package assert_test @@ -13,6 +13,7 @@ import ( "net/url" "path/filepath" "reflect" + "slices" "testing" "time" @@ -45,6 +46,14 @@ func ExampleDirExists() { // Output: success: true } +func ExampleDirNotExists() { + t := new(testing.T) + success := assert.DirNotExists(t, filepath.Join(testDataPath(), "non_existing_dir")) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleElementsMatch() { t := new(testing.T) success := assert.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) @@ -93,6 +102,14 @@ func ExampleEqualExportedValues() { // Output: success: true } +func ExampleEqualT() { + t := new(testing.T) + success := assert.EqualT(t, 123, 123) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleEqualValues() { t := new(testing.T) success := assert.EqualValues(t, uint32(123), int32(123)) @@ -209,6 +226,14 @@ func ExampleFileNotEmpty() { // Output: success: true } +func ExampleFileNotExists() { + t := new(testing.T) + success := assert.FileNotExists(t, filepath.Join(testDataPath(), "non_existing_file")) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleGreater() { t := new(testing.T) success := assert.Greater(t, 2, 1) @@ -361,6 +386,14 @@ func ExampleIsDecreasing() { // Output: success: true } +func ExampleIsDecreasingT() { + t := new(testing.T) + success := assert.IsDecreasingT(t, []int{3, 2, 1}) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleIsIncreasing() { t := new(testing.T) success := assert.IsIncreasing(t, []int{1, 2, 3}) @@ -369,6 +402,14 @@ func ExampleIsIncreasing() { // Output: success: true } +func ExampleIsIncreasingT() { + t := new(testing.T) + success := assert.IsIncreasingT(t, []int{1, 2, 3}) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleIsNonDecreasing() { t := new(testing.T) success := assert.IsNonDecreasing(t, []int{1, 1, 2}) @@ -377,6 +418,14 @@ func ExampleIsNonDecreasing() { // Output: success: true } +func ExampleIsNonDecreasingT() { + t := new(testing.T) + success := assert.IsNonDecreasingT(t, []int{1, 1, 2}) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleIsNonIncreasing() { t := new(testing.T) success := assert.IsNonIncreasing(t, []int{2, 1, 1}) @@ -385,6 +434,22 @@ func ExampleIsNonIncreasing() { // Output: success: true } +func ExampleIsNonIncreasingT() { + t := new(testing.T) + success := assert.IsNonIncreasingT(t, []int{2, 1, 1}) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + +func ExampleIsNotOfTypeT() { + t := new(testing.T) + success := assert.IsNotOfTypeT[myType](t, 123.123) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleIsNotType() { t := new(testing.T) success := assert.IsNotType(t, int32(123), int64(456)) @@ -393,6 +458,14 @@ func ExampleIsNotType() { // Output: success: true } +func ExampleIsOfTypeT() { + t := new(testing.T) + success := assert.IsOfTypeT[myType](t, myType(123.123)) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleIsType() { t := new(testing.T) success := assert.IsType(t, 123, 456) @@ -473,59 +546,59 @@ func ExampleLessT() { // Output: success: true } -func ExampleNegative() { +func ExampleMapContainsT() { t := new(testing.T) - success := assert.Negative(t, -1) + success := assert.MapContainsT(t, map[string]string{"A": "B"}, "A") fmt.Printf("success: %t\n", success) // Output: success: true } -func ExampleNegativeT() { +func ExampleMapNotContainsT() { t := new(testing.T) - success := assert.NegativeT(t, -1) + success := assert.MapNotContainsT(t, map[string]string{"A": "B"}, "C") fmt.Printf("success: %t\n", success) // Output: success: true } -func ExampleNever() { +func ExampleNegative() { t := new(testing.T) - success := assert.Never(t, func() bool { - return false - }, 100*time.Millisecond, 20*time.Millisecond) + success := assert.Negative(t, -1) fmt.Printf("success: %t\n", success) // Output: success: true } -func ExampleNil() { +func ExampleNegativeT() { t := new(testing.T) - success := assert.Nil(t, nil) + success := assert.NegativeT(t, -1) fmt.Printf("success: %t\n", success) // Output: success: true } -func ExampleNoDirExists() { +func ExampleNever() { t := new(testing.T) - success := assert.NoDirExists(t, filepath.Join(testDataPath(), "non_existing_dir")) + success := assert.Never(t, func() bool { + return false + }, 100*time.Millisecond, 20*time.Millisecond) fmt.Printf("success: %t\n", success) // Output: success: true } -func ExampleNoError() { +func ExampleNil() { t := new(testing.T) - success := assert.NoError(t, nil) + success := assert.Nil(t, nil) fmt.Printf("success: %t\n", success) // Output: success: true } -func ExampleNoFileExists() { +func ExampleNoError() { t := new(testing.T) - success := assert.NoFileExists(t, filepath.Join(testDataPath(), "non_existing_file")) + success := assert.NoError(t, nil) fmt.Printf("success: %t\n", success) // Output: success: true @@ -571,6 +644,14 @@ func ExampleNotEqual() { // Output: success: true } +func ExampleNotEqualT() { + t := new(testing.T) + success := assert.NotEqualT(t, 123, 456) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleNotEqualValues() { t := new(testing.T) success := assert.NotEqualValues(t, uint32(123), int32(456)) @@ -652,6 +733,22 @@ func ExampleNotSame() { // Output: success: true } +func ExampleNotSameT() { + t := new(testing.T) + success := assert.NotSameT(t, &staticVar, ptr("static string")) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + +func ExampleNotSortedT() { + t := new(testing.T) + success := assert.NotSortedT(t, []int{3, 1, 3}) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleNotSubset() { t := new(testing.T) success := assert.NotSubset(t, []int{1, 2, 3}, []int{4, 5}) @@ -738,6 +835,86 @@ func ExampleSame() { // Output: success: true } +func ExampleSameT() { + t := new(testing.T) + success := assert.SameT(t, &staticVar, staticVarPtr) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + +func ExampleSeqContainsT() { + t := new(testing.T) + success := assert.SeqContainsT(t, slices.Values([]string{"A", "B"}), "A") + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + +func ExampleSeqNotContainsT() { + t := new(testing.T) + success := assert.SeqNotContainsT(t, slices.Values([]string{"A", "B"}), "C") + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + +func ExampleSliceContainsT() { + t := new(testing.T) + success := assert.SliceContainsT(t, []string{"A", "B"}, "A") + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + +func ExampleSliceNotContainsT() { + t := new(testing.T) + success := assert.SliceNotContainsT(t, []string{"A", "B"}, "C") + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + +func ExampleSliceNotSubsetT() { + t := new(testing.T) + success := assert.SliceNotSubsetT(t, []int{1, 2, 3}, []int{4, 5}) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + +func ExampleSliceSubsetT() { + t := new(testing.T) + success := assert.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + +func ExampleSortedT() { + t := new(testing.T) + success := assert.SortedT(t, []int{1, 1, 3}) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + +func ExampleStringContainsT() { + t := new(testing.T) + success := assert.StringContainsT(t, "AB", "A") + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + +func ExampleStringNotContainsT() { + t := new(testing.T) + success := assert.StringNotContainsT(t, "AB", "C") + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleSubset() { t := new(testing.T) success := assert.Subset(t, []int{1, 2, 3}, []int{1, 2}) @@ -847,3 +1024,5 @@ type dummyError struct { func (d *dummyError) Error() string { return "dummy error" } + +type myType float64 diff --git a/assert/assert_format.go b/assert/assert_format.go index d5ca7852b..f5295aead 100644 --- a/assert/assert_format.go +++ b/assert/assert_format.go @@ -2,11 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package assert import ( + "iter" "net/http" "net/url" "reflect" @@ -45,6 +46,16 @@ func DirExistsf(t T, path string, msg string, args ...any) bool { return assertions.DirExists(t, path, forwardArgs(msg, args)) } +// DirNotExistsf is the same as [DirNotExists], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func DirNotExistsf(t T, path string, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.DirNotExists(t, path, forwardArgs(msg, args)) +} + // 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. @@ -62,7 +73,7 @@ func ElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, args . if h, ok := t.(H); ok { h.Helper() } - return assertions.ElementsMatchT(t, listA, listB, forwardArgs(msg, args)) + return assertions.ElementsMatchT[E](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]. @@ -105,6 +116,16 @@ func EqualExportedValuesf(t T, expected any, actual any, msg string, args ...any return assertions.EqualExportedValues(t, expected, actual, forwardArgs(msg, args)) } +// EqualTf is the same as [EqualT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func EqualTf[V comparable](t T, expected V, actual V, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.EqualT[V](t, expected, actual, forwardArgs(msg, args)) +} + // 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. @@ -222,7 +243,7 @@ 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)) + return assertions.FalseT[B](t, value, forwardArgs(msg, args)) } // FileEmptyf is the same as [FileEmpty], but it accepts a format msg string to format arguments like [fmt.Printf]. @@ -255,6 +276,16 @@ func FileNotEmptyf(t T, path string, msg string, args ...any) bool { return assertions.FileNotEmpty(t, path, forwardArgs(msg, args)) } +// FileNotExistsf is the same as [FileNotExists], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func FileNotExistsf(t T, path string, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.FileNotExists(t, path, forwardArgs(msg, args)) +} + // 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. @@ -282,7 +313,7 @@ func GreaterOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg st if h, ok := t.(H); ok { h.Helper() } - return assertions.GreaterOrEqualT(t, e1, e2, forwardArgs(msg, args)) + return assertions.GreaterOrEqualT[Orderable](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]. @@ -292,7 +323,7 @@ func GreaterTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, a if h, ok := t.(H); ok { h.Helper() } - return assertions.GreaterT(t, e1, e2, forwardArgs(msg, args)) + return assertions.GreaterT[Orderable](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]. @@ -402,7 +433,7 @@ func InDeltaTf[Number Measurable](t T, expected Number, actual Number, delta Num if h, ok := t.(H); ok { h.Helper() } - return assertions.InDeltaT(t, expected, actual, delta, forwardArgs(msg, args)) + return assertions.InDeltaT[Number](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]. @@ -432,7 +463,7 @@ func InEpsilonTf[Number Measurable](t T, expected Number, actual Number, epsilon if h, ok := t.(H); ok { h.Helper() } - return assertions.InEpsilonT(t, expected, actual, epsilon, forwardArgs(msg, args)) + return assertions.InEpsilonT[Number](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]. @@ -445,6 +476,16 @@ func IsDecreasingf(t T, object any, msg string, args ...any) bool { return assertions.IsDecreasing(t, object, forwardArgs(msg, args)) } +// IsDecreasingTf is the same as [IsDecreasingT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func IsDecreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.IsDecreasingT[OrderedSlice, E](t, collection, forwardArgs(msg, args)) +} + // 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. @@ -455,6 +496,16 @@ func IsIncreasingf(t T, object any, msg string, args ...any) bool { return assertions.IsIncreasing(t, object, forwardArgs(msg, args)) } +// IsIncreasingTf is the same as [IsIncreasingT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func IsIncreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.IsIncreasingT[OrderedSlice, E](t, collection, forwardArgs(msg, args)) +} + // 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. @@ -465,6 +516,16 @@ func IsNonDecreasingf(t T, object any, msg string, args ...any) bool { return assertions.IsNonDecreasing(t, object, forwardArgs(msg, args)) } +// IsNonDecreasingTf is the same as [IsNonDecreasingT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func IsNonDecreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.IsNonDecreasingT[OrderedSlice, E](t, collection, forwardArgs(msg, args)) +} + // 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. @@ -475,6 +536,26 @@ func IsNonIncreasingf(t T, object any, msg string, args ...any) bool { return assertions.IsNonIncreasing(t, object, forwardArgs(msg, args)) } +// IsNonIncreasingTf is the same as [IsNonIncreasingT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func IsNonIncreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.IsNonIncreasingT[OrderedSlice, E](t, collection, forwardArgs(msg, args)) +} + +// IsNotOfTypeTf is the same as [IsNotOfTypeT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func IsNotOfTypeTf[EType any](t T, object any, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.IsNotOfTypeT[EType](t, object, forwardArgs(msg, args)) +} + // 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. @@ -485,6 +566,16 @@ func IsNotTypef(t T, theType any, object any, msg string, args ...any) bool { return assertions.IsNotType(t, theType, object, forwardArgs(msg, args)) } +// IsOfTypeTf is the same as [IsOfTypeT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func IsOfTypeTf[EType any](t T, object any, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.IsOfTypeT[EType](t, object, forwardArgs(msg, args)) +} + // 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. @@ -522,7 +613,7 @@ func JSONEqTf[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msg string, args if h, ok := t.(H); ok { h.Helper() } - return assertions.JSONEqT(t, expected, actual, forwardArgs(msg, args)) + return assertions.JSONEqT[EDoc, ADoc](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]. @@ -572,7 +663,7 @@ func LessOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg strin if h, ok := t.(H); ok { h.Helper() } - return assertions.LessOrEqualT(t, e1, e2, forwardArgs(msg, args)) + return assertions.LessOrEqualT[Orderable](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]. @@ -582,77 +673,77 @@ func LessTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args if h, ok := t.(H); ok { h.Helper() } - return assertions.LessT(t, e1, e2, forwardArgs(msg, args)) + return assertions.LessT[Orderable](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]. +// MapContainsTf is the same as [MapContainsT], but it 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 { +func MapContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.Negative(t, e, forwardArgs(msg, args)) + return assertions.MapContainsT[Map, K, V](t, m, key, forwardArgs(msg, args)) } -// NegativeTf is the same as [NegativeT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// MapNotContainsTf is the same as [MapNotContainsT], but it 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 { +func MapNotContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.NegativeT(t, e, forwardArgs(msg, args)) + return assertions.MapNotContainsT[Map, K, V](t, m, key, forwardArgs(msg, args)) } -// Neverf is the same as [Never], but it accepts a format msg string to format arguments like [fmt.Printf]. +// 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 Neverf(t T, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...any) bool { +func Negativef(t T, e any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.Never(t, condition, waitFor, tick, forwardArgs(msg, args)) + return assertions.Negative(t, e, forwardArgs(msg, args)) } -// Nilf is the same as [Nil], but it 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 Nilf(t T, object any, msg string, args ...any) bool { +func NegativeTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.Nil(t, object, forwardArgs(msg, args)) + return assertions.NegativeT[SignedNumber](t, e, forwardArgs(msg, args)) } -// NoDirExistsf is the same as [NoDirExists], but it accepts a format msg string to format arguments like [fmt.Printf]. +// 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 NoDirExistsf(t T, path string, msg string, args ...any) bool { +func Neverf(t T, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.NoDirExists(t, path, forwardArgs(msg, args)) + return assertions.Never(t, condition, waitFor, tick, forwardArgs(msg, args)) } -// NoErrorf is the same as [NoError], but it 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 NoErrorf(t T, err error, msg string, args ...any) bool { +func Nilf(t T, object any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.NoError(t, err, forwardArgs(msg, args)) + return assertions.Nil(t, object, forwardArgs(msg, args)) } -// NoFileExistsf is the same as [NoFileExists], but it 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 NoFileExistsf(t T, path string, msg string, args ...any) bool { +func NoErrorf(t T, err error, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } - return assertions.NoFileExists(t, path, forwardArgs(msg, args)) + return assertions.NoError(t, err, forwardArgs(msg, args)) } // NotContainsf is the same as [NotContains], but it accepts a format msg string to format arguments like [fmt.Printf]. @@ -682,7 +773,7 @@ func NotElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, arg if h, ok := t.(H); ok { h.Helper() } - return assertions.NotElementsMatchT(t, listA, listB, forwardArgs(msg, args)) + return assertions.NotElementsMatchT[E](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]. @@ -705,6 +796,16 @@ func NotEqualf(t T, expected any, actual any, msg string, args ...any) bool { return assertions.NotEqual(t, expected, actual, forwardArgs(msg, args)) } +// NotEqualTf is the same as [NotEqualT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func NotEqualTf[V comparable](t T, expected V, actual V, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.NotEqualT[V](t, expected, actual, forwardArgs(msg, args)) +} + // 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. @@ -792,7 +893,7 @@ func NotRegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, ar if h, ok := t.(H); ok { h.Helper() } - return assertions.NotRegexpT(t, rx, actual, forwardArgs(msg, args)) + return assertions.NotRegexpT[Rex, ADoc](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]. @@ -805,6 +906,26 @@ func NotSamef(t T, expected any, actual any, msg string, args ...any) bool { return assertions.NotSame(t, expected, actual, forwardArgs(msg, args)) } +// NotSameTf is the same as [NotSameT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func NotSameTf[P any](t T, expected *P, actual *P, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.NotSameT[P](t, expected, actual, forwardArgs(msg, args)) +} + +// NotSortedTf is the same as [NotSortedT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func NotSortedTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.NotSortedT[OrderedSlice, E](t, collection, forwardArgs(msg, args)) +} + // 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. @@ -872,7 +993,7 @@ func PositiveTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, arg if h, ok := t.(H); ok { h.Helper() } - return assertions.PositiveT(t, e, forwardArgs(msg, args)) + return assertions.PositiveT[SignedNumber](t, e, forwardArgs(msg, args)) } // Regexpf is the same as [Regexp], but it accepts a format msg string to format arguments like [fmt.Printf]. @@ -892,7 +1013,7 @@ func RegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, args if h, ok := t.(H); ok { h.Helper() } - return assertions.RegexpT(t, rx, actual, forwardArgs(msg, args)) + return assertions.RegexpT[Rex, ADoc](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]. @@ -905,6 +1026,106 @@ func Samef(t T, expected any, actual any, msg string, args ...any) bool { return assertions.Same(t, expected, actual, forwardArgs(msg, args)) } +// SameTf is the same as [SameT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SameTf[P any](t T, expected *P, actual *P, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SameT[P](t, expected, actual, forwardArgs(msg, args)) +} + +// SeqContainsTf is the same as [SeqContainsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SeqContainsTf[E comparable](t T, iter iter.Seq[E], element E, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SeqContainsT[E](t, iter, element, forwardArgs(msg, args)) +} + +// SeqNotContainsTf is the same as [SeqNotContainsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SeqNotContainsTf[E comparable](t T, iter iter.Seq[E], element E, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SeqNotContainsT[E](t, iter, element, forwardArgs(msg, args)) +} + +// SliceContainsTf is the same as [SliceContainsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SliceContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SliceContainsT[Slice, E](t, s, element, forwardArgs(msg, args)) +} + +// SliceNotContainsTf is the same as [SliceNotContainsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SliceNotContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SliceNotContainsT[Slice, E](t, s, element, forwardArgs(msg, args)) +} + +// SliceNotSubsetTf is the same as [SliceNotSubsetT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SliceNotSubsetTf[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msg string, args ...any) (ok bool) { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SliceNotSubsetT[Slice, E](t, list, subset, forwardArgs(msg, args)) +} + +// SliceSubsetTf is the same as [SliceSubsetT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SliceSubsetTf[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msg string, args ...any) (ok bool) { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SliceSubsetT[Slice, E](t, list, subset, forwardArgs(msg, args)) +} + +// SortedTf is the same as [SortedT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SortedTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SortedT[OrderedSlice, E](t, collection, forwardArgs(msg, args)) +} + +// StringContainsTf is the same as [StringContainsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func StringContainsTf[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.StringContainsT[ADoc, EDoc](t, str, substring, forwardArgs(msg, args)) +} + +// StringNotContainsTf is the same as [StringNotContainsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func StringNotContainsTf[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.StringNotContainsT[ADoc, EDoc](t, str, substring, forwardArgs(msg, args)) +} + // 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. @@ -932,7 +1153,7 @@ 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)) + return assertions.TrueT[B](t, value, forwardArgs(msg, args)) } // WithinDurationf is the same as [WithinDuration], but it accepts a format msg string to format arguments like [fmt.Printf]. @@ -982,7 +1203,7 @@ func YAMLEqTf[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msg string, args if h, ok := t.(H); ok { h.Helper() } - return assertions.YAMLEqT(t, expected, actual, forwardArgs(msg, args)) + return assertions.YAMLEqT[EDoc, ADoc](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]. diff --git a/assert/assert_format_test.go b/assert/assert_format_test.go index c9fdfcb89..781a9ffab 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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package assert @@ -13,6 +13,7 @@ import ( "net/url" "path/filepath" "reflect" + "slices" "testing" "time" ) @@ -89,6 +90,30 @@ func TestDirExistsf(t *testing.T) { }) } +func TestDirNotExistsf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := DirNotExistsf(t, filepath.Join(testDataPath(), "non_existing_dir"), "test message") + if !result { + t.Error("DirNotExistsf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := DirNotExistsf(mock, filepath.Join(testDataPath(), "existing_dir"), "test message") + if result { + t.Error("DirNotExistsf should return false on failure") + } + if !mock.failed { + t.Error("DirNotExists should mark test as failed") + } + }) +} + func TestElementsMatchf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -233,6 +258,30 @@ func TestEqualExportedValuesf(t *testing.T) { }) } +func TestEqualTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := EqualTf(t, 123, 123, "test message") + if !result { + t.Error("EqualTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := EqualTf(mock, 123, 456, "test message") + if result { + t.Error("EqualTf should return false on failure") + } + if !mock.failed { + t.Error("EqualT should mark test as failed") + } + }) +} + func TestEqualValuesf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -577,6 +626,30 @@ func TestFileNotEmptyf(t *testing.T) { }) } +func TestFileNotExistsf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := FileNotExistsf(t, filepath.Join(testDataPath(), "non_existing_file"), "test message") + if !result { + t.Error("FileNotExistsf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := FileNotExistsf(mock, filepath.Join(testDataPath(), "existing_file"), "test message") + if result { + t.Error("FileNotExistsf should return false on failure") + } + if !mock.failed { + t.Error("FileNotExists should mark test as failed") + } + }) +} + func TestGreaterf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1033,6 +1106,30 @@ func TestIsDecreasingf(t *testing.T) { }) } +func TestIsDecreasingTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := IsDecreasingTf(t, []int{3, 2, 1}, "test message") + if !result { + t.Error("IsDecreasingTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := IsDecreasingTf(mock, []int{1, 2, 3}, "test message") + if result { + t.Error("IsDecreasingTf should return false on failure") + } + if !mock.failed { + t.Error("IsDecreasingT should mark test as failed") + } + }) +} + func TestIsIncreasingf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1057,6 +1154,30 @@ func TestIsIncreasingf(t *testing.T) { }) } +func TestIsIncreasingTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := IsIncreasingTf(t, []int{1, 2, 3}, "test message") + if !result { + t.Error("IsIncreasingTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := IsIncreasingTf(mock, []int{1, 1, 2}, "test message") + if result { + t.Error("IsIncreasingTf should return false on failure") + } + if !mock.failed { + t.Error("IsIncreasingT should mark test as failed") + } + }) +} + func TestIsNonDecreasingf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1071,7 +1192,7 @@ func TestIsNonDecreasingf(t *testing.T) { t.Parallel() mock := new(mockT) - result := IsNonDecreasingf(mock, []int{2, 1, 1}, "test message") + result := IsNonDecreasingf(mock, []int{2, 1, 0}, "test message") if result { t.Error("IsNonDecreasingf should return false on failure") } @@ -1081,6 +1202,30 @@ func TestIsNonDecreasingf(t *testing.T) { }) } +func TestIsNonDecreasingTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := IsNonDecreasingTf(t, []int{1, 1, 2}, "test message") + if !result { + t.Error("IsNonDecreasingTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := IsNonDecreasingTf(mock, []int{2, 1, 0}, "test message") + if result { + t.Error("IsNonDecreasingTf should return false on failure") + } + if !mock.failed { + t.Error("IsNonDecreasingT should mark test as failed") + } + }) +} + func TestIsNonIncreasingf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1105,6 +1250,54 @@ func TestIsNonIncreasingf(t *testing.T) { }) } +func TestIsNonIncreasingTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := IsNonIncreasingTf(t, []int{2, 1, 1}, "test message") + if !result { + t.Error("IsNonIncreasingTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := IsNonIncreasingTf(mock, []int{1, 2, 3}, "test message") + if result { + t.Error("IsNonIncreasingTf should return false on failure") + } + if !mock.failed { + t.Error("IsNonIncreasingT should mark test as failed") + } + }) +} + +func TestIsNotOfTypeTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := IsNotOfTypeTf[myType](t, 123.123, "test message") + if !result { + t.Error("IsNotOfTypeTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := IsNotOfTypeTf[myType](mock, myType(123.123), "test message") + if result { + t.Error("IsNotOfTypeTf should return false on failure") + } + if !mock.failed { + t.Error("IsNotOfTypeT should mark test as failed") + } + }) +} + func TestIsNotTypef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1129,6 +1322,30 @@ func TestIsNotTypef(t *testing.T) { }) } +func TestIsOfTypeTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := IsOfTypeTf[myType](t, myType(123.123), "test message") + if !result { + t.Error("IsOfTypeTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := IsOfTypeTf[myType](mock, 123.123, "test message") + if result { + t.Error("IsOfTypeTf should return false on failure") + } + if !mock.failed { + t.Error("IsOfTypeT should mark test as failed") + } + }) +} + func TestIsTypef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1369,13 +1586,13 @@ func TestLessTf(t *testing.T) { }) } -func TestNegativef(t *testing.T) { +func TestMapContainsTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - result := Negativef(t, -1, "test message") + result := MapContainsTf(t, map[string]string{"A": "B"}, "A", "test message") if !result { - t.Error("Negativef should return true on success") + t.Error("MapContainsTf should return true on success") } }) @@ -1383,23 +1600,23 @@ func TestNegativef(t *testing.T) { t.Parallel() mock := new(mockT) - result := Negativef(mock, 1, "test message") + result := MapContainsTf(mock, map[string]string{"A": "B"}, "C", "test message") if result { - t.Error("Negativef should return false on failure") + t.Error("MapContainsTf should return false on failure") } if !mock.failed { - t.Error("Negative should mark test as failed") + t.Error("MapContainsT should mark test as failed") } }) } -func TestNegativeTf(t *testing.T) { +func TestMapNotContainsTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - result := NegativeTf(t, -1, "test message") + result := MapNotContainsTf(t, map[string]string{"A": "B"}, "C", "test message") if !result { - t.Error("NegativeTf should return true on success") + t.Error("MapNotContainsTf should return true on success") } }) @@ -1407,23 +1624,23 @@ func TestNegativeTf(t *testing.T) { t.Parallel() mock := new(mockT) - result := NegativeTf(mock, 1, "test message") + result := MapNotContainsTf(mock, map[string]string{"A": "B"}, "A", "test message") if result { - t.Error("NegativeTf should return false on failure") + t.Error("MapNotContainsTf should return false on failure") } if !mock.failed { - t.Error("NegativeT should mark test as failed") + t.Error("MapNotContainsT should mark test as failed") } }) } -func TestNeverf(t *testing.T) { +func TestNegativef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - result := Neverf(t, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond, "test message") + result := Negativef(t, -1, "test message") if !result { - t.Error("Neverf should return true on success") + t.Error("Negativef should return true on success") } }) @@ -1431,23 +1648,23 @@ func TestNeverf(t *testing.T) { t.Parallel() mock := new(mockT) - result := Neverf(mock, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond, "test message") + result := Negativef(mock, 1, "test message") if result { - t.Error("Neverf should return false on failure") + t.Error("Negativef should return false on failure") } if !mock.failed { - t.Error("Never should mark test as failed") + t.Error("Negative should mark test as failed") } }) } -func TestNilf(t *testing.T) { +func TestNegativeTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - result := Nilf(t, nil, "test message") + result := NegativeTf(t, -1, "test message") if !result { - t.Error("Nilf should return true on success") + t.Error("NegativeTf should return true on success") } }) @@ -1455,23 +1672,23 @@ func TestNilf(t *testing.T) { t.Parallel() mock := new(mockT) - result := Nilf(mock, "not nil", "test message") + result := NegativeTf(mock, 1, "test message") if result { - t.Error("Nilf should return false on failure") + t.Error("NegativeTf should return false on failure") } if !mock.failed { - t.Error("Nil should mark test as failed") + t.Error("NegativeT should mark test as failed") } }) } -func TestNoDirExistsf(t *testing.T) { +func TestNeverf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - result := NoDirExistsf(t, filepath.Join(testDataPath(), "non_existing_dir"), "test message") + result := Neverf(t, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond, "test message") if !result { - t.Error("NoDirExistsf should return true on success") + t.Error("Neverf should return true on success") } }) @@ -1479,23 +1696,23 @@ func TestNoDirExistsf(t *testing.T) { t.Parallel() mock := new(mockT) - result := NoDirExistsf(mock, filepath.Join(testDataPath(), "existing_dir"), "test message") + result := Neverf(mock, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond, "test message") if result { - t.Error("NoDirExistsf should return false on failure") + t.Error("Neverf should return false on failure") } if !mock.failed { - t.Error("NoDirExists should mark test as failed") + t.Error("Never should mark test as failed") } }) } -func TestNoErrorf(t *testing.T) { +func TestNilf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - result := NoErrorf(t, nil, "test message") + result := Nilf(t, nil, "test message") if !result { - t.Error("NoErrorf should return true on success") + t.Error("Nilf should return true on success") } }) @@ -1503,23 +1720,23 @@ func TestNoErrorf(t *testing.T) { t.Parallel() mock := new(mockT) - result := NoErrorf(mock, ErrTest, "test message") + result := Nilf(mock, "not nil", "test message") if result { - t.Error("NoErrorf should return false on failure") + t.Error("Nilf should return false on failure") } if !mock.failed { - t.Error("NoError should mark test as failed") + t.Error("Nil should mark test as failed") } }) } -func TestNoFileExistsf(t *testing.T) { +func TestNoErrorf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - result := NoFileExistsf(t, filepath.Join(testDataPath(), "non_existing_file"), "test message") + result := NoErrorf(t, nil, "test message") if !result { - t.Error("NoFileExistsf should return true on success") + t.Error("NoErrorf should return true on success") } }) @@ -1527,12 +1744,12 @@ func TestNoFileExistsf(t *testing.T) { t.Parallel() mock := new(mockT) - result := NoFileExistsf(mock, filepath.Join(testDataPath(), "existing_file"), "test message") + result := NoErrorf(mock, ErrTest, "test message") if result { - t.Error("NoFileExistsf should return false on failure") + t.Error("NoErrorf should return false on failure") } if !mock.failed { - t.Error("NoFileExists should mark test as failed") + t.Error("NoError should mark test as failed") } }) } @@ -1657,6 +1874,30 @@ func TestNotEqualf(t *testing.T) { }) } +func TestNotEqualTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := NotEqualTf(t, 123, 456, "test message") + if !result { + t.Error("NotEqualTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := NotEqualTf(mock, 123, 123, "test message") + if result { + t.Error("NotEqualTf should return false on failure") + } + if !mock.failed { + t.Error("NotEqualT should mark test as failed") + } + }) +} + func TestNotEqualValuesf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1897,6 +2138,54 @@ func TestNotSamef(t *testing.T) { }) } +func TestNotSameTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := NotSameTf(t, &staticVar, ptr("static string"), "test message") + if !result { + t.Error("NotSameTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := NotSameTf(mock, &staticVar, staticVarPtr, "test message") + if result { + t.Error("NotSameTf should return false on failure") + } + if !mock.failed { + t.Error("NotSameT should mark test as failed") + } + }) +} + +func TestNotSortedTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := NotSortedTf(t, []int{3, 1, 3}, "test message") + if !result { + t.Error("NotSortedTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := NotSortedTf(mock, []int{1, 4, 8}, "test message") + if result { + t.Error("NotSortedTf should return false on failure") + } + if !mock.failed { + t.Error("NotSortedT should mark test as failed") + } + }) +} + func TestNotSubsetf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -2137,6 +2426,246 @@ func TestSamef(t *testing.T) { }) } +func TestSameTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := SameTf(t, &staticVar, staticVarPtr, "test message") + if !result { + t.Error("SameTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SameTf(mock, &staticVar, ptr("static string"), "test message") + if result { + t.Error("SameTf should return false on failure") + } + if !mock.failed { + t.Error("SameT should mark test as failed") + } + }) +} + +func TestSeqContainsTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := SeqContainsTf(t, slices.Values([]string{"A", "B"}), "A", "test message") + if !result { + t.Error("SeqContainsTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SeqContainsTf(mock, slices.Values([]string{"A", "B"}), "C", "test message") + if result { + t.Error("SeqContainsTf should return false on failure") + } + if !mock.failed { + t.Error("SeqContainsT should mark test as failed") + } + }) +} + +func TestSeqNotContainsTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := SeqNotContainsTf(t, slices.Values([]string{"A", "B"}), "C", "test message") + if !result { + t.Error("SeqNotContainsTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SeqNotContainsTf(mock, slices.Values([]string{"A", "B"}), "A", "test message") + if result { + t.Error("SeqNotContainsTf should return false on failure") + } + if !mock.failed { + t.Error("SeqNotContainsT should mark test as failed") + } + }) +} + +func TestSliceContainsTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := SliceContainsTf(t, []string{"A", "B"}, "A", "test message") + if !result { + t.Error("SliceContainsTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SliceContainsTf(mock, []string{"A", "B"}, "C", "test message") + if result { + t.Error("SliceContainsTf should return false on failure") + } + if !mock.failed { + t.Error("SliceContainsT should mark test as failed") + } + }) +} + +func TestSliceNotContainsTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := SliceNotContainsTf(t, []string{"A", "B"}, "C", "test message") + if !result { + t.Error("SliceNotContainsTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SliceNotContainsTf(mock, []string{"A", "B"}, "A", "test message") + if result { + t.Error("SliceNotContainsTf should return false on failure") + } + if !mock.failed { + t.Error("SliceNotContainsT should mark test as failed") + } + }) +} + +func TestSliceNotSubsetTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := SliceNotSubsetTf(t, []int{1, 2, 3}, []int{4, 5}, "test message") + if !result { + t.Error("SliceNotSubsetTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SliceNotSubsetTf(mock, []int{1, 2, 3}, []int{1, 2}, "test message") + if result { + t.Error("SliceNotSubsetTf should return false on failure") + } + if !mock.failed { + t.Error("SliceNotSubsetT should mark test as failed") + } + }) +} + +func TestSliceSubsetTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := SliceSubsetTf(t, []int{1, 2, 3}, []int{1, 2}, "test message") + if !result { + t.Error("SliceSubsetTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SliceSubsetTf(mock, []int{1, 2, 3}, []int{4, 5}, "test message") + if result { + t.Error("SliceSubsetTf should return false on failure") + } + if !mock.failed { + t.Error("SliceSubsetT should mark test as failed") + } + }) +} + +func TestSortedTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := SortedTf(t, []int{1, 1, 3}, "test message") + if !result { + t.Error("SortedTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SortedTf(mock, []int{1, 4, 2}, "test message") + if result { + t.Error("SortedTf should return false on failure") + } + if !mock.failed { + t.Error("SortedT should mark test as failed") + } + }) +} + +func TestStringContainsTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := StringContainsTf(t, "AB", "A", "test message") + if !result { + t.Error("StringContainsTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := StringContainsTf(mock, "AB", "C", "test message") + if result { + t.Error("StringContainsTf should return false on failure") + } + if !mock.failed { + t.Error("StringContainsT should mark test as failed") + } + }) +} + +func TestStringNotContainsTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + result := StringNotContainsTf(t, "AB", "C", "test message") + if !result { + t.Error("StringNotContainsTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := StringNotContainsTf(mock, "AB", "A", "test message") + if result { + t.Error("StringNotContainsTf should return false on failure") + } + if !mock.failed { + t.Error("StringNotContainsT should mark test as failed") + } + }) +} + func TestSubsetf(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 5a5edeed9..47acb10b5 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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package assert @@ -91,6 +91,26 @@ func (a *Assertions) DirExistsf(path string, msg string, args ...any) bool { return assertions.DirExists(a.t, path, forwardArgs(msg, args)) } +// DirNotExists is the same as [DirNotExists], as a method rather than a package-level function. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func (a *Assertions) DirNotExists(path string, msgAndArgs ...any) bool { + if h, ok := a.t.(H); ok { + h.Helper() + } + return assertions.DirNotExists(a.t, path, msgAndArgs...) +} + +// DirNotExistsf is the same as [Assertions.DirNotExists], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func (a *Assertions) DirNotExistsf(path string, msg string, args ...any) bool { + if h, ok := a.t.(H); ok { + h.Helper() + } + return assertions.DirNotExists(a.t, path, forwardArgs(msg, args)) +} + // ElementsMatch is the same as [ElementsMatch], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. @@ -471,6 +491,26 @@ func (a *Assertions) FileNotEmptyf(path string, msg string, args ...any) bool { return assertions.FileNotEmpty(a.t, path, forwardArgs(msg, args)) } +// FileNotExists is the same as [FileNotExists], as a method rather than a package-level function. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func (a *Assertions) FileNotExists(path string, msgAndArgs ...any) bool { + if h, ok := a.t.(H); ok { + h.Helper() + } + return assertions.FileNotExists(a.t, path, msgAndArgs...) +} + +// FileNotExistsf is the same as [Assertions.FileNotExists], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func (a *Assertions) FileNotExistsf(path string, msg string, args ...any) bool { + if h, ok := a.t.(H); ok { + h.Helper() + } + return assertions.FileNotExists(a.t, path, forwardArgs(msg, args)) +} + // Greater is the same as [Greater], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. @@ -1051,26 +1091,6 @@ func (a *Assertions) Nilf(object any, msg string, args ...any) bool { return assertions.Nil(a.t, object, forwardArgs(msg, args)) } -// NoDirExists is the same as [NoDirExists], as a method rather than a package-level function. -// -// Upon failure, the test [T] is marked as failed and continues execution. -func (a *Assertions) NoDirExists(path string, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { - h.Helper() - } - return assertions.NoDirExists(a.t, path, msgAndArgs...) -} - -// 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 { - if h, ok := a.t.(H); ok { - h.Helper() - } - return assertions.NoDirExists(a.t, path, forwardArgs(msg, args)) -} - // NoError is the same as [NoError], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. @@ -1091,26 +1111,6 @@ func (a *Assertions) NoErrorf(err error, msg string, args ...any) bool { return assertions.NoError(a.t, err, forwardArgs(msg, args)) } -// NoFileExists is the same as [NoFileExists], as a method rather than a package-level function. -// -// Upon failure, the test [T] is marked as failed and continues execution. -func (a *Assertions) NoFileExists(path string, msgAndArgs ...any) bool { - if h, ok := a.t.(H); ok { - h.Helper() - } - return assertions.NoFileExists(a.t, path, msgAndArgs...) -} - -// 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 { - if h, ok := a.t.(H); ok { - h.Helper() - } - return assertions.NoFileExists(a.t, path, forwardArgs(msg, args)) -} - // NotContains is the same as [NotContains], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. diff --git a/assert/assert_forward_test.go b/assert/assert_forward_test.go index c52c70a98..ef4a0de14 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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package assert @@ -179,6 +179,60 @@ func TestAssertionsDirExistsf(t *testing.T) { }) } +func TestAssertionsDirNotExists(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + + a := New(t) + result := a.DirNotExists(filepath.Join(testDataPath(), "non_existing_dir")) + if !result { + t.Error("Assertions.DirNotExists should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + a := New(mock) + result := a.DirNotExists(filepath.Join(testDataPath(), "existing_dir")) + if result { + t.Error("Assertions.DirNotExists should return false on failure") + } + if !mock.failed { + t.Error("DirNotExists should mark test as failed") + } + }) +} + +func TestAssertionsDirNotExistsf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + + a := New(t) + result := a.DirNotExistsf(filepath.Join(testDataPath(), "non_existing_dir"), "test message") + if !result { + t.Error("Assertions.DirNotExists should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + a := New(mock) + result := a.DirNotExistsf(filepath.Join(testDataPath(), "existing_dir"), "test message") + if result { + t.Error("Assertions.DirNotExists should return false on failure") + } + if !mock.failed { + t.Error("Assertions.DirNotExists should mark test as failed") + } + }) +} + func TestAssertionsElementsMatch(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1168,6 +1222,60 @@ func TestAssertionsFileNotEmptyf(t *testing.T) { }) } +func TestAssertionsFileNotExists(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + + a := New(t) + result := a.FileNotExists(filepath.Join(testDataPath(), "non_existing_file")) + if !result { + t.Error("Assertions.FileNotExists should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + a := New(mock) + result := a.FileNotExists(filepath.Join(testDataPath(), "existing_file")) + if result { + t.Error("Assertions.FileNotExists should return false on failure") + } + if !mock.failed { + t.Error("FileNotExists should mark test as failed") + } + }) +} + +func TestAssertionsFileNotExistsf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + + a := New(t) + result := a.FileNotExistsf(filepath.Join(testDataPath(), "non_existing_file"), "test message") + if !result { + t.Error("Assertions.FileNotExists should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + a := New(mock) + result := a.FileNotExistsf(filepath.Join(testDataPath(), "existing_file"), "test message") + if result { + t.Error("Assertions.FileNotExists should return false on failure") + } + if !mock.failed { + t.Error("Assertions.FileNotExists should mark test as failed") + } + }) +} + func TestAssertionsGreater(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -2049,7 +2157,7 @@ func TestAssertionsIsNonDecreasing(t *testing.T) { mock := new(mockT) a := New(mock) - result := a.IsNonDecreasing([]int{2, 1, 1}) + result := a.IsNonDecreasing([]int{2, 1, 0}) if result { t.Error("Assertions.IsNonDecreasing should return false on failure") } @@ -2076,7 +2184,7 @@ func TestAssertionsIsNonDecreasingf(t *testing.T) { mock := new(mockT) a := New(mock) - result := a.IsNonDecreasingf([]int{2, 1, 1}, "test message") + result := a.IsNonDecreasingf([]int{2, 1, 0}, "test message") if result { t.Error("Assertions.IsNonDecreasing should return false on failure") } @@ -2734,60 +2842,6 @@ func TestAssertionsNilf(t *testing.T) { }) } -func TestAssertionsNoDirExists(t *testing.T) { - t.Parallel() - t.Run("success", func(t *testing.T) { - t.Parallel() - - a := New(t) - result := a.NoDirExists(filepath.Join(testDataPath(), "non_existing_dir")) - if !result { - t.Error("Assertions.NoDirExists should return true on success") - } - }) - - t.Run("failure", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - a := New(mock) - result := a.NoDirExists(filepath.Join(testDataPath(), "existing_dir")) - if result { - t.Error("Assertions.NoDirExists should return false on failure") - } - if !mock.failed { - t.Error("NoDirExists should mark test as failed") - } - }) -} - -func TestAssertionsNoDirExistsf(t *testing.T) { - t.Parallel() - t.Run("success", func(t *testing.T) { - t.Parallel() - - a := New(t) - result := a.NoDirExistsf(filepath.Join(testDataPath(), "non_existing_dir"), "test message") - if !result { - t.Error("Assertions.NoDirExists should return true on success") - } - }) - - t.Run("failure", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - a := New(mock) - result := a.NoDirExistsf(filepath.Join(testDataPath(), "existing_dir"), "test message") - if result { - t.Error("Assertions.NoDirExists should return false on failure") - } - if !mock.failed { - t.Error("Assertions.NoDirExists should mark test as failed") - } - }) -} - func TestAssertionsNoError(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -2842,60 +2896,6 @@ func TestAssertionsNoErrorf(t *testing.T) { }) } -func TestAssertionsNoFileExists(t *testing.T) { - t.Parallel() - t.Run("success", func(t *testing.T) { - t.Parallel() - - a := New(t) - result := a.NoFileExists(filepath.Join(testDataPath(), "non_existing_file")) - if !result { - t.Error("Assertions.NoFileExists should return true on success") - } - }) - - t.Run("failure", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - a := New(mock) - result := a.NoFileExists(filepath.Join(testDataPath(), "existing_file")) - if result { - t.Error("Assertions.NoFileExists should return false on failure") - } - if !mock.failed { - t.Error("NoFileExists should mark test as failed") - } - }) -} - -func TestAssertionsNoFileExistsf(t *testing.T) { - t.Parallel() - t.Run("success", func(t *testing.T) { - t.Parallel() - - a := New(t) - result := a.NoFileExistsf(filepath.Join(testDataPath(), "non_existing_file"), "test message") - if !result { - t.Error("Assertions.NoFileExists should return true on success") - } - }) - - t.Run("failure", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - a := New(mock) - result := a.NoFileExistsf(filepath.Join(testDataPath(), "existing_file"), "test message") - if result { - t.Error("Assertions.NoFileExists should return false on failure") - } - if !mock.failed { - t.Error("Assertions.NoFileExists should mark test as failed") - } - }) -} - func TestAssertionsNotContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { diff --git a/assert/assert_helpers.go b/assert/assert_helpers.go index b4216c990..5c1f0b295 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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package assert diff --git a/assert/assert_helpers_test.go b/assert/assert_helpers_test.go index e687cc1ce..a7c3c33e0 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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package assert diff --git a/assert/assert_types.go b/assert/assert_types.go index 038328fe6..bcf6fcccf 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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package assert @@ -57,7 +57,7 @@ type ( // 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]. + // This is used by [GreaterT], [GreaterOrEqualT], [LessT], [LessOrEqualT], [IsIncreasingT], [IsDecreasingT]. // // NOTE: since [time.Time] is a struct, custom types which redeclare [time.Time] are not supported. Ordered = assertions.Ordered diff --git a/codegen/internal/generator/funcmaps/funcmaps.go b/codegen/internal/generator/funcmaps/funcmaps.go index 0a168ae54..cba923ef0 100644 --- a/codegen/internal/generator/funcmaps/funcmaps.go +++ b/codegen/internal/generator/funcmaps/funcmaps.go @@ -48,6 +48,7 @@ func FuncMap() template.FuncMap { "docStringPackage": docStringPackage, "forward": forward, "godocbadge": godocbadge, + "hasPrefix": strings.HasPrefix, "hasSuffix": strings.HasSuffix, "imports": printImports, "mdformat": FormatMarkdown, // From markdown.go @@ -354,7 +355,7 @@ func slugize(in string) string { switch r { case '.', '_', ' ', '\t', ':': return '-' - case '[', ']', ',': + case '[', ']', ',', '~': return -1 default: return r diff --git a/codegen/internal/generator/templates/assertion_assertions.gotmpl b/codegen/internal/generator/templates/assertion_assertions.gotmpl index b3c9d2bf3..22ca06fd7 100644 --- a/codegen/internal/generator/templates/assertion_assertions.gotmpl +++ b/codegen/internal/generator/templates/assertion_assertions.gotmpl @@ -23,7 +23,7 @@ func {{ .GenericName }}({{ params .AllParams }}) {{ returns .Returns }} { if h, ok := t.(H); ok { h.Helper() } - return {{ .TargetPackage }}.{{ .Name }}({{ forward .AllParams }}) + return {{ .TargetPackage }}.{{ .GenericCallName }}({{ forward .AllParams }}) } {{- end }} {{- end }} diff --git a/codegen/internal/generator/templates/assertion_assertions_test.gotmpl b/codegen/internal/generator/templates/assertion_assertions_test.gotmpl index 390ba742c..bb946f231 100644 --- a/codegen/internal/generator/templates/assertion_assertions_test.gotmpl +++ b/codegen/internal/generator/templates/assertion_assertions_test.gotmpl @@ -32,10 +32,10 @@ func Test{{ .Name }}(t *testing.T) { t.Parallel() {{- if $isRequire }} - {{ $fn.Name }}(t, {{ .TestedValue }}) + {{ $fn.Name }}{{ if hasSuffix $fn.Name "OfTypeT" }}[myType]{{ end }}(t, {{ .TestedValue }}) // require functions don't return a value {{- else }} - result := {{ $fn.Name }}(t, {{ .TestedValue }}) + result := {{ $fn.Name }}{{ if hasSuffix $fn.Name "OfTypeT" }}[myType]{{ end }}(t, {{ .TestedValue }}) if !result { t.Error("{{ $fn.Name }} should return true on success") } @@ -48,10 +48,10 @@ func Test{{ .Name }}(t *testing.T) { mock := new({{ $fn.UseMock }}) {{- if $isRequire }} - {{ $fn.Name }}(mock, {{ .TestedValue }}) + {{ $fn.Name }}{{ if hasSuffix $fn.Name "OfTypeT" }}[myType]{{ end }}(mock, {{ .TestedValue }}) // require functions don't return a value {{- else }} - result := {{ $fn.Name }}(mock, {{ .TestedValue }}) + result := {{ $fn.Name }}{{ if hasSuffix $fn.Name "OfTypeT" }}[myType]{{ end }}(mock, {{ .TestedValue }}) if result { t.Error("{{ $fn.Name }} should return false on failure") } @@ -66,7 +66,7 @@ func Test{{ .Name }}(t *testing.T) { t.Parallel() Panics(t, func() { - {{ $fn.Name }}(t, {{ .TestedValue }}) + {{ $fn.Name }}{{ if hasSuffix $fn.Name "OfTypeT" }}[myType]{{ end }}(t, {{ .TestedValue }}) }, "{{ .AssertionMessage }}") }) {{- end }} @@ -149,3 +149,5 @@ type dummyError struct { func (d *dummyError) Error() string { return "dummy error" } + +type myType float64 diff --git a/codegen/internal/generator/templates/assertion_examples_test.gotmpl b/codegen/internal/generator/templates/assertion_examples_test.gotmpl index 2760085d2..66380aeb2 100644 --- a/codegen/internal/generator/templates/assertion_examples_test.gotmpl +++ b/codegen/internal/generator/templates/assertion_examples_test.gotmpl @@ -35,14 +35,14 @@ func Example{{ .Name }}() { {{- if (not $first) }}{{ println "" }}{{- end }}{{ $first = false }} t := new(testing.T) {{- if $isRequire }} - {{ $pkg }}.{{ $fn.Name }}(t, {{ relocate .TestedValues $pkg }}) + {{ $pkg }}.{{ $fn.Name }}{{ if hasSuffix $fn.Name "OfTypeT" }}[myType]{{ end }}(t, {{ relocate .TestedValues $pkg }}) fmt.Println("passed") {{- if $runnable }} // Output: passed {{- end }} {{- else }} - success := {{ $pkg }}.{{ $fn.Name }}(t, {{ relocate .TestedValues $pkg }}) + success := {{ $pkg }}.{{ $fn.Name }}{{ if hasSuffix $fn.Name "OfTypeT" }}[myType]{{ end }}(t, {{ relocate .TestedValues $pkg }}) fmt.Printf("success: %t\n", success) {{- if $runnable }} @@ -107,3 +107,5 @@ type dummyError struct { func (d *dummyError) Error() string { return "dummy error" } + +type myType float64 diff --git a/codegen/internal/generator/templates/assertion_format.gotmpl b/codegen/internal/generator/templates/assertion_format.gotmpl index 45d120975..791fc75ac 100644 --- a/codegen/internal/generator/templates/assertion_format.gotmpl +++ b/codegen/internal/generator/templates/assertion_format.gotmpl @@ -21,7 +21,7 @@ import ( {{ docStringPackage $.Package }} 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)) + return {{ .TargetPackage }}.{{ .GenericCallName }}(t, {{ forward .Params }}, forwardArgs(msg, args)) } {{- end }} {{- end }} diff --git a/codegen/internal/generator/templates/assertion_format_test.gotmpl b/codegen/internal/generator/templates/assertion_format_test.gotmpl index 78b82e47d..49ca09325 100644 --- a/codegen/internal/generator/templates/assertion_format_test.gotmpl +++ b/codegen/internal/generator/templates/assertion_format_test.gotmpl @@ -33,10 +33,10 @@ func Test{{ .Name }}f(t *testing.T) { t.Parallel() {{- if $isRequire }} - {{ $fn.Name }}f(t, {{ .TestedValue }}, "test message") + {{ $fn.Name }}f{{ if hasSuffix $fn.Name "OfTypeT" }}[myType]{{ end }}(t, {{ .TestedValue }}, "test message") // require functions don't return a value {{- else }} - result := {{ $fn.Name }}f(t, {{ .TestedValue }}, "test message") + result := {{ $fn.Name }}f{{ if hasSuffix $fn.Name "OfTypeT" }}[myType]{{ end }}(t, {{ .TestedValue }}, "test message") if !result { t.Error("{{ $fn.Name }}f should return true on success") } @@ -49,10 +49,10 @@ func Test{{ .Name }}f(t *testing.T) { mock := new({{ $fn.UseMock }}) {{- if $isRequire }} - {{ $fn.Name }}f(mock, {{ .TestedValue }}, "test message") + {{ $fn.Name }}f{{ if hasSuffix $fn.Name "OfTypeT" }}[myType]{{ end }}(mock, {{ .TestedValue }}, "test message") // require functions don't return a value {{- else }} - result := {{ $fn.Name }}f(mock, {{ .TestedValue }}, "test message") + result := {{ $fn.Name }}f{{ if hasSuffix $fn.Name "OfTypeT" }}[myType]{{ end }}(mock, {{ .TestedValue }}, "test message") if result { t.Error("{{ $fn.Name }}f should return false on failure") } @@ -67,7 +67,7 @@ func Test{{ .Name }}f(t *testing.T) { t.Parallel() Panicsf(t, func() { - {{ $fn.Name }}f(t, {{ .TestedValue }}, "test message") + {{ $fn.Name }}f{{ if hasSuffix $fn.Name "OfTypeT" }}[myType]{{ end }}(t, {{ .TestedValue }}, "test message") }, "{{ .AssertionMessage }}") }) {{- end }} diff --git a/codegen/internal/generator/templates/requirement_assertions.gotmpl b/codegen/internal/generator/templates/requirement_assertions.gotmpl index 4b3ec961c..8fcc24dc8 100644 --- a/codegen/internal/generator/templates/requirement_assertions.gotmpl +++ b/codegen/internal/generator/templates/requirement_assertions.gotmpl @@ -24,7 +24,7 @@ func {{ .GenericName }}({{ params .AllParams }}) { {{- if or (eq .Name "Fail") (eq .Name "FailNow") }}{{/* special semantics for these two, which can only fail */}} _ = {{ .TargetPackage }}.{{ .Name }}({{ forward .AllParams }}) {{- else }} - if {{ .TargetPackage }}.{{ .Name }}({{ forward .AllParams }}) { + if {{ .TargetPackage }}.{{ .GenericCallName }}({{ forward .AllParams }}) { return } {{- end }} diff --git a/codegen/internal/generator/templates/requirement_format.gotmpl b/codegen/internal/generator/templates/requirement_format.gotmpl index 8e146809f..d2a311724 100644 --- a/codegen/internal/generator/templates/requirement_format.gotmpl +++ b/codegen/internal/generator/templates/requirement_format.gotmpl @@ -24,7 +24,7 @@ func {{ .GenericName "f" }}(t T, {{ params .Params }}, msg string, args ...any) {{- 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)) {{- else }} - if {{ .TargetPackage }}.{{ .Name }}(t, {{ forward .Params }}, forwardArgs(msg, args)) { + if {{ .TargetPackage }}.{{ .GenericCallName }}(t, {{ forward .Params }}, forwardArgs(msg, args)) { return } {{- end }} diff --git a/codegen/internal/model/model.go b/codegen/internal/model/model.go index c04237f1b..e06761630 100644 --- a/codegen/internal/model/model.go +++ b/codegen/internal/model/model.go @@ -103,7 +103,7 @@ type Function struct { // 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 + if !f.IsGeneric { // means len(f.TypeParams) == 0 return f.Name + suffix } @@ -122,7 +122,7 @@ func (f Function) GenericName(suffixes ...string) string { 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 { + if len(f.TypeParams) <= i+1+1 || f.TypeParams[i+1+1].Constraint != p.Constraint { w.WriteByte(' ') w.WriteString(p.Constraint) } @@ -132,6 +132,30 @@ func (f Function) GenericName(suffixes ...string) string { return w.String() } +// GenericCallName renders the function name with explicit type parameters. +// This is used when forwarding type parameters, as all type parameters may not be always infered from the arguments. +func (f Function) GenericCallName(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) + + for _, p := range f.TypeParams[1:] { + w.WriteString(", ") + w.WriteString(p.Name) + } + w.WriteByte(']') + + return w.String() +} + func (f Function) HasTest() bool { return len(f.Tests) > 0 } diff --git a/docs/doc-site/api/_index.md b/docs/doc-site/api/_index.md index f01a44070..435d15298 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-18 +modified: 2026-01-20 --- **Go testing assertions for the rest of us** @@ -33,21 +33,21 @@ Each domain contains assertions regrouped by their use case (e.g. http, json, er --- - [Boolean](./boolean.md) - Asserting Boolean Values (4) -- [Collection](./collection.md) - Asserting Slices And Maps (9) +- [Collection](./collection.md) - Asserting Slices And Maps (19) - [Comparison](./comparison.md) - Comparing Ordered Values (12) - [Condition](./condition.md) - Expressing Assertions Using Conditions (4) -- [Equality](./equality.md) - Asserting Two Things Are Equal (12) +- [Equality](./equality.md) - Asserting Two Things Are Equal (16) - [Error](./error.md) - Asserting Errors (8) - [File](./file.md) - Asserting OS Files (6) - [Http](./http.md) - Asserting HTTP Response And Body (7) - [Json](./json.md) - Asserting JSON Documents (3) - [Number](./number.md) - Asserting Numbers (7) -- [Ordering](./ordering.md) - Asserting How Collections Are Ordered (4) +- [Ordering](./ordering.md) - Asserting How Collections Are Ordered (10) - [Panic](./panic.md) - Asserting A Panic Behavior (4) - [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) +- [Type](./type.md) - Asserting Types Rather Than Values (10) - [Yaml](./yaml.md) - Asserting Yaml Documents (3) - [Common](./common.md) - Other Uncategorized Helpers (4) @@ -67,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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/boolean.md b/docs/doc-site/api/boolean.md index 01e28abbb..64fb03a32 100644 --- a/docs/doc-site/api/boolean.md +++ b/docs/doc-site/api/boolean.md @@ -1,7 +1,7 @@ --- title: "Boolean" description: "Asserting Boolean Values" -modified: 2026-01-18 +modified: 2026-01-20 weight: 1 domains: - "boolean" @@ -235,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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/collection.md b/docs/doc-site/api/collection.md index 7d3dbae1d..1f0a706d7 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-18 +modified: 2026-01-20 weight: 2 domains: - "collection" @@ -14,6 +14,10 @@ keywords: - "ElementsMatchTf" - "Len" - "Lenf" + - "MapContainsT" + - "MapContainsTf" + - "MapNotContainsT" + - "MapNotContainsTf" - "NotContains" - "NotContainsf" - "NotElementsMatch" @@ -22,6 +26,22 @@ keywords: - "NotElementsMatchTf" - "NotSubset" - "NotSubsetf" + - "SeqContainsT" + - "SeqContainsTf" + - "SeqNotContainsT" + - "SeqNotContainsTf" + - "SliceContainsT" + - "SliceContainsTf" + - "SliceNotContainsT" + - "SliceNotContainsTf" + - "SliceNotSubsetT" + - "SliceNotSubsetTf" + - "SliceSubsetT" + - "SliceSubsetTf" + - "StringContainsT" + - "StringContainsTf" + - "StringNotContainsT" + - "StringNotContainsTf" - "Subset" - "Subsetf" --- @@ -35,7 +55,7 @@ Asserting Slices And Maps _All links point to _ -This domain exposes 9 functionalities. +This domain exposes 19 functionalities. Generic assertions are marked with a {{% icon icon="star" color=orange %}} ```tree @@ -43,10 +63,20 @@ Generic assertions are marked with a {{% icon icon="star" color=orange %}} - [ElementsMatch](#elementsmatch) | angles-right - [ElementsMatchT[E comparable]](#elementsmatchte-comparable) | star | orange - [Len](#len) | angles-right +- [MapContainsT[Map ~map[K]V, K comparable, V any]](#mapcontainstmap-mapkv-k-comparable-v-any) | star | orange +- [MapNotContainsT[Map ~map[K]V, K comparable, V any]](#mapnotcontainstmap-mapkv-k-comparable-v-any) | star | orange - [NotContains](#notcontains) | angles-right - [NotElementsMatch](#notelementsmatch) | angles-right - [NotElementsMatchT[E comparable]](#notelementsmatchte-comparable) | star | orange - [NotSubset](#notsubset) | angles-right +- [SeqContainsT[E comparable]](#seqcontainste-comparable) | star | orange +- [SeqNotContainsT[E comparable]](#seqnotcontainste-comparable) | star | orange +- [SliceContainsT[Slice ~[]E, E comparable]](#slicecontainstslice-e-e-comparable) | star | orange +- [SliceNotContainsT[Slice ~[]E, E comparable]](#slicenotcontainstslice-e-e-comparable) | star | orange +- [SliceNotSubsetT[Slice ~[]E, E comparable]](#slicenotsubsettslice-e-e-comparable) | star | orange +- [SliceSubsetT[Slice ~[]E, E comparable]](#slicesubsettslice-e-e-comparable) | star | orange +- [StringContainsT[ADoc, EDoc Text]](#stringcontainstadoc-edoc-text) | star | orange +- [StringNotContainsT[ADoc, EDoc Text]](#stringnotcontainstadoc-edoc-text) | star | orange - [Subset](#subset) | angles-right ``` @@ -96,7 +126,7 @@ 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#L63) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Contains](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L65) {{% /tab %}} {{< /tabs >}} @@ -145,7 +175,7 @@ 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#L276) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ElementsMatch](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L522) {{% /tab %}} {{< /tabs >}} @@ -190,7 +220,7 @@ the number of appearances of each of them in both lists should match. |--|--| | [`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) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ElementsMatchT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L595) {{% /tab %}} {{< /tabs >}} @@ -245,13 +275,99 @@ See also [reflect.Len](https://pkg.go.dev/reflect#Len). |--|--| | [`assertions.Len(t T, object any, length int, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Len) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Len](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L31) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Len](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L33) > **Note** > (proposals) this does not currently support iterators, or collection objects that have a Len() method. {{% /tab %}} {{< /tabs >}} +### MapContainsT[Map ~map[K]V, K comparable, V any] {{% icon icon="star" color=orange %}}{#mapcontainstmap-mapkv-k-comparable-v-any} + +MapContainsT asserts that the specified map contains a key. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.MapContainsT(t, map[string]string{"Hello": "x","World": "y"}, "World") +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: map[string]string{"A": "B"}, "A" + failure: map[string]string{"A": "B"}, "C" +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.MapContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapContainsT) | package-level function | +| [`assert.MapContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapContainsTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.MapContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#MapContainsT) | package-level function | +| [`require.MapContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#MapContainsTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.MapContainsT(t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#MapContainsT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#MapContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L164) +{{% /tab %}} +{{< /tabs >}} + +### MapNotContainsT[Map ~map[K]V, K comparable, V any] {{% icon icon="star" color=orange %}}{#mapnotcontainstmap-mapkv-k-comparable-v-any} + +MapNotContainsT asserts that the specified map does not contain a key. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.MapNotContainsT(t, map[string]string{"Hello": "x","World": "y"}, "hi") +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: map[string]string{"A": "B"}, "C" + failure: map[string]string{"A": "B"}, "A" +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.MapNotContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapNotContainsT) | package-level function | +| [`assert.MapNotContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapNotContainsTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.MapNotContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#MapNotContainsT) | package-level function | +| [`require.MapNotContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#MapNotContainsTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.MapNotContainsT(t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#MapNotContainsT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#MapNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L290) +{{% /tab %}} +{{< /tabs >}} + ### NotContains{#notcontains} NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the @@ -298,7 +414,7 @@ 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#L93) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotContains](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L191) {{% /tab %}} {{< /tabs >}} @@ -350,7 +466,7 @@ 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#L313) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatch](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L559) {{% /tab %}} {{< /tabs >}} @@ -398,7 +514,7 @@ This is an inverse of ElementsMatch. |--|--| | [`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) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatchT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L631) {{% /tab %}} {{< /tabs >}} @@ -451,7 +567,355 @@ 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#L204) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSubset](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L424) +{{% /tab %}} +{{< /tabs >}} + +### SeqContainsT[E comparable] {{% icon icon="star" color=orange %}}{#seqcontainste-comparable} + +SeqContainsT asserts that the specified iterator contains a comparable element. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World") +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: slices.Values([]string{"A","B"}), "A" + failure: slices.Values([]string{"A","B"}), "C" +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.SeqContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SeqContainsT) | package-level function | +| [`assert.SeqContainsTf[E comparable](t T, iter iter.Seq[E], element E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SeqContainsTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.SeqContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SeqContainsT) | package-level function | +| [`require.SeqContainsTf[E comparable](t T, iter iter.Seq[E], element E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SeqContainsTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.SeqContainsT(t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SeqContainsT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SeqContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L140) +{{% /tab %}} +{{< /tabs >}} + +### SeqNotContainsT[E comparable] {{% icon icon="star" color=orange %}}{#seqnotcontainste-comparable} + +SeqNotContainsT asserts that the specified iterator does not contain a comparable element. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World") +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: slices.Values([]string{"A","B"}), "C" + failure: slices.Values([]string{"A","B"}), "A" +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.SeqNotContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SeqNotContainsT) | package-level function | +| [`assert.SeqNotContainsTf[E comparable](t T, iter iter.Seq[E], element E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SeqNotContainsTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.SeqNotContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SeqNotContainsT) | package-level function | +| [`require.SeqNotContainsTf[E comparable](t T, iter iter.Seq[E], element E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SeqNotContainsTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.SeqNotContainsT(t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SeqNotContainsT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SeqNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L266) +{{% /tab %}} +{{< /tabs >}} + +### SliceContainsT[Slice ~[]E, E comparable] {{% icon icon="star" color=orange %}}{#slicecontainstslice-e-e-comparable} + +SliceContainsT asserts that the specified slice contains a comparable element. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.SliceContainsT(t, []{"Hello","World"}, "World") +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: []string{"A","B"}, "A" + failure: []string{"A","B"}, "C" +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.SliceContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceContainsT) | package-level function | +| [`assert.SliceContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceContainsTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.SliceContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceContainsT) | package-level function | +| [`require.SliceContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceContainsTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.SliceContainsT(t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceContainsT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L117) +{{% /tab %}} +{{< /tabs >}} + +### SliceNotContainsT[Slice ~[]E, E comparable] {{% icon icon="star" color=orange %}}{#slicenotcontainstslice-e-e-comparable} + +SliceNotContainsT asserts that the specified slice does not contain a comparable element. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.SliceNotContainsT(t, []{"Hello","World"}, "hi") +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: []string{"A","B"}, "C" + failure: []string{"A","B"}, "A" +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.SliceNotContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceNotContainsT) | package-level function | +| [`assert.SliceNotContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceNotContainsTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.SliceNotContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceNotContainsT) | package-level function | +| [`require.SliceNotContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceNotContainsTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.SliceNotContainsT(t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceNotContainsT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L243) +{{% /tab %}} +{{< /tabs >}} + +### SliceNotSubsetT[Slice ~[]E, E comparable] {{% icon icon="star" color=orange %}}{#slicenotsubsettslice-e-e-comparable} + +SliceNotSubsetT asserts that a slice of comparable elements does not contain all the elements given in the subset. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.SliceNotSubsetT(t, []int{1, 2, 3}, []int{1, 4}) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: []int{1, 2, 3}, []int{4, 5} + failure: []int{1, 2, 3}, []int{1, 2} +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.SliceNotSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceNotSubsetT) | package-level function | +| [`assert.SliceNotSubsetTf[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceNotSubsetTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.SliceNotSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceNotSubsetT) | package-level function | +| [`require.SliceNotSubsetTf[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceNotSubsetTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.SliceNotSubsetT(t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceNotSubsetT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceNotSubsetT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L495) +{{% /tab %}} +{{< /tabs >}} + +### SliceSubsetT[Slice ~[]E, E comparable] {{% icon icon="star" color=orange %}}{#slicesubsettslice-e-e-comparable} + +SliceSubsetT asserts that a slice of comparable elements contains all the elements given in the subset. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: []int{1, 2, 3}, []int{1, 2} + failure: []int{1, 2, 3}, []int{4, 5} +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.SliceSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceSubsetT) | package-level function | +| [`assert.SliceSubsetTf[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceSubsetTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.SliceSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceSubsetT) | package-level function | +| [`require.SliceSubsetTf[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceSubsetTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.SliceSubsetT(t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceSubsetT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceSubsetT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L393) +{{% /tab %}} +{{< /tabs >}} + +### StringContainsT[ADoc, EDoc Text] {{% icon icon="star" color=orange %}}{#stringcontainstadoc-edoc-text} + +StringContainsT asserts that a string contains the specified substring. + +Strings may be go strings or []byte. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.StringContainsT(t, "Hello World", "World") +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: "AB", "A" + failure: "AB", "C" +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.StringContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#StringContainsT) | package-level function | +| [`assert.StringContainsTf[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#StringContainsTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.StringContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#StringContainsT) | package-level function | +| [`require.StringContainsTf[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#StringContainsTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.StringContainsT(t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#StringContainsT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#StringContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L94) +{{% /tab %}} +{{< /tabs >}} + +### StringNotContainsT[ADoc, EDoc Text] {{% icon icon="star" color=orange %}}{#stringnotcontainstadoc-edoc-text} + +StringNotContainsT asserts that a string does not contain the specified substring. + +Strings may be go strings or []byte. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.StringNotContainsT(t, "Hello World", "hi") +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: "AB", "C" + failure: "AB", "A" +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.StringNotContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#StringNotContainsT) | package-level function | +| [`assert.StringNotContainsTf[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#StringNotContainsTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.StringNotContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#StringNotContainsT) | package-level function | +| [`require.StringNotContainsTf[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#StringNotContainsTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.StringNotContainsT(t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#StringNotContainsT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#StringNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L220) {{% /tab %}} {{< /tabs >}} @@ -505,7 +969,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#L127) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Subset](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L321) {{% /tab %}} {{< /tabs >}} @@ -525,5 +989,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/common.md b/docs/doc-site/api/common.md index d700fd0e8..3d6054584 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-18 +modified: 2026-01-20 weight: 18 domains: - "common" @@ -160,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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/comparison.md b/docs/doc-site/api/comparison.md index c6da96545..190d45889 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-18 +modified: 2026-01-20 weight: 3 domains: - "comparison" @@ -689,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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/condition.md b/docs/doc-site/api/condition.md index 4faddbaac..3a2576b6f 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-18 +modified: 2026-01-20 weight: 4 domains: - "condition" @@ -294,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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/equality.md b/docs/doc-site/api/equality.md index 76b5715aa..6983d8f66 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-18 +modified: 2026-01-20 weight: 5 domains: - "equality" @@ -12,6 +12,8 @@ keywords: - "Equalf" - "EqualExportedValues" - "EqualExportedValuesf" + - "EqualT" + - "EqualTf" - "EqualValues" - "EqualValuesf" - "Exactly" @@ -22,14 +24,20 @@ keywords: - "NotEmptyf" - "NotEqual" - "NotEqualf" + - "NotEqualT" + - "NotEqualTf" - "NotEqualValues" - "NotEqualValuesf" - "NotNil" - "NotNilf" - "NotSame" - "NotSamef" + - "NotSameT" + - "NotSameTf" - "Same" - "Samef" + - "SameT" + - "SameTf" --- Asserting Two Things Are Equal @@ -41,21 +49,26 @@ Asserting Two Things Are Equal _All links point to _ -This domain exposes 12 functionalities. +This domain exposes 16 functionalities. +Generic assertions are marked with a {{% icon icon="star" color=orange %}} ```tree - [Empty](#empty) | angles-right - [Equal](#equal) | angles-right - [EqualExportedValues](#equalexportedvalues) | angles-right +- [EqualT[V comparable]](#equaltv-comparable) | star | orange - [EqualValues](#equalvalues) | angles-right - [Exactly](#exactly) | angles-right - [Nil](#nil) | angles-right - [NotEmpty](#notempty) | angles-right - [NotEqual](#notequal) | angles-right +- [NotEqualT[V comparable]](#notequaltv-comparable) | star | orange - [NotEqualValues](#notequalvalues) | angles-right - [NotNil](#notnil) | angles-right - [NotSame](#notsame) | angles-right +- [NotSameT[P any]](#notsametp-any) | star | orange - [Same](#same) | angles-right +- [SameT[P any]](#sametp-any) | star | orange ``` ### Empty{#empty} @@ -111,7 +124,7 @@ 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#L289) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Empty](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L406) {{% /tab %}} {{< /tabs >}} @@ -217,7 +230,57 @@ 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#L174) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#EqualExportedValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L291) +{{% /tab %}} +{{< /tabs >}} + +### EqualT[V comparable] {{% icon icon="star" color=orange %}}{#equaltv-comparable} + +EqualT asserts that two objects of the same comparable type are equal. + +Pointer variable equality is determined based on the equality of the memory addresses (unlike [Equal], but like [Same]). + +Functions, slices and maps are not comparable. See also [ComparisonOperators](https://go.dev/ref/spec#Comparison_operators.). + +If you need to compare values of non-comparable types, or compare pointers by the value they point to, +use [Equal] instead. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.EqualT(t, 123, 123) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: 123, 123 + failure: 123, 456 +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.EqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualT) | package-level function | +| [`assert.EqualTf[V comparable](t T, expected V, actual V, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.EqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EqualT) | package-level function | +| [`require.EqualTf[V comparable](t T, expected V, actual V, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EqualTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.EqualT(t T, expected V, actual V, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#EqualT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#EqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L67) {{% /tab %}} {{< /tabs >}} @@ -265,7 +328,7 @@ 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#L140) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#EqualValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L257) {{% /tab %}} {{< /tabs >}} @@ -312,7 +375,7 @@ 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#L211) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Exactly](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L328) {{% /tab %}} {{< /tabs >}} @@ -359,7 +422,7 @@ 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#L258) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Nil](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L375) {{% /tab %}} {{< /tabs >}} @@ -408,7 +471,7 @@ 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#L314) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotEmpty](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L431) {{% /tab %}} {{< /tabs >}} @@ -457,7 +520,52 @@ 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#L340) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotEqual](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L89) +{{% /tab %}} +{{< /tabs >}} + +### NotEqualT[V comparable] {{% icon icon="star" color=orange %}}{#notequaltv-comparable} + +NotEqualT asserts that the specified values of the same comparable type are NOT equal. + +See [EqualT]. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.NotEqualT(t, obj1, obj2) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: 123, 456 + failure: 123, 123 +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.NotEqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotEqualT) | package-level function | +| [`assert.NotEqualTf[V comparable](t T, expected V, actual V, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotEqualTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.NotEqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotEqualT) | package-level function | +| [`require.NotEqualTf[V comparable](t T, expected V, actual V, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotEqualTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.NotEqualT(t T, expected V, actual V, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotEqualT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotEqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L118) {{% /tab %}} {{< /tabs >}} @@ -504,7 +612,7 @@ 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#L367) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotEqualValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L454) {{% /tab %}} {{< /tabs >}} @@ -551,7 +659,7 @@ 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#L237) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotNil](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L354) {{% /tab %}} {{< /tabs >}} @@ -601,7 +709,52 @@ 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#L109) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSame](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L199) +{{% /tab %}} +{{< /tabs >}} + +### NotSameT[P any] {{% icon icon="star" color=orange %}}{#notsametp-any} + +NotSameT asserts that two pointers do not reference the same object. + +See [SameT] + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.NotSameT(t, ptr1, ptr2) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: &staticVar, ptr("static string") + failure: &staticVar, staticVarPtr +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.NotSameT[P any](t T, expected *P, actual *P, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotSameT) | package-level function | +| [`assert.NotSameTf[P any](t T, expected *P, actual *P, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotSameTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.NotSameT[P any](t T, expected *P, actual *P, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotSameT) | package-level function | +| [`require.NotSameTf[P any](t T, expected *P, actual *P, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotSameTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.NotSameT(t T, expected *P, actual *P, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotSameT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSameT](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L231) {{% /tab %}} {{< /tabs >}} @@ -651,7 +804,50 @@ 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#L75) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Same](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L140) +{{% /tab %}} +{{< /tabs >}} + +### SameT[P any] {{% icon icon="star" color=orange %}}{#sametp-any} + +SameT asserts that two pointers of the same type reference the same object. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.SameT(t, ptr1, ptr2) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: &staticVar, staticVarPtr + failure: &staticVar, ptr("static string") +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.SameT[P any](t T, expected *P, actual *P, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SameT) | package-level function | +| [`assert.SameTf[P any](t T, expected *P, actual *P, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SameTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.SameT[P any](t T, expected *P, actual *P, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SameT) | package-level function | +| [`require.SameTf[P any](t T, expected *P, actual *P, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SameTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.SameT(t T, expected *P, actual *P, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SameT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SameT](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L171) {{% /tab %}} {{< /tabs >}} @@ -671,5 +867,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/error.md b/docs/doc-site/api/error.md index c4ab3eaf4..9602b9ded 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-18 +modified: 2026-01-20 weight: 6 domains: - "error" @@ -453,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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/file.md b/docs/doc-site/api/file.md index 100930ad8..45ae3387a 100644 --- a/docs/doc-site/api/file.md +++ b/docs/doc-site/api/file.md @@ -1,23 +1,23 @@ --- title: "File" description: "Asserting OS Files" -modified: 2026-01-18 +modified: 2026-01-20 weight: 7 domains: - "file" keywords: - "DirExists" - "DirExistsf" + - "DirNotExists" + - "DirNotExistsf" - "FileEmpty" - "FileEmptyf" - "FileExists" - "FileExistsf" - "FileNotEmpty" - "FileNotEmptyf" - - "NoDirExists" - - "NoDirExistsf" - - "NoFileExists" - - "NoFileExistsf" + - "FileNotExists" + - "FileNotExistsf" --- Asserting OS Files @@ -33,11 +33,11 @@ This domain exposes 6 functionalities. ```tree - [DirExists](#direxists) | angles-right +- [DirNotExists](#dirnotexists) | angles-right - [FileEmpty](#fileempty) | angles-right - [FileExists](#fileexists) | angles-right - [FileNotEmpty](#filenotempty) | angles-right -- [NoDirExists](#nodirexists) | angles-right -- [NoFileExists](#nofileexists) | angles-right +- [FileNotExists](#filenotexists) | angles-right ``` ### DirExists{#direxists} @@ -88,6 +88,54 @@ if the path is a file rather a directory or there is an error checking whether i {{% /tab %}} {{< /tabs >}} +### DirNotExists{#dirnotexists} + +DirNotExists checks whether a directory does not exist in the given path. +It fails if the path points to an existing _directory_ only. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.DirNotExists(t, "path/to/directory") +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: filepath.Join(testDataPath(),"non_existing_dir") + failure: filepath.Join(testDataPath(),"existing_dir") +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.DirNotExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#DirNotExists) | package-level function | +| [`assert.DirNotExistsf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#DirNotExistsf) | formatted variant | +| [`assert.(*Assertions).DirNotExists(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.DirNotExists) | method variant | +| [`assert.(*Assertions).DirNotExistsf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.DirNotExistsf) | method formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.DirNotExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#DirNotExists) | package-level function | +| [`require.DirNotExistsf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#DirNotExistsf) | formatted variant | +| [`require.(*Assertions).DirNotExists(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.DirNotExists) | method variant | +| [`require.(*Assertions).DirNotExistsf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.DirNotExistsf) | method formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.DirNotExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#DirNotExists) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#DirNotExists](https://github.com/go-openapi/testify/blob/master/internal/assertions/file.go#L107) +{{% /tab %}} +{{< /tabs >}} + ### FileEmpty{#fileempty} FileEmpty checks whether a file exists in the given path and is empty. @@ -232,64 +280,16 @@ It fails if the file is empty, if the path points to a directory or there is an {{% /tab %}} {{< /tabs >}} -### 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. - -{{% expand title="Examples" %}} -{{< tabs >}} -{{% tab title="Usage" %}} -```go - assertions.NoDirExists(t, "path/to/directory") -``` -{{< /tab >}} -{{% tab title="Examples" %}} -```go - success: filepath.Join(testDataPath(),"non_existing_dir") - failure: filepath.Join(testDataPath(),"existing_dir") -``` -{{< /tab >}} -{{< /tabs >}} -{{% /expand %}} - -{{< tabs >}} -{{% tab title="assert" style="secondary" %}} -| Signature | Usage | -|--|--| -| [`assert.NoDirExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NoDirExists) | package-level function | -| [`assert.NoDirExistsf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NoDirExistsf) | formatted variant | -| [`assert.(*Assertions).NoDirExists(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NoDirExists) | method variant | -| [`assert.(*Assertions).NoDirExistsf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NoDirExistsf) | method formatted variant | -{{% /tab %}} -{{% tab title="require" style="secondary" %}} -| Signature | Usage | -|--|--| -| [`require.NoDirExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NoDirExists) | package-level function | -| [`require.NoDirExistsf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NoDirExistsf) | formatted variant | -| [`require.(*Assertions).NoDirExists(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NoDirExists) | method variant | -| [`require.(*Assertions).NoDirExistsf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NoDirExistsf) | method formatted variant | -{{% /tab %}} - -{{% tab title="internal" style="accent" icon="wrench" %}} -| Signature | Usage | -|--|--| -| [`assertions.NoDirExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NoDirExists) | internal implementation | - -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NoDirExists](https://github.com/go-openapi/testify/blob/master/internal/assertions/file.go#L107) -{{% /tab %}} -{{< /tabs >}} - -### NoFileExists{#nofileexists} +### FileNotExists{#filenotexists} -NoFileExists checks whether a file does not exist in a given path. It fails +FileNotExists checks whether a file does not exist in a given path. It fails if the path points to an existing _file_ only. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go - assertions.NoFileExists(t, "path/to/file") + assertions.FileNotExists(t, "path/to/file") ``` {{< /tab >}} {{% tab title="Examples" %}} @@ -305,26 +305,26 @@ if the path points to an existing _file_ only. {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| -| [`assert.NoFileExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NoFileExists) | package-level function | -| [`assert.NoFileExistsf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NoFileExistsf) | formatted variant | -| [`assert.(*Assertions).NoFileExists(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NoFileExists) | method variant | -| [`assert.(*Assertions).NoFileExistsf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NoFileExistsf) | method formatted variant | +| [`assert.FileNotExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#FileNotExists) | package-level function | +| [`assert.FileNotExistsf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#FileNotExistsf) | formatted variant | +| [`assert.(*Assertions).FileNotExists(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.FileNotExists) | method variant | +| [`assert.(*Assertions).FileNotExistsf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.FileNotExistsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| -| [`require.NoFileExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NoFileExists) | package-level function | -| [`require.NoFileExistsf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NoFileExistsf) | formatted variant | -| [`require.(*Assertions).NoFileExists(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NoFileExists) | method variant | -| [`require.(*Assertions).NoFileExistsf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NoFileExistsf) | method formatted variant | +| [`require.FileNotExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#FileNotExists) | package-level function | +| [`require.FileNotExistsf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#FileNotExistsf) | formatted variant | +| [`require.(*Assertions).FileNotExists(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.FileNotExists) | method variant | +| [`require.(*Assertions).FileNotExistsf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.FileNotExistsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| -| [`assertions.NoFileExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NoFileExists) | internal implementation | +| [`assertions.FileNotExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#FileNotExists) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NoFileExists](https://github.com/go-openapi/testify/blob/master/internal/assertions/file.go#L52) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#FileNotExists](https://github.com/go-openapi/testify/blob/master/internal/assertions/file.go#L52) {{% /tab %}} {{< /tabs >}} @@ -344,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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/http.md b/docs/doc-site/api/http.md index cb54d08af..c67a91298 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-18 +modified: 2026-01-20 weight: 8 domains: - "http" @@ -382,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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/json.md b/docs/doc-site/api/json.md index 420a38712..a1febdd42 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-18 +modified: 2026-01-20 weight: 9 domains: - "json" @@ -197,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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/number.md b/docs/doc-site/api/number.md index 608708ffb..1a080e0a0 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-18 +modified: 2026-01-20 weight: 10 domains: - "number" @@ -443,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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/ordering.md b/docs/doc-site/api/ordering.md index a14f974a1..2acdc61d4 100644 --- a/docs/doc-site/api/ordering.md +++ b/docs/doc-site/api/ordering.md @@ -1,19 +1,31 @@ --- title: "Ordering" description: "Asserting How Collections Are Ordered" -modified: 2026-01-18 +modified: 2026-01-20 weight: 11 domains: - "ordering" keywords: - "IsDecreasing" - "IsDecreasingf" + - "IsDecreasingT" + - "IsDecreasingTf" - "IsIncreasing" - "IsIncreasingf" + - "IsIncreasingT" + - "IsIncreasingTf" - "IsNonDecreasing" - "IsNonDecreasingf" + - "IsNonDecreasingT" + - "IsNonDecreasingTf" - "IsNonIncreasing" - "IsNonIncreasingf" + - "IsNonIncreasingT" + - "IsNonIncreasingTf" + - "NotSortedT" + - "NotSortedTf" + - "SortedT" + - "SortedTf" --- Asserting How Collections Are Ordered @@ -25,18 +37,25 @@ Asserting How Collections Are Ordered _All links point to _ -This domain exposes 4 functionalities. +This domain exposes 10 functionalities. +Generic assertions are marked with a {{% icon icon="star" color=orange %}} ```tree - [IsDecreasing](#isdecreasing) | angles-right +- [IsDecreasingT[OrderedSlice ~[]E, E Ordered]](#isdecreasingtorderedslice-e-e-ordered) | star | orange - [IsIncreasing](#isincreasing) | angles-right +- [IsIncreasingT[OrderedSlice ~[]E, E Ordered]](#isincreasingtorderedslice-e-e-ordered) | star | orange - [IsNonDecreasing](#isnondecreasing) | angles-right +- [IsNonDecreasingT[OrderedSlice ~[]E, E Ordered]](#isnondecreasingtorderedslice-e-e-ordered) | star | orange - [IsNonIncreasing](#isnonincreasing) | angles-right +- [IsNonIncreasingT[OrderedSlice ~[]E, E Ordered]](#isnonincreasingtorderedslice-e-e-ordered) | star | orange +- [NotSortedT[OrderedSlice ~[]E, E Ordered]](#notsortedtorderedslice-e-e-ordered) | star | orange +- [SortedT[OrderedSlice ~[]E, E Ordered]](#sortedtorderedslice-e-e-ordered) | star | orange ``` ### IsDecreasing{#isdecreasing} -IsDecreasing asserts that the collection is decreasing. +IsDecreasing asserts that the collection is strictly decreasing. {{% expand title="Examples" %}} {{< tabs >}} @@ -79,13 +98,58 @@ IsDecreasing asserts that the collection is decreasing. |--|--| | [`assertions.IsDecreasing(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsDecreasing) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsDecreasing](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L63) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsDecreasing](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L170) +{{% /tab %}} +{{< /tabs >}} + +### IsDecreasingT[OrderedSlice ~[]E, E Ordered] {{% icon icon="star" color=orange %}}{#isdecreasingtorderedslice-e-e-ordered} + +IsDecreasingT asserts that a slice of [Ordered] is strictly decreasing. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.IsDecreasingT(t, []int{2, 1, 0}) + assertions.IsDecreasingT(t, []float{2, 1}) + assertions.IsDecreasingT(t, []string{"b", "a"}) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: []int{3, 2, 1} + failure: []int{1, 2, 3} +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.IsDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsDecreasingT) | package-level function | +| [`assert.IsDecreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsDecreasingTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.IsDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsDecreasingT) | package-level function | +| [`require.IsDecreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsDecreasingTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.IsDecreasingT(t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsDecreasingT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsDecreasingT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L190) {{% /tab %}} {{< /tabs >}} ### IsIncreasing{#isincreasing} -IsIncreasing asserts that the collection is increasing. +IsIncreasing asserts that the collection is strictly increasing. {{% expand title="Examples" %}} {{< tabs >}} @@ -128,13 +192,58 @@ IsIncreasing asserts that the collection is increasing. |--|--| | [`assertions.IsIncreasing(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsIncreasing) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsIncreasing](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L23) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsIncreasing](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L24) +{{% /tab %}} +{{< /tabs >}} + +### IsIncreasingT[OrderedSlice ~[]E, E Ordered] {{% icon icon="star" color=orange %}}{#isincreasingtorderedslice-e-e-ordered} + +IsIncreasingT asserts that a slice of [Ordered] is strictly increasing. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.IsIncreasingT(t, []int{1, 2, 3}) + assertions.IsIncreasingT(t, []float{1, 2}) + assertions.IsIncreasingT(t, []string{"a", "b"}) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: []int{1, 2, 3} + failure: []int{1, 1, 2} +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.IsIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsIncreasingT) | package-level function | +| [`assert.IsIncreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsIncreasingTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.IsIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsIncreasingT) | package-level function | +| [`require.IsIncreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsIncreasingTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.IsIncreasingT(t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsIncreasingT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsIncreasingT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L44) {{% /tab %}} {{< /tabs >}} ### IsNonDecreasing{#isnondecreasing} -IsNonDecreasing asserts that the collection is not decreasing. +IsNonDecreasing asserts that the collection is not strictly decreasing. {{% expand title="Examples" %}} {{< tabs >}} @@ -148,7 +257,7 @@ IsNonDecreasing asserts that the collection is not decreasing. {{% tab title="Examples" %}} ```go success: []int{1, 1, 2} - failure: []int{2, 1, 1} + failure: []int{2, 1, 0} ``` {{< /tab >}} {{< /tabs >}} @@ -177,7 +286,52 @@ IsNonDecreasing asserts that the collection is not decreasing. |--|--| | [`assertions.IsNonDecreasing(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNonDecreasing) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNonDecreasing](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L83) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNonDecreasing](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L216) +{{% /tab %}} +{{< /tabs >}} + +### IsNonDecreasingT[OrderedSlice ~[]E, E Ordered] {{% icon icon="star" color=orange %}}{#isnondecreasingtorderedslice-e-e-ordered} + +IsNonDecreasingT asserts that a slice of [Ordered] is not decreasing. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.IsNonDecreasingT(t, []int{1, 1, 2}) + assertions.IsNonDecreasingT(t, []float{1, 2}) + assertions.IsNonDecreasingT(t, []string{"a", "b"}) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: []int{1, 1, 2} + failure: []int{2, 1, 0} +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.IsNonDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNonDecreasingT) | package-level function | +| [`assert.IsNonDecreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNonDecreasingTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.IsNonDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNonDecreasingT) | package-level function | +| [`require.IsNonDecreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNonDecreasingTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.IsNonDecreasingT(t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNonDecreasingT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNonDecreasingT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L236) {{% /tab %}} {{< /tabs >}} @@ -226,7 +380,146 @@ IsNonIncreasing asserts that the collection is not increasing. |--|--| | [`assertions.IsNonIncreasing(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNonIncreasing) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNonIncreasing](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L43) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNonIncreasing](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L124) +{{% /tab %}} +{{< /tabs >}} + +### IsNonIncreasingT[OrderedSlice ~[]E, E Ordered] {{% icon icon="star" color=orange %}}{#isnonincreasingtorderedslice-e-e-ordered} + +IsNonIncreasingT asserts that a slice of [Ordered] is NOT strictly increasing. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.IsNonIncreasing(t, []int{2, 1, 1}) + assertions.IsNonIncreasing(t, []float{2, 1}) + assertions.IsNonIncreasing(t, []string{"b", "a"}) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: []int{2, 1, 1} + failure: []int{1, 2, 3} +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.IsNonIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNonIncreasingT) | package-level function | +| [`assert.IsNonIncreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNonIncreasingTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.IsNonIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNonIncreasingT) | package-level function | +| [`require.IsNonIncreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNonIncreasingTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.IsNonIncreasingT(t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNonIncreasingT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNonIncreasingT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L144) +{{% /tab %}} +{{< /tabs >}} + +### NotSortedT[OrderedSlice ~[]E, E Ordered] {{% icon icon="star" color=orange %}}{#notsortedtorderedslice-e-e-ordered} + +NotSortedT asserts that the slice of [Ordered] is NOT sorted (i.e. non-strictly increasing). + +Unlike [IsDecreasingT], it accepts slices that are neither increasing nor decreasing. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.NotSortedT(t, []int{3, 2, 3}) + assertions.NotSortedT(t, []float{2, 1}) + assertions.NotSortedT(t, []string{"b", "a"}) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: []int{3, 1, 3} + failure: []int{1, 4, 8} +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.NotSortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotSortedT) | package-level function | +| [`assert.NotSortedTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotSortedTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.NotSortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotSortedT) | package-level function | +| [`require.NotSortedTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotSortedTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.NotSortedT(t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotSortedT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSortedT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L99) +{{% /tab %}} +{{< /tabs >}} + +### SortedT[OrderedSlice ~[]E, E Ordered] {{% icon icon="star" color=orange %}}{#sortedtorderedslice-e-e-ordered} + +SortedT asserts that the slice of [Ordered] is sorted (i.e. non-strictly increasing). + +Unlike [IsIncreasingT], it accepts elements to be equal. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.SortedT(t, []int{1, 2, 3}) + assertions.SortedT(t, []float{1, 2}) + assertions.SortedT(t, []string{"a", "b"}) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: []int{1, 1, 3} + failure: []int{1, 4, 2} +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.SortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SortedT) | package-level function | +| [`assert.SortedTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SortedTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.SortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SortedT) | package-level function | +| [`require.SortedTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SortedTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.SortedT(t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SortedT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SortedT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L72) {{% /tab %}} {{< /tabs >}} @@ -246,5 +539,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/panic.md b/docs/doc-site/api/panic.md index 88dd74bfa..de68b1483 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-18 +modified: 2026-01-20 weight: 12 domains: - "panic" @@ -241,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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/string.md b/docs/doc-site/api/string.md index 0a98d70f9..7fc68a217 100644 --- a/docs/doc-site/api/string.md +++ b/docs/doc-site/api/string.md @@ -1,7 +1,7 @@ --- title: "String" description: "Asserting Strings" -modified: 2026-01-18 +modified: 2026-01-20 weight: 13 domains: - "string" @@ -241,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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/testing.md b/docs/doc-site/api/testing.md index 8edd73bcd..3a0e121c4 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-18 +modified: 2026-01-20 weight: 14 domains: - "testing" @@ -136,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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/time.md b/docs/doc-site/api/time.md index 4c0f8fca9..29a19a1b3 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-18 +modified: 2026-01-20 weight: 15 domains: - "time" @@ -138,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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/type.md b/docs/doc-site/api/type.md index 56fbddbc1..4159d7ca7 100644 --- a/docs/doc-site/api/type.md +++ b/docs/doc-site/api/type.md @@ -1,15 +1,19 @@ --- title: "Type" description: "Asserting Types Rather Than Values" -modified: 2026-01-18 +modified: 2026-01-20 weight: 16 domains: - "type" keywords: - "Implements" - "Implementsf" + - "IsNotOfTypeT" + - "IsNotOfTypeTf" - "IsNotType" - "IsNotTypef" + - "IsOfTypeT" + - "IsOfTypeTf" - "IsType" - "IsTypef" - "Kind" @@ -33,11 +37,14 @@ Asserting Types Rather Than Values _All links point to _ -This domain exposes 8 functionalities. +This domain exposes 10 functionalities. +Generic assertions are marked with a {{% icon icon="star" color=orange %}} ```tree - [Implements](#implements) | angles-right +- [IsNotOfTypeT[EType any]](#isnotoftypetetype-any) | star | orange - [IsNotType](#isnottype) | angles-right +- [IsOfTypeT[EType any]](#isoftypetetype-any) | star | orange - [IsType](#istype) | angles-right - [Kind](#kind) | angles-right - [NotImplements](#notimplements) | angles-right @@ -93,6 +100,49 @@ Implements asserts that an object is implemented by the specified interface. {{% /tab %}} {{< /tabs >}} +### IsNotOfTypeT[EType any] {{% icon icon="star" color=orange %}}{#isnotoftypetetype-any} + +IsNotOfTypeT asserts that an object is of a given type. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.IsOfType[MyType](t,myVar) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: 123.123 + failure: myType(123.123) +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.IsNotOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNotOfTypeT) | package-level function | +| [`assert.IsNotOfTypeTf[EType any](t T, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNotOfTypeTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.IsNotOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNotOfTypeT) | package-level function | +| [`require.IsNotOfTypeTf[EType any](t T, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNotOfTypeTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.IsNotOfTypeT(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNotOfTypeT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNotOfTypeT](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L141) +{{% /tab %}} +{{< /tabs >}} + ### IsNotType{#isnottype} IsNotType asserts that the specified objects are not of the same type. @@ -136,7 +186,50 @@ IsNotType asserts that the specified objects are not of the same type. |--|--| | [`assertions.IsNotType(t T, theType any, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNotType) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNotType](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L96) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNotType](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L120) +{{% /tab %}} +{{< /tabs >}} + +### IsOfTypeT[EType any] {{% icon icon="star" color=orange %}}{#isoftypetetype-any} + +IsOfTypeT asserts that an object is of a given type. + +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.IsOfTypeT[MyType](t,myVar) +``` +{{< /tab >}} +{{% tab title="Examples" %}} +```go + success: myType(123.123) + failure: 123.123 +``` +{{< /tab >}} +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.IsOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsOfTypeT) | package-level function | +| [`assert.IsOfTypeTf[EType any](t T, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsOfTypeTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.IsOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsOfTypeT) | package-level function | +| [`require.IsOfTypeTf[EType any](t T, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsOfTypeTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.IsOfTypeT(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsOfTypeT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsOfTypeT](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L96) {{% /tab %}} {{< /tabs >}} @@ -233,7 +326,7 @@ are comparable to [reflect.Invalid](https://pkg.go.dev/reflect#Invalid). See als |--|--| | [`assertions.Kind(t T, expectedKind reflect.Kind, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Kind) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Kind](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L162) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Kind](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L210) {{% /tab %}} {{< /tabs >}} @@ -330,7 +423,7 @@ are comparable to [reflect.Invalid](https://pkg.go.dev/reflect#Invalid). See als |--|--| | [`assertions.NotKind(t T, expectedKind reflect.Kind, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotKind) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotKind](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L195) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotKind](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L243) {{% /tab %}} {{< /tabs >}} @@ -377,7 +470,7 @@ NotZero asserts that i is not the zero value for its type. |--|--| | [`assertions.NotZero(t T, i any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotZero) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotZero](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L138) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotZero](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L186) {{% /tab %}} {{< /tabs >}} @@ -424,7 +517,7 @@ Zero asserts that i is the zero value for its type. |--|--| | [`assertions.Zero(t T, i any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Zero) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Zero](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L117) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Zero](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L165) {{% /tab %}} {{< /tabs >}} @@ -444,5 +537,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/api/yaml.md b/docs/doc-site/api/yaml.md index 1fe014f07..4b18c9058 100644 --- a/docs/doc-site/api/yaml.md +++ b/docs/doc-site/api/yaml.md @@ -1,7 +1,7 @@ --- title: "Yaml" description: "Asserting Yaml Documents" -modified: 2026-01-18 +modified: 2026-01-20 weight: 17 domains: - "yaml" @@ -202,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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] --> diff --git a/docs/doc-site/project/maintainers/ROADMAP.md b/docs/doc-site/project/maintainers/ROADMAP.md index aa9db261d..da24f14ca 100644 --- a/docs/doc-site/project/maintainers/ROADMAP.md +++ b/docs/doc-site/project/maintainers/ROADMAP.md @@ -22,10 +22,10 @@ timeline : removed deprecated : optional dependencies (colorized) : upstream PRs: Kind/NotKind - : generics - v2.2 (Fev 2026) : generics (cont.) + v2.2 (Fev 2026) : : generics + : SortedT, NotSortedT : JSON assertions. JSONMarshalsAs... - : IsSorted, IsSortedFunc + : complete test refactoring : more benchmarks. Perf improvements v2.3 (Mar 2026) : other extensions (TBD) : more documentation and examples diff --git a/hack/doc-site/hugo/hugo.yaml b/hack/doc-site/hugo/hugo.yaml index c90e9b24b..47922f67b 100644 --- a/hack/doc-site/hugo/hugo.yaml +++ b/hack/doc-site/hugo/hugo.yaml @@ -14,9 +14,9 @@ contentDir: content # Output formats outputs: home: - - HTML - - RSS - - SEARCH + - html + - rss + - print # Markup configuration markup: diff --git a/internal/assertions/benchmarks_test.go b/internal/assertions/benchmarks_test.go index b2946e593..88e02b15c 100644 --- a/internal/assertions/benchmarks_test.go +++ b/internal/assertions/benchmarks_test.go @@ -3,7 +3,50 @@ package assertions -import "testing" +import ( + "slices" + "testing" +) + +// Helper functions to reduce duplication in benchmarks + +// benchmarkComparison runs a benchmark comparing reflection vs generic implementations. +func benchmarkComparison( + b *testing.B, + name string, + reflectFn func(*mockT), + genericFn func(*mockT), +) { + b.Helper() + mockT := &mockT{} + + b.Run("reflect/"+name, func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + reflectFn(mockT) + } + }) + + b.Run("generic/"+name, func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + genericFn(mockT) + } + }) +} + +// benchmarkGenericOnly runs a benchmark for generic-only functions (no reflection equivalent). +func benchmarkGenericOnly(b *testing.B, genericFn func(*mockT)) { + b.Helper() + mockT := &mockT{} + + b.Run("generic", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + genericFn(mockT) + } + }) +} func Benchmark_isEmpty(b *testing.B) { b.ReportAllocs() @@ -49,91 +92,39 @@ func BenchmarkBytesEqual(b *testing.B) { // 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") - } - }) + benchmarkComparison(b, "int", + func(t *mockT) { Greater(t, 100, 50) }, + func(t *mockT) { GreaterT(t, 100, 50) }, + ) + + benchmarkComparison(b, "float64", + func(t *mockT) { Greater(t, 100.5, 50.5) }, + func(t *mockT) { GreaterT(t, 100.5, 50.5) }, + ) + + benchmarkComparison(b, "string", + func(t *mockT) { Greater(t, "beta", "alpha") }, + func(t *mockT) { GreaterT(t, "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) - } - }) + benchmarkComparison(b, "int", + func(t *mockT) { Less(t, 50, 100) }, + func(t *mockT) { LessT(t, 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) - } - }) + benchmarkComparison(b, "small_10", + func(t *mockT) { ElementsMatch(t, smallA, smallB) }, + func(t *mockT) { ElementsMatchT(t, smallA, smallB) }, + ) // Medium slices (100 elements) mediumA := make([]int, 100) @@ -142,20 +133,10 @@ func BenchmarkElementsMatch(b *testing.B) { 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) - } - }) + benchmarkComparison(b, "medium_100", + func(t *mockT) { ElementsMatch(t, mediumA, mediumB) }, + func(t *mockT) { ElementsMatchT(t, mediumA, mediumB) }, + ) // Large slices (1000 elements) largeA := make([]int, 1000) @@ -164,111 +145,308 @@ func BenchmarkElementsMatch(b *testing.B) { 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) - } - }) + benchmarkComparison(b, "large_1000", + func(t *mockT) { ElementsMatch(t, largeA, largeB) }, + func(t *mockT) { ElementsMatchT(t, 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) - } - }) + benchmarkComparison(b, "strings_5", + func(t *mockT) { ElementsMatch(t, stringsA, stringsB) }, + func(t *mockT) { ElementsMatchT(t, 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) - } - }) + benchmarkComparison(b, "", + func(t *mockT) { NotElementsMatch(t, sliceA, sliceB) }, + func(t *mockT) { NotElementsMatchT(t, sliceA, sliceB) }, + ) } // BenchmarkPositive compares Positive (reflect) vs PositiveT (generic). func BenchmarkPositive(b *testing.B) { - mockT := &mockT{} + benchmarkComparison(b, "int", + func(t *mockT) { Positive(t, 42) }, + func(t *mockT) { PositiveT(t, 42) }, + ) + benchmarkComparison(b, "float64", + func(t *mockT) { Positive(t, 42.5) }, + func(t *mockT) { PositiveT(t, 42.5) }, + ) +} - b.Run("reflect/int", func(b *testing.B) { - b.ReportAllocs() - for b.Loop() { - Positive(mockT, 42) - } - }) +// BenchmarkNegative compares Negative (reflect) vs NegativeT (generic). +func BenchmarkNegative(b *testing.B) { + benchmarkComparison(b, "int", + func(t *mockT) { Negative(t, -42) }, + func(t *mockT) { NegativeT(t, -42) }, + ) +} - b.Run("generic/int", func(b *testing.B) { - b.ReportAllocs() - for b.Loop() { - PositiveT(mockT, 42) - } - }) +// BenchmarkEqual compares Equal (reflect) vs EqualT (generic). +func BenchmarkEqual(b *testing.B) { + benchmarkComparison(b, "int", + func(t *mockT) { Equal(t, 42, 42) }, + func(t *mockT) { EqualT(t, 42, 42) }, + ) + benchmarkComparison(b, "string", + func(t *mockT) { Equal(t, "hello", "hello") }, + func(t *mockT) { EqualT(t, "hello", "hello") }, + ) + benchmarkComparison(b, "float64", + func(t *mockT) { Equal(t, 3.14, 3.14) }, + func(t *mockT) { EqualT(t, 3.14, 3.14) }, + ) +} - b.Run("reflect/float64", func(b *testing.B) { - b.ReportAllocs() - for b.Loop() { - Positive(mockT, 42.5) - } - }) +// BenchmarkNotEqual compares NotEqual (reflect) vs NotEqualT (generic). +func BenchmarkNotEqual(b *testing.B) { + benchmarkComparison(b, "int", + func(t *mockT) { NotEqual(t, 42, 43) }, + func(t *mockT) { NotEqualT(t, 42, 43) }, + ) +} - b.Run("generic/float64", func(b *testing.B) { - b.ReportAllocs() - for b.Loop() { - PositiveT(mockT, 42.5) - } - }) +// BenchmarkSame compares Same (reflect) vs SameT (generic). +func BenchmarkSame(b *testing.B) { + v := 42 + p := &v + benchmarkComparison(b, "", + func(t *mockT) { Same(t, p, p) }, + func(t *mockT) { SameT(t, p, p) }, + ) } -// BenchmarkNegative compares Negative (reflect) vs NegativeT (generic). -func BenchmarkNegative(b *testing.B) { - mockT := &mockT{} +// BenchmarkNotSame compares NotSame (reflect) vs NotSameT (generic). +func BenchmarkNotSame(b *testing.B) { + v1, v2 := 42, 42 + p1, p2 := &v1, &v2 + benchmarkComparison(b, "", + func(t *mockT) { NotSame(t, p1, p2) }, + func(t *mockT) { NotSameT(t, p1, p2) }, + ) +} - b.Run("reflect/int", func(b *testing.B) { - b.ReportAllocs() - for b.Loop() { - Negative(mockT, -42) - } - }) +// BenchmarkGreaterOrEqual compares GreaterOrEqual (reflect) vs GreaterOrEqualT (generic). +func BenchmarkGreaterOrEqual(b *testing.B) { + benchmarkComparison(b, "int", + func(t *mockT) { GreaterOrEqual(t, 100, 50) }, + func(t *mockT) { GreaterOrEqualT(t, 100, 50) }, + ) +} - b.Run("generic/int", func(b *testing.B) { - b.ReportAllocs() - for b.Loop() { - NegativeT(mockT, -42) - } - }) +// BenchmarkLessOrEqual compares LessOrEqual (reflect) vs LessOrEqualT (generic). +func BenchmarkLessOrEqual(b *testing.B) { + benchmarkComparison(b, "int", + func(t *mockT) { LessOrEqual(t, 50, 100) }, + func(t *mockT) { LessOrEqualT(t, 50, 100) }, + ) +} + +// BenchmarkIsIncreasing compares IsIncreasing (reflect) vs IsIncreasingT (generic). +func BenchmarkIsIncreasing(b *testing.B) { + slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + benchmarkComparison(b, "", + func(t *mockT) { IsIncreasing(t, slice) }, + func(t *mockT) { IsIncreasingT(t, slice) }, + ) +} + +// BenchmarkIsNonIncreasing compares IsNonIncreasing (reflect) vs IsNonIncreasingT (generic). +func BenchmarkIsNonIncreasing(b *testing.B) { + slice := []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1} + benchmarkComparison(b, "", + func(t *mockT) { IsNonIncreasing(t, slice) }, + func(t *mockT) { IsNonIncreasingT(t, slice) }, + ) +} + +// BenchmarkIsDecreasing compares IsDecreasing (reflect) vs IsDecreasingT (generic). +func BenchmarkIsDecreasing(b *testing.B) { + slice := []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1} + benchmarkComparison(b, "", + func(t *mockT) { IsDecreasing(t, slice) }, + func(t *mockT) { IsDecreasingT(t, slice) }, + ) +} + +// BenchmarkIsNonDecreasing compares IsNonDecreasing (reflect) vs IsNonDecreasingT (generic). +func BenchmarkIsNonDecreasing(b *testing.B) { + slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + benchmarkComparison(b, "", + func(t *mockT) { IsNonDecreasing(t, slice) }, + func(t *mockT) { IsNonDecreasingT(t, slice) }, + ) +} + +// BenchmarkContains compares Contains (reflect) vs various ContainsT generics. +func BenchmarkContains(b *testing.B) { + benchmarkComparison(b, "string", + func(t *mockT) { Contains(t, "hello world", "world") }, + func(t *mockT) { StringContainsT(t, "hello world", "world") }, + ) + + slice := []int{1, 2, 3, 4, 5} + benchmarkComparison(b, "slice", + func(t *mockT) { Contains(t, slice, 3) }, + func(t *mockT) { SliceContainsT(t, slice, 3) }, + ) + + m := map[string]int{"a": 1, "b": 2, "c": 3} + benchmarkComparison(b, "map", + func(t *mockT) { Contains(t, m, "b") }, + func(t *mockT) { MapContainsT(t, m, "b") }, + ) +} + +// BenchmarkNotContains compares NotContains (reflect) vs various NotContainsT generics. +func BenchmarkNotContains(b *testing.B) { + benchmarkComparison(b, "string", + func(t *mockT) { NotContains(t, "hello world", "xyz") }, + func(t *mockT) { StringNotContainsT(t, "hello world", "xyz") }, + ) + + slice := []int{1, 2, 3, 4, 5} + benchmarkComparison(b, "slice", + func(t *mockT) { NotContains(t, slice, 99) }, + func(t *mockT) { SliceNotContainsT(t, slice, 99) }, + ) +} + +// BenchmarkSubset compares Subset (reflect) vs SubsetT (generic). +func BenchmarkSubset(b *testing.B) { + list := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + subset := []int{2, 4, 6} + benchmarkComparison(b, "", + func(t *mockT) { Subset(t, list, subset) }, + func(t *mockT) { SliceSubsetT(t, list, subset) }, + ) +} + +// BenchmarkNotSubset compares NotSubset (reflect) vs NotSubsetT (generic). +func BenchmarkNotSubset(b *testing.B) { + list := []int{1, 2, 3, 4, 5} + subset := []int{1, 2, 99} + benchmarkComparison(b, "", + func(t *mockT) { NotSubset(t, list, subset) }, + func(t *mockT) { SliceNotSubsetT(t, list, subset) }, + ) +} + +// BenchmarkInDelta compares InDelta (reflect) vs InDeltaT (generic). +func BenchmarkInDelta(b *testing.B) { + benchmarkComparison(b, "float64", + func(t *mockT) { InDelta(t, 3.14, 3.15, 0.02) }, + func(t *mockT) { InDeltaT(t, 3.14, 3.15, 0.02) }, + ) + benchmarkComparison(b, "int", + func(t *mockT) { InDelta(t, 100, 102, 5) }, + func(t *mockT) { InDeltaT(t, 100, 102, 5) }, + ) +} + +// BenchmarkInEpsilon compares InEpsilon (reflect) vs InEpsilonT (generic). +func BenchmarkInEpsilon(b *testing.B) { + benchmarkComparison(b, "float64", + func(t *mockT) { InEpsilon(t, 100.0, 101.0, 0.02) }, + func(t *mockT) { InEpsilonT(t, 100.0, 101.0, 0.02) }, + ) +} + +// BenchmarkTrue compares True (reflect) vs TrueT (generic). +func BenchmarkTrue(b *testing.B) { + benchmarkComparison(b, "", + func(t *mockT) { True(t, true) }, + func(t *mockT) { TrueT(t, true) }, + ) +} + +// BenchmarkFalse compares False (reflect) vs FalseT (generic). +func BenchmarkFalse(b *testing.B) { + benchmarkComparison(b, "", + func(t *mockT) { False(t, false) }, + func(t *mockT) { FalseT(t, false) }, + ) +} + +// BenchmarkRegexp compares Regexp (reflect) vs RegexpT (generic). +func BenchmarkRegexp(b *testing.B) { + benchmarkComparison(b, "string", + func(t *mockT) { Regexp(t, `^\d{3}-\d{4}$`, "123-4567") }, + func(t *mockT) { RegexpT(t, `^\d{3}-\d{4}$`, "123-4567") }, + ) +} + +// BenchmarkNotRegexp compares NotRegexp (reflect) vs NotRegexpT (generic). +func BenchmarkNotRegexp(b *testing.B) { + benchmarkComparison(b, "string", + func(t *mockT) { NotRegexp(t, `^\d{3}-\d{4}$`, "hello") }, + func(t *mockT) { NotRegexpT(t, `^\d{3}-\d{4}$`, "hello") }, + ) +} + +// BenchmarkSorted benchmarks SortedT (generic-only, no reflection version). +func BenchmarkSorted(b *testing.B) { + slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + benchmarkGenericOnly(b, func(t *mockT) { SortedT(t, slice) }) +} + +// BenchmarkNotSorted benchmarks NotSortedT (generic-only, no reflection version). +func BenchmarkNotSorted(b *testing.B) { + slice := []int{1, 3, 2, 4, 5} + benchmarkGenericOnly(b, func(t *mockT) { NotSortedT(t, slice) }) +} + +// BenchmarkSeqContains compares Contains (reflect) vs SeqContainsT (generic) for iterators. +func BenchmarkSeqContains(b *testing.B) { + slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + benchmarkComparison(b, "", + func(t *mockT) { Contains(t, slices.Values(slice), 5) }, + func(t *mockT) { SeqContainsT(t, slices.Values(slice), 5) }, + ) +} + +// BenchmarkSeqNotContains compares NotContains (reflect) vs SeqNotContainsT (generic) for iterators. +func BenchmarkSeqNotContains(b *testing.B) { + slice := []int{1, 2, 3, 4, 5} + benchmarkComparison(b, "", + func(t *mockT) { NotContains(t, slices.Values(slice), 99) }, + func(t *mockT) { SeqNotContainsT(t, slices.Values(slice), 99) }, + ) +} + +// BenchmarkJSONEq compares JSONEq (reflect) vs JSONEqT (generic). +func BenchmarkJSONEq(b *testing.B) { + expected := `{"name":"John","age":30}` + actual := `{"age":30,"name":"John"}` + benchmarkComparison(b, "", + func(t *mockT) { JSONEq(t, expected, actual) }, + func(t *mockT) { JSONEqT(t, expected, actual) }, + ) +} + +// BenchmarkIsOfType compares IsType (reflect) vs IsOfTypeT (generic). +func BenchmarkIsOfType(b *testing.B) { + var expected int + actual := 42 + benchmarkComparison(b, "", + func(t *mockT) { IsType(t, expected, actual) }, + func(t *mockT) { IsOfTypeT[int](t, actual) }, + ) +} + +// BenchmarkIsNotOfType compares IsNotType (reflect) vs IsNotOfTypeT (generic). +func BenchmarkIsNotOfType(b *testing.B) { + var expected int + actual := "string" + benchmarkComparison(b, "", + func(t *mockT) { IsNotType(t, expected, actual) }, + func(t *mockT) { IsNotOfTypeT[int](t, actual) }, + ) } diff --git a/internal/assertions/collection.go b/internal/assertions/collection.go index c27b8dc6d..ddceac45d 100644 --- a/internal/assertions/collection.go +++ b/internal/assertions/collection.go @@ -6,7 +6,9 @@ package assertions import ( "bytes" "fmt" + "iter" "reflect" + "slices" "strings" ) @@ -77,6 +79,102 @@ func Contains(t T, s, contains any, msgAndArgs ...any) bool { return true } +// StringContainsT asserts that a string contains the specified substring. +// +// Strings may be go strings or []byte. +// +// # Usage +// +// assertions.StringContainsT(t, "Hello World", "World") +// +// # Examples +// +// success: "AB", "A" +// failure: "AB", "C" +func StringContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool { + // Domain: collection + if h, ok := t.(H); ok { + h.Helper() + } + + if !strings.Contains(string(str), string(substring)) { + return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", str), substring), msgAndArgs...) + } + + return true +} + +// SliceContainsT asserts that the specified slice contains a comparable element. +// +// # Usage +// +// assertions.SliceContainsT(t, []{"Hello","World"}, "World") +// +// # Examples +// +// success: []string{"A","B"}, "A" +// failure: []string{"A","B"}, "C" +func SliceContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool { + // Domain: collection + if h, ok := t.(H); ok { + h.Helper() + } + + if !slices.Contains(s, element) { + return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", s), element), msgAndArgs...) + } + + return true +} + +// SeqContainsT asserts that the specified iterator contains a comparable element. +// +// # Usage +// +// assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World") +// +// # Examples +// +// success: slices.Values([]string{"A","B"}), "A" +// failure: slices.Values([]string{"A","B"}), "C" +func SeqContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool { + // Domain: collection + if h, ok := t.(H); ok { + h.Helper() + } + + s := slices.Collect(iter) + if !slices.Contains(s, element) { + return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", s), element), msgAndArgs...) + } + + return true +} + +// MapContainsT asserts that the specified map contains a key. +// +// # Usage +// +// assertions.MapContainsT(t, map[string]string{"Hello": "x","World": "y"}, "World") +// +// # Examples +// +// success: map[string]string{"A": "B"}, "A" +// failure: map[string]string{"A": "B"}, "C" +func MapContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool { + // Domain: collection + if h, ok := t.(H); ok { + h.Helper() + } + + _, ok := m[key] + if !ok { + return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", m), key), msgAndArgs...) + } + + return true +} + // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // @@ -107,6 +205,102 @@ func NotContains(t T, s, contains any, msgAndArgs ...any) bool { return true } +// StringNotContainsT asserts that a string does not contain the specified substring. +// +// Strings may be go strings or []byte. +// +// # Usage +// +// assertions.StringNotContainsT(t, "Hello World", "hi") +// +// # Examples +// +// success: "AB", "C" +// failure: "AB", "A" +func StringNotContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool { + // Domain: collection + if h, ok := t.(H); ok { + h.Helper() + } + + if strings.Contains(string(str), string(substring)) { + return Fail(t, fmt.Sprintf("%s should not contain %#v", truncatingFormat("%#v", str), substring), msgAndArgs...) + } + + return true +} + +// SliceNotContainsT asserts that the specified slice does not contain a comparable element. +// +// # Usage +// +// assertions.SliceNotContainsT(t, []{"Hello","World"}, "hi") +// +// # Examples +// +// success: []string{"A","B"}, "C" +// failure: []string{"A","B"}, "A" +func SliceNotContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool { + // Domain: collection + if h, ok := t.(H); ok { + h.Helper() + } + + if slices.Contains(s, element) { + return Fail(t, fmt.Sprintf("%s should not contain %#v", truncatingFormat("%#v", s), element), msgAndArgs...) + } + + return true +} + +// SeqNotContainsT asserts that the specified iterator does not contain a comparable element. +// +// # Usage +// +// assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World") +// +// # Examples +// +// success: slices.Values([]string{"A","B"}), "C" +// failure: slices.Values([]string{"A","B"}), "A" +func SeqNotContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool { + // Domain: collection + if h, ok := t.(H); ok { + h.Helper() + } + + s := slices.Collect(iter) + if slices.Contains(s, element) { + return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", s), element), msgAndArgs...) + } + + return true +} + +// MapNotContainsT asserts that the specified map does not contain a key. +// +// # Usage +// +// assertions.MapNotContainsT(t, map[string]string{"Hello": "x","World": "y"}, "hi") +// +// # Examples +// +// success: map[string]string{"A": "B"}, "C" +// failure: map[string]string{"A": "B"}, "A" +func MapNotContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool { + // Domain: collection + if h, ok := t.(H); ok { + h.Helper() + } + + _, ok := m[key] + if ok { + return Fail(t, fmt.Sprintf("%s should not contain %#v", truncatingFormat("%#v", m), key), msgAndArgs...) + } + + return true +} + // Subset asserts that the list (array, slice, or map) contains all elements // given in the subset (array, slice, or map). // @@ -129,6 +323,7 @@ func Subset(t T, list, subset any, msgAndArgs ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } + if subset == nil { return true // we consider nil to be equal to the nil set } @@ -185,6 +380,31 @@ func Subset(t T, list, subset any, msgAndArgs ...any) (ok bool) { return true } +// SliceSubsetT asserts that a slice of comparable elements contains all the elements given in the subset. +// +// # Usage +// +// assertions.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) +// +// # Examples +// +// success: []int{1, 2, 3}, []int{1, 2} +// failure: []int{1, 2, 3}, []int{4, 5} +func SliceSubsetT[Slice ~[]E, E comparable](t T, list, subset Slice, msgAndArgs ...any) (ok bool) { + // Domain: collection + if h, ok := t.(H); ok { + h.Helper() + } + + for _, element := range subset { + if !slices.Contains(list, element) { + return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", list), element), msgAndArgs...) + } + } + + return true +} + // NotSubset asserts that the list (array, slice, or map) does NOT contain all // elements given in the subset (array, slice, or map). // Map elements are key-value pairs unless compared with an array or slice where @@ -247,6 +467,7 @@ func NotSubset(t T, list, subset any, msgAndArgs ...any) (ok bool) { } subsetList = reflect.ValueOf(keys) } + for i := range subsetList.Len() { element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) @@ -261,6 +482,31 @@ func NotSubset(t T, list, subset any, msgAndArgs ...any) (ok bool) { return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%q", subset), truncatingFormat("%q", list)), msgAndArgs...) } +// SliceNotSubsetT asserts that a slice of comparable elements does not contain all the elements given in the subset. +// +// # Usage +// +// assertions.SliceNotSubsetT(t, []int{1, 2, 3}, []int{1, 4}) +// +// # Examples +// +// success: []int{1, 2, 3}, []int{4, 5} +// failure: []int{1, 2, 3}, []int{1, 2} +func SliceNotSubsetT[Slice ~[]E, E comparable](t T, list, subset Slice, msgAndArgs ...any) (ok bool) { + // Domain: collection + if h, ok := t.(H); ok { + h.Helper() + } + + for _, element := range subset { + if !slices.Contains(list, element) { + return true + } + } + + return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%q", subset), truncatingFormat("%q", list)), msgAndArgs...) +} + // 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, // the number of appearances of each of them in both lists should match. diff --git a/internal/assertions/collection_test.go b/internal/assertions/collection_test.go index 871607e42..d056c3c3c 100644 --- a/internal/assertions/collection_test.go +++ b/internal/assertions/collection_test.go @@ -734,3 +734,533 @@ func testElementsMatchTStructPair() (matchTest, notMatchTest func(*testing.T)) { } return matchTest, notMatchTest } + +// Generic Contains function tests + +type containsTestCase struct { + name string + container any + element any + shouldPass bool +} + +func stringContainsTCases() iter.Seq[containsTestCase] { + return slices.Values([]containsTestCase{ + // Success cases + {name: "string/contains", container: "hello world", element: "world", shouldPass: true}, + {name: "string/contains-start", container: "hello world", element: "hello", shouldPass: true}, + {name: "string/contains-middle", container: "hello world", element: "lo wo", shouldPass: true}, + {name: "[]byte/contains", container: []byte("hello"), element: []byte("ell"), shouldPass: true}, + + // Failure cases + {name: "string/not-contains", container: "hello world", element: "xyz", shouldPass: false}, + {name: "string/case-sensitive", container: "hello world", element: "WORLD", shouldPass: false}, + {name: "[]byte/not-contains", container: []byte("hello"), element: []byte("xyz"), shouldPass: false}, + }) +} + +func sliceContainsTCases() iter.Seq[containsTestCase] { + return slices.Values([]containsTestCase{ + // Success cases + {name: "int/contains", container: []int{1, 2, 3}, element: 2, shouldPass: true}, + {name: "int/contains-first", container: []int{1, 2, 3}, element: 1, shouldPass: true}, + {name: "int/contains-last", container: []int{1, 2, 3}, element: 3, shouldPass: true}, + {name: "string/contains", container: []string{"a", "b", "c"}, element: "b", shouldPass: true}, + {name: "float64/contains", container: []float64{1.1, 2.2, 3.3}, element: 2.2, shouldPass: true}, + + // Failure cases + {name: "int/not-contains", container: []int{1, 2, 3}, element: 5, shouldPass: false}, + {name: "string/not-contains", container: []string{"a", "b", "c"}, element: "d", shouldPass: false}, + {name: "empty-slice", container: []int{}, element: 1, shouldPass: false}, + }) +} + +func mapContainsTCases() iter.Seq[containsTestCase] { + return slices.Values([]containsTestCase{ + // Success cases + {name: "string-int/has-key", container: map[string]int{"a": 1, "b": 2}, element: "a", shouldPass: true}, + {name: "int-string/has-key", container: map[int]string{1: "one", 2: "two"}, element: 1, shouldPass: true}, + {name: "string-string/has-key", container: map[string]string{"x": "y"}, element: "x", shouldPass: true}, + + // Failure cases + {name: "string-int/no-key", container: map[string]int{"a": 1, "b": 2}, element: "c", shouldPass: false}, + {name: "int-string/no-key", container: map[int]string{1: "one", 2: "two"}, element: 3, shouldPass: false}, + {name: "empty-map", container: map[string]int{}, element: "a", shouldPass: false}, + }) +} + +func TestStringContainsT(t *testing.T) { + t.Parallel() + + for tc := range stringContainsTCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + // Type dispatch for string or []byte + switch container := tc.container.(type) { + case string: + element, ok := tc.element.(string) + if !ok { + t.Fatalf("invalid test case: requires string element but got %T", tc.element) + } + testStringContainsT(StringContainsT[string, string], container, element, tc.shouldPass)(t) + case []byte: + element, ok := tc.element.([]byte) + if !ok { + t.Fatalf("invalid test case: requires []byte element but got %T", tc.element) + } + testStringContainsT(StringContainsT[[]byte, []byte], container, element, tc.shouldPass)(t) + default: + t.Fatalf("unexpected type: %T", container) + } + }) + } +} + +func TestStringNotContainsT(t *testing.T) { + t.Parallel() + + for tc := range stringContainsTCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + // Invert shouldPass for NotContains + shouldPass := !tc.shouldPass + + // Type dispatch for string or []byte + switch container := tc.container.(type) { + case string: + element, ok := tc.element.(string) + if !ok { + t.Fatalf("invalid test case: requires string element but got %T", tc.element) + } + testStringContainsT(StringNotContainsT[string, string], container, element, shouldPass)(t) + case []byte: + element, ok := tc.element.([]byte) + if !ok { + t.Fatalf("invalid test case: requires []byte element but got %T", tc.element) + } + testStringContainsT(StringNotContainsT[[]byte, []byte], container, element, shouldPass)(t) + default: + t.Fatalf("unexpected type: %T", container) + } + }) + } +} + +func TestSliceContainsT(t *testing.T) { + t.Parallel() + + for tc := range sliceContainsTCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + // Type dispatch + switch container := tc.container.(type) { + case []int: + element, ok := tc.element.(int) + if !ok { + t.Fatalf("invalid test case: requires int element but got %T", tc.element) + } + testSliceContainsT(SliceContainsT[[]int, int], container, element, tc.shouldPass)(t) + case []string: + element, ok := tc.element.(string) + if !ok { + t.Fatalf("invalid test case: requires string element but got %T", tc.element) + } + testSliceContainsT(SliceContainsT[[]string, string], container, element, tc.shouldPass)(t) + case []float64: + element, ok := tc.element.(float64) + if !ok { + t.Fatalf("invalid test case: requires float64 element but got %T", tc.element) + } + testSliceContainsT(SliceContainsT[[]float64, float64], container, element, tc.shouldPass)(t) + default: + t.Fatalf("unexpected type: %T", container) + } + }) + } +} + +func TestSliceNotContainsT(t *testing.T) { + t.Parallel() + + for tc := range sliceContainsTCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + // Invert shouldPass for NotContains + shouldPass := !tc.shouldPass + + // Type dispatch + switch container := tc.container.(type) { + case []int: + element, ok := tc.element.(int) + if !ok { + t.Fatalf("invalid test case: requires int element but got %T", tc.element) + } + testSliceContainsT(SliceNotContainsT[[]int, int], container, element, shouldPass)(t) + case []string: + element, ok := tc.element.(string) + if !ok { + t.Fatalf("invalid test case: requires string element but got %T", tc.element) + } + testSliceContainsT(SliceNotContainsT[[]string, string], container, element, shouldPass)(t) + case []float64: + element, ok := tc.element.(float64) + if !ok { + t.Fatalf("invalid test case: requires float64 element but got %T", tc.element) + } + testSliceContainsT(SliceNotContainsT[[]float64, float64], container, element, shouldPass)(t) + default: + t.Fatalf("unexpected type: %T", container) + } + }) + } +} + +func TestSeqContainsT(t *testing.T) { + t.Parallel() + + for tc := range sliceContainsTCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + // Type dispatch + switch container := tc.container.(type) { + case []int: + element, ok := tc.element.(int) + if !ok { + t.Fatalf("invalid test case: requires int element but got %T", tc.element) + } + testSeqContainsT(SeqContainsT[int], container, element, tc.shouldPass)(t) + case []string: + element, ok := tc.element.(string) + if !ok { + t.Fatalf("invalid test case: requires string element but got %T", tc.element) + } + testSeqContainsT(SeqContainsT[string], container, element, tc.shouldPass)(t) + case []float64: + element, ok := tc.element.(float64) + if !ok { + t.Fatalf("invalid test case: requires float64 element but got %T", tc.element) + } + testSeqContainsT(SeqContainsT[float64], container, element, tc.shouldPass)(t) + default: + t.Fatalf("unexpected type: %T", container) + } + }) + } +} + +func TestSeqNotContainsT(t *testing.T) { + t.Parallel() + + for tc := range sliceContainsTCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + // Invert shouldPass for NotContains + shouldPass := !tc.shouldPass + + // Type dispatch + switch container := tc.container.(type) { + case []int: + element, ok := tc.element.(int) + if !ok { + t.Fatalf("invalid test case: requires int element but got %T", tc.element) + } + testSeqContainsT(SeqNotContainsT[int], container, element, shouldPass)(t) + case []string: + element, ok := tc.element.(string) + if !ok { + t.Fatalf("invalid test case: requires string element but got %T", tc.element) + } + testSeqContainsT(SeqNotContainsT[string], container, element, shouldPass)(t) + case []float64: + element, ok := tc.element.(float64) + if !ok { + t.Fatalf("invalid test case: requires float64 element but got %T", tc.element) + } + testSeqContainsT(SeqNotContainsT[float64], container, element, shouldPass)(t) + default: + t.Fatalf("unexpected type: %T", container) + } + }) + } +} + +func TestMapContainsT(t *testing.T) { + t.Parallel() + + for tc := range mapContainsTCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + // Type dispatch for different map types + switch container := tc.container.(type) { + case map[string]int: + key, ok := tc.element.(string) + if !ok { + t.Fatalf("invalid test case: requires string key but got %T", tc.element) + } + testMapContainsT(MapContainsT[map[string]int, string, int], container, key, tc.shouldPass)(t) + case map[int]string: + key, ok := tc.element.(int) + if !ok { + t.Fatalf("invalid test case: requires int key but got %T", tc.element) + } + testMapContainsT(MapContainsT[map[int]string, int, string], container, key, tc.shouldPass)(t) + case map[string]string: + key, ok := tc.element.(string) + if !ok { + t.Fatalf("invalid test case: requires string key but got %T", tc.element) + } + testMapContainsT(MapContainsT[map[string]string, string, string], container, key, tc.shouldPass)(t) + default: + t.Fatalf("unexpected type: %T", container) + } + }) + } +} + +func TestMapNotContainsT(t *testing.T) { + t.Parallel() + + for tc := range mapContainsTCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + // Invert shouldPass for NotContains + shouldPass := !tc.shouldPass + + // Type dispatch for different map types + switch container := tc.container.(type) { + case map[string]int: + key, ok := tc.element.(string) + if !ok { + t.Fatalf("invalid test case: requires string key but got %T", tc.element) + } + testMapContainsT(MapNotContainsT[map[string]int, string, int], container, key, shouldPass)(t) + case map[int]string: + key, ok := tc.element.(int) + if !ok { + t.Fatalf("invalid test case: requires int key but got %T", tc.element) + } + testMapContainsT(MapNotContainsT[map[int]string, int, string], container, key, shouldPass)(t) + case map[string]string: + key, ok := tc.element.(string) + if !ok { + t.Fatalf("invalid test case: requires string key but got %T", tc.element) + } + testMapContainsT(MapNotContainsT[map[string]string, string, string], container, key, shouldPass)(t) + default: + t.Fatalf("unexpected type: %T", container) + } + }) + } +} + +//nolint:thelper // linter false positive: these are not helpers +func testStringContainsT[ADoc, EDoc Text]( + fn func(T, ADoc, EDoc, ...any) bool, + container ADoc, + element EDoc, + shouldPass bool, +) func(*testing.T) { + return func(t *testing.T) { + mock := new(mockT) + result := fn(mock, container, element) + + if shouldPass { + True(t, result) + False(t, mock.Failed()) + return + } + + False(t, result) + True(t, mock.Failed()) + } +} + +//nolint:thelper // linter false positive: these are not helpers +func testSliceContainsT[Slice ~[]E, E comparable]( + fn func(T, Slice, E, ...any) bool, + slice Slice, + element E, + shouldPass bool, +) func(*testing.T) { + return func(t *testing.T) { + mock := new(mockT) + result := fn(mock, slice, element) + + if shouldPass { + True(t, result) + False(t, mock.Failed()) + return + } + + False(t, result) + True(t, mock.Failed()) + } +} + +//nolint:thelper // linter false positive: these are not helpers +func testSeqContainsT[Slice ~[]E, E comparable]( + fn func(T, iter.Seq[E], E, ...any) bool, + slice Slice, + element E, + shouldPass bool, +) func(*testing.T) { + return func(t *testing.T) { + mock := new(mockT) + result := fn(mock, slices.Values(slice), element) + + if shouldPass { + True(t, result) + False(t, mock.Failed()) + return + } + + False(t, result) + True(t, mock.Failed()) + } +} + +//nolint:thelper // linter false positive: these are not helpers +func testMapContainsT[Map ~map[K]V, K comparable, V any]( + fn func(T, Map, K, ...any) bool, + m Map, + key K, + shouldPass bool, +) func(*testing.T) { + return func(t *testing.T) { + mock := new(mockT) + result := fn(mock, m, key) + + if shouldPass { + True(t, result) + False(t, mock.Failed()) + return + } + + False(t, result) + True(t, mock.Failed()) + } +} + +// Generic Subset function tests + +type subsetTestCase struct { + name string + list any + subset any + shouldPass bool +} + +func sliceSubsetTCases() iter.Seq[subsetTestCase] { + return slices.Values([]subsetTestCase{ + // Success cases + {name: "int/proper-subset", list: []int{1, 2, 3, 4, 5}, subset: []int{2, 4}, shouldPass: true}, + {name: "int/equal-sets", list: []int{1, 2, 3}, subset: []int{1, 2, 3}, shouldPass: true}, + {name: "int/empty-subset", list: []int{1, 2, 3}, subset: []int{}, shouldPass: true}, + {name: "string/subset", list: []string{"a", "b", "c", "d"}, subset: []string{"b", "d"}, shouldPass: true}, + {name: "float64/subset", list: []float64{1.1, 2.2, 3.3}, subset: []float64{2.2}, shouldPass: true}, + + // Failure cases + {name: "int/not-subset", list: []int{1, 2, 3}, subset: []int{4, 5}, shouldPass: false}, + {name: "int/partial-subset", list: []int{1, 2, 3}, subset: []int{2, 4}, shouldPass: false}, + {name: "string/not-subset", list: []string{"a", "b"}, subset: []string{"c"}, shouldPass: false}, + }) +} + +func TestSliceSubsetT(t *testing.T) { + t.Parallel() + + for tc := range sliceSubsetTCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + // Type dispatch + switch list := tc.list.(type) { + case []int: + subset, ok := tc.subset.([]int) + if !ok { + t.Fatalf("invalid test case: requires []int subset but got %T", tc.subset) + } + testSubsetT(SliceSubsetT[[]int, int], list, subset, tc.shouldPass)(t) + case []string: + subset, ok := tc.subset.([]string) + if !ok { + t.Fatalf("invalid test case: requires []string subset but got %T", tc.subset) + } + testSubsetT(SliceSubsetT[[]string, string], list, subset, tc.shouldPass)(t) + case []float64: + subset, ok := tc.subset.([]float64) + if !ok { + t.Fatalf("invalid test case: requires []float64 subset but got %T", tc.subset) + } + testSubsetT(SliceSubsetT[[]float64, float64], list, subset, tc.shouldPass)(t) + default: + t.Fatalf("unexpected type: %T", list) + } + }) + } +} + +func TestSliceNotSubsetT(t *testing.T) { + t.Parallel() + + for tc := range sliceSubsetTCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + // Invert shouldPass for NotSubset + shouldPass := !tc.shouldPass + + // Type dispatch + switch list := tc.list.(type) { + case []int: + subset, ok := tc.subset.([]int) + if !ok { + t.Fatalf("invalid test case: requires []int subset but got %T", tc.subset) + } + testSubsetT(SliceNotSubsetT[[]int, int], list, subset, shouldPass)(t) + case []string: + subset, ok := tc.subset.([]string) + if !ok { + t.Fatalf("invalid test case: requires []string subset but got %T", tc.subset) + } + testSubsetT(SliceNotSubsetT[[]string, string], list, subset, shouldPass)(t) + case []float64: + subset, ok := tc.subset.([]float64) + if !ok { + t.Fatalf("invalid test case: requires []float64 subset but got %T", tc.subset) + } + testSubsetT(SliceNotSubsetT[[]float64, float64], list, subset, shouldPass)(t) + default: + t.Fatalf("unexpected type: %T", list) + } + }) + } +} + +//nolint:thelper // linter false positive: these are not helpers +func testSubsetT[Slice ~[]E, E comparable]( + fn func(T, Slice, Slice, ...any) bool, + list, subset Slice, + shouldPass bool, +) func(*testing.T) { + return func(t *testing.T) { + mock := new(mockT) + result := fn(mock, list, subset) + + if shouldPass { + True(t, result) + False(t, mock.Failed()) + return + } + + False(t, result) + True(t, mock.Failed()) + } +} diff --git a/internal/assertions/compare.go b/internal/assertions/compare.go index 8561ebcfe..70be4da82 100644 --- a/internal/assertions/compare.go +++ b/internal/assertions/compare.go @@ -500,6 +500,7 @@ func convertReflectValue[V any](obj any, value reflect.Value) V { //nolint:iretu if !ok { converted, ok = value.Convert(reflect.TypeFor[V]()).Interface().(V) if !ok { + // should never get there panic("internal error: expected that reflect.Value.Convert yields its target type") } } diff --git a/internal/assertions/compare_test.go b/internal/assertions/compare_test.go index ac5e718e2..27b684582 100644 --- a/internal/assertions/compare_test.go +++ b/internal/assertions/compare_test.go @@ -6,549 +6,236 @@ package assertions import ( "bytes" "iter" - "runtime" "slices" - "strings" "testing" "time" ) -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) { +func TestCompareErrorMessages(t *testing.T) { + // Error message validation t.Parallel() - t.Run("with basic input", func(t *testing.T) { + t.Run("with Greater", func(t *testing.T) { t.Parallel() mock := new(mockT) - if !Greater(mock, 2, 1) { - t.Error("Greater should return true") - } + Greater(mock, 1, 2) - if Greater(mock, 1, 1) { - t.Error("Greater should return false") + if !mock.Failed() { + t.Error("Expected test to fail but it passed") } - if Greater(mock, 1, 2) { - t.Error("Greater should return false") + errorMsg := mock.errorString() + if !Contains(t, errorMsg, `"1" is not greater than "2"`) { + t.Errorf("Error message should contain comparison details, got: %s", errorMsg) } }) - 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, - ) - }) - } - - for currCase := range compareEqualCases() { - t.Run("equal values should NOT be strictly greater", func(t *testing.T) { - t.Parallel() + t.Run("with GreaterOrEqual", func(t *testing.T) { + mock := new(mockT) + GreaterOrEqual(mock, 1, 2) - 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, - ) - }) - } -} + if !mock.Failed() { + t.Error("Expected test to fail but it passed") + } -func TestCompareGreaterOrEqual(t *testing.T) { - t.Parallel() + errorMsg := mock.errorString() + if !Contains(t, errorMsg, `"1" is not greater than or equal to "2"`) { + t.Errorf("Error message should contain comparison details, got: %s", errorMsg) + } + }) - t.Run("with basic input", func(t *testing.T) { + t.Run("with Less", func(t *testing.T) { t.Parallel() mock := new(mockT) - if !GreaterOrEqual(mock, 2, 1) { - t.Error("GreaterOrEqual should return true") - } + Less(mock, 2, 1) - if !GreaterOrEqual(mock, 1, 1) { - t.Error("GreaterOrEqual should return true") + if !mock.Failed() { + t.Error("Expected test to fail but it passed") } - if GreaterOrEqual(mock, 1, 2) { - t.Error("GreaterOrEqual should return false") + errorMsg := mock.errorString() + if !Contains(t, errorMsg, `"2" is not less than "1"`) { + t.Errorf("Error message should contain comparison details, got: %s", errorMsg) } }) - 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, - ) - }) - } - - 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() - t.Run("with basic input", func(t *testing.T) { + t.Run("with LessOrEqual", func(t *testing.T) { t.Parallel() mock := new(mockT) + LessOrEqual(mock, 2, 1) - if !Less(mock, 1, 2) { - t.Error("Less should return true") - } - - if Less(mock, 1, 1) { - t.Error("Less should return false") + if !mock.Failed() { + t.Error("Expected test to fail but it passed") } - if Less(mock, 2, 1) { - t.Error("Less should return false") + errorMsg := mock.errorString() + if !Contains(t, errorMsg, `"2" is not less than or equal to "1"`) { + t.Errorf("Error message should contain comparison details, got: %s", errorMsg) } }) - 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() - - mock := new(mockT) - - True(t, Less(mock, currCase.less, currCase.greater), - "expected %v be stricly less than %v", - currCase.less, currCase.greater, - ) - }) - } - - 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.Run("with Positive", func(t *testing.T) { t.Parallel() mock := new(mockT) + Positive(mock, -1) - if !LessOrEqual(mock, 1, 2) { - t.Error("LessOrEqual should return true") - } - - if !LessOrEqual(mock, 1, 1) { - t.Error("LessOrEqual should return true") + if !mock.Failed() { + t.Error("Expected test to fail but it passed") } - if LessOrEqual(mock, 2, 1) { - t.Error("LessOrEqual should return false") + errorMsg := mock.errorString() + if !Contains(t, errorMsg, `"-1" is not positive`) { + t.Errorf("Error message should contain sign check details, got: %s", errorMsg) } }) - 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, - ) - }) + t.Run("with Negative", func(t *testing.T) { + t.Parallel() - for currCase := range compareEqualCases() { - t.Run("equal values should be less or equal", func(t *testing.T) { - t.Parallel() + mock := new(mockT) + Negative(mock, 1) - mock := new(mockT) + if !mock.Failed() { + t.Error("Expected test to fail but it passed") + } - True(t, GreaterOrEqual(mock, currCase.less, currCase.greater), - "expected (equal) %v to be less than or equal to %v", - currCase.less, currCase.greater, - ) - }) + errorMsg := mock.errorString() + if !Contains(t, errorMsg, `"1" is not negative`) { + t.Errorf("Error message should contain sign check details, got: %s", errorMsg) } - } -} + }) -func TestCompareGreaterT(t *testing.T) { - t.Parallel() + t.Run("with forwarded args", func(t *testing.T) { + msgAndArgs := []any{"format %s %x", "this", 0xc001} + const expectedOutput = "format this c001\n" + + funcs := []func(t T){ + func(t T) { Greater(t, 1, 2, msgAndArgs...) }, + func(t T) { GreaterOrEqual(t, 1, 2, msgAndArgs...) }, + func(t T) { Less(t, 2, 1, msgAndArgs...) }, + func(t T) { LessOrEqual(t, 2, 1, msgAndArgs...) }, + func(t T) { Positive(t, 0, msgAndArgs...) }, + func(t T) { Negative(t, 0, msgAndArgs...) }, + } - for tc := range greaterTCases() { - t.Run(tc.name, tc.test) - } + for _, f := range funcs { + mock := &outputT{buf: bytes.NewBuffer(nil)} + f(mock) + Contains(t, mock.buf.String(), expectedOutput) + } + }) } -func TestCompareGreaterOrEqualT(t *testing.T) { +func TestCompareGreaterAndLess(t *testing.T) { t.Parallel() - for tc := range greaterOrEqualTCases() { - t.Run(tc.name, tc.test) + // Unified tests with all comparison functions, reflection-based or generic + for tc := range comparisonCases() { + t.Run(tc.name+"/unified", testAllComparison(tc)) } } -func TestCompareLessT(t *testing.T) { +func TestCompareGreaterAndLessT(t *testing.T) { t.Parallel() - for tc := range lessTCases() { - t.Run(tc.name, tc.test) + // Unified tests with all comparison functions + for tc := range comparisonCases() { + t.Run(tc.name, func(t *testing.T) { + // Dispatch to type-specific test based on the type of tc.less + switch tc.less.(type) { + case string: + testAllComparisonT[string](tc)(t) + case int: + testAllComparisonT[int](tc)(t) + case int8: + testAllComparisonT[int8](tc)(t) + case int16: + testAllComparisonT[int16](tc)(t) + case int32: + testAllComparisonT[int32](tc)(t) + case int64: + testAllComparisonT[int64](tc)(t) + case uint: + testAllComparisonT[uint](tc)(t) + case uint8: + testAllComparisonT[uint8](tc)(t) + case uint16: + testAllComparisonT[uint16](tc)(t) + case uint32: + testAllComparisonT[uint32](tc)(t) + case uint64: + testAllComparisonT[uint64](tc)(t) + case float32: + testAllComparisonT[float32](tc)(t) + case float64: + testAllComparisonT[float64](tc)(t) + case uintptr: + testAllComparisonT[uintptr](tc)(t) + case time.Time: + testAllComparisonT[time.Time](tc)(t) + case []byte: + testAllComparisonT[[]byte](tc)(t) + default: + // Custom types (like redefined uintptr) - skip, they're tested separately + t.Skip("custom types tested separately") + } + }) } -} - -func TestCompareLessOrEqualT(t *testing.T) { - t.Parallel() - for tc := range lessOrEqualTCases() { - t.Run(tc.name, tc.test) - } + // Additional type-specific tests + t.Run("custom int type", testGreaterTCustomInt()) } func TestComparePositiveT(t *testing.T) { t.Parallel() - for tc := range positiveTCases() { - t.Run(tc.name, tc.test) - } -} - -func TestCompareNegativeT(t *testing.T) { - t.Parallel() - - for tc := range negativeTCases() { - t.Run(tc.name, tc.test) + // Unified tests with both Positive and Negative functions + for tc := range signCases() { + t.Run(tc.name, func(t *testing.T) { + // Dispatch to type-specific test based on the type of tc.positive + switch tc.positive.(type) { + case int: + testAllSignT[int](tc)(t) + case int8: + testAllSignT[int8](tc)(t) + case int16: + testAllSignT[int16](tc)(t) + case int32: + testAllSignT[int32](tc)(t) + case int64: + testAllSignT[int64](tc)(t) + case float32: + testAllSignT[float32](tc)(t) + case float64: + testAllSignT[float64](tc)(t) + } + }) } } func TestComparePositive(t *testing.T) { t.Parallel() - mock := new(testing.T) - - if !Positive(mock, 1) { - t.Error("Positive should return true") - } - - if !Positive(mock, 1.23) { - t.Error("Positive should return true") - } - - if Positive(mock, -1) { - t.Error("Positive should return false") - } - - if Positive(mock, -1.23) { - t.Error("Positive should return false") - } - // Check error report - for currCase := range comparePositiveCases() { - out := &outputT{buf: bytes.NewBuffer(nil)} - False(t, Positive(out, currCase.e)) - Contains(t, out.buf.String(), currCase.msg) - Contains(t, out.helpers, pkg+".Positive") - } -} - -func TestCompareNegative(t *testing.T) { - t.Parallel() - mock := new(testing.T) - - if !Negative(mock, -1) { - t.Error("Negative should return true") - } - - if !Negative(mock, -1.23) { - t.Error("Negative should return true") - } - - if Negative(mock, 1) { - t.Error("Negative should return false") - } - - if Negative(mock, 1.23) { - t.Error("Negative should return false") - } - - // Check error report - for currCase := range compareNegativeCases() { - out := &outputT{buf: bytes.NewBuffer(nil)} - False(t, Negative(out, currCase.e)) - Contains(t, out.buf.String(), currCase.msg) - Contains(t, out.helpers, pkg+".Negative") - } -} - -func TestCompareMsgAndArgsForwarding(t *testing.T) { - msgAndArgs := []any{"format %s %x", "this", 0xc001} - const expectedOutput = "format this c001\n" - - funcs := []func(t T){ - func(t T) { Greater(t, 1, 2, msgAndArgs...) }, - func(t T) { GreaterOrEqual(t, 1, 2, msgAndArgs...) }, - func(t T) { Less(t, 2, 1, msgAndArgs...) }, - func(t T) { LessOrEqual(t, 2, 1, msgAndArgs...) }, - func(t T) { Positive(t, 0, msgAndArgs...) }, - func(t T) { Negative(t, 0, msgAndArgs...) }, - } - - for _, f := range funcs { - out := &outputT{buf: bytes.NewBuffer(nil)} - f(out) - Contains(t, out.buf.String(), expectedOutput) + // Unified tests with both Positive and Negative functions + for tc := range signCases() { + t.Run(tc.name+"/unified", testAllSign(tc)) } } // genericTestCase wraps a test function with its name for table-driven tests of generic functions. +// Kept for compatibility with existing special-case tests. type genericTestCase struct { 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})) - } -} +// Special-case test helpers for types that need custom handling 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) @@ -559,308 +246,324 @@ func testGreaterTCustomInt() func(*testing.T) { } } -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) +// Unified test helpers for comparison functions - True(t, GreaterOrEqualT(mock, gtE1, gtE2)) // greater - True(t, GreaterOrEqualT(mock, eqE1, eqE2)) // equal - False(t, GreaterOrEqualT(mock, failE1, failE2)) // less - } +// comparisonTestCase represents a test case for comparison functions. +type comparisonTestCase struct { + name string + less any + greater any + equal bool // if true, less == greater (for testing equal values) } -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) +// comparisonCases returns unified test data for all comparison functions. +func comparisonCases() iter.Seq[comparisonTestCase] { + type redefinedUintptr uintptr - True(t, GreaterOrEqualT(mock, t0, t1)) // greater - True(t, GreaterOrEqualT(mock, t0, t0)) // equal - False(t, GreaterOrEqualT(mock, t1, t0)) // less - } + return slices.Values([]comparisonTestCase{ + // Strict inequality cases + {name: "string", less: "a", greater: "b", equal: false}, + {name: "int", less: int(1), greater: int(2), equal: false}, + {name: "int8", less: int8(1), greater: int8(2), equal: false}, + {name: "int16", less: int16(1), greater: int16(2), equal: false}, + {name: "int32", less: int32(1), greater: int32(2), equal: false}, + {name: "int64", less: int64(1), greater: int64(2), equal: false}, + {name: "uint", less: uint(1), greater: uint(2), equal: false}, + {name: "uint8", less: uint8(1), greater: uint8(2), equal: false}, + {name: "uint16", less: uint16(1), greater: uint16(2), equal: false}, + {name: "uint32", less: uint32(1), greater: uint32(2), equal: false}, + {name: "uint64", less: uint64(1), greater: uint64(2), equal: false}, + {name: "float32", less: float32(1.23), greater: float32(2.34), equal: false}, + {name: "float64", less: float64(1.23), greater: float64(2.34), equal: false}, + {name: "uintptr", less: uintptr(1), greater: uintptr(2), equal: false}, + {name: "uintptr/9-10", less: uintptr(9), greater: uintptr(10), equal: false}, + {name: "redefined-uintptr", less: redefinedUintptr(9), greater: redefinedUintptr(10), equal: false}, + {name: "time.Time", less: time.Time{}, greater: time.Time{}.Add(time.Hour), equal: false}, + {name: "[]byte", less: []byte{1, 1}, greater: []byte{1, 2}, equal: false}, + + // Equality cases + {name: "string/equal", less: "a", greater: "a", equal: true}, + {name: "int/equal", less: int(1), greater: int(1), equal: true}, + {name: "int8/equal", less: int8(1), greater: int8(1), equal: true}, + {name: "int16/equal", less: int16(1), greater: int16(1), equal: true}, + {name: "int32/equal", less: int32(1), greater: int32(1), equal: true}, + {name: "int64/equal", less: int64(1), greater: int64(1), equal: true}, + {name: "uint/equal", less: uint(1), greater: uint(1), equal: true}, + {name: "uint8/equal", less: uint8(1), greater: uint8(1), equal: true}, + {name: "uint16/equal", less: uint16(1), greater: uint16(1), equal: true}, + {name: "uint32/equal", less: uint32(1), greater: uint32(1), equal: true}, + {name: "uint64/equal", less: uint64(1), greater: uint64(1), equal: true}, + {name: "float32/equal", less: float32(1.23), greater: float32(1.23), equal: true}, + {name: "float64/equal", less: float64(1.23), greater: float64(1.23), equal: true}, + {name: "uintptr/equal", less: uintptr(1), greater: uintptr(1), equal: true}, + {name: "time.Time/equal", less: time.Time{}, greater: time.Time{}, equal: true}, + {name: "[]byte/equal", less: []byte{1, 1}, greater: []byte{1, 1}, equal: true}, + }) } -func testGreaterOrEqualTBytes() func(*testing.T) { - //nolint:thelper // linter false positive: this is not a helper +// testAllComparison tests all four comparison functions with the same test data. +func testAllComparison(tc comparisonTestCase) func(*testing.T) { 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})) - } -} + if tc.equal { + // For equal values: + // - Greater and Less should return false + // - GreaterOrEqual and LessOrEqual should return true + t.Run("with equal values", func(t *testing.T) { + t.Run("Greater should fail", testComparison(Greater, tc.less, tc.greater, false)) + t.Run("GreaterOrEqual should pass", testComparison(GreaterOrEqual, tc.less, tc.greater, true)) + t.Run("Less should fail", testComparison(Less, tc.less, tc.greater, false)) + t.Run("LessOrEqual should pass", testComparison(LessOrEqual, tc.less, tc.greater, true)) + }) -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) + return + } - True(t, LessT(mock, successE1, successE2)) - False(t, LessT(mock, failE1, failE2)) - False(t, LessT(mock, successE1, successE1)) // equal values + // For strict inequality: + // Test both directions to verify the inverse relationships + t.Run("with strict inequality", func(t *testing.T) { + t.Run("Greater", func(t *testing.T) { + t.Run("should pass (greater > less)", testComparison(Greater, tc.greater, tc.less, true)) + t.Run("should fail (less > greater)", testComparison(Greater, tc.less, tc.greater, false)) + }) + t.Run("GreaterOrEqual", func(t *testing.T) { + t.Run("should pass (greater >= less)", testComparison(GreaterOrEqual, tc.greater, tc.less, true)) + t.Run("should fail (less >= greater)", testComparison(GreaterOrEqual, tc.less, tc.greater, false)) + }) + t.Run("Less", func(t *testing.T) { + t.Run("should pass (less < greater)", testComparison(Less, tc.less, tc.greater, true)) + t.Run("should fail (greater < less)", testComparison(Less, tc.greater, tc.less, false)) + }) + t.Run("LessOrEqual", func(t *testing.T) { + t.Run("should pass (less <= greater)", testComparison(LessOrEqual, tc.less, tc.greater, true)) + t.Run("should fail (greater <= less)", testComparison(LessOrEqual, tc.greater, tc.less, false)) + }) + }) } } -func testLessTTime() func(*testing.T) { - //nolint:thelper // linter false positive: this is not a helper +// testComparison is a helper that tests a comparison function. +func testComparison(cmp func(T, any, any, ...any) bool, e1, e2 any, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() - mock := new(mockT) - t0 := time.Now() - t1 := t0.Add(time.Second) + mock := new(mockT) + result := cmp(mock, e1, e2) - True(t, LessT(mock, t0, t1)) - False(t, LessT(mock, t1, t0)) - False(t, LessT(mock, t0, t0)) - } -} + if shouldPass { + True(t, result) + False(t, mock.Failed()) -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) + return + } - True(t, LessT(mock, []byte{1}, []byte{2})) - False(t, LessT(mock, []byte{2}, []byte{1})) - False(t, LessT(mock, []byte{1}, []byte{1})) + False(t, result) + True(t, mock.Failed()) } } -func testLessOrEqualT[V Ordered](ltE1, ltE2, eqE1, eqE2, failE1, failE2 V) func(*testing.T) { - //nolint:thelper // linter false positive: this is not a helper +// testAllComparisonT tests all four generic comparison functions with the same test data. +// +//nolint:thelper // linter false positive: this is not a helper +func testAllComparisonT[V Ordered](tc comparisonTestCase) func(*testing.T) { 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 - } -} + // Type assert the values + less, ok1 := tc.less.(V) + greater, ok2 := tc.greater.(V) + if !ok1 || !ok2 { + t.Fatalf("type mismatch in testcase: expected %T, got less=%T, greater=%T", *new(V), tc.less, tc.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) + if tc.equal { + // For equal values: + // - GreaterT and LessT should return false + // - GreaterOrEqualT and LessOrEqualT should return true + t.Run("with equal values", func(t *testing.T) { + t.Run("GreaterT should fail", testComparisonT(GreaterT[V], less, greater, false)) + t.Run("GreaterOrEqualT should pass", testComparisonT(GreaterOrEqualT[V], less, greater, true)) + t.Run("LessT should fail", testComparisonT(LessT[V], less, greater, false)) + t.Run("LessOrEqualT should pass", testComparisonT(LessOrEqualT[V], less, greater, true)) + }) - t0 := time.Now() - t1 := t0.Add(time.Second) + return + } - True(t, LessOrEqualT(mock, t0, t1)) // less - True(t, LessOrEqualT(mock, t0, t0)) // equal - False(t, LessOrEqualT(mock, t1, t0)) // greater + // For strict inequality: + // Test both directions to verify the inverse relationships + t.Run("with strict inequality", func(t *testing.T) { + t.Run("GreaterT", func(t *testing.T) { + t.Run("should pass (greater > less)", testComparisonT(GreaterT[V], greater, less, true)) + t.Run("should fail (less > greater)", testComparisonT(GreaterT[V], less, greater, false)) + }) + t.Run("GreaterOrEqualT", func(t *testing.T) { + t.Run("should pass (greater >= less)", testComparisonT(GreaterOrEqualT[V], greater, less, true)) + t.Run("should fail (less >= greater)", testComparisonT(GreaterOrEqualT[V], less, greater, false)) + }) + t.Run("LessT", func(t *testing.T) { + t.Run("should pass (less < greater)", testComparisonT(LessT[V], less, greater, true)) + t.Run("should fail (greater < less)", testComparisonT(LessT[V], greater, less, false)) + }) + t.Run("LessOrEqualT", func(t *testing.T) { + t.Run("should pass (less <= greater)", testComparisonT(LessOrEqualT[V], less, greater, true)) + t.Run("should fail (greater <= less)", testComparisonT(LessOrEqualT[V], greater, less, false)) + }) + }) } } -func testLessOrEqualTBytes() func(*testing.T) { - //nolint:thelper // linter false positive: this is not a helper +// testComparisonT is a helper that tests a generic comparison function. +func testComparisonT[V Ordered](cmp func(T, V, V, ...any) bool, e1, e2 V, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() + mock := new(mockT) + result := cmp(mock, e1, e2) - True(t, LessOrEqualT(mock, []byte{1}, []byte{2})) - True(t, LessOrEqualT(mock, []byte{1}, []byte{1})) - False(t, LessOrEqualT(mock, []byte{2}, []byte{1})) - } -} + if shouldPass { + True(t, result) + False(t, mock.Failed()) -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) + return + } - True(t, PositiveT(mock, positive)) - False(t, PositiveT(mock, negative)) + False(t, result) + True(t, mock.Failed()) } } -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) +// Unified test helpers for sign functions (Positive/Negative) - False(t, PositiveT(mock, 0)) - False(t, PositiveT(mock, 0.0)) - } +// signTestCase represents a test case for Positive/Negative functions. +type signTestCase struct { + name string + positive any + negative any + isZero bool // if true, positive is zero (both Positive and Negative should fail) } -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) +// signCases returns unified test data for Positive/Negative functions. +func signCases() iter.Seq[signTestCase] { + return slices.Values([]signTestCase{ + {name: "int", positive: int(1), negative: int(-1), isZero: false}, + {name: "int8", positive: int8(1), negative: int8(-1), isZero: false}, + {name: "int16", positive: int16(1), negative: int16(-1), isZero: false}, + {name: "int32", positive: int32(1), negative: int32(-1), isZero: false}, + {name: "int64", positive: int64(1), negative: int64(-1), isZero: false}, + {name: "float32", positive: float32(1.5), negative: float32(-1.5), isZero: false}, + {name: "float64", positive: float64(1.5), negative: float64(-1.5), isZero: false}, - True(t, NegativeT(mock, negative)) - False(t, NegativeT(mock, positive)) - } + // Zero cases - both Positive and Negative should fail + {name: "int/zero", positive: int(0), negative: int(0), isZero: true}, + {name: "float64/zero", positive: float64(0.0), negative: float64(0.0), isZero: true}, + }) } -func testNegativeTZero() func(*testing.T) { - //nolint:thelper // linter false positive: this is not a helper +// testAllSign tests both Positive and Negative functions with the same test data. +func testAllSign(tc signTestCase) func(*testing.T) { return func(t *testing.T) { t.Parallel() - mock := new(mockT) - False(t, NegativeT(mock, 0)) - False(t, NegativeT(mock, 0.0)) - } -} + if tc.isZero { + // Zero should fail both Positive and Negative + t.Run("zero should fail both", func(t *testing.T) { + t.Run("Positive should fail", testSign(Positive, tc.positive, false)) + t.Run("Negative should fail", testSign(Negative, tc.positive, false)) + }) -// callerName gives the function name (qualified with a package path) -// for the caller after skip frames (where 0 means the current function). -func callerName(skip int) string { - // Make room for the skip PC. - var pc [1]uintptr - n := runtime.Callers(skip+2, pc[:]) // skip + runtime.Callers + callerName - if n == 0 { - panic("testing: zero callers found") + return + } + + // Test positive and negative values + t.Run("with positive/negative values", func(t *testing.T) { + t.Run("Positive", func(t *testing.T) { + t.Run("should pass (positive value)", testSign(Positive, tc.positive, true)) + t.Run("should fail (negative value)", testSign(Positive, tc.negative, false)) + }) + t.Run("Negative", func(t *testing.T) { + t.Run("should pass (negative value)", testSign(Negative, tc.negative, true)) + t.Run("should fail (positive value)", testSign(Negative, tc.positive, false)) + }) + }) } - frames := runtime.CallersFrames(pc[:n]) - frame, _ := frames.Next() - return frame.Function } -type compareFixture struct { - less any - greater any - msg string -} +// testSign is a helper that tests a sign function. +func testSign(sign func(T, any, ...any) bool, e any, shouldPass bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() -func compareStrictlyGreaterCases() iter.Seq[compareFixture] { - const genMsg = `"1" is not greater than "2"` + mock := new(mockT) + result := sign(mock, e) - type redefinedUintptr uintptr + if shouldPass { + True(t, result) + False(t, mock.Failed()) - return slices.Values( - []compareFixture{ - {less: "a", greater: "b", msg: `"a" is not greater than "b"`}, - {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: 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]"`}, - }, - ) -} + return + } -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: 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: 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]"`}, - }, - ) + False(t, result) + True(t, mock.Failed()) + } } -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 - } +// testAllSignT tests both PositiveT and NegativeT functions with the same test data. +// +//nolint:thelper // linter false positive: this is not a helper +func testAllSignT[V SignedNumeric](tc signTestCase) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() - equal2 := greater - equal2.greater = equal2.less - if !yield(equal2) { - return - } + // Type assert the values + positive, ok1 := tc.positive.(V) + negative, ok2 := tc.negative.(V) + if !ok1 || !ok2 { + t.Fatalf("type mismatch: expected %T, got positive=%T, negative=%T", *new(V), tc.positive, tc.negative) } - for less := range compareStrictlyLessCases() { - less.msg = "" - equal1 := less - equal1.less = equal1.greater - if !yield(equal1) { - return - } + if tc.isZero { + // Zero should fail both PositiveT and NegativeT + t.Run("zero should fail both", func(t *testing.T) { + t.Run("PositiveT should fail", testSignT(PositiveT[V], positive, false)) + t.Run("NegativeT should fail", testSignT(NegativeT[V], positive, false)) + }) - equal2 := less - equal2.greater = equal2.less - if !yield(equal2) { - return - } + return } - } -} -type compareTestCase struct { - e any - msg string + // Test positive and negative values + t.Run("with positive/negative values", func(t *testing.T) { + t.Run("PositiveT", func(t *testing.T) { + t.Run("should pass (positive value)", testSignT(PositiveT[V], positive, true)) + t.Run("should fail (negative value)", testSignT(PositiveT[V], negative, false)) + }) + t.Run("NegativeT", func(t *testing.T) { + t.Run("should pass (negative value)", testSignT(NegativeT[V], negative, true)) + t.Run("should fail (positive value)", testSignT(NegativeT[V], positive, false)) + }) + }) + } } -type comparePositiveCase = compareTestCase +// testSignT is a helper that tests a generic sign function. +func testSignT[V SignedNumeric](sign func(T, V, ...any) bool, e V, shouldPass bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() -type compareNegativeCase = compareTestCase + mock := new(mockT) + result := sign(mock, e) -func comparePositiveCases() iter.Seq[comparePositiveCase] { - const genMsg = `"-1" is not positive` + if shouldPass { + True(t, result) + False(t, mock.Failed()) - return slices.Values([]comparePositiveCase{ - {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`}, - }) -} + return + } -func compareNegativeCases() iter.Seq[compareNegativeCase] { - const genMsg = `"1" is not negative` - - return slices.Values([]compareNegativeCase{ - {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`}, - }) + False(t, result) + True(t, mock.Failed()) + } } diff --git a/internal/assertions/equal.go b/internal/assertions/equal.go index 554bbb9ca..17a40fc44 100644 --- a/internal/assertions/equal.go +++ b/internal/assertions/equal.go @@ -32,103 +32,105 @@ func Equal(t T, expected, actual any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } + if err := validateEqualArgs(expected, actual); err != nil { return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)", expected, actual, err), msgAndArgs...) } if !ObjectsAreEqual(expected, actual) { - diff := diff(expected, actual) - expectedStr, actualStr := formatUnequalValues(expected, actual) + return failWithDiff(t, expected, actual, msgAndArgs...) + } - if colors.Enabled() { - expectedStr = colors.ExpectedColorizer()(expectedStr) - actualStr = colors.ActualColorizer()(actualStr) - } + return true +} - return Fail(t, - fmt.Sprintf("Not equal: \n"+ - "expected: %s\n"+ - "actual : %s%s", - expectedStr, - actualStr, diff), - msgAndArgs..., - ) +// EqualT asserts that two objects of the same comparable type are equal. +// +// Pointer variable equality is determined based on the equality of the memory addresses (unlike [Equal], but like [Same]). +// +// Functions, slices and maps are not comparable. See also [ComparisonOperators]. +// +// If you need to compare values of non-comparable types, or compare pointers by the value they point to, +// use [Equal] instead. +// +// # Usage +// +// assertions.EqualT(t, 123, 123) +// +// # Examples +// +// success: 123, 123 +// failure: 123, 456 +// +// [ComparisonOperators]: https://go.dev/ref/spec#Comparison_operators. +func EqualT[V comparable](t T, expected, actual V, msgAndArgs ...any) bool { + // Domain: equality + if expected != actual { + return failWithDiff(t, expected, actual, msgAndArgs...) } return true } -// Same asserts that two pointers reference the same object. -// -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. +// NotEqual asserts that the specified values are NOT equal. // // # Usage // -// assertions.Same(t, ptr1, ptr2) +// assertions.NotEqual(t, obj1, obj2) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +// +// Function equality cannot be determined and will always fail. // // # Examples // -// success: &staticVar, staticVarPtr -// failure: &staticVar, ptr("static string") -func Same(t T, expected, actual any, msgAndArgs ...any) bool { +// success: 123, 456 +// failure: 123, 123 +func NotEqual(t T, expected, actual any, msgAndArgs ...any) bool { // Domain: equality if h, ok := t.(H); ok { h.Helper() } - - same, ok := samePointers(expected, actual) - if !ok { - return Fail(t, "Both arguments must be pointers", msgAndArgs...) + if err := validateEqualArgs(expected, actual); err != nil { + return Fail(t, fmt.Sprintf("Invalid operation: %#v != %#v (%s)", + expected, actual, err), msgAndArgs...) } - if !same { - // both are pointers but not the same type & pointing to the same address - return Fail(t, fmt.Sprintf("Not same: \n"+ - "expected: %[2]s (%[1]T)(%[1]p)\n"+ - "actual : %[4]s (%[3]T)(%[3]p)", expected, truncatingFormat("%#v", expected), actual, truncatingFormat("%#v", actual)), msgAndArgs...) + if ObjectsAreEqual(expected, actual) { + return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) } return true } -// NotSame asserts that two pointers do not reference the same object. +// NotEqualT asserts that the specified values of the same comparable type are NOT equal. // -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. +// See [EqualT]. // // # Usage // -// assertions.NotSame(t, ptr1, ptr2) +// assertions.NotEqualT(t, obj1, obj2) // // # Examples // -// success: &staticVar, ptr("static string") -// failure: &staticVar, staticVarPtr -func NotSame(t T, expected, actual any, msgAndArgs ...any) bool { +// success: 123, 456 +// failure: 123, 123 +func NotEqualT[V comparable](t T, expected, actual V, msgAndArgs ...any) bool { // Domain: equality - if h, ok := t.(H); ok { - h.Helper() - } - - same, ok := samePointers(expected, actual) - if !ok { - // fails when the arguments are not pointers - return !(Fail(t, "Both arguments must be pointers", msgAndArgs...)) + if expected == actual { + return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) } - if same { - return Fail(t, fmt.Sprintf( - "Expected and actual point to the same object: %p %s", - expected, truncatingFormat("%#v", expected)), msgAndArgs...) - } return true } // EqualValues asserts that two objects are equal or convertible to the larger // type and equal. // +// Function equality cannot be determined and will always fail. +// // # Usage // // assertions.EqualValues(t, uint32(123), int32(123)) @@ -143,6 +145,11 @@ func EqualValues(t T, expected, actual any, msgAndArgs ...any) bool { h.Helper() } + if err := validateEqualArgs(expected, actual); err != nil { + return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)", + expected, actual, err), msgAndArgs...) + } + if !ObjectsAreEqualValues(expected, actual) { diff := diff(expected, actual) expected, actual = formatUnequalValues(expected, actual) @@ -154,9 +161,42 @@ func EqualValues(t T, expected, actual any, msgAndArgs ...any) bool { return true } +// NotEqualValues asserts that two objects are not equal even when converted to the same type. +// +// Function equality cannot be determined and will always fail. +// +// # Usage +// +// assertions.NotEqualValues(t, obj1, obj2) +// +// # Examples +// +// success: uint32(123), int32(456) +// failure: uint32(123), int32(123) +func NotEqualValues(t T, expected, actual any, msgAndArgs ...any) bool { + // Domain: equality + if h, ok := t.(H); ok { + h.Helper() + } + + if err := validateEqualArgs(expected, actual); err != nil { + return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)", + expected, actual, err), msgAndArgs...) + } + + if ObjectsAreEqualValues(expected, actual) { + return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) + } + + return true +} + // 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 -// that could potentially differ. +// fields are also equal. +// +// This is useful for comparing structs that have private fields that could potentially differ. +// +// Function equality cannot be determined and will always fail. // // # Usage // @@ -177,6 +217,11 @@ func EqualExportedValues(t T, expected, actual any, msgAndArgs ...any) bool { h.Helper() } + if err := validateEqualArgs(expected, actual); err != nil { + return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)", + expected, actual, err), msgAndArgs...) + } + aType := reflect.TypeOf(expected) bType := reflect.TypeOf(actual) @@ -224,180 +269,31 @@ func Exactly(t T, expected, actual any, msgAndArgs ...any) bool { return Equal(t, expected, actual, msgAndArgs...) } -// NotNil asserts that the specified object is not nil. -// -// # Usage -// -// assertions.NotNil(t, err) -// -// # Examples -// -// success: "not nil" -// failure: nil -func NotNil(t T, object any, msgAndArgs ...any) bool { - // Domain: equality - if !isNil(object) { - return true - } - if h, ok := t.(H); ok { - h.Helper() - } - return Fail(t, "Expected value not to be nil.", msgAndArgs...) -} - -// Nil asserts that the specified object is nil. -// -// # Usage -// -// assertions.Nil(t, err) -// -// # Examples -// -// success: nil -// failure: "not nil" -func Nil(t T, object any, msgAndArgs ...any) bool { - // Domain: equality - if isNil(object) { - return true - } - if h, ok := t.(H); ok { - h.Helper() - } - return Fail(t, "Expected nil, but got: "+truncatingFormat("%#v", object), msgAndArgs...) -} - -// Empty asserts that the given value is "empty". -// -// Zero values are "empty". -// -// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). -// -// Slices, maps and channels with zero length are "empty". -// -// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". -// -// # Usage -// -// assertions.Empty(t, obj) -// -// # Examples -// -// success: "" -// failure: "not empty" -// -// [Zero values]: https://go.dev/ref/spec#The_zero_value -func Empty(t T, object any, msgAndArgs ...any) bool { - // Domain: equality - pass := isEmpty(object) - if !pass { - if h, ok := t.(H); ok { - h.Helper() - } - Fail(t, "Should be empty, but was "+truncatingFormat("%v", object), msgAndArgs...) - } - - return pass -} - -// NotEmpty asserts that the specified object is NOT [Empty]. -// -// # Usage -// -// if assert.NotEmpty(t, obj) { -// assertions.Equal(t, "two", obj[1]) -// } -// -// # Examples -// -// success: "not empty" -// failure: "" -func NotEmpty(t T, object any, msgAndArgs ...any) bool { - // Domain: equality - pass := !isEmpty(object) - if !pass { - if h, ok := t.(H); ok { - h.Helper() - } - Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...) - } - - return pass -} - -// NotEqual asserts that the specified values are NOT equal. -// -// # Usage -// -// assertions.NotEqual(t, obj1, obj2) -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). -// -// # Examples -// -// success: 123, 456 -// failure: 123, 123 -func NotEqual(t T, expected, actual any, msgAndArgs ...any) bool { - // Domain: equality - if h, ok := t.(H); ok { - h.Helper() - } - if err := validateEqualArgs(expected, actual); err != nil { - return Fail(t, fmt.Sprintf("Invalid operation: %#v != %#v (%s)", - expected, actual, err), msgAndArgs...) - } - - if ObjectsAreEqual(expected, actual) { - return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) - } - - return true -} - -// NotEqualValues asserts that two objects are not equal even when converted to the same type. -// -// # Usage -// -// assertions.NotEqualValues(t, obj1, obj2) -// -// # Examples -// -// success: uint32(123), int32(456) -// failure: uint32(123), int32(123) -func NotEqualValues(t T, expected, actual any, msgAndArgs ...any) bool { - // Domain: equality +func failWithDiff(t T, expected, actual any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } - if ObjectsAreEqualValues(expected, actual) { - return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) - } + diff := diff(expected, actual) + expectedStr, actualStr := formatUnequalValues(expected, actual) - return true -} - -// isNil checks if a specified object is nil or not, without Failing. -func isNil(object any) bool { - if object == nil { - return true + if colors.Enabled() { + expectedStr = colors.ExpectedColorizer()(expectedStr) + actualStr = colors.ActualColorizer()(actualStr) } - value := reflect.ValueOf(object) - switch value.Kind() { - case - reflect.Chan, reflect.Func, - reflect.Interface, reflect.Map, - reflect.Ptr, reflect.Slice, reflect.UnsafePointer: - - return value.IsNil() - default: - return false - } + return Fail(t, + fmt.Sprintf("Not equal: \n"+ + "expected: %s\n"+ + "actual : %s%s", + expectedStr, + actualStr, diff), + msgAndArgs..., + ) } // validateEqualArgs checks whether provided arguments can be safely used in the -// Equal/NotEqual functions. +// Equal/NotEqual/EqualValues/NotEqualValues functions. func validateEqualArgs(expected, actual any) error { if expected == nil && actual == nil { return nil @@ -409,7 +305,7 @@ func validateEqualArgs(expected, actual any) error { return nil } -// samePointers checks if two generic interface objects are pointers of the same +// samePointers checks if two arbitrary interface objects are pointers of the same // type pointing to the same object. // // It returns two values: same indicating if they are the same type and point to the same object, @@ -448,35 +344,6 @@ func formatUnequalValues(expected, actual any) (e string, a string) { } } -// isEmpty gets whether the specified object is considered empty or not. -func isEmpty(object any) bool { - // get nil case out of the way - if object == nil { - return true - } - - return isEmptyValue(reflect.ValueOf(object)) -} - -// isEmptyValue gets whether the specified reflect.Value is considered empty or not. -func isEmptyValue(objValue reflect.Value) bool { - if objValue.IsZero() { - return true - } - // Special cases of non-zero values that we consider empty - switch objValue.Kind() { - // collection types are empty when they have no element - // Note: array types are empty when they match their zero-initialized state. - case reflect.Chan, reflect.Map, reflect.Slice: - return objValue.Len() == 0 - // non-nil pointers are empty if the value they point to is empty - case reflect.Ptr: - return isEmptyValue(objValue.Elem()) - default: - return false - } -} - // copyExportedFields iterates downward through nested data structures and creates a copy // that only contains the exported struct fields. func copyExportedFields(expected any) any { @@ -520,8 +387,9 @@ func copyExportedFields(expected any) any { } for i := range expectedValue.Len() { index := expectedValue.Index(i) - if isNil(index) { - continue + if !index.CanInterface() { + // this should not be possible with current reflect, since values are retrieved from an array or slice, not a struct + panic(fmt.Errorf("internal error: can't resolve Interface() for value %v", index)) } unexportedRemoved := copyExportedFields(index.Interface()) result.Index(i).Set(reflect.ValueOf(unexportedRemoved)) @@ -532,6 +400,10 @@ func copyExportedFields(expected any) any { result := reflect.MakeMap(expectedType) for _, k := range expectedValue.MapKeys() { index := expectedValue.MapIndex(k) + if !index.CanInterface() { + // this should not be possible with current reflect, since values are retrieved from a map, not a struct + panic(fmt.Errorf("internal error: can't resolve Interface() for value %v", index)) + } unexportedRemoved := copyExportedFields(index.Interface()) result.SetMapIndex(k, reflect.ValueOf(unexportedRemoved)) } diff --git a/internal/assertions/equal_impl_test.go b/internal/assertions/equal_impl_test.go index 263155a36..0a40b5c80 100644 --- a/internal/assertions/equal_impl_test.go +++ b/internal/assertions/equal_impl_test.go @@ -12,6 +12,8 @@ import ( "time" ) +const shortpkg = "assertions" + func TestEqualUnexportedImplementationDetails(t *testing.T) { t.Parallel() @@ -228,3 +230,63 @@ func formatUnequalCases() iter.Seq[formatUnequalCase] { {uint64(123), uint64(124), `123`, `124`, "uint64 should print clean"}, }) } + +type samePointersCase struct { + name string + args args + same BoolAssertionFunc + ok BoolAssertionFunc +} + +type args struct { + first any + second any +} + +func equalSamePointersCases() iter.Seq[samePointersCase] { + p := ptr(2) + return slices.Values([]samePointersCase{ + { + name: "1 != 2", + args: args{first: 1, second: 2}, + same: False, + ok: False, + }, + { + name: "1 != 1 (not same ptr)", + args: args{first: 1, second: 1}, + same: False, + ok: False, + }, + { + name: "ptr(1) == ptr(1)", + args: args{first: p, second: p}, + same: True, + ok: True, + }, + { + name: "int(1) != float32(1)", + args: args{first: int(1), second: float32(1)}, + same: False, + ok: False, + }, + { + name: "array != slice", + args: args{first: [2]int{1, 2}, second: []int{1, 2}}, + same: False, + ok: False, + }, + { + name: "non-pointer vs pointer (1 != ptr(2))", + args: args{first: 1, second: p}, + same: False, + ok: False, + }, + { + name: "pointer vs non-pointer (ptr(2) != 1)", + args: args{first: p, second: 1}, + same: False, + ok: False, + }, + }) +} diff --git a/internal/assertions/equal_pointer.go b/internal/assertions/equal_pointer.go new file mode 100644 index 000000000..c492c4200 --- /dev/null +++ b/internal/assertions/equal_pointer.go @@ -0,0 +1,126 @@ +package assertions + +import "fmt" + +// Same asserts that two pointers reference the same object. +// +// Both arguments must be pointer variables. +// +// Pointer variable sameness is determined based on the equality of both type and value. +// +// Unlike [Equal] pointers, [Same] pointers point to the same memory address. +// +// # Usage +// +// assertions.Same(t, ptr1, ptr2) +// +// # Examples +// +// success: &staticVar, staticVarPtr +// failure: &staticVar, ptr("static string") +func Same(t T, expected, actual any, msgAndArgs ...any) bool { + // Domain: equality + if h, ok := t.(H); ok { + h.Helper() + } + + same, ok := samePointers(expected, actual) + if !ok { + return Fail(t, "Both arguments must be pointers", msgAndArgs...) + } + + if !same { + // both are pointers but not the same type & pointing to the same address + return Fail(t, fmt.Sprintf("Not same: \n"+ + "expected: %[2]s (%[1]T)(%[1]p)\n"+ + "actual : %[4]s (%[3]T)(%[3]p)", expected, truncatingFormat("%#v", expected), actual, truncatingFormat("%#v", actual)), msgAndArgs...) + } + + return true +} + +// SameT asserts that two pointers of the same type reference the same object. +// +// See [Same]. +// +// # Usage +// +// assertions.SameT(t, ptr1, ptr2) +// +// # Examples +// +// success: &staticVar, staticVarPtr +// failure: &staticVar, ptr("static string") +func SameT[P any](t T, expected, actual *P, msgAndArgs ...any) bool { + // Domain: equality + if h, ok := t.(H); ok { + h.Helper() + } + + if expected != actual { + return Fail(t, fmt.Sprintf("Not same: \n"+ + "expected: %[2]s (%[1]T)(%[1]p)\n"+ + "actual : %[4]s (%[3]T)(%[3]p)", expected, truncatingFormat("%#v", expected), actual, truncatingFormat("%#v", actual)), msgAndArgs...) + } + + return true +} + +// NotSame asserts that two pointers do not reference the same object. +// +// See [Same]. +// +// # Usage +// +// assertions.NotSame(t, ptr1, ptr2) +// +// # Examples +// +// success: &staticVar, ptr("static string") +// failure: &staticVar, staticVarPtr +func NotSame(t T, expected, actual any, msgAndArgs ...any) bool { + // Domain: equality + if h, ok := t.(H); ok { + h.Helper() + } + + same, ok := samePointers(expected, actual) + if !ok { + // fails when the arguments are not pointers + return Fail(t, "Both arguments must be pointers", msgAndArgs...) + } + + if same { + return Fail(t, fmt.Sprintf( + "Expected and actual point to the same object: %p %s", + expected, truncatingFormat("%#v", expected)), msgAndArgs...) + } + return true +} + +// NotSameT asserts that two pointers do not reference the same object. +// +// See [SameT]. +// +// # Usage +// +// assertions.NotSameT(t, ptr1, ptr2) +// +// # Examples +// +// success: &staticVar, ptr("static string") +// failure: &staticVar, staticVarPtr +func NotSameT[P any](t T, expected, actual *P, msgAndArgs ...any) bool { + // Domain: equality + if h, ok := t.(H); ok { + h.Helper() + } + + if expected == actual { + return Fail(t, fmt.Sprintf( + "Expected and actual point to the same object: %p %s", + expected, truncatingFormat("%#v", expected)), msgAndArgs...) + } + + return true +} diff --git a/internal/assertions/equal_pointer_test.go b/internal/assertions/equal_pointer_test.go new file mode 100644 index 000000000..3d3cc6a82 --- /dev/null +++ b/internal/assertions/equal_pointer_test.go @@ -0,0 +1,180 @@ +package assertions + +import ( + "fmt" + "iter" + "slices" + "testing" +) + +// Pointer identity tests (Same, SameT, NotSame, NotSameT). +func TestEqualPointers(t *testing.T) { + t.Parallel() + + for tc := range unifiedPointerPairCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + expected, actual := tc.makeValues() + + if !tc.reflectionOnly { + t.Run("with Same", testPointerAssertion(tc, sameKind, Same, expected, actual)) + t.Run("with NotSame", testPointerAssertion(tc, notSameKind, NotSame, expected, actual)) + + // Generic variants - type dispatch + t.Run("with SameT", testPointerAssertionT(tc, sameTKind, expected, actual)) + t.Run("with NotSameT", testPointerAssertionT(tc, notSameTKind, expected, actual)) + + return + } + + // Reflection-only cases (non-pointer args, different types, one nil) + t.Run("with Same (reflection)", testPointerAssertion(tc, sameKind, Same, expected, actual)) + t.Run("with NotSame (reflection)", testPointerAssertion(tc, notSameKind, NotSame, expected, actual)) + }) + } +} + +type pointerPairTestCase struct { + name string + makeValues func() (expected, actual any) + relationship pairRelationship + reflectionOnly bool +} + +func unifiedPointerPairCases() iter.Seq[pointerPairTestCase] { + const hello = "hello" + + return slices.Values([]pointerPairTestCase{ + // Both nil + {"both-nil/ptr", func() (any, any) { return (*int)(nil), (*int)(nil) }, bothNil, false}, + {"both-nil/different-types", func() (any, any) { return (*int)(nil), (*string)(nil) }, differentTypes, true}, + + // One nil + {"one-nil/first", func() (any, any) { v := 42; return (*int)(nil), &v }, oneNil, true}, + {"one-nil/second", func() (any, any) { v := 42; return &v, (*int)(nil) }, oneNil, true}, + + // Same identity - both point to same address + {"same-identity/int", func() (any, any) { v := 42; return &v, &v }, sameIdentity, false}, + {"same-identity/string", func() (any, any) { s := hello; return &s, &s }, sameIdentity, false}, + {"same-identity/float64", func() (any, any) { f := 3.14; return &f, &f }, sameIdentity, false}, + + // Different identity - point to different adresses + {"different-identity/equal-values", func() (any, any) { v1, v2 := 42, 42; return &v1, &v2 }, differentIdentity, false}, + {"different-identity/different-values", func() (any, any) { v1, v2 := 42, 43; return &v1, &v2 }, differentIdentity, false}, + + // Different types (reflection-only) + {"different-types/int-string", func() (any, any) { + i, s := 1, hello + return &i, &s + }, differentTypes, true}, + + // Edge cases (always false) + {"not-pointer/right", func() (any, any) { v1 := 12; return &v1, 1 }, notPointer, true}, + {"not-pointer/left", func() (any, any) { v1 := 12; return 1, &v1 }, notPointer, true}, + {"not-pointer/both", func() (any, any) { return 1, 1 }, notPointer, true}, + }) +} + +func testPointerAssertion( + tc pointerPairTestCase, + kind pointerAssertionKind, + pointerAssertion func(T, any, any, ...any) bool, + expected, actual any, +) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := pointerAssertion(mock, expected, actual) + shouldPass := expectedStatusForPointerAssertion(kind, tc.relationship) + shouldPassOrFail(t, mock, result, shouldPass) + } +} + +type pointerAssertionKind int + +const ( + sameKind pointerAssertionKind = iota + sameTKind + notSameKind + notSameTKind +) + +type pairRelationship int + +const ( + bothNil pairRelationship = iota + oneNil + sameIdentity + differentIdentity + differentTypes + notPointer +) + +func expectedStatusForPointerAssertion(kind pointerAssertionKind, relationship pairRelationship) bool { + positive := kind == sameKind || kind == sameTKind + + switch relationship { + case notPointer: + return false + case sameIdentity, bothNil: + // Two nil pointers of the same type are considered "same" in Go + return positive + case oneNil, differentIdentity, differentTypes: + return !positive + default: + panic(fmt.Errorf("test case configuration error: invalid pairRelationship: %d", relationship)) + } +} + +func testPointerAssertionT(tc pointerPairTestCase, kind pointerAssertionKind, expected, actual any) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + stop := func(expected string, actual any) { + t.Fatalf("test case error: expected=%s, actual=%T", expected, actual) + } + + // Type switch with safety check + // + // Add more supported types to the switch with new test cases + var result bool + switch exp := expected.(type) { + case *int: + act, ok := actual.(*int) + if !ok { + stop("*int", actual) + } + result = testPointerGenericAssertion(mock, kind, exp, act) + case *string: + act, ok := actual.(*string) + if !ok { + stop("*string", actual) + } + result = testPointerGenericAssertion(mock, kind, exp, act) + case *float64: + act, ok := actual.(*float64) + if !ok { + stop("*float64", actual) + } + result = testPointerGenericAssertion(mock, kind, exp, act) + default: + t.Fatalf("unsupported type: %T", expected) + } + + shouldPass := expectedStatusForPointerAssertion(kind, tc.relationship) + shouldPassOrFail(t, mock, result, shouldPass) + } +} + +func testPointerGenericAssertion[P any](mock T, kind pointerAssertionKind, expected, actual *P) bool { + switch kind { + case sameTKind: + return SameT(mock, expected, actual) + case notSameTKind: + return NotSameT(mock, expected, actual) + default: + panic(fmt.Errorf("test case configuration error: invalid pointerAssertionKind: %d", kind)) + } +} diff --git a/internal/assertions/equal_test.go b/internal/assertions/equal_test.go index d5a6ebae9..abba4b8dc 100644 --- a/internal/assertions/equal_test.go +++ b/internal/assertions/equal_test.go @@ -4,859 +4,925 @@ package assertions import ( - "errors" "fmt" "iter" - "os" - "reflect" "regexp" "slices" + "strings" "testing" - "time" ) -const shortpkg = "assertions" - -func TestEqualNotNil(t *testing.T) { - t.Parallel() - mock := new(testing.T) - - if !NotNil(mock, new(AssertionTesterConformingObject)) { - t.Error("NotNil should return true: object is not nil") - } - - if NotNil(mock, nil) { - t.Error("NotNil should return false: object is nil") - } - - if NotNil(mock, (*struct{})(nil)) { - t.Error("NotNil should return false: object is (*struct{})(nil)") - } -} - -func TestEqualNil(t *testing.T) { - t.Parallel() - mock := new(testing.T) - - if !Nil(mock, nil) { - t.Error("Nil should return true: object is nil") - } - - if !Nil(mock, (*struct{})(nil)) { - t.Error("Nil should return true: object is (*struct{})(nil)") - } - - if Nil(mock, new(AssertionTesterConformingObject)) { - t.Error("Nil should return false: object is not nil") - } -} - -func TestEqualSameWithSliceTooLongToPrint(t *testing.T) { - t.Parallel() - mock := new(mockT) - - longSlice := make([]int, 1_000_000) - Same(mock, &[]int{}, &longSlice) - Contains(t, mock.errorString(), `&[]int{0, 0, 0,`) -} - -func TestEqualNotSameWithSliceTooLongToPrint(t *testing.T) { - t.Parallel() - mock := new(mockT) - - longSlice := make([]int, 1_000_000) - NotSame(mock, &longSlice, &longSlice) - Contains(t, mock.errorString(), `&[]int{0, 0, 0,`) -} - -func TestEqualNotEqualWithSliceTooLongToPrint(t *testing.T) { - t.Parallel() - mock := new(mockT) - - longSlice := make([]int, 1_000_000) - NotEqual(mock, longSlice, longSlice) - Contains(t, mock.errorString(), ` - Error Trace: - Error: Should not be: []int{0, 0, 0,`) - Contains(t, mock.errorString(), `<... truncated>`) -} - -func TestEqualNotEqualValuesWithSliceTooLongToPrint(t *testing.T) { - t.Parallel() - mock := new(mockT) - - longSlice := make([]int, 1_000_000) - NotEqualValues(mock, longSlice, longSlice) - Contains(t, mock.errorString(), ` - Error Trace: - Error: Should not be: []int{0, 0, 0,`) - Contains(t, mock.errorString(), `<... truncated>`) -} - -func TestEqual(t *testing.T) { - t.Parallel() - - for c := range equalCases() { - t.Run(fmt.Sprintf("Equal(%#v, %#v)", c.expected, c.actual), func(t *testing.T) { - t.Parallel() - mock := new(testing.T) - - res := Equal(mock, c.expected, c.actual) - if res != c.result { - t.Errorf("Equal(%#v, %#v) should return %#v: %s", c.expected, c.actual, c.result, c.remark) - } - }) - } -} - -func TestEqualSame(t *testing.T) { +func TestEqualErrorMessages(t *testing.T) { t.Parallel() - mock := new(mockT) - - if Same(mock, ptr(1), ptr(1)) { - t.Error("Same should return false") - } - if Same(mock, 1, 1) { - t.Error("Same should return false") - } - - p := ptr(2) - if Same(mock, p, *p) { - t.Error("Same should return false") - } - - if !Same(mock, p, p) { - t.Error("Same should return true") - } - - t.Run("same object, different type", func(t *testing.T) { - type s struct { - i int - } - type sPtr *s - ps := &s{1} - dps := sPtr(ps) - if Same(mock, dps, ps) { - t.Error("Same should return false") + t.Run("should render when slice too long to print", testTooLongToPrint()) + t.Run("error message should match expression", func(t *testing.T) { + // checking error messsages on Equal with a regexp. The object of the test is Equal, not Regexp + for tc := range stringEqualFormattingCases() { + t.Run(tc.name, func(t *testing.T) { + mock := &bufferT{} + + isEqual := Equal(mock, tc.equalWant, tc.equalGot, tc.msgAndArgs...) + if isEqual { + t.Errorf("expected %q to be different than %q", tc.equalGot, tc.equalWant) + + return + } + + rex := regexp.MustCompile(tc.want) + match := rex.MatchString(mock.buf.String()) + if !match { + t.Errorf("expected message to match %q, but got:\n%s", tc.want, mock.buf.String()) + } + }) } - expPat := fmt.Sprintf(`expected: &%[1]s.s\{i:1\} \(%[1]s.sPtr\)\((0x[a-f0-9]+)\)\s*\n`, shortpkg) + - fmt.Sprintf(`\s+actual : &%[1]s.s\{i:1\} \(\*%[1]s.s\)\((0x[a-f0-9]+)\)`, shortpkg) - Regexp(t, regexp.MustCompile(expPat), mock.errorString()) }) } -func TestEqualNotSame(t *testing.T) { - t.Parallel() - mock := new(testing.T) - - if !NotSame(mock, ptr(1), ptr(1)) { - t.Error("NotSame should return true; different pointers") - } - - if !NotSame(mock, 1, 1) { - t.Error("NotSame should return true; constant inputs") - } - - p := ptr(2) - if !NotSame(mock, p, *p) { - t.Error("NotSame should return true; mixed-type inputs") - } - - if NotSame(mock, p, p) { - t.Error("NotSame should return false") - } -} - -func TestEqualNotEqual(t *testing.T) { - t.Parallel() - - for c := range equalNotEqualCases() { - t.Run(fmt.Sprintf("NotEqual(%#v, %#v)", c.expected, c.actual), func(t *testing.T) { - t.Parallel() - mock := new(testing.T) - - res := NotEqual(mock, c.expected, c.actual) - - if res != c.result { - t.Errorf("NotEqual(%#v, %#v) should return %#v", c.expected, c.actual, c.result) - } - }) - } -} - +// Test NotEqualValues. func TestEqualValuesAndNotEqualValues(t *testing.T) { t.Parallel() - for c := range equalValuesCases() { + for tc := range equalValuesCases() { mock := new(testing.T) - // Test NotEqualValues - t.Run(fmt.Sprintf("NotEqualValues(%#v, %#v)", c.expected, c.actual), func(t *testing.T) { - res := NotEqualValues(mock, c.expected, c.actual) - - if res != c.notEqualResult { - t.Errorf("NotEqualValues(%#v, %#v) should return %#v", c.expected, c.actual, c.notEqualResult) + t.Run(tc.name, func(t *testing.T) { + res := NotEqualValues(mock, tc.expected, tc.actual) + if res != tc.notEqualValue { + t.Errorf("NotEqualValues(%#v, %#v) should return %t", tc.expected, tc.actual, tc.notEqualValue) } }) // Test EqualValues (inverse of NotEqualValues) - t.Run(fmt.Sprintf("EqualValues(%#v, %#v)", c.expected, c.actual), func(t *testing.T) { - expectedEqualResult := !c.notEqualResult // EqualValues should return opposite of NotEqualValues - res := EqualValues(mock, c.expected, c.actual) - - if res != expectedEqualResult { - t.Errorf("EqualValues(%#v, %#v) should return %#v", c.expected, c.actual, expectedEqualResult) + t.Run(tc.name, func(t *testing.T) { + res := EqualValues(mock, tc.expected, tc.actual) + if res != tc.equalValue { + t.Errorf("EqualValues(%#v, %#v) should return %t", tc.expected, tc.actual, tc.equalValue) } }) } } -func TestEqualEmpty(t *testing.T) { - t.Parallel() - - // Proposal for enhancement: redundant test context declaration - chWithValue := make(chan struct{}, 1) - chWithValue <- struct{}{} - var tiP *time.Time - var tiNP time.Time - var s *string - var f *os.File - sP := &s - x := 1 - xP := &x - - type TString string - type TStruct struct { - x int - } - - t.Run("should be empty", func(t *testing.T) { - mock := new(testing.T) - - True(t, Empty(mock, ""), "Empty string is empty") - True(t, Empty(mock, nil), "Nil is empty") - True(t, Empty(mock, []string{}), "Empty string array is empty") - True(t, Empty(mock, 0), "Zero int value is empty") - True(t, Empty(mock, false), "False value is empty") - True(t, Empty(mock, make(chan struct{})), "Channel without values is empty") - True(t, Empty(mock, s), "Nil string pointer is empty") - True(t, Empty(mock, f), "Nil os.File pointer is empty") - True(t, Empty(mock, tiP), "Nil time.Time pointer is empty") - True(t, Empty(mock, tiNP), "time.Time is empty") - True(t, Empty(mock, TStruct{}), "struct with zero values is empty") - True(t, Empty(mock, TString("")), "empty aliased string is empty") - True(t, Empty(mock, sP), "ptr to nil value is empty") - True(t, Empty(mock, [1]int{}), "array is state") - }) - - t.Run("should not be empty", func(t *testing.T) { - mock := new(testing.T) - - False(t, Empty(mock, "something"), "Non Empty string is not empty") - False(t, Empty(mock, errors.New("something")), "Non nil object is not empty") - False(t, Empty(mock, []string{"something"}), "Non empty string array is not empty") - False(t, Empty(mock, 1), "Non-zero int value is not empty") - False(t, Empty(mock, true), "True value is not empty") - False(t, Empty(mock, chWithValue), "Channel with values is not empty") - False(t, Empty(mock, TStruct{x: 1}), "struct with initialized values is empty") - False(t, Empty(mock, TString("abc")), "non-empty aliased string is empty") - False(t, Empty(mock, xP), "ptr to non-nil value is not empty") - False(t, Empty(mock, [1]int{42}), "array is not state") - }) - - // error messages validation - for tt := range equalEmptyCases() { - t.Run(tt.name, func(t *testing.T) { - mock := new(captureT) - - res := Empty(mock, tt.value) - mock.checkResultAndErrMsg(t, res, tt.expectedResult, tt.expectedErrMsg) - }) - } -} - -func TestEqualNotEmpty(t *testing.T) { +// Test EqualExportedValues. +func TestEqualExportedValues(t *testing.T) { t.Parallel() - t.Run("should not be empty", func(t *testing.T) { - mock := new(testing.T) - - False(t, NotEmpty(mock, ""), "Empty string is empty") - False(t, NotEmpty(mock, nil), "Nil is empty") - False(t, NotEmpty(mock, []string{}), "Empty string array is empty") - False(t, NotEmpty(mock, 0), "Zero int value is empty") - False(t, NotEmpty(mock, false), "False value is empty") - False(t, NotEmpty(mock, make(chan struct{})), "Channel without values is empty") - False(t, NotEmpty(mock, [1]int{}), "array is state") - }) + for tc := range objectEqualExportedValuesCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() - t.Run("should be empty", func(t *testing.T) { - mock := new(testing.T) + mockT := new(mockT) - chWithValue := make(chan struct{}, 1) - chWithValue <- struct{}{} - - False(t, NotEmpty(mock, ""), "Empty string is empty") - True(t, NotEmpty(mock, "something"), "Non Empty string is not empty") - True(t, NotEmpty(mock, errors.New("something")), "Non nil object is not empty") - True(t, NotEmpty(mock, []string{"something"}), "Non empty string array is not empty") - True(t, NotEmpty(mock, 1), "Non-zero int value is not empty") - True(t, NotEmpty(mock, true), "True value is not empty") - True(t, NotEmpty(mock, chWithValue), "Channel with values is not empty") - True(t, NotEmpty(mock, [1]int{42}), "array is not state") - }) + actual := EqualExportedValues(mockT, tc.expected, tc.actual) + if actual != tc.expectedEqual { + t.Errorf("Expected EqualExportedValues to be %t, but was %t", tc.expectedEqual, actual) + } - // error messages validation - for tt := range equalNotEmptyCases() { - t.Run(tt.name, func(t *testing.T) { - mock := new(captureT) + if tc.expectedFailMsg == "" { + // skip error message check + return + } - res := NotEmpty(mock, tt.value) - mock.checkResultAndErrMsg(t, tt.expectedResult, res, tt.expectedErrMsg) + actualFail := mockT.errorString() + if !strings.Contains(actualFail, tc.expectedFailMsg) { + t.Errorf("Contains failure should include %q but was %q", tc.expectedFailMsg, actualFail) + } }) } } -func TestEqualExactly(t *testing.T) { +// Deep equality tests (Equal, EqualT, NotEqual, NotEqualT, Exactly). +func TestEqualDeepEqual(t *testing.T) { t.Parallel() - for c := range equalExactlyCases() { - t.Run(fmt.Sprintf("Exactly(%#v, %#v)", c.expected, c.actual), func(t *testing.T) { + for tc := range unifiedEqualityCases() { + t.Run(tc.name, func(t *testing.T) { t.Parallel() - mock := new(testing.T) - - res := Exactly(mock, c.expected, c.actual) - if res != c.result { - t.Errorf("Exactly(%#v, %#v) should return %#v", c.expected, c.actual, c.result) + expected, actual := tc.makeValues() + + if !tc.reflectionOnly { + t.Run("with Equal", testEqualityAssertion(tc, equalKind, Equal, expected, actual)) + t.Run("with NotEqual", testEqualityAssertion(tc, notEqualKind, NotEqual, expected, actual)) + t.Run("with Exactly", testEqualityAssertion(tc, exactlyKind, Exactly, expected, actual)) + + // Generic variants - type dispatch + t.Run("with EqualT", testEqualityAssertionT(tc, equalTKind, expected, actual)) + t.Run("with NotEqualT", testEqualityAssertionT(tc, notEqualTKind, expected, actual)) + } else { + // Reflection-only cases + t.Run("with Equal (reflection)", testEqualityAssertion(tc, equalKind, Equal, expected, actual)) + t.Run("with NotEqual (reflection)", testEqualityAssertion(tc, notEqualKind, NotEqual, expected, actual)) + t.Run("with Exactly (reflection)", testEqualityAssertion(tc, exactlyKind, Exactly, expected, actual)) } }) } } -func TestEqualBytes(t *testing.T) { - t.Parallel() +type equalityRelationship int - i := 0 - for c := range equalBytesCases() { - Equal(t, reflect.DeepEqual(c.a, c.b), ObjectsAreEqual(c.a, c.b), "case %d failed", i) - i++ - } -} +const ( + eqBothNil equalityRelationship = iota + eqOneNil + eqSameIdentity + eqEqualValueComparable + eqEqualValueNonComparable + eqDifferentValueSameType + eqDifferentType + eqFunction +) -func TestEqualValuePanics(t *testing.T) { - t.Parallel() +func testEqualityAssertion(tc equalityTestCase, kind equalityAssertionKind, equalityAssertion func(T, any, any, ...any) bool, expected, actual any) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() - for tt := range panicCases() { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() + mock := new(mockT) + result := equalityAssertion(mock, expected, actual) + shouldPass := expectedStatusForEqualityAssertion(kind, tc.relationship) + shouldPassOrFail(t, mock, result, shouldPass) + } +} - mock := new(mockT) - NotPanics(t, func() { - Equal(mock, tt.value1, tt.value2) - }, "should not panic") +//nolint:gocognit,gocyclo,cyclop // no other way here than a big type switch +func testEqualityAssertionT(tc equalityTestCase, kind equalityAssertionKind, expected, actual any) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() - if !tt.expectEqual { - True(t, mock.Failed(), "should have failed") - Contains(t, mock.errorString(), "Not equal:", "error message should mention inequality") + mock := new(mockT) + stop := func(expected, actual any) { + t.Fatalf("test case error: expected=%s, actual=%T", expected, actual) + } - return + // Type switch with safety check + // + // Add more (comparable) types when new test cases require it. + var result bool + switch exp := expected.(type) { + case int: + act, ok := actual.(int) + if !ok { + stop("int", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case int8: + act, ok := actual.(int8) + if !ok { + stop("int8", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case int16: + act, ok := actual.(int16) + if !ok { + stop("int16", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case int32: + act, ok := actual.(int32) + if !ok { + stop("int32", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case int64: + act, ok := actual.(int64) + if !ok { + stop("int32", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case uint: + act, ok := actual.(uint) + if !ok { + stop("uint", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case uint8: + act, ok := actual.(uint8) + if !ok { + stop("uint8", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case uint16: + act, ok := actual.(uint16) + if !ok { + stop("uint16", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case uint32: + act, ok := actual.(uint32) + if !ok { + stop("uint32", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case uint64: + act, ok := actual.(uint64) + if !ok { + stop("uint64", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case string: + act, ok := actual.(string) + if !ok { + stop("string", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case bool: + act, ok := actual.(bool) + if !ok { + stop("bool", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case float32: + act, ok := actual.(float32) + if !ok { + stop("float32", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case float64: + act, ok := actual.(float64) + if !ok { + stop("float64", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case *int: + act, ok := actual.(*int) + if !ok { + stop("*int", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case *string: + act, ok := actual.(*string) + if !ok { + stop("*string", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case myType: + act, ok := actual.(myType) + if !ok { + stop("myType", actual) } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case struct{}: + act, ok := actual.(struct{}) + if !ok { + stop("struct{}", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + case *struct{}: + act, ok := actual.(*struct{}) + if !ok { + stop("*struct{}", actual) + } + result = testEqualityGenericAssertion(mock, kind, exp, act) + default: + t.Fatalf("unsupported type: %T", expected) + } - False(t, mock.Failed(), "should have been successful") - Empty(t, mock.errorString()) - }) + shouldPass := expectedStatusForEqualityAssertion(kind, tc.relationship) + shouldPassOrFail(t, mock, result, shouldPass) } } -type panicCase struct { - name string - value1 any - value2 any - expectEqual bool +type equalityTestCase struct { + name string + makeValues func() (expected, actual any) + relationship equalityRelationship + reflectionOnly bool } -func panicCases() iter.Seq[panicCase] { - type structWithUnexportedMapWithArrayKey struct { +type ( + structWithUnexportedMapWithArrayKey struct { m any } - type s struct { + s struct { f map[[1]byte]int } - return slices.Values([]panicCase{ - { - // from issue https://github.com/stretchr/testify/pull/1816 - name: "panic behavior on struct with array key and unexported field (some keys vs none)", - value1: structWithUnexportedMapWithArrayKey{ - map[[1]byte]*struct{}{ - {1}: nil, - {2}: nil, - }, - }, - value2: structWithUnexportedMapWithArrayKey{ - map[[1]byte]*struct{}{}, + myType string + myMap map[string]any +) + +func unifiedEqualityCases() iter.Seq[equalityTestCase] { + const hello = "hello" + s1 := struct{}{} + p1 := &s1 + p2 := &s1 + f1 := func() bool { return true } + + return slices.Values([]equalityTestCase{ + // Both nil + {"both-nil/ptr", func() (any, any) { return (*int)(nil), (*int)(nil) }, eqBothNil, false}, + {"both-nil/interface", func() (any, any) { return (any)(nil), (any)(nil) }, eqBothNil, true}, + + // One nil (reflection only - type mismatch) + {"one-nil/first", func() (any, any) { v := 42; return nil, &v }, eqOneNil, true}, + {"one-nil/second", func() (any, any) { v := 42; return &v, nil }, eqOneNil, true}, + {"one-nil/bytes", func() (any, any) { return nil, make([]byte, 0) }, eqOneNil, true}, + {"one-nil/struct", func() (any, any) { return nil, new(AssertionTesterConformingObject) }, eqOneNil, true}, + + // Same identity (pointers to same object) + {"same-identity/int-ptr", func() (any, any) { v := 42; return &v, &v }, eqSameIdentity, false}, + {"same-identity/string-ptr", func() (any, any) { s := hello; return &s, &s }, eqSameIdentity, false}, + { + "same-identity/pointer-to-struct", func() (any, any) { + return p1, p2 }, - expectEqual: false, + eqSameIdentity, false, }, + + // Equal value, comparable (core types - start with 5) + {"equal-comparable/int", func() (any, any) { return 42, 42 }, eqEqualValueComparable, false}, + {"equal-comparable/string", func() (any, any) { return hello, hello }, eqEqualValueComparable, false}, + {"equal-comparable/bool-true", func() (any, any) { return true, true }, eqEqualValueComparable, false}, + {"equal-comparable/bool-false", func() (any, any) { return false, false }, eqEqualValueComparable, false}, + {"equal-comparable/float64", func() (any, any) { return 3.14, 3.14 }, eqEqualValueComparable, false}, + {"equal-comparable/float32", func() (any, any) { return float32(3.14), float32(3.14) }, eqEqualValueComparable, false}, + {"equal-comparable/int8", func() (any, any) { return int8(10), int8(10) }, eqEqualValueComparable, false}, + {"equal-comparable/int16", func() (any, any) { return int16(100), int16(100) }, eqEqualValueComparable, false}, + {"equal-comparable/int32", func() (any, any) { return int32(1000), int32(1000) }, eqEqualValueComparable, false}, + {"equal-comparable/int64", func() (any, any) { return int64(10000), int64(10000) }, eqEqualValueComparable, false}, + {"equal-comparable/uint", func() (any, any) { return uint(42), uint(42) }, eqEqualValueComparable, false}, + {"equal-comparable/uint8", func() (any, any) { return uint8(10), uint8(10) }, eqEqualValueComparable, false}, + {"equal-comparable/uint16", func() (any, any) { return uint16(100), uint16(100) }, eqEqualValueComparable, false}, + {"equal-comparable/uint32", func() (any, any) { return uint32(1000), uint32(1000) }, eqEqualValueComparable, false}, + {"equal-comparable/uint64", func() (any, any) { return uint64(10000), uint64(10000) }, eqEqualValueComparable, false}, + {"equal-comparable/~string", func() (any, any) { return myType("1"), myType("1") }, eqEqualValueComparable, false}, { - name: "panic behavior on struct with array key and unexported field (same keys)", - value1: structWithUnexportedMapWithArrayKey{ - map[[1]byte]*struct{}{ - {1}: nil, - {2}: nil, - }, - }, - value2: structWithUnexportedMapWithArrayKey{ - map[[1]byte]*struct{}{ - {2}: nil, - {1}: nil, - }, + "equal-comparable/anonymous-struct", func() (any, any) { + return struct{}{}, struct{}{} + }, eqEqualValueComparable, false, + }, + { + "equal-comparable/pointer-to-anonymous-struct", func() (any, any) { + return &struct{}{}, &struct{}{} // this a special case in go, as the pointer to this empty type is not allocated: values are equal + }, eqEqualValueComparable, false, + }, + + // Equal value, non-comparable (reflection only) + {"equal-non-comparable/slice", func() (any, any) { return []int{1, 2, 3}, []int{1, 2, 3} }, eqEqualValueNonComparable, true}, + {"equal-non-comparable/struct-ptr", func() (any, any) { return &struct{}{}, &struct{}{} }, eqEqualValueNonComparable, true}, + {"equal-non-comparable/bytes", func() (any, any) { return []byte(hello), []byte(hello) }, eqEqualValueNonComparable, true}, + {"equal-non-comparable/map", func() (any, any) { return myMap{"bar": 1}, myMap{"bar": 1} }, eqEqualValueNonComparable, true}, + { + "equal-non-comparable/bytes-zero-same-len", func() (any, any) { + return make([]byte, 2), make([]byte, 2) + }, eqEqualValueNonComparable, true, + }, + { + "equal-non-comparable/bytes-zero-same-len-diff-cap", func() (any, any) { + return make([]byte, 2), make([]byte, 2, 3) + }, eqEqualValueNonComparable, true, + }, + { + "equal-non-comparable/bytes-zero-same-len-diff-cap", func() (any, any) { + return new(AssertionTesterConformingObject), new(AssertionTesterConformingObject) + }, eqEqualValueNonComparable, true, + }, + { + "equal-non-comparable/map-unexported-struct", func() (any, any) { + return structWithUnexportedMapWithArrayKey{map[[1]byte]*struct{}{{1}: nil, {2}: nil}}, + structWithUnexportedMapWithArrayKey{map[[1]byte]*struct{}{{2}: nil, {1}: nil}} }, - expectEqual: true, + eqEqualValueNonComparable, true, }, { - name: "panic behavior on struct with array key and unexported field (non-nil values)", - value1: structWithUnexportedMapWithArrayKey{ - map[[1]byte]*struct{}{ - {1}: {}, - {2}: nil, - }, + "equal-non-comparable/map-unexported-struct-non-nil", func() (any, any) { + return structWithUnexportedMapWithArrayKey{map[[1]byte]*struct{}{{1}: {}, {2}: nil}}, + structWithUnexportedMapWithArrayKey{map[[1]byte]*struct{}{{1}: {}, {2}: nil}} }, - value2: structWithUnexportedMapWithArrayKey{ - map[[1]byte]*struct{}{ - {1}: {}, - {2}: nil, - }, + eqEqualValueNonComparable, true, + }, + + // Different value, same type + {"diff-value/int", func() (any, any) { return 42, 43 }, eqDifferentValueSameType, false}, + {"diff-value/string", func() (any, any) { return hello, "world" }, eqDifferentValueSameType, false}, + {"diff-value/bool", func() (any, any) { return true, false }, eqDifferentValueSameType, false}, + {"diff-value/float64", func() (any, any) { return 3.14, 2.71 }, eqDifferentValueSameType, false}, + {"diff-value/float32", func() (any, any) { return float32(3.15), float32(3.14) }, eqDifferentValueSameType, false}, + {"diff-value/int8", func() (any, any) { return int8(10), int8(11) }, eqDifferentValueSameType, false}, + {"diff-value/int16", func() (any, any) { return int16(110), int16(100) }, eqDifferentValueSameType, false}, + {"diff-value/int32", func() (any, any) { return int32(1003), int32(1000) }, eqDifferentValueSameType, false}, + {"diff-value/int64", func() (any, any) { return int64(10400), int64(10000) }, eqDifferentValueSameType, false}, + {"diff-value/uint", func() (any, any) { return uint(43), uint(42) }, eqDifferentValueSameType, false}, + {"diff-value/uint8", func() (any, any) { return uint8(10), uint8(11) }, eqDifferentValueSameType, false}, + {"diff-value/uint16", func() (any, any) { return uint16(101), uint16(100) }, eqDifferentValueSameType, false}, + {"diff-value/uint32", func() (any, any) { return uint32(1040), uint32(1000) }, eqDifferentValueSameType, false}, + {"diff-value/uint64", func() (any, any) { return uint64(10000), uint64(14000) }, eqDifferentValueSameType, false}, + {"diff-value/~string", func() (any, any) { return myType("1"), myType("2") }, eqDifferentValueSameType, false}, + {"diff-value/slice", func() (any, any) { return []int{1, 2}, []int{1, 3} }, eqDifferentValueSameType, true}, + {"diff-value/map", func() (any, any) { return myMap{"bar": 1}, myMap{"bar": 2} }, eqDifferentValueSameType, true}, + { + "diff-value/map-unexported-struct", func() (any, any) { + return structWithUnexportedMapWithArrayKey{map[[1]byte]*struct{}{{1}: nil, {2}: nil}}, + structWithUnexportedMapWithArrayKey{map[[1]byte]*struct{}{}} }, - expectEqual: true, + eqDifferentValueSameType, true, }, { - name: "panic behavior on struct with array key and unexported field (different, non-nil values)", - value1: structWithUnexportedMapWithArrayKey{ - map[[1]byte]*struct{}{ - {1}: {}, - {2}: nil, - }, + "diff-value/map-unexported-struct-non-nil", func() (any, any) { + return structWithUnexportedMapWithArrayKey{map[[1]byte]*struct{}{{1}: {}, {2}: nil}}, + structWithUnexportedMapWithArrayKey{map[[1]byte]*struct{}{{1}: nil, {2}: {}}} }, - value2: structWithUnexportedMapWithArrayKey{ - map[[1]byte]*struct{}{ - {1}: nil, - {2}: {}, - }, - }, - expectEqual: false, + eqDifferentValueSameType, true, }, { - name: "panic behavior on map with array key", - value1: s{ - f: map[[1]byte]int{ - {0x1}: 0, - {0x2}: 0, - }, + "diff-value/func", func() (any, any) { + return func() int { return 23 }, func() int { return 24 } }, - value2: s{}, - expectEqual: false, + eqFunction, true, }, + { + "same-value/func", func() (any, any) { + return f1, f1 + }, + eqFunction, true, + }, + + // Different type (reflection only - can't use with generics) + {"diff-type/int-uint", func() (any, any) { return 42, uint(42) }, eqDifferentType, true}, + {"diff-type/int-int64", func() (any, any) { return 42, int64(42) }, eqDifferentType, true}, + {"diff-type/int-float64", func() (any, any) { return 10, 10.0 }, eqDifferentType, true}, + {"diff-type/float32-float64", func() (any, any) { return float32(10), float64(10) }, eqDifferentType, true}, + {"diff-value/edge-case-map", func() (any, any) { // this case used to panic + return s{ + f: map[[1]byte]int{ + {0x1}: 0, + {0x2}: 0, + }, + }, + s{} + }, eqDifferentValueSameType, true}, }) } -type equalCase struct { - expected any - actual any - result bool - remark string -} +type equalityAssertionKind int -func equalCases() iter.Seq[equalCase] { - type myType string - var m map[string]any - return slices.Values([]equalCase{ - {"Hello World", "Hello World", true, ""}, - {123, 123, true, ""}, - {123.5, 123.5, true, ""}, - {[]byte("Hello World"), []byte("Hello World"), true, ""}, - {nil, nil, true, ""}, - {int32(123), int32(123), true, ""}, - {uint64(123), uint64(123), true, ""}, - {myType("1"), myType("1"), true, ""}, - {&struct{}{}, &struct{}{}, true, "pointer equality is based on equality of underlying value"}, - - // Not expected to be equal - {m["bar"], "something", false, ""}, - {myType("1"), myType("2"), false, ""}, - - // A case that might be confusing, especially with numeric literals - {10, uint(10), false, ""}, - {int(1), uint(1), false, ""}, - }) -} +const ( + equalKind equalityAssertionKind = iota + equalTKind + notEqualKind + notEqualTKind + exactlyKind +) -type samePointersCase struct { - name string - args args - same BoolAssertionFunc - ok BoolAssertionFunc +func expectedStatusForEqualityAssertion(kind equalityAssertionKind, relationship equalityRelationship) bool { + positive := kind == equalKind || kind == equalTKind || kind == exactlyKind + + switch relationship { + case eqFunction: + // A special validation is carried out to reject function types with an error + return false + case eqBothNil, eqSameIdentity, eqEqualValueComparable, eqEqualValueNonComparable: + return positive + case eqOneNil, eqDifferentValueSameType: + return !positive + case eqDifferentType: + // Exactly requires exact type match (fails on different types) + // Equal uses reflection (can handle different types) + // EqualT requires same type (won't compile with different types) + if kind == exactlyKind { + return false + } + // Equal might handle some type coercion, but generally fails + return !positive + default: + panic(fmt.Errorf("test case configuration error: invalid equalityRelationship: %d", relationship)) + } } -type args struct { - first any - second any +func testEqualityGenericAssertion[V comparable](mock T, kind equalityAssertionKind, expected, actual V) bool { + switch kind { + case equalTKind: + return EqualT(mock, expected, actual) + case notEqualTKind: + return NotEqualT(mock, expected, actual) + default: + panic(fmt.Errorf("test case configuration error: invalid equalityAssertionKind for generic: %d", kind)) + } } -func ptr(i int) *int { - return &i -} +func testTooLongToPrint() func(*testing.T) { + const ( + expected = `&[]int{0, 0, 0,` + message = ` + Error Trace: + Error: Should not be: []int{0, 0, 0,` + trailer = `<... truncated>` + ) -func equalSamePointersCases() iter.Seq[samePointersCase] { - p := ptr(2) - return slices.Values([]samePointersCase{ - { - name: "1 != 2", - args: args{first: 1, second: 2}, - same: False, - ok: False, - }, - { - name: "1 != 1 (not same ptr)", - args: args{first: 1, second: 1}, - same: False, - ok: False, - }, - { - name: "ptr(1) == ptr(1)", - args: args{first: p, second: p}, - same: True, - ok: True, - }, - { - name: "int(1) != float32(1)", - args: args{first: int(1), second: float32(1)}, - same: False, - ok: False, - }, - { - name: "array != slice", - args: args{first: [2]int{1, 2}, second: []int{1, 2}}, - same: False, - ok: False, - }, - { - name: "non-pointer vs pointer (1 != ptr(2))", - args: args{first: 1, second: p}, - same: False, - ok: False, - }, - { - name: "pointer vs non-pointer (ptr(2) != 1)", - args: args{first: p, second: 1}, - same: False, - ok: False, - }, - }) -} + return func(t *testing.T) { + t.Run("with Same", func(t *testing.T) { + t.Parallel() -type equalNotEqualCase struct { - expected any - actual any - result bool -} + mock := new(mockT) -func equalNotEqualCases() iter.Seq[equalNotEqualCase] { - return slices.Values([]equalNotEqualCase{ - // cases that are expected not to match - {"Hello World", "Hello World!", true}, - {123, 1234, true}, - {123.5, 123.55, true}, - {[]byte("Hello World"), []byte("Hello World!"), true}, - {nil, new(AssertionTesterConformingObject), true}, + longSlice := make([]int, 1_000_000) + result := Same(mock, &[]int{}, &longSlice) + if result { + t.Errorf("expected Same to fail") + return + } - // cases that are expected to match - {nil, nil, false}, - {"Hello World", "Hello World", false}, - {123, 123, false}, - {123.5, 123.5, false}, - {[]byte("Hello World"), []byte("Hello World"), false}, - {new(AssertionTesterConformingObject), new(AssertionTesterConformingObject), false}, - {&struct{}{}, &struct{}{}, false}, - {func() int { return 23 }, func() int { return 24 }, false}, - // A case that might be confusing, especially with numeric literals - {int(10), uint(10), true}, + if !strings.Contains(mock.errorString(), expected) { + t.Errorf("expected message to contain %q but got: %q", expected, mock.errorString()) + } + }) + + t.Run("with NotSame", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + + longSlice := make([]int, 1_000_000) + result := NotSame(mock, &longSlice, &longSlice) + if result { + t.Errorf("expected NotSame to fail") + return + } + + if !strings.Contains(mock.errorString(), expected) { + t.Errorf("expected message to contain %q but got: %q", expected, mock.errorString()) + } + }) + + t.Run("with NotEqual", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + + longSlice := make([]int, 1_000_000) + result := NotEqual(mock, longSlice, longSlice) + if result { + t.Errorf("expected NotEqual to fail") + return + } + + if !strings.Contains(mock.errorString(), message) { + t.Errorf("expected message to contain %q but got: %q", message, mock.errorString()) + } + + if !strings.Contains(mock.errorString(), trailer) { + t.Errorf("expected message to contain %q but got: %q", trailer, mock.errorString()) + } + }) + + t.Run("with NotEqualValues", func(t *testing.T) { + t.Parallel() + mock := new(mockT) + + longSlice := make([]int, 1_000_000) + result := NotEqualValues(mock, longSlice, longSlice) + if result { + t.Errorf("expected NotEqualValues to fail") + return + } + + if !strings.Contains(mock.errorString(), message) { + t.Errorf("expected message to contain %q but got: %q", message, mock.errorString()) + } + const trailer = `<... truncated>` + if !strings.Contains(mock.errorString(), trailer) { + t.Errorf("expected message to contain %q but got: %q", trailer, mock.errorString()) + } + }) + } +} + +type equalStringCase struct { + name string + equalWant string + equalGot string + msgAndArgs []any + want string +} + +func stringEqualFormattingCases() iter.Seq[equalStringCase] { + return slices.Values([]equalStringCase{ + { + name: "multiline diff message", + equalWant: "hi, \nmy name is", + equalGot: "what,\nmy name is", + want: "\t[a-z]+.go:\\d+: \n" + + "\t+Error Trace:\t\n+" + + "\t+Error:\\s+Not equal:\\s+\n" + + "\\s+expected: \"hi, \\\\nmy name is\"\n" + + "\\s+actual\\s+: " + "\"what,\\\\nmy name is\"\n" + + "\\s+Diff:\n" + + "\\s+-+ Expected\n\\s+\\++ " + + "Actual\n" + + "\\s+@@ -1,2 \\+1,2 @@\n" + + "\\s+-hi, \n\\s+\\+what,\n" + + "\\s+my name is", + }, + { + name: "single line diff message", + equalWant: "want", + equalGot: "got", + want: "\t[a-z]+.go:\\d+: \n" + + "\t+Error Trace:\t\n" + + "\t+Error:\\s+Not equal:\\s+\n" + + "\\s+expected: \"want\"\n" + + "\\s+actual\\s+: \"got\"\n" + + "\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ " + + "Actual\n" + + "\\s+@@ -1 \\+1 @@\n" + + "\\s+-want\n" + + "\\s+\\+got\n", + }, + { + name: "diff message with args", + equalWant: "want", + equalGot: "got", + msgAndArgs: []any{"hello, %v!", "world"}, + want: "\t[a-z]+.go:[0-9]+: \n" + + "\t+Error Trace:\t\n" + + "\t+Error:\\s+Not equal:\\s+\n" + + "\\s+expected: \"want\"\n" + + "\\s+actual\\s+: \"got\"\n" + + "\\s+Diff:\n" + + "\\s+-+ Expected\n" + + "\\s+\\++ Actual\n" + + "\\s+@@ -1 \\+1 @@\n" + + "\\s+-want\n" + + "\\s+\\+got\n" + + "\\s+Messages:\\s+hello, world!\n", + }, + { + name: "diff message with integer arg", + equalWant: "want", + equalGot: "got", + msgAndArgs: []any{123}, + want: "\t[a-z]+.go:[0-9]+: \n" + + "\t+Error Trace:\t\n" + + "\t+Error:\\s+Not equal:\\s+\n" + + "\\s+expected: \"want\"\n" + + "\\s+actual\\s+: \"got\"\n" + + "\\s+Diff:\n" + + "\\s+-+ Expected\n" + + "\\s+\\++ Actual\n" + + "\\s+@@ -1 \\+1 @@\n" + + "\\s+-want\n" + + "\\s+\\+got\n" + + "\\s+Messages:\\s+123\n", + }, + { + name: "diff message with struct arg", + equalWant: "want", + equalGot: "got", + msgAndArgs: []any{struct{ a string }{"hello"}}, + want: "\t[a-z]+.go:[0-9]+: \n" + + "\t+Error Trace:\t\n" + + "\t+Error:\\s+Not equal:\\s+\n" + + "\\s+expected: \"want\"\n" + + "\\s+actual\\s+: \"got\"\n" + + "\\s+Diff:\n" + + "\\s+-+ Expected\n" + + "\\s+\\++ Actual\n" + + "\\s+@@ -1 \\+1 @@\n" + + "\\s+-want\n" + + "\\s+\\+got\n" + + "\\s+Messages:\\s+{a:hello}\n", + }, }) } type equalValuesCase struct { - expected any - actual any - notEqualResult bool // result for NotEqualValues + name string + expected any + actual any + equalValue bool + notEqualValue bool // notEqualValue = !equalValue, except for invalid types (e.g. functions) } func equalValuesCases() iter.Seq[equalValuesCase] { return slices.Values([]equalValuesCase{ // cases that are expected not to match - {"Hello World", "Hello World!", true}, - {123, 1234, true}, - {123.5, 123.55, true}, - {[]byte("Hello World"), []byte("Hello World!"), true}, - {nil, new(AssertionTesterConformingObject), true}, + {"not-equal/string", "Hello World", "Hello World!", false, true}, + {"not-equal/int", 123, 1234, false, true}, + {"not-equal/float64", 123.5, 123.55, false, true}, + {"not-equal/[]byte", []byte("Hello World"), []byte("Hello World!"), false, true}, + {"not-equal/nil-not-nil", nil, new(AssertionTesterConformingObject), false, true}, + {"not-equal/converted", uint(10), int(11), false, true}, // cases that are expected to match - {nil, nil, false}, - {"Hello World", "Hello World", false}, - {123, 123, false}, - {123.5, 123.5, false}, - {[]byte("Hello World"), []byte("Hello World"), false}, - {new(AssertionTesterConformingObject), new(AssertionTesterConformingObject), false}, - {&struct{}{}, &struct{}{}, false}, - - // Different behavior from NotEqual() - {func() int { return 23 }, func() int { return 24 }, true}, - {int(10), int(11), true}, - {int(10), uint(10), false}, - - {struct{}{}, struct{}{}, false}, + {"equal/nil-nil", nil, nil, true, false}, + {"equal/string", "Hello World", "Hello World", true, false}, + {"equal/int", 123, 123, true, false}, + {"equal/float64", 123.5, 123.5, true, false}, + {"equal/[]byte", []byte("Hello World"), []byte("Hello World"), true, false}, + {"equal/pointer-to-struct", new(AssertionTesterConformingObject), new(AssertionTesterConformingObject), true, false}, + {"equal/pointer-to-anonymous-struct", &struct{}{}, &struct{}{}, true, false}, + {"equal/converted", int(10), uint(10), true, false}, + {"equal/anonymous-struct", struct{}{}, struct{}{}, true, false}, + + // always fail + {"always-fail/func", func() int { return 23 }, func() int { return 24 }, false, false}, }) } -type equalEmptyCase struct { - name string - value any - expectedResult bool - expectedErrMsg string +type objectEqualExportedValuesCase struct { + name string + expected any + actual any + expectedEqual bool + expectedFailMsg string } -func equalEmptyCases() iter.Seq[equalEmptyCase] { - chWithValue := make(chan struct{}, 1) - chWithValue <- struct{}{} - // var tiP *time.Time - // var tiNP time.Time - // var s *string - // var f *os.File - // sP := &s - x := 1 - xP := &x - - type TString string - type TStruct struct { - x int +func objectEqualExportedValuesCases() iter.Seq[objectEqualExportedValuesCase] { + type specialKey struct { + a string } - return slices.Values([]equalEmptyCase{ - { - name: "Non Empty string is not empty", - value: "something", - expectedResult: false, - expectedErrMsg: "Should be empty, but was something\n", - }, - { - name: "Non nil object is not empty", - value: errors.New("something"), - expectedResult: false, - expectedErrMsg: "Should be empty, but was something\n", - }, - { - name: "Non empty string array is not empty", - value: []string{"something"}, - expectedResult: false, - expectedErrMsg: "Should be empty, but was [something]\n", - }, - { - name: "Non-zero int value is not empty", - value: 1, - expectedResult: false, - expectedErrMsg: "Should be empty, but was 1\n", - }, - { - name: "True value is not empty", - value: true, - expectedResult: false, - expectedErrMsg: "Should be empty, but was true\n", - }, - { - name: "Channel with values is not empty", - value: chWithValue, - expectedResult: false, - expectedErrMsg: fmt.Sprintf("Should be empty, but was %v\n", chWithValue), - }, - { - name: "struct with initialized values is empty", - value: TStruct{x: 1}, - expectedResult: false, - expectedErrMsg: "Should be empty, but was {1}\n", - }, - { - name: "non-empty aliased string is empty", - value: TString("abc"), - expectedResult: false, - expectedErrMsg: "Should be empty, but was abc\n", - }, - { - name: "ptr to non-nil value is not empty", - value: xP, - expectedResult: false, - expectedErrMsg: fmt.Sprintf("Should be empty, but was %p\n", xP), - }, - { - name: "array is not state", - value: [1]int{42}, - expectedResult: false, - expectedErrMsg: "Should be empty, but was [42]\n", - }, - - // Here are some edge cases - { - name: "string with only spaces is not empty", - value: " ", - expectedResult: false, - expectedErrMsg: "Should be empty, but was \n", // Proposal for enhancement: FIX THIS strange error message - }, - { - name: "string with a line feed is not empty", - value: "\n", - expectedResult: false, - // Proposal for enhancement: This is the exact same error message as for an empty string - expectedErrMsg: "Should be empty, but was \n", // Proposal for enhancement: FIX THIS strange error message - }, - { - name: "string with only tabulation and lines feed is not empty", - value: "\n\t\n", - expectedResult: false, - // Proposal for enhancement: The line feeds and tab are not helping to spot what is expected - expectedErrMsg: "" + // this syntax is used to show how errors are reported. - "Should be empty, but was \n" + - "\t\n", - }, - { - name: "string with trailing lines feed is not empty", - value: "foo\n\n", - expectedResult: false, - // Proposal for enhancement: it's not clear if one or two lines feed are expected - expectedErrMsg: "Should be empty, but was foo\n\n", - }, - { - name: "string with leading and trailing tabulation and lines feed is not empty", - value: "\n\nfoo\t\n\t\n", - expectedResult: false, - // Proposal for enhancement: The line feeds and tab are not helping to figure what is expected - expectedErrMsg: "" + - "Should be empty, but was \n" + - "\n" + - "foo\t\n" + - "\t\n", - }, - + return slices.Values([]objectEqualExportedValuesCase{ { - name: "non-printable character is not empty", - value: "\u00a0", // NO-BREAK SPACE UNICODE CHARACTER - expectedResult: false, - // Proposal for enhancement: here you cannot figure out what is expected - expectedErrMsg: "Should be empty, but was \u00a0\n", + name: "edge-case/func", + expected: func() {}, + actual: func() {}, + expectedEqual: false, }, - - // Here we are testing there is no error message on success { - name: "Empty string is empty", - value: "", - expectedResult: true, - expectedErrMsg: "", - }, - }) -} - -type equalNotEmptyCase struct { - name string - value any - expectedResult bool - expectedErrMsg string -} - -func equalNotEmptyCases() iter.Seq[equalNotEmptyCase] { - return slices.Values([]equalNotEmptyCase{ - { - name: "Empty string is empty", - value: "", - expectedResult: false, - expectedErrMsg: `Should NOT be empty, but was ` + "\n", // Proposal for enhancement: FIX THIS strange error message - }, - { - name: "Nil is empty", - value: nil, - expectedResult: false, - expectedErrMsg: "Should NOT be empty, but was \n", + name: "edge-case/expect-nil", + expected: nil, + actual: nil, + expectedEqual: true, }, { - name: "Empty string array is empty", - value: []string{}, - expectedResult: false, - expectedErrMsg: "Should NOT be empty, but was []\n", + name: "edge-case/expect-nil-actual-not-nil", + expected: nil, + actual: 1, + expectedEqual: false, }, { - name: "Zero int value is empty", - value: 0, - expectedResult: false, - expectedErrMsg: "Should NOT be empty, but was 0\n", + name: "edge-case/map-with-struct-key", + expected: map[specialKey]S{{a: "a"}: {}}, + actual: map[specialKey]S{{a: "a"}: {}}, + expectedEqual: true, }, { - name: "False value is empty", - value: false, - expectedResult: false, - expectedErrMsg: "Should NOT be empty, but was false\n", + name: "equal-values/map", + expected: map[string]S{ + "key": {1, Nested{2, 3}, 4, Nested{5, 6}}, + }, + actual: map[string]S{ + "key": {1, Nested{2, nil}, nil, Nested{}}, + }, + expectedEqual: true, }, { - name: "array is state", - value: [1]int{}, - expectedResult: false, - expectedErrMsg: "Should NOT be empty, but was [0]\n", + name: "diff-values/map", + expected: map[string]S{ + "key": {1, Nested{2, 3}, 4, Nested{5, 6}}, + }, + actual: map[string]S{ + "x": {1, Nested{2, nil}, nil, Nested{}}, + }, + expectedEqual: false, + }, + { + name: "equal-values/nested-struct", + expected: S{1, Nested{2, 3}, 4, Nested{5, 6}}, + actual: S{1, Nested{2, nil}, nil, Nested{}}, + expectedEqual: true, + }, + { + name: "diff-values/nested-struct(1)", + expected: S{1, Nested{2, 3}, 4, Nested{5, 6}}, + actual: S{1, Nested{1, nil}, nil, Nested{}}, + expectedEqual: false, + expectedFailMsg: fmt.Sprintf(` + Diff: + --- Expected + +++ Actual + @@ -3,3 +3,3 @@ + Exported2: (%s.Nested) { + - Exported: (int) 2, + + Exported: (int) 1, + notExported: (interface {}) `, + shortpkg), + }, + { + name: "diff-values/nested-struct(2)", + expected: S3{&Nested{1, 2}, &Nested{3, 4}}, + actual: S3{&Nested{"a", 2}, &Nested{3, 4}}, + expectedEqual: false, + expectedFailMsg: fmt.Sprintf(` + Diff: + --- Expected + +++ Actual + @@ -2,3 +2,3 @@ + Exported1: (*%s.Nested)({ + - Exported: (int) 1, + + Exported: (string) (len=1) "a", + notExported: (interface {}) `, + shortpkg), + }, + { + name: "diff-values/inner-slice", + expected: S4{[]*Nested{ + {1, 2}, + {3, 4}, + }}, + actual: S4{[]*Nested{ + {1, "a"}, + {2, "b"}, + }}, + expectedEqual: false, + expectedFailMsg: fmt.Sprintf(` + Diff: + --- Expected + +++ Actual + @@ -7,3 +7,3 @@ + (*%s.Nested)({ + - Exported: (int) 3, + + Exported: (int) 2, + notExported: (interface {}) `, + shortpkg), + }, + { + name: "equal-values/inner-array-unexported-diff", + expected: S{[2]int{1, 2}, Nested{2, 3}, 4, Nested{5, 6}}, + actual: S{[2]int{1, 2}, Nested{2, nil}, nil, Nested{}}, + expectedEqual: true, + }, + { + name: "equal-values/inner-array", + expected: &S{1, Nested{2, 3}, 4, Nested{5, 6}}, + actual: &S{1, Nested{2, nil}, nil, Nested{}}, + expectedEqual: true, + }, + { + name: "diff-values/inner-slice-exported-diff", + expected: &S{1, Nested{2, 3}, 4, Nested{5, 6}}, + actual: &S{1, Nested{1, nil}, nil, Nested{}}, + expectedEqual: false, + expectedFailMsg: fmt.Sprintf(` + Diff: + --- Expected + +++ Actual + @@ -3,3 +3,3 @@ + Exported2: (%s.Nested) { + - Exported: (int) 2, + + Exported: (int) 1, + notExported: (interface {}) `, + shortpkg), + }, + { + name: "equal-values/slice", + expected: []int{1, 2}, + actual: []int{1, 2}, + expectedEqual: true, + }, + { + name: "diff-values/slice", + expected: []int{1, 2}, + actual: []int{1, 3}, + expectedEqual: false, + expectedFailMsg: ` + Diff: + --- Expected + +++ Actual + @@ -2,3 +2,3 @@ + (int) 1, + - (int) 2 + + (int) 3 + }`, + }, + { + name: "equal-values/slice-of-pointers", + expected: []*int{ptr(1), nil, ptr(2)}, + actual: []*int{ptr(1), nil, ptr(2)}, + expectedEqual: true, + }, + { + name: "equal-values/slice-of-struct", + expected: []*Nested{ + {1, 2}, + {3, 4}, + }, + actual: []*Nested{ + {1, "a"}, + {3, "b"}, + }, + expectedEqual: true, }, - - // Here we are testing there is no error message on success { - name: "Non Empty string is not empty", - value: "something", - expectedResult: true, - expectedErrMsg: "", + name: "diff-values/slice-of-struct", + expected: []*Nested{ + {1, 2}, + {3, 4}, + }, + actual: []*Nested{ + {1, "a"}, + {2, "b"}, + }, + expectedEqual: false, + expectedFailMsg: fmt.Sprintf(` + Diff: + --- Expected + +++ Actual + @@ -6,3 +6,3 @@ + (*%s.Nested)({ + - Exported: (int) 3, + + Exported: (int) 2, + notExported: (interface {}) `, + shortpkg), }, }) } - -type diffTestingStruct struct { - A string - B int -} - -func (d *diffTestingStruct) String() string { - return d.A -} - -type equalExactlyCase struct { - expected any - actual any - result bool -} - -func equalExactlyCases() iter.Seq[equalExactlyCase] { - a := float32(1) - b := float64(1) - c := float32(1) - d := float32(2) - - return slices.Values([]equalExactlyCase{ - {a, b, false}, - {a, d, false}, - {a, c, true}, - {nil, a, false}, - {a, nil, false}, - }) -} - -type equalBytesCase struct { - a, b []byte -} - -func equalBytesCases() iter.Seq[equalBytesCase] { - return slices.Values([]equalBytesCase{ - {make([]byte, 2), make([]byte, 2)}, - {make([]byte, 2), make([]byte, 2, 3)}, - {nil, make([]byte, 0)}, - }) -} diff --git a/internal/assertions/equal_unary.go b/internal/assertions/equal_unary.go new file mode 100644 index 000000000..5d84122b3 --- /dev/null +++ b/internal/assertions/equal_unary.go @@ -0,0 +1,154 @@ +package assertions + +import ( + "fmt" + "reflect" +) + +// Nil asserts that the specified object is nil. +// +// # Usage +// +// assertions.Nil(t, err) +// +// # Examples +// +// success: nil +// failure: "not nil" +func Nil(t T, object any, msgAndArgs ...any) bool { + // Domain: equality + if isNil(object) { + return true + } + if h, ok := t.(H); ok { + h.Helper() + } + return Fail(t, "Expected nil, but got: "+truncatingFormat("%#v", object), msgAndArgs...) +} + +// NotNil asserts that the specified object is not nil. +// +// # Usage +// +// assertions.NotNil(t, err) +// +// # Examples +// +// success: "not nil" +// failure: nil +func NotNil(t T, object any, msgAndArgs ...any) bool { + // Domain: equality + if !isNil(object) { + return true + } + if h, ok := t.(H); ok { + h.Helper() + } + return Fail(t, "Expected value not to be nil.", msgAndArgs...) +} + +// Empty asserts that the given value is "empty". +// +// Zero values are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". +// +// # Usage +// +// assertions.Empty(t, obj) +// +// # Examples +// +// success: "" +// failure: "not empty" +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value +func Empty(t T, object any, msgAndArgs ...any) bool { + // Domain: equality + pass := isEmpty(object) + if !pass { + if h, ok := t.(H); ok { + h.Helper() + } + Fail(t, "Should be empty, but was "+truncatingFormat("%v", object), msgAndArgs...) + } + + return pass +} + +// NotEmpty asserts that the specified object is NOT [Empty]. +// +// # Usage +// +// if assert.NotEmpty(t, obj) { +// assertions.Equal(t, "two", obj[1]) +// } +// +// # Examples +// +// success: "not empty" +// failure: "" +func NotEmpty(t T, object any, msgAndArgs ...any) bool { + // Domain: equality + pass := !isEmpty(object) + if !pass { + if h, ok := t.(H); ok { + h.Helper() + } + Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...) + } + + return pass +} + +// isNil checks if a specified object is nil or not, without Failing. +func isNil(object any) bool { + if object == nil { + return true + } + + value := reflect.ValueOf(object) + switch value.Kind() { + case + reflect.Chan, reflect.Func, + reflect.Interface, reflect.Map, + reflect.Ptr, reflect.Slice, reflect.UnsafePointer: + + return value.IsNil() + default: + return false + } +} + +// isEmpty gets whether the specified object is considered empty or not. +func isEmpty(object any) bool { + // get nil case out of the way + if object == nil { + return true + } + + return isEmptyValue(reflect.ValueOf(object)) +} + +// isEmptyValue gets whether the specified reflect.Value is considered empty or not. +func isEmptyValue(objValue reflect.Value) bool { + if objValue.IsZero() { + return true + } + // Special cases of non-zero values that we consider empty + switch objValue.Kind() { + // collection types are empty when they have no element + // Note: array types are empty when they match their zero-initialized state. + case reflect.Chan, reflect.Map, reflect.Slice: + return objValue.Len() == 0 + // non-nil pointers are empty if the value they point to is empty + case reflect.Ptr: + return isEmptyValue(objValue.Elem()) + default: + return false + } +} diff --git a/internal/assertions/equal_unary_test.go b/internal/assertions/equal_unary_test.go new file mode 100644 index 000000000..7385dfc4b --- /dev/null +++ b/internal/assertions/equal_unary_test.go @@ -0,0 +1,281 @@ +package assertions + +import ( + "errors" + "fmt" + "iter" + "slices" + "testing" +) + +func TestEqualUnaryErrorMessages(t *testing.T) { + // error messages validation + for tc := range equalEmptyCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + mock := new(captureT) + + res := Empty(mock, tc.value) + mock.checkResultAndErrMsg(t, res, tc.expectedResult, tc.expectedErrMsg) + }) + } +} + +// Unary assertion tests (Nil, NotNil, Empty, NotEmpty). +func TestEqualUnaryAssertions(t *testing.T) { + t.Parallel() + + for tc := range unifiedUnaryCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + t.Run("with Nil", testUnaryAssertion(tc, nilKind, Nil)) + t.Run("with NotNil", testUnaryAssertion(tc, notNilKind, NotNil)) + t.Run("with Empty", testUnaryAssertion(tc, emptyKind, Empty)) + t.Run("with NotEmpty", testUnaryAssertion(tc, notEmptyKind, NotEmpty)) + }) + } +} + +type unaryTestCase struct { + name string + object any + category objectCategory +} + +func unifiedUnaryCases() iter.Seq[unaryTestCase] { + chWithValue := make(chan struct{}, 1) + chWithValue <- struct{}{} + x := 1 + xP := &x + z := 0 + zP := &z + + type TString string + type TStruct struct { + x int + } + + return slices.Values([]unaryTestCase{ + // Nil category + {"nil/nil-ptr", (*int)(nil), nilCategory}, + {"nil/nil-slice", []int(nil), nilCategory}, + {"nil/nil-interface", (any)(nil), nilCategory}, + {"nil/nil-struct-ptr", (*struct{})(nil), nilCategory}, + + // Empty non-nil category + {"empty/slice", []int{}, emptyNonNil}, + {"empty/string", "", emptyNonNil}, + {"empty/zero-int", 0, emptyNonNil}, + {"empty/zero-bool", false, emptyNonNil}, + {"empty/channel", make(chan struct{}), emptyNonNil}, + {"empty/zero-struct", TStruct{}, emptyNonNil}, + {"empty/aliased-string", TString(""), emptyNonNil}, + {"empty/zero-array", [1]int{}, emptyNonNil}, + {"empty/zero-ptr", zP, emptyNonNil}, + + // Non-empty comparable category + {"non-empty/int", 42, nonEmptyComparable}, + {"non-empty/string", "hello", nonEmptyComparable}, + {"non-empty/bool", true, nonEmptyComparable}, + {"non-empty/slice", []int{1}, nonEmptyComparable}, + {"non-empty/channel", chWithValue, nonEmptyComparable}, + {"non-empty/struct", TStruct{x: 1}, nonEmptyComparable}, + {"non-empty/aliased-string", TString("abc"), nonEmptyComparable}, + {"non-empty/ptr", xP, nonEmptyComparable}, + {"non-empty/array", [1]int{42}, nonEmptyComparable}, + + // Non-empty non-comparable category + {"non-empty/error", errors.New("something"), nonEmptyNonComparable}, + }) +} + +type unaryAssertionKind int + +const ( + nilKind unaryAssertionKind = iota + notNilKind + emptyKind + notEmptyKind +) + +type objectCategory int + +const ( + nilCategory objectCategory = iota + emptyNonNil + nonEmptyComparable + nonEmptyNonComparable +) + +// expectedStatusForUnaryAssertion returns the expected semantics for a given assertion (Nil, Empty, ...) +// and a given category of input. +func expectedStatusForUnaryAssertion(kind unaryAssertionKind, category objectCategory) bool { + switch kind { + case nilKind: + return category == nilCategory + case notNilKind: + return category != nilCategory + case emptyKind: + return category == nilCategory || category == emptyNonNil + case notEmptyKind: + return category == nonEmptyComparable || category == nonEmptyNonComparable + default: + panic(fmt.Errorf("test case configuration error: invalid unaryAssertionKind: %d", kind)) + } +} + +func testUnaryAssertion(tc unaryTestCase, kind unaryAssertionKind, unaryAssertion func(T, any, ...any) bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := unaryAssertion(mock, tc.object) + shouldPass := expectedStatusForUnaryAssertion(kind, tc.category) + shouldPassOrFail(t, mock, result, shouldPass) + } +} + +type equalEmptyCase struct { + name string + value any + expectedResult bool + expectedErrMsg string +} + +func equalEmptyCases() iter.Seq[equalEmptyCase] { + chWithValue := make(chan struct{}, 1) + chWithValue <- struct{}{} + // var tiP *time.Time + // var tiNP time.Time + // var s *string + // var f *os.File + // sP := &s + x := 1 + xP := &x + + type TString string + type TStruct struct { + x int + } + + return slices.Values([]equalEmptyCase{ + { + name: "Non Empty string is not empty", + value: "something", + expectedResult: false, + expectedErrMsg: "Should be empty, but was something\n", + }, + { + name: "Non nil object is not empty", + value: errors.New("something"), + expectedResult: false, + expectedErrMsg: "Should be empty, but was something\n", + }, + { + name: "Non empty string array is not empty", + value: []string{"something"}, + expectedResult: false, + expectedErrMsg: "Should be empty, but was [something]\n", + }, + { + name: "Non-zero int value is not empty", + value: 1, + expectedResult: false, + expectedErrMsg: "Should be empty, but was 1\n", + }, + { + name: "True value is not empty", + value: true, + expectedResult: false, + expectedErrMsg: "Should be empty, but was true\n", + }, + { + name: "Channel with values is not empty", + value: chWithValue, + expectedResult: false, + expectedErrMsg: fmt.Sprintf("Should be empty, but was %v\n", chWithValue), + }, + { + name: "struct with initialized values is empty", + value: TStruct{x: 1}, + expectedResult: false, + expectedErrMsg: "Should be empty, but was {1}\n", + }, + { + name: "non-empty aliased string is empty", + value: TString("abc"), + expectedResult: false, + expectedErrMsg: "Should be empty, but was abc\n", + }, + { + name: "ptr to non-nil value is not empty", + value: xP, + expectedResult: false, + expectedErrMsg: fmt.Sprintf("Should be empty, but was %p\n", xP), + }, + { + name: "array is not state", + value: [1]int{42}, + expectedResult: false, + expectedErrMsg: "Should be empty, but was [42]\n", + }, + + // Here are some edge cases + { + name: "string with only spaces is not empty", + value: " ", + expectedResult: false, + expectedErrMsg: "Should be empty, but was \n", // Proposal for enhancement: FIX THIS strange error message + }, + { + name: "string with a line feed is not empty", + value: "\n", + expectedResult: false, + // Proposal for enhancement: This is the exact same error message as for an empty string + expectedErrMsg: "Should be empty, but was \n", // Proposal for enhancement: FIX THIS strange error message + }, + { + name: "string with only tabulation and lines feed is not empty", + value: "\n\t\n", + expectedResult: false, + // Proposal for enhancement: The line feeds and tab are not helping to spot what is expected + expectedErrMsg: "" + // this syntax is used to show how errors are reported. + "Should be empty, but was \n" + + "\t\n", + }, + { + name: "string with trailing lines feed is not empty", + value: "foo\n\n", + expectedResult: false, + // Proposal for enhancement: it's not clear if one or two lines feed are expected + expectedErrMsg: "Should be empty, but was foo\n\n", + }, + { + name: "string with leading and trailing tabulation and lines feed is not empty", + value: "\n\nfoo\t\n\t\n", + expectedResult: false, + // Proposal for enhancement: The line feeds and tab are not helping to figure what is expected + expectedErrMsg: "" + + "Should be empty, but was \n" + + "\n" + + "foo\t\n" + + "\t\n", + }, + { + name: "non-printable character is not empty", + value: "\u00a0", // NO-BREAK SPACE UNICODE CHARACTER + expectedResult: false, + // Proposal for enhancement: here you cannot figure out what is expected + expectedErrMsg: "Should be empty, but was \u00a0\n", + }, + // Here we are testing there is no error message on success + { + name: "Empty string is empty", + value: "", + expectedResult: true, + expectedErrMsg: "", + }, + }) +} diff --git a/internal/assertions/file.go b/internal/assertions/file.go index 086b06e72..4efe7ad08 100644 --- a/internal/assertions/file.go +++ b/internal/assertions/file.go @@ -38,18 +38,18 @@ func FileExists(t T, path string, msgAndArgs ...any) bool { return true } -// NoFileExists checks whether a file does not exist in a given path. It fails +// FileNotExists checks whether a file does not exist in a given path. It fails // if the path points to an existing _file_ only. // // # Usage // -// assertions.NoFileExists(t, "path/to/file") +// assertions.FileNotExists(t, "path/to/file") // // # Examples // // success: filepath.Join(testDataPath(),"non_existing_file") // failure: filepath.Join(testDataPath(),"existing_file") -func NoFileExists(t T, path string, msgAndArgs ...any) bool { +func FileNotExists(t T, path string, msgAndArgs ...any) bool { // Domain: file if h, ok := t.(H); ok { h.Helper() @@ -93,18 +93,18 @@ func DirExists(t T, path string, msgAndArgs ...any) bool { return true } -// NoDirExists checks whether a directory does not exist in the given path. +// DirNotExists checks whether a directory does not exist in the given path. // It fails if the path points to an existing _directory_ only. // // # Usage // -// assertions.NoDirExists(t, "path/to/directory") +// assertions.DirNotExists(t, "path/to/directory") // // # Examples // // success: filepath.Join(testDataPath(),"non_existing_dir") // failure: filepath.Join(testDataPath(),"existing_dir") -func NoDirExists(t T, path string, msgAndArgs ...any) bool { +func DirNotExists(t T, path string, msgAndArgs ...any) bool { // Domain: file if h, ok := t.(H); ok { h.Helper() diff --git a/internal/assertions/file_test.go b/internal/assertions/file_test.go index cf94abd26..1067c055c 100644 --- a/internal/assertions/file_test.go +++ b/internal/assertions/file_test.go @@ -30,25 +30,25 @@ func TestFileExists(t *testing.T) { True(t, FileExists(mock, link)) } -func TestFileNoFileExists(t *testing.T) { +func TestFileFileNotExists(t *testing.T) { t.Parallel() mock := new(testing.T) - False(t, NoFileExists(mock, filepath.Join("testdata", "existing_file"))) + False(t, FileNotExists(mock, filepath.Join("testdata", "existing_file"))) mock = new(testing.T) - True(t, NoFileExists(mock, "non_existent_file")) + True(t, FileNotExists(mock, "non_existent_file")) mock = new(testing.T) - True(t, NoFileExists(mock, filepath.Join("testdata", "existing_dir"))) + True(t, FileNotExists(mock, filepath.Join("testdata", "existing_dir"))) link := getTempSymlinkPath(t, filepath.Join("testdata", "existing_file")) mock = new(testing.T) - False(t, NoFileExists(mock, link)) + False(t, FileNotExists(mock, link)) link = getTempSymlinkPath(t, "non_existent_file") mock = new(testing.T) - False(t, NoFileExists(mock, link)) + False(t, FileNotExists(mock, link)) } func TestFileDirExists(t *testing.T) { @@ -72,25 +72,25 @@ func TestFileDirExists(t *testing.T) { False(t, DirExists(mock, link)) } -func TestFileNoDirExists(t *testing.T) { +func TestFileDirNotExists(t *testing.T) { t.Parallel() mock := new(testing.T) - True(t, NoDirExists(mock, filepath.Join("testdata", "existing_file"))) + True(t, DirNotExists(mock, filepath.Join("testdata", "existing_file"))) mock = new(testing.T) - True(t, NoDirExists(mock, "non_existent_dir")) + True(t, DirNotExists(mock, "non_existent_dir")) mock = new(testing.T) - False(t, NoDirExists(mock, filepath.Join("testdata", "existing_dir"))) + False(t, DirNotExists(mock, filepath.Join("testdata", "existing_dir"))) link := getTempSymlinkPath(t, filepath.Join("testdata", "existing_file")) mock = new(testing.T) - True(t, NoDirExists(mock, link)) + True(t, DirNotExists(mock, link)) link = getTempSymlinkPath(t, "non_existent_dir") mock = new(testing.T) - True(t, NoDirExists(mock, link)) + True(t, DirNotExists(mock, link)) } func TestFileEmpty(t *testing.T) { @@ -146,7 +146,7 @@ func TestFileNotEmpty(t *testing.T) { link = getTempSymlinkPath(t, "non_existent_file") mock = new(testing.T) - False(t, NoFileExists(mock, link)) + False(t, FileNotExists(mock, link)) } func getTempSymlinkPath(t *testing.T, file string) string { diff --git a/internal/assertions/generics.go b/internal/assertions/generics.go index 14dd82ca7..659d8a966 100644 --- a/internal/assertions/generics.go +++ b/internal/assertions/generics.go @@ -24,7 +24,7 @@ type ( // 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]. + // This is used by [GreaterT], [GreaterOrEqualT], [LessT], [LessOrEqualT], [IsIncreasingT], [IsDecreasingT]. // // NOTE: since [time.Time] is a struct, custom types which redeclare [time.Time] are not supported. Ordered interface { diff --git a/internal/assertions/helpers_impl_test.go b/internal/assertions/helpers_impl_test.go index 311add0a5..9e1b0d447 100644 --- a/internal/assertions/helpers_impl_test.go +++ b/internal/assertions/helpers_impl_test.go @@ -86,6 +86,15 @@ type diffCase struct { expected string } +type diffTestingStruct struct { + A string + B int +} + +func (d *diffTestingStruct) String() string { + return d.A +} + func diffCases() iter.Seq[diffCase] { const n = 5 type Key struct { diff --git a/internal/assertions/mock_test.go b/internal/assertions/mock_test.go index 5112bd52a..b2ac98c52 100644 --- a/internal/assertions/mock_test.go +++ b/internal/assertions/mock_test.go @@ -101,6 +101,10 @@ type outputT struct { helpers map[string]struct{} } +func newOutputMock() *outputT { + return &outputT{buf: bytes.NewBuffer(nil)} +} + // Implements T. func (t *outputT) Errorf(format string, args ...any) { s := fmt.Sprintf(format, args...) @@ -241,8 +245,46 @@ func parseLabeledOutput(output string) []labeledContent { return contents } +// callerName gives the function name (qualified with a package path) +// for the caller after skip frames (where 0 means the current function). +func callerName(skip int) string { + // Make room for the skip PC. + var pc [1]uintptr + n := runtime.Callers(skip+2, pc[:]) // skip + runtime.Callers + callerName + if n == 0 { + panic("testing: zero callers found") + } + frames := runtime.CallersFrames(pc[:n]) + frame, _ := frames.Next() + return frame.Function +} + type testCase struct { expected any actual any result bool } + +func shouldPassOrFail(t *testing.T, mock *mockT, result, shouldPass bool) { + t.Helper() + + if shouldPass { + t.Run("should pass", func(t *testing.T) { + if !result || mock.Failed() { + t.Errorf("expected to pass") + } + }) + + return + } + + t.Run("should fail", func(t *testing.T) { + if result || !mock.Failed() { + t.Errorf("expected to fail") + } + }) +} + +func ptr(i int) *int { + return &i +} diff --git a/internal/assertions/number_test.go b/internal/assertions/number_test.go index ee2a414e5..6a471417d 100644 --- a/internal/assertions/number_test.go +++ b/internal/assertions/number_test.go @@ -4,339 +4,346 @@ package assertions import ( - "fmt" "iter" "math" "slices" + "strings" "testing" - "time" ) -func TestNumberInDelta(t *testing.T) { +func TestNumberInDeltaTErrorMessage(t *testing.T) { t.Parallel() - t.Run("with simple input", func(t *testing.T) { - t.Parallel() + mock := new(mockT) - mock := new(testing.T) + // Test that error message shows correct difference + result := InDeltaT(mock, 10, 1, 5) + if result || !mock.Failed() { + t.Error("Expected test to fail but it passed") + } - 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") - }) + // Verify the error message contains the actual difference (9) + errorMsg := mock.errorString() + if !strings.Contains(errorMsg, "difference was 9") { + t.Errorf("Error message should contain 'difference was 9', got: %s", errorMsg) + } +} - t.Run("with edge cases", func(t *testing.T) { +func TestNumberInEpsilonTErrorMessage(t *testing.T) { + t.Parallel() + + t.Run("relative error message", func(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(mockT) - 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") + // Test relative error: 100 vs 110 has 10% error, exceeds 5% epsilon + result := InEpsilonT(mock, 100.0, 110.0, 0.05) + if result || !mock.Failed() { + t.Error("Expected test to fail but it passed") + } + + // Verify the error message contains relative error + errorMsg := mock.errorString() + if !strings.Contains(errorMsg, "Relative error is too high") { + t.Errorf("Error message should contain 'Relative error is too high', got: %s", errorMsg) + } + + // Should show actual relative error of 0.1 (10%) + if !Contains(t, errorMsg, "0.1") { + t.Errorf("Error message should contain '0.1' (10%% relative error), got: %s", errorMsg) + } }) - 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() + t.Run("absolute error message for zero expected", func(t *testing.T) { + t.Parallel() - mock := new(testing.T) + mock := new(mockT) - True(t, InDelta(mock, tc.a, tc.b, tc.delta), "Expected |%V - %V| <= %v", tc.a, tc.b, tc.delta) - }) + // Test absolute error: expected=0, actual=0.5, epsilon=0.1 + result := InEpsilonT(mock, 0.0, 0.5, 0.1) + if result || !mock.Failed() { + t.Error("Expected test to fail but it passed") + } + + // Verify the error message mentions absolute error + errorMsg := mock.errorString() + if !strings.Contains(errorMsg, "Expected value is zero, using absolute error comparison") { + t.Errorf("Error message should mention absolute error comparison, got: %s", errorMsg) + } + // Should show actual absolute difference of 0.5 + if !strings.Contains(errorMsg, "0.5") { + t.Errorf("Error message should contain '0.5' (absolute difference), got: %s", errorMsg) } }) } -func TestNumberInDeltaT(t *testing.T) { +func TestNumberInDeltaEdgeCases(t *testing.T) { t.Parallel() - t.Run("with simple input", func(t *testing.T) { + t.Run("InDelta specific (type conversion)", 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") + result := InDelta(mock, "", nil, 1) + if result { + t.Errorf("Expected non numerals 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") - }) +func TestNumberInDelta(t *testing.T) { + t.Parallel() - for tc := range deltaTCases() { + // run all test cases with both InDelta and InDeltaT + // + // NOTE: testing pattern, focused on the expected result (true/false) and _NOT_ the content of the returned message. + // - deltaCases: loop over generic test cases AND type combinations (reason: not all types are compatible, e.g. uint64 may overflow float64) + // - testAllDelta: dispatch over the assertion variants (reflection-based, generic, X vs NotX semantics) + // Single assertion test functions: + // - testDelta + // - testDeltaT + for tc := range deltaCases() { t.Run(tc.name, tc.test) } } func TestNumberInDeltaSlice(t *testing.T) { t.Parallel() - mock := new(testing.T) - - True(t, InDeltaSlice(mock, - []float64{1.001, math.NaN(), 0.999}, - []float64{1, math.NaN(), 1}, - 0.1), "{1.001, NaN, 0.009} is element-wise close to {1, NaN, 1} in delta=0.1") - True(t, InDeltaSlice(mock, - []float64{1, math.NaN(), 2}, - []float64{0, math.NaN(), 3}, - 1), "{1, NaN, 2} is element-wise close to {0, NaN, 3} in delta=1") - - False(t, InDeltaSlice(mock, - []float64{1, math.NaN(), 2}, - []float64{0, math.NaN(), 3}, - 0.1), "{1, NaN, 2} is not element-wise close to {0, NaN, 3} in delta=0.1") - - False(t, InDeltaSlice(mock, "", nil, 1), "Expected non numeral slices to fail") + // only have a reflection-based assertion here + for tc := range deltaSliceCases() { + t.Run(tc.name, tc.test) + } } func TestNumberInDeltaMapValues(t *testing.T) { t.Parallel() mock := new(testing.T) + // only have a reflection-based assertion here for tc := range numberInDeltaMapCases() { - tc.f(t, InDeltaMapValues(mock, tc.expect, tc.actual, tc.delta), tc.title+"\n"+diff(tc.expect, tc.actual)) + tc.f(t, InDeltaMapValues(mock, tc.expect, tc.actual, tc.delta), tc.name+"\n"+diff(tc.expect, tc.actual)) } } func TestNumberInEpsilon(t *testing.T) { t.Parallel() - for tc := range numberInEpsilonTrueCases() { - 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() { - 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, - ) - }) + // run all test cases with both InEpsilon and InEpsilonT + for tc := range epsilonCases() { + t.Run(tc.name, tc.test) } } func TestNumberInEpsilonSlice(t *testing.T) { t.Parallel() - mock := new(testing.T) - - True(t, InEpsilonSlice(mock, - []float64{2.2, math.NaN(), 2.0}, - []float64{2.1, math.NaN(), 2.1}, - 0.06), "{2.2, NaN, 2.0} is element-wise close to {2.1, NaN, 2.1} in epsilon=0.06") - - False(t, InEpsilonSlice(mock, - []float64{2.2, 2.0}, - []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 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 { - a, b any - delta float64 -} -func numberInDeltaCases() iter.Seq[numberInDeltaCase] { - type myFloat float32 - - return slices.Values([]numberInDeltaCase{ - {uint(2), uint(1), 1}, - {uint8(2), uint8(1), 1}, - {uint16(2), uint16(1), 1}, - {uint32(2), uint32(1), 1}, - {uint64(2), uint64(1), 1}, - {int(2), int(1), 1}, - {int8(2), int8(1), 1}, - {int16(2), int16(1), 1}, - {int32(2), int32(1), 1}, - {int64(2), int64(1), 1}, - {float32(2), float32(1), 1}, - {float64(2), float64(1), 1}, - {myFloat(2), myFloat(1), 1}, - }) -} - -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)) + for tc := range epsilonSliceCases() { + t.Run(tc.name, tc.test) } } -func deltaTCases() iter.Seq[genericTestCase] { +// Helper functions and test data for InDelta/InDeltaT + +func deltaCases() iter.Seq[genericTestCase] { return slices.Values([]genericTestCase{ + // Simple input cases + {"simple/within-delta-1.001-1-0.01", testAllDelta(1.001, 1.0, 0.01, true)}, + {"simple/within-delta-1-1.001-0.01", testAllDelta(1.0, 1.001, 0.01, true)}, + {"simple/within-delta-1-2-1", testAllDelta(1.0, 2.0, 1.0, true)}, + {"simple/exceeds-delta-1-2-0.5", testAllDelta(1.0, 2.0, 0.5, false)}, + {"simple/exceeds-delta-2-1-0.5", testAllDelta(2.0, 1.0, 0.5, false)}, + + // Edge cases - NaN + {"edge/nan-for-actual", testAllDelta(42.0, math.NaN(), 0.01, false)}, + {"edge/nan-for-expected", testAllDelta(math.NaN(), 42.0, 0.01, false)}, + {"edge/nan-for-both", testAllDelta(math.NaN(), math.NaN(), 0.01, true)}, + // 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)}, + {"int/success", testAllDelta(int(2), int(1), int(1), true)}, + {"int8/success", testAllDelta(int8(2), int8(1), int8(1), true)}, + {"int16/success", testAllDelta(int16(2), int16(1), int16(1), true)}, + {"int32/success", testAllDelta(int32(2), int32(1), int32(1), true)}, + {"int64/success", testAllDelta(int64(2), int64(1), int64(1), true)}, + {"uint/success", testAllDelta(uint(2), uint(1), uint(1), true)}, + {"uint8/success", testAllDelta(uint8(2), uint8(1), uint8(1), true)}, + {"uint16/success", testAllDelta(uint16(2), uint16(1), uint16(1), true)}, + {"uint32/success", testAllDelta(uint32(2), uint32(1), uint32(1), true)}, + {"uint64/success", testAllDelta(uint64(2), uint64(1), uint64(1), true)}, + {"float32/success", testAllDelta(float32(2.0), float32(1.0), float32(1.0), true)}, + {"float64/success", testAllDelta(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)}, + {"int/failure", testAllDelta(int(10), int(1), int(5), false)}, + {"uint/failure", testAllDelta(uint(10), uint(1), uint(5), false)}, + {"float64/failure", testAllDelta(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)}, + {"int/exact", testAllDelta(int(5), int(5), int(0), true)}, + {"uint/exact", testAllDelta(uint(5), uint(5), uint(0), true)}, + {"float64/exact", testAllDelta(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)}, + {"int/zero", testAllDelta(int(0), int(0), int(0), true)}, + {"uint/zero", testAllDelta(uint(0), uint(0), uint(0), true)}, + {"float64/zero", testAllDelta(0.0, 0.0, 0.0, true)}, + {"int/near-zero", testAllDelta(int(1), int(0), int(1), true)}, + {"float64/near-zero", testAllDelta(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)}, + {"int/negative", testAllDelta(int(-5), int(-4), int(2), true)}, + {"int/negative-fail", testAllDelta(int(-10), int(-1), int(5), false)}, + {"float64/negative", testAllDelta(-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)}, + {"int/mixed", testAllDelta(int(5), int(-5), int(10), true)}, + {"int/mixed-fail", testAllDelta(int(5), int(-5), int(9), false)}, + {"float64/mixed", testAllDelta(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)}, + {"uint/expected-greater", testAllDelta(uint(100), uint(50), uint(60), true)}, + {"uint/actual-greater", testAllDelta(uint(50), uint(100), uint(60), true)}, + {"uint8/max-value", testAllDelta(uint8(255), uint8(250), uint8(10), true)}, + {"uint8/max-value-fail", testAllDelta(uint8(255), uint8(250), uint8(4), false)}, + {"uint16/large-diff", testAllDelta(uint16(60000), uint16(40000), uint16(25000), true)}, + {"uint32/large-diff", testAllDelta(uint32(4000000000), uint32(3000000000), uint32(1000000001), true)}, + {"uint64/large-diff", testAllDelta(uint64(10000000000), uint64(5000000000), uint64(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)}, + {"uint8/boundary-expected-gt-actual", testAllDelta(uint8(200), uint8(100), uint8(100), true)}, + {"uint8/boundary-expected-gt-actual-fail", testAllDelta(uint8(200), uint8(100), uint8(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)}, + {"uint8/boundary-actual-gt-expected", testAllDelta(uint8(100), uint8(200), uint8(100), true)}, + {"uint8/boundary-actual-gt-expected-fail", testAllDelta(uint8(100), uint8(200), uint8(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)}, + {"float32/both-nan", testAllDelta(float32(math.NaN()), float32(math.NaN()), float32(1.0), true)}, + {"float32/expected-nan", testAllDelta(float32(math.NaN()), float32(1.0), float32(1.0), false)}, + {"float32/actual-nan", testAllDelta(float32(1.0), float32(math.NaN()), float32(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)}, + {"float64/both-nan", testAllDelta(math.NaN(), math.NaN(), 1.0, true)}, + {"float64/expected-nan", testAllDelta(math.NaN(), 1.0, 1.0, false)}, + {"float64/actual-nan", testAllDelta(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)}, + {"float32/both-plus-inf", testAllDelta(float32(math.Inf(1)), float32(math.Inf(1)), float32(1.0), true)}, + {"float32/expected-plus-inf-actual-minus-inf", testAllDelta(float32(math.Inf(1)), float32(math.Inf(-1)), float32(1.0), false)}, + {"float32/expected-plus-inf-actual-finite", testAllDelta(float32(math.Inf(1)), float32(1.0), float32(1.0), false)}, + {"float32/expected-finite-actual-plus-inf", testAllDelta(float32(1.0), float32(math.Inf(1)), float32(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)}, + {"float64/both-plus-inf", testAllDelta(math.Inf(1), math.Inf(1), 1.0, true)}, + {"float64/expected-plus-inf-actual-minus-inf", testAllDelta(math.Inf(1), math.Inf(-1), 1.0, false)}, + {"float64/expected-plus-inf-actual-finite", testAllDelta(math.Inf(1), 1.0, 1.0, false)}, + {"float64/expected-finite-actual-plus-inf", testAllDelta(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)}, + {"float32/both-minus-inf", testAllDelta(float32(math.Inf(-1)), float32(math.Inf(-1)), float32(1.0), true)}, + {"float32/expected-minus-inf-actual-plus-inf", testAllDelta(float32(math.Inf(-1)), float32(math.Inf(1)), float32(1.0), false)}, + {"float32/expected-minus-inf-actual-finite", testAllDelta(float32(math.Inf(-1)), float32(1.0), float32(1.0), false)}, + {"float32/expected-finite-actual-minus-inf", testAllDelta(float32(1.0), float32(math.Inf(-1)), float32(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)}, + {"float64/both-minus-inf", testAllDelta(math.Inf(-1), math.Inf(-1), 1.0, true)}, + {"float64/expected-minus-inf-actual-plus-inf", testAllDelta(math.Inf(-1), math.Inf(1), 1.0, false)}, + {"float64/expected-minus-inf-actual-finite", testAllDelta(math.Inf(-1), 1.0, 1.0, false)}, + {"float64/expected-finite-actual-minus-inf", testAllDelta(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)}, + {"float64/delta-nan", testAllDelta(1.0, 1.0, math.NaN(), false)}, + {"float32/delta-nan", testAllDelta(float32(1.0), float32(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)}, + {"float64/delta-plus-inf", testAllDelta(1.0, 1.0, math.Inf(1), false)}, + {"float64/delta-minus-inf", testAllDelta(1.0, 1.0, math.Inf(-1), false)}, + {"float32/delta-plus-inf", testAllDelta(float32(1.0), float32(1.0), float32(math.Inf(1)), false)}, + {"float32/delta-minus-inf", testAllDelta(float32(1.0), float32(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)}, + {"float64/small-delta", testAllDelta(1.0, 1.0000001, 0.000001, true)}, + {"float64/small-delta-fail", testAllDelta(1.0, 1.0000001, 0.00000001, false)}, + {"float32/small-delta", testAllDelta(float32(1.0), float32(1.00001), float32(0.0001), true)}, + {"float32/small-delta-fail", testAllDelta(float32(1.0), float32(1.00001), float32(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)}, + {"int64/large-values", testAllDelta(int64(9223372036854775800), int64(9223372036854775700), int64(200), true)}, + {"uint64/large-values", testAllDelta(uint64(18446744073709551600), uint64(18446744073709551500), uint64(200), true)}, + {"float64/large-values", testAllDelta(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)}, + {"int/zero-delta-different", testAllDelta(int(5), int(6), int(0), false)}, + {"float64/zero-delta-different", testAllDelta(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)}, + {"int/commutative-1", testAllDelta(int(10), int(5), int(6), true)}, + {"int/commutative-2", testAllDelta(int(5), int(10), int(6), true)}, + {"float64/commutative-1", testAllDelta(10.0, 5.0, 6.0, true)}, + {"float64/commutative-2", testAllDelta(5.0, 10.0, 6.0, true)}, }) } +// testAllDelta tests both InDelta and InDeltaT with the same input. +// +//nolint:thelper // linter false positive: this is not a helper +func testAllDelta[Number Measurable](expected, actual, delta Number, shouldPass bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + if shouldPass { + t.Run("should pass", func(t *testing.T) { + t.Run("with InDelta", testDelta(expected, actual, delta, true)) + t.Run("with InDeltaT", testDeltaT(expected, actual, delta, true)) + }) + } else { + t.Run("should fail", func(t *testing.T) { + t.Run("with InDelta", testDelta(expected, actual, delta, false)) + t.Run("with InDeltaT", testDeltaT(expected, actual, delta, false)) + }) + } + } +} + +func testDelta[Number Measurable](expected, actual, delta Number, shouldPass bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + // InDelta requires delta as float64, so convert it + result := InDelta(mock, expected, actual, float64(delta)) + + if shouldPass { + True(t, result) + False(t, mock.Failed()) + } else { + False(t, result) + True(t, mock.Failed()) + } + } +} + +func testDeltaT[Number Measurable](expected, actual, delta Number, shouldPass bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := InDeltaT(mock, expected, actual, delta) + + if shouldPass { + True(t, result) + False(t, mock.Failed()) + } else { + False(t, result) + True(t, mock.Failed()) + } + } +} + +// Helper functions and test data for InDeltaMapValues + type numberInDeltaMapCase struct { - title string + name string expect any actual any f func(T, bool, ...any) bool @@ -349,7 +356,7 @@ func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { return slices.Values([]numberInDeltaMapCase{ { - title: "Within delta", + name: "Within delta", expect: map[string]float64{ "foo": 1.0, "bar": 2.0, @@ -364,7 +371,7 @@ func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { f: True, }, { - title: "Within delta", + name: "Within delta", expect: map[int]float64{ 1: 1.0, 2: 2.0, @@ -377,7 +384,7 @@ func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { f: True, }, { - title: "Different number of keys", + name: "Different number of keys", expect: map[int]float64{ 1: 1.0, 2: 2.0, @@ -389,7 +396,7 @@ func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { f: False, }, { - title: "Within delta with zero value", + name: "Within delta with zero value", expect: map[string]float64{ "zero": 0, }, @@ -400,7 +407,7 @@ func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { f: True, }, { - title: "With missing key with zero value", + name: "With missing key with zero value", expect: map[string]float64{ "zero": 0, "foo": 0, @@ -412,25 +419,25 @@ func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { f: False, }, { - title: "With nil maps", + name: "With nil maps", expect: map[string]float64(nil), actual: map[string]float64(nil), f: True, }, { - title: "With nil values (not a map)", + name: "With nil values (not a map)", expect: map[string]float64(nil), actual: []float64(nil), f: False, }, { - title: "With nil values (not a map)", + name: "With nil values (not a map)", expect: []float64(nil), actual: map[string]float64(nil), f: False, }, { - title: "With expected nil keys", + name: "With expected nil keys", expect: map[*string]float64{ &keyA: 1.00, (*string)(nil): 2.00, @@ -442,7 +449,7 @@ func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { f: True, }, { - title: "With expected invalid value", + name: "With expected invalid value", expect: map[string]any{ keyA: &iface, }, @@ -454,282 +461,317 @@ func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { }) } -type numberInEpsilonCase struct { - a, b any - epsilon float64 -} +// Helper functions and test data for InEpsilon/InEpsilonT -func numberInEpsilonTrueCases() iter.Seq[numberInEpsilonCase] { - return slices.Values([]numberInEpsilonCase{ - {uint8(2), uint16(2), .001}, - {2.1, 2.2, 0.1}, - {2.2, 2.1, 0.1}, - {-2.1, -2.2, 0.1}, - {-2.2, -2.1, 0.1}, - {uint64(100), uint8(101), 0.01}, - {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 - }) -} +func epsilonCases() iter.Seq[genericTestCase] { + return slices.Values([]genericTestCase{ + // Simple input cases + {"simple/1pct-error-within-2pct-epsilon", testAllEpsilon(100.0, 101.0, 0.02, true)}, + {"simple/5pct-error-exceeds-2pct-epsilon", testAllEpsilon(100.0, 105.0, 0.02, false)}, + {"simple/exact-match-zero-epsilon", testAllEpsilon(100.0, 100.0, 0.0, true)}, -func numberInEpsilonFalseCases() iter.Seq[numberInEpsilonCase] { - return slices.Values([]numberInEpsilonCase{ - {uint8(2), int16(-2), .001}, - {uint64(100), uint8(102), 0.01}, - {2.1, 2.2, 0.001}, - {2.2, 2.1, 0.001}, - {2.1, -2.2, 1}, - {2.1, "bla-bla", 0}, - {0.1, -0.1, 1.99}, - {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}, - {0, 0, math.NaN()}, - {math.Inf(1), 1, 1}, - {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}, - }) -} + // Edge cases - NaN + {"edge/nan-for-actual", testAllEpsilon(42.0, math.NaN(), 0.01, false)}, + {"edge/nan-for-expected", testAllEpsilon(math.NaN(), 42.0, 0.01, false)}, + {"edge/nan-for-both", testAllEpsilon(math.NaN(), math.NaN(), 0.01, true)}, -func TestNumberInEpsilonT(t *testing.T) { - t.Parallel() + // Edge cases - zero expected (uses absolute error) + {"edge/zero-expected-within", testAllEpsilon(0.0, 0.009, 0.01, true)}, + {"edge/zero-expected-exceeds", testAllEpsilon(0.0, 0.011, 0.01, false)}, - t.Run("with simple input", func(t *testing.T) { - t.Parallel() + // All numeric types - basic success cases (12 cases) + {"int/success", testAllEpsilon(int(100), int(101), 0.02, true)}, // 1% error < 2% epsilon + {"int8/success", testAllEpsilon(int8(100), int8(101), 0.02, true)}, // 1% error < 2% epsilon + {"int16/success", testAllEpsilon(int16(100), int16(101), 0.02, true)}, // 1% error < 2% epsilon + {"int32/success", testAllEpsilon(int32(100), int32(101), 0.02, true)}, // 1% error < 2% epsilon + {"int64/success", testAllEpsilon(int64(100), int64(101), 0.02, true)}, // 1% error < 2% epsilon + {"uint/success", testAllEpsilon(uint(100), uint(101), 0.02, true)}, // 1% error < 2% epsilon + {"uint8/success", testAllEpsilon(uint8(100), uint8(101), 0.02, true)}, // 1% error < 2% epsilon + {"uint16/success", testAllEpsilon(uint16(100), uint16(101), 0.02, true)}, // 1% error < 2% epsilon + {"uint32/success", testAllEpsilon(uint32(100), uint32(101), 0.02, true)}, // 1% error < 2% epsilon + {"uint64/success", testAllEpsilon(uint64(100), uint64(101), 0.02, true)}, // 1% error < 2% epsilon + {"float32/success", testAllEpsilon(float32(100.0), float32(101.0), 0.02, true)}, // 1% error < 2% epsilon + {"float64/success", testAllEpsilon(100.0, 101.0, 0.02, true)}, // 1% error < 2% epsilon - mock := new(testing.T) + // Basic failure cases (3 cases) + {"int/failure", testAllEpsilon(int(100), int(110), 0.05, false)}, // 10% error > 5% epsilon + {"uint/failure", testAllEpsilon(uint(100), uint(110), 0.05, false)}, // 10% error > 5% epsilon + {"float64/failure", testAllEpsilon(100.0, 110.0, 0.05, false)}, // 10% error > 5% epsilon - // 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") - }) + // Exact match (3 cases) + {"int/exact", testAllEpsilon(int(100), int(100), 0.0, true)}, // Exact match + {"uint/exact", testAllEpsilon(uint(100), uint(100), 0.0, true)}, // Exact match + {"float64/exact", testAllEpsilon(100.0, 100.0, 0.0, true)}, // Exact match - t.Run("with edge cases", func(t *testing.T) { - t.Parallel() + // Zero expected value - uses absolute error (8 cases) + {"int/both-zero", testAllEpsilon(int(0), int(0), 0.01, true)}, // Both zero + {"uint/both-zero", testAllEpsilon(uint(0), uint(0), 0.01, true)}, // Both zero + {"float64/both-zero", testAllEpsilon(0.0, 0.0, 0.01, true)}, // Both zero + {"float64/zero-expected-within", testAllEpsilon(0.0, 0.009, 0.01, true)}, // |0.009| <= 0.01 + {"float64/zero-expected-at-boundary", testAllEpsilon(0.0, 0.01, 0.01, true)}, // |0.01| <= 0.01 + {"float64/zero-expected-exceed", testAllEpsilon(0.0, 0.011, 0.01, false)}, // |0.011| > 0.01 + {"float64/zero-expected-large", testAllEpsilon(0.0, 100.0, 0.01, false)}, // |100| > 0.01 + {"int/zero-expected-negative", testAllEpsilon(int(0), int(-5), 10.0, true)}, // |-5| <= 10 (absolute) - mock := new(testing.T) + // Near-zero values (2 cases) + {"float64/near-zero-success", testAllEpsilon(0.001, 0.00101, 0.02, true)}, // 1% error < 2% + {"float64/near-zero-failure", testAllEpsilon(0.001, 0.00110, 0.05, false)}, // 10% error > 5% - // 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") + // Negative numbers (3 cases) + {"int/negative", testAllEpsilon(int(-100), int(-101), 0.02, true)}, // 1% error < 2% + {"int/negative-fail", testAllEpsilon(int(-100), int(-110), 0.05, false)}, // 10% error > 5% + {"float64/negative", testAllEpsilon(-100.0, -101.0, 0.02, true)}, // 1% error < 2% - // 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") - }) + // Mixed positive/negative (3 cases) + {"int/mixed-small", testAllEpsilon(int(100), int(-100), 2.1, true)}, // 200% error <= 210% epsilon + {"int/mixed-fail", testAllEpsilon(int(100), int(-100), 1.9, false)}, // 200% error > 190% epsilon + {"float64/mixed", testAllEpsilon(100.0, -100.0, 2.1, true)}, // 200% error <= 210% epsilon - for tc := range epsilonTCases() { - t.Run(tc.name, tc.test) - } -} + // Float32 NaN cases (3 cases) + {"float32/both-nan", testAllEpsilon(float32(math.NaN()), float32(math.NaN()), 0.01, true)}, + {"float32/expected-nan", testAllEpsilon(float32(math.NaN()), float32(42.0), 0.01, false)}, + {"float32/actual-nan", testAllEpsilon(float32(42.0), float32(math.NaN()), 0.01, false)}, -func TestInDeltaTErrorMessage(t *testing.T) { - t.Parallel() + // Float64 NaN cases (3 cases) + {"float64/both-nan", testAllEpsilon(math.NaN(), math.NaN(), 0.01, true)}, + {"float64/expected-nan", testAllEpsilon(math.NaN(), 42.0, 0.01, false)}, + {"float64/actual-nan", testAllEpsilon(42.0, math.NaN(), 0.01, false)}, - mock := new(mockT) + // Float32 +Inf cases (4 cases) + {"float32/both-plus-inf", testAllEpsilon(float32(math.Inf(1)), float32(math.Inf(1)), 0.01, true)}, + {"float32/expected-plus-inf-actual-minus-inf", testAllEpsilon(float32(math.Inf(1)), float32(math.Inf(-1)), 0.01, false)}, + {"float32/expected-plus-inf-actual-finite", testAllEpsilon(float32(math.Inf(1)), float32(100.0), 0.01, false)}, + {"float32/expected-finite-actual-plus-inf", testAllEpsilon(float32(100.0), float32(math.Inf(1)), 0.01, false)}, - // Test that error message shows correct difference - InDeltaT(mock, 10, 1, 5) + // Float64 +Inf cases (4 cases) + {"float64/both-plus-inf", testAllEpsilon(math.Inf(1), math.Inf(1), 0.01, true)}, + {"float64/expected-plus-inf-actual-minus-inf", testAllEpsilon(math.Inf(1), math.Inf(-1), 0.01, false)}, + {"float64/expected-plus-inf-actual-finite", testAllEpsilon(math.Inf(1), 100.0, 0.01, false)}, + {"float64/expected-finite-actual-plus-inf", testAllEpsilon(100.0, math.Inf(1), 0.01, false)}, - if !mock.Failed() { - t.Error("Expected test to fail but it passed") - } + // Float32 -Inf cases (4 cases) + {"float32/both-minus-inf", testAllEpsilon(float32(math.Inf(-1)), float32(math.Inf(-1)), 0.01, true)}, + {"float32/expected-minus-inf-actual-plus-inf", testAllEpsilon(float32(math.Inf(-1)), float32(math.Inf(1)), 0.01, false)}, + {"float32/expected-minus-inf-actual-finite", testAllEpsilon(float32(math.Inf(-1)), float32(100.0), 0.01, false)}, + {"float32/expected-finite-actual-minus-inf", testAllEpsilon(float32(100.0), float32(math.Inf(-1)), 0.01, false)}, - // 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) - } -} + // Float64 -Inf cases (4 cases) + {"float64/both-minus-inf", testAllEpsilon(math.Inf(-1), math.Inf(-1), 0.01, true)}, + {"float64/expected-minus-inf-actual-plus-inf", testAllEpsilon(math.Inf(-1), math.Inf(1), 0.01, false)}, + {"float64/expected-minus-inf-actual-finite", testAllEpsilon(math.Inf(-1), 100.0, 0.01, false)}, + {"float64/expected-finite-actual-minus-inf", testAllEpsilon(100.0, math.Inf(-1), 0.01, false)}, -func TestInEpsilonTErrorMessage(t *testing.T) { - t.Parallel() + // Epsilon validation (6 cases) + {"float64/epsilon-negative", testAllEpsilon(100.0, 100.0, -0.01, false)}, // Negative epsilon + {"float64/epsilon-nan", testAllEpsilon(100.0, 100.0, math.NaN(), false)}, // NaN epsilon + {"float32/epsilon-nan", testAllEpsilon(float32(100.0), float32(100.0), math.NaN(), false)}, // NaN epsilon + {"float64/epsilon-plus-inf", testAllEpsilon(100.0, 100.0, math.Inf(1), false)}, // +Inf epsilon + {"float64/epsilon-minus-inf", testAllEpsilon(100.0, 100.0, math.Inf(-1), false)}, // -Inf epsilon + {"float32/epsilon-plus-inf", testAllEpsilon(float32(100.0), float32(100.0), math.Inf(1), false)}, // +Inf epsilon - t.Run("relative error message", func(t *testing.T) { - t.Parallel() + // Precision testing (4 cases) + {"float64/small-epsilon-pass", testAllEpsilon(1.0, 1.000001, 0.00001, true)}, // Very small error + {"float64/small-epsilon-fail", testAllEpsilon(1.0, 1.000011, 0.00001, false)}, // Exceeds small epsilon + {"float32/small-epsilon-pass", testAllEpsilon(float32(1.0), float32(1.000001), 0.00001, true)}, // Very small error + {"float32/small-epsilon-fail", testAllEpsilon(float32(1.0), float32(1.000011), 0.00001, false)}, // Exceeds small epsilon - mock := new(mockT) + // Large values (3 cases) + {"int64/large-values", testAllEpsilon(int64(1000000000), int64(1010000000), 0.02, true)}, // 1% error < 2% + {"uint64/large-values", testAllEpsilon(uint64(1000000000), uint64(1010000000), 0.02, true)}, // 1% error < 2% + {"float64/large-values", testAllEpsilon(1e15, 1.01e15, 0.02, true)}, // 1% error < 2% - // Test relative error: 100 vs 110 has 10% error, exceeds 5% epsilon - InEpsilonT(mock, 100.0, 110.0, 0.05) + // Edge cases (4 cases) + {"int/zero-epsilon-same", testAllEpsilon(int(100), int(100), 0.0, true)}, // Zero epsilon, exact match + {"float64/zero-epsilon-different", testAllEpsilon(100.0, 100.1, 0.0, false)}, // Zero epsilon, different + {"int/large-epsilon", testAllEpsilon(int(100), int(200), 1.5, true)}, // 100% error < 150% epsilon + {"float64/boundary", testAllEpsilon(100.0, 102.0, 0.02, true)}, // Exactly 2% error with 2% epsilon + }) +} - if !mock.Failed() { - t.Error("Expected test to fail but it passed") - } +// testAllEpsilon tests both InEpsilon and InEpsilonT with the same input. +// +//nolint:thelper // linter false positive: this is not a helper +func testAllEpsilon[Number Measurable](expected, actual Number, epsilon float64, shouldPass bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() - // 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) + if shouldPass { + t.Run("should pass", func(t *testing.T) { + t.Run("with InEpsilon", testEpsilon(expected, actual, epsilon, true)) + t.Run("with InEpsilonT", testEpsilonT(expected, actual, epsilon, true)) + }) + } else { + t.Run("should fail", func(t *testing.T) { + t.Run("with InEpsilon", testEpsilon(expected, actual, epsilon, false)) + t.Run("with InEpsilonT", testEpsilonT(expected, actual, epsilon, false)) + }) } - }) + } +} - t.Run("absolute error message for zero expected", func(t *testing.T) { +func testEpsilon[Number Measurable](expected, actual Number, epsilon float64, shouldPass bool) func(*testing.T) { + return func(t *testing.T) { t.Parallel() mock := new(mockT) + result := InEpsilon(mock, expected, actual, epsilon) - // 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) + if shouldPass { + True(t, result) + False(t, mock.Failed()) + } else { + False(t, result) + True(t, mock.Failed()) } - }) + } } -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) +func testEpsilonT[Number Measurable](expected, actual Number, epsilon float64, shouldPass bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() - // 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% + mock := new(mockT) + result := InEpsilonT(mock, expected, actual, epsilon) - // 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% + if shouldPass { + True(t, result) + False(t, mock.Failed()) + } else { + False(t, result) + True(t, mock.Failed()) + } + } +} - // 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 +// Helper functions and test data for InDeltaSlice - // 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)}, +func deltaSliceCases() iter.Seq[genericTestCase] { + return slices.Values([]genericTestCase{ + // Success cases - slices are element-wise within delta + { + "within-delta-with-nan", + testDeltaSlice( + []float64{1.001, math.NaN(), 0.999}, + []float64{1, math.NaN(), 1}, + 0.1, + true, + ), + }, + { + "within-delta-1.0", + testDeltaSlice( + []float64{1, math.NaN(), 2}, + []float64{0, math.NaN(), 3}, + 1, + true, + ), + }, - // 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)}, + // Failure cases - slices are not element-wise within delta + { + "not-within-delta", + testDeltaSlice( + []float64{1, math.NaN(), 2}, + []float64{0, math.NaN(), 3}, + 0.1, + 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)}, + // Edge cases - invalid inputs + { + "invalid-non-slice-inputs", + testDeltaSlice("", nil, 1, 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)}, +//nolint:thelper // linter false positive: this is not a helper +func testDeltaSlice(expected, actual any, delta float64, shouldPass bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() - // 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)}, + mock := new(mockT) + result := InDeltaSlice(mock, expected, actual, delta) - // 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)}, + if shouldPass { + True(t, result) + False(t, mock.Failed()) + } else { + False(t, result) + True(t, mock.Failed()) + } + } +} - // 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 +// Helper functions and test data for InEpsilonSlice - // 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 +func epsilonSliceCases() iter.Seq[genericTestCase] { + return slices.Values([]genericTestCase{ + // Success cases - slices are element-wise within epsilon + { + "within-epsilon-with-nan", + testEpsilonSlice( + []float64{2.2, math.NaN(), 2.0}, + []float64{2.1, math.NaN(), 2.1}, + 0.06, + true, + ), + }, - // 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% + // Failure cases - slices are not element-wise within epsilon + { + "not-within-epsilon", + testEpsilonSlice( + []float64{2.2, 2.0}, + []float64{2.1, 2.1}, + 0.04, + false, + ), + }, - // 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 + // Edge cases - invalid inputs + { + "invalid-expected-nil", + testEpsilonSlice("", nil, 1, false), + }, + { + "invalid-actual-nil", + testEpsilonSlice(nil, "", 1, false), + }, + { + "invalid-expected-not-slice", + testEpsilonSlice(1, []int{}, 1, false), + }, + { + "invalid-actual-not-slice", + testEpsilonSlice([]int{}, 1, 1, false), + }, + { + "invalid-expected-non-numeric-slice", + testEpsilonSlice([]string{}, []int{}, 1, false), + }, + { + "invalid-actual-non-numeric-slice", + testEpsilonSlice([]int{}, []string{}, 1, false), + }, }) } //nolint:thelper // linter false positive: this is not a helper -func testEpsilonT[Number Measurable](expected, actual Number, epsilon float64, shouldPass bool) func(*testing.T) { +func testEpsilonSlice(expected, actual any, epsilon float64, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() - mock := &mockT{} - result := InEpsilonT(mock, expected, actual, epsilon) + mock := new(mockT) + result := InEpsilonSlice(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") + True(t, result) + False(t, mock.Failed()) } else { - False(t, result, "Expected InEpsilonT(%v, %v, %v) to fail", expected, actual, epsilon) - True(t, mock.Failed(), "Mock should have failed") + False(t, result) + True(t, mock.Failed()) } } } diff --git a/internal/assertions/object_test.go b/internal/assertions/object_test.go index 0f6349fef..88217263d 100644 --- a/internal/assertions/object_test.go +++ b/internal/assertions/object_test.go @@ -8,7 +8,6 @@ import ( "iter" "math" "slices" - "strings" "testing" "time" ) @@ -27,6 +26,30 @@ func TestObjectsAreEqual(t *testing.T) { } } +/* redundant with Equal +func TestEqualBytes(t *testing.T) { + t.Parallel() + + i := 0 + for c := range equalBytesCases() { + Equal(t, reflect.DeepEqual(c.a, c.b), ObjectsAreEqual(c.a, c.b), "case %d failed", i) + i++ + } +} + +type equalBytesCase struct { + a, b []byte +} + +func equalBytesCases() iter.Seq[equalBytesCase] { + return slices.Values([]equalBytesCase{ + {make([]byte, 2), make([]byte, 2)}, + {make([]byte, 2), make([]byte, 2, 3)}, + {nil, make([]byte, 0)}, + }) +} +*/ + func TestObjectsAreEqualValues(t *testing.T) { t.Parallel() @@ -54,26 +77,6 @@ func TestObjectsCopyExportedFields(t *testing.T) { } } -func TestObjectsEqualExportedValues(t *testing.T) { - t.Parallel() - - for c := range objectEqualExportedValuesCases() { - t.Run("", func(t *testing.T) { - mockT := new(mockT) - - actual := EqualExportedValues(mockT, c.value1, c.value2) - if actual != c.expectedEqual { - t.Errorf("Expected EqualExportedValues to be %t, but was %t", c.expectedEqual, actual) - } - - actualFail := mockT.errorString() - if !strings.Contains(actualFail, c.expectedFail) { - t.Errorf("Contains failure should include %q but was %q", c.expectedFail, actualFail) - } - }) - } -} - type Nested struct { Exported any notExported any @@ -234,147 +237,3 @@ func objectCopyExportedFieldsCases() iter.Seq[objectCopyFieldsCase] { }, }) } - -type objectEqualExportedValuesCase struct { - value1 any - value2 any - expectedEqual bool - expectedFail string -} - -func objectEqualExportedValuesCases() iter.Seq[objectEqualExportedValuesCase] { - return slices.Values([]objectEqualExportedValuesCase{ - { - value1: S{1, Nested{2, 3}, 4, Nested{5, 6}}, - value2: S{1, Nested{2, nil}, nil, Nested{}}, - expectedEqual: true, - }, - { - value1: S{1, Nested{2, 3}, 4, Nested{5, 6}}, - value2: S{1, Nested{1, nil}, nil, Nested{}}, - expectedEqual: false, - expectedFail: fmt.Sprintf(` - Diff: - --- Expected - +++ Actual - @@ -3,3 +3,3 @@ - Exported2: (%s.Nested) { - - Exported: (int) 2, - + Exported: (int) 1, - notExported: (interface {}) `, - shortpkg), - }, - { - value1: S3{&Nested{1, 2}, &Nested{3, 4}}, - value2: S3{&Nested{"a", 2}, &Nested{3, 4}}, - expectedEqual: false, - expectedFail: fmt.Sprintf(` - Diff: - --- Expected - +++ Actual - @@ -2,3 +2,3 @@ - Exported1: (*%s.Nested)({ - - Exported: (int) 1, - + Exported: (string) (len=1) "a", - notExported: (interface {}) `, - shortpkg), - }, - { - value1: S4{[]*Nested{ - {1, 2}, - {3, 4}, - }}, - value2: S4{[]*Nested{ - {1, "a"}, - {2, "b"}, - }}, - expectedEqual: false, - expectedFail: fmt.Sprintf(` - Diff: - --- Expected - +++ Actual - @@ -7,3 +7,3 @@ - (*%s.Nested)({ - - Exported: (int) 3, - + Exported: (int) 2, - notExported: (interface {}) `, - shortpkg), - }, - { - value1: S{[2]int{1, 2}, Nested{2, 3}, 4, Nested{5, 6}}, - value2: S{[2]int{1, 2}, Nested{2, nil}, nil, Nested{}}, - expectedEqual: true, - }, - { - value1: &S{1, Nested{2, 3}, 4, Nested{5, 6}}, - value2: &S{1, Nested{2, nil}, nil, Nested{}}, - expectedEqual: true, - }, - { - value1: &S{1, Nested{2, 3}, 4, Nested{5, 6}}, - value2: &S{1, Nested{1, nil}, nil, Nested{}}, - expectedEqual: false, - expectedFail: fmt.Sprintf(` - Diff: - --- Expected - +++ Actual - @@ -3,3 +3,3 @@ - Exported2: (%s.Nested) { - - Exported: (int) 2, - + Exported: (int) 1, - notExported: (interface {}) `, - shortpkg), - }, - { - value1: []int{1, 2}, - value2: []int{1, 2}, - expectedEqual: true, - }, - { - value1: []int{1, 2}, - value2: []int{1, 3}, - expectedEqual: false, - expectedFail: ` - Diff: - --- Expected - +++ Actual - @@ -2,3 +2,3 @@ - (int) 1, - - (int) 2 - + (int) 3 - }`, - }, - { - value1: []*Nested{ - {1, 2}, - {3, 4}, - }, - value2: []*Nested{ - {1, "a"}, - {3, "b"}, - }, - expectedEqual: true, - }, - { - value1: []*Nested{ - {1, 2}, - {3, 4}, - }, - value2: []*Nested{ - {1, "a"}, - {2, "b"}, - }, - expectedEqual: false, - expectedFail: fmt.Sprintf(` - Diff: - --- Expected - +++ Actual - @@ -6,3 +6,3 @@ - (*%s.Nested)({ - - Exported: (int) 3, - + Exported: (int) 2, - notExported: (interface {}) `, - shortpkg), - }, - }) -} diff --git a/internal/assertions/order.go b/internal/assertions/order.go index c44b83666..f29613edc 100644 --- a/internal/assertions/order.go +++ b/internal/assertions/order.go @@ -6,9 +6,10 @@ package assertions import ( "fmt" "reflect" + "slices" ) -// IsIncreasing asserts that the collection is increasing. +// IsIncreasing asserts that the collection is strictly increasing. // // # Usage // @@ -20,12 +21,103 @@ import ( // // success: []int{1, 2, 3} // failure: []int{1, 1, 2} -func IsIncreasing(t T, object any, msgAndArgs ...any) bool { +func IsIncreasing(t T, collection any, msgAndArgs ...any) bool { // Domain: ordering if h, ok := t.(H); ok { h.Helper() } - return isOrdered(t, object, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) + + values, ok, err := isStrictlyOrdered(collection, false) + if err != nil { + return Fail(t, err.Error(), msgAndArgs...) + } + if !ok { + return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", values...), msgAndArgs...) + } + + return true +} + +// IsIncreasingT asserts that a slice of [Ordered] is strictly increasing. +// +// # Usage +// +// assertions.IsIncreasingT(t, []int{1, 2, 3}) +// assertions.IsIncreasingT(t, []float{1, 2}) +// assertions.IsIncreasingT(t, []string{"a", "b"}) +// +// # Examples +// +// success: []int{1, 2, 3} +// failure: []int{1, 1, 2} +func IsIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { + // Domain: ordering + if h, ok := t.(H); ok { + h.Helper() + } + + isIncreasing := slices.IsSortedFunc(collection, compareStrictOrdered) + if !isIncreasing { + return Fail(t, "should be increasing", msgAndArgs...) + } + + return true +} + +// SortedT asserts that the slice of [Ordered] is sorted (i.e. non-strictly increasing). +// +// Unlike [IsIncreasingT], it accepts elements to be equal. +// +// # Usage +// +// assertions.SortedT(t, []int{1, 2, 3}) +// assertions.SortedT(t, []float{1, 2}) +// assertions.SortedT(t, []string{"a", "b"}) +// +// # Examples +// +// success: []int{1, 1, 3} +// failure: []int{1, 4, 2} +func SortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { + // Domain: ordering + if h, ok := t.(H); ok { + h.Helper() + } + + isSorted := slices.IsSortedFunc(collection, compareOrdered) + if !isSorted { + return Fail(t, "should be sorted", msgAndArgs...) + } + + return true +} + +// NotSortedT asserts that the slice of [Ordered] is NOT sorted (i.e. non-strictly increasing). +// +// Unlike [IsDecreasingT], it accepts slices that are neither increasing nor decreasing. +// +// # Usage +// +// assertions.NotSortedT(t, []int{3, 2, 3}) +// assertions.NotSortedT(t, []float{2, 1}) +// assertions.NotSortedT(t, []string{"b", "a"}) +// +// # Examples +// +// success: []int{3, 1, 3} +// failure: []int{1, 4, 8} +func NotSortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { + // Domain: ordering + if h, ok := t.(H); ok { + h.Helper() + } + + isSorted := slices.IsSortedFunc(collection, compareOrdered) + if isSorted { + return Fail(t, "should not be sorted", msgAndArgs...) + } + + return true } // IsNonIncreasing asserts that the collection is not increasing. @@ -40,15 +132,50 @@ func IsIncreasing(t T, object any, msgAndArgs ...any) bool { // // success: []int{2, 1, 1} // failure: []int{1, 2, 3} -func IsNonIncreasing(t T, object any, msgAndArgs ...any) bool { +func IsNonIncreasing(t T, collection any, msgAndArgs ...any) bool { + // Domain: ordering + if h, ok := t.(H); ok { + h.Helper() + } + + _, ok, err := isStrictlyOrdered(collection, false) + if err != nil { + return Fail(t, err.Error(), msgAndArgs...) + } + if !ok { + return true + } + + return Fail(t, "should not be increasing", msgAndArgs...) +} + +// IsNonIncreasingT asserts that a slice of [Ordered] is NOT strictly increasing. +// +// # Usage +// +// assertions.IsNonIncreasing(t, []int{2, 1, 1}) +// assertions.IsNonIncreasing(t, []float{2, 1}) +// assertions.IsNonIncreasing(t, []string{"b", "a"}) +// +// # Examples +// +// success: []int{2, 1, 1} +// failure: []int{1, 2, 3} +func IsNonIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { // Domain: ordering if h, ok := t.(H); ok { h.Helper() } - return isOrdered(t, object, []compareResult{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) + + isIncreasing := slices.IsSortedFunc(collection, compareStrictOrdered) + if isIncreasing { + return Fail(t, "should not be increasing", msgAndArgs...) + } + + return true } -// IsDecreasing asserts that the collection is decreasing. +// IsDecreasing asserts that the collection is strictly decreasing. // // # Usage // @@ -60,15 +187,51 @@ func IsNonIncreasing(t T, object any, msgAndArgs ...any) bool { // // success: []int{3, 2, 1} // failure: []int{1, 2, 3} -func IsDecreasing(t T, object any, msgAndArgs ...any) bool { +func IsDecreasing(t T, collection any, msgAndArgs ...any) bool { + // Domain: ordering + if h, ok := t.(H); ok { + h.Helper() + } + + values, ok, err := isStrictlyOrdered(collection, true) + if err != nil { + return Fail(t, err.Error(), msgAndArgs...) + } + if !ok { + values = append(values, msgAndArgs...) + return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", values...), msgAndArgs...) + } + + return true +} + +// IsDecreasingT asserts that a slice of [Ordered] is strictly decreasing. +// +// # Usage +// +// assertions.IsDecreasingT(t, []int{2, 1, 0}) +// assertions.IsDecreasingT(t, []float{2, 1}) +// assertions.IsDecreasingT(t, []string{"b", "a"}) +// +// # Examples +// +// success: []int{3, 2, 1} +// failure: []int{1, 2, 3} +func IsDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { // Domain: ordering if h, ok := t.(H); ok { h.Helper() } - return isOrdered(t, object, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) + + isDecreasing := slices.IsSortedFunc(collection, reverseCompareStrictOrdered) + if !isDecreasing { + return Fail(t, "should be decreasing", msgAndArgs...) + } + + return true } -// IsNonDecreasing asserts that the collection is not decreasing. +// IsNonDecreasing asserts that the collection is not strictly decreasing. // // # Usage // @@ -79,30 +242,72 @@ func IsDecreasing(t T, object any, msgAndArgs ...any) bool { // # Examples // // success: []int{1, 1, 2} -// failure: []int{2, 1, 1} -func IsNonDecreasing(t T, object any, msgAndArgs ...any) bool { +// failure: []int{2, 1, 0} +func IsNonDecreasing(t T, collection any, msgAndArgs ...any) bool { + // Domain: ordering + if h, ok := t.(H); ok { + h.Helper() + } + + _, ok, err := isStrictlyOrdered(collection, true) + if err != nil { + return Fail(t, err.Error(), msgAndArgs...) + } + if !ok { + return true + } + + return Fail(t, "should not be decreasing", msgAndArgs...) +} + +// IsNonDecreasingT asserts that a slice of [Ordered] is not decreasing. +// +// # Usage +// +// assertions.IsNonDecreasingT(t, []int{1, 1, 2}) +// assertions.IsNonDecreasingT(t, []float{1, 2}) +// assertions.IsNonDecreasingT(t, []string{"a", "b"}) +// +// # Examples +// +// success: []int{1, 1, 2} +// failure: []int{2, 1, 0} +func IsNonDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { // Domain: ordering if h, ok := t.(H); ok { h.Helper() } - return isOrdered(t, object, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) + + isDecreasing := slices.IsSortedFunc(collection, reverseCompareStrictOrdered) + if isDecreasing { + return Fail(t, "should not be decreasing", msgAndArgs...) + } + + return true } -// isOrdered checks that collection contains orderable elements. -func isOrdered(t T, object any, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...any) bool { +// isStrictlyOrdered checks that collection contains orderable elements, which are strictly ordered. +// +// It returns an error if the object can't be ordered. +// When not strictly ordered, it returns the first 2 offending values found. +func isStrictlyOrdered(object any, reverseOrder bool) ([]any, bool, error) { objKind := reflect.TypeOf(object).Kind() if objKind != reflect.Slice && objKind != reflect.Array { - return Fail(t, fmt.Sprintf("object %T is not an ordered collection", object), msgAndArgs...) + return nil, false, fmt.Errorf("object %T is not an ordered collection", object) } objValue := reflect.ValueOf(object) objLen := objValue.Len() if objLen <= 1 { - return true + return nil, true, nil } value := objValue.Index(0) + if !value.CanInterface() { + // this should not be possible with current reflect, since values are retrieved from an array or slice, not a struct + panic(fmt.Errorf("internal error: can't resolve Interface() for value %v", value)) + } valueInterface := value.Interface() firstValueKind := value.Kind() @@ -111,18 +316,45 @@ func isOrdered(t T, object any, allowedComparesResults []compareResult, failMess prevValueInterface := valueInterface value = objValue.Index(i) + if !value.CanInterface() { + panic(fmt.Errorf("internal error: can't resolve Interface() for value %v", value)) + } valueInterface = value.Interface() compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind) if !isComparable { - return Fail(t, fmt.Sprintf(`Can not compare type "%T" and "%T"`, value, prevValue), msgAndArgs...) + return nil, false, fmt.Errorf(`cannot compare type "%T" and "%T"`, value, prevValue) } - if !containsValue(allowedComparesResults, compareResult) { - return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...) + if (!reverseOrder && compareResult != -1) || (reverseOrder && compareResult != 1) { + return []any{prevValueInterface, valueInterface}, false, nil } } - return true + return nil, true, nil +} + +func compareStrictOrdered[E Ordered](a, b E) int { + v := compareOrderedWithAny[E](a, b) + if v == 0 { + return -1 + } + + return v +} + +func compareOrdered[E Ordered](a, b E) int { + v := compareOrderedWithAny[E](a, b) + + return v +} + +func reverseCompareStrictOrdered[E Ordered](a, b E) int { + v := compareOrderedWithAny[E](b, a) + if v == 0 { + return -1 + } + + return v } diff --git a/internal/assertions/order_impl_test.go b/internal/assertions/order_impl_test.go new file mode 100644 index 000000000..a84222005 --- /dev/null +++ b/internal/assertions/order_impl_test.go @@ -0,0 +1,52 @@ +package assertions + +import "testing" + +func TestOrderUnexportedImplementationDetails(t *testing.T) { + t.Parallel() + + t.Run("compareStrictOrdered", testCompareStrictOrdered) + t.Run("reverseCompareStrictOrdered", testReverseCompareStrictOrdered) +} + +func testCompareStrictOrdered(t *testing.T) { + t.Parallel() + + // Expectations: + // a < b : -1 + // a == b : -1 (attention standard cmp.Compare yield 0) + // a > b : 1 + res := compareStrictOrdered(1, 0) + if res != 1 { + t.Fatalf("expected 1 > 0") + } + res = compareStrictOrdered(0, 0) + if res != -1 { + t.Fatalf("expected !(0 > 0)") + } + res = compareStrictOrdered(0, 1) + if res != -1 { + t.Fatalf("expected !(0 > 1)") + } +} + +func testReverseCompareStrictOrdered(t *testing.T) { + t.Parallel() + + // Expectations: + // a < b : 1 + // a == b : -1 (attention standard cmp.Compare yield 0) + // a > b : -1 + res := reverseCompareStrictOrdered(1, 0) + if res != -1 { + t.Fatalf("expected !(1 < 0)") + } + res = reverseCompareStrictOrdered(0, 0) + if res != -1 { + t.Fatalf("expected !(0 < 0)") + } + res = reverseCompareStrictOrdered(0, 1) + if res != 1 { + t.Fatalf("expected 0 < 1)") + } +} diff --git a/internal/assertions/order_test.go b/internal/assertions/order_test.go index 25e370e88..11c4b1c9d 100644 --- a/internal/assertions/order_test.go +++ b/internal/assertions/order_test.go @@ -4,241 +4,423 @@ package assertions import ( - "bytes" "fmt" "iter" "slices" + "strings" "testing" + "time" ) -type orderedFixture struct { - collection any - msg string -} - -func TestOrderIsIncreasing(t *testing.T) { +func TestOrderErrorMessages(t *testing.T) { t.Parallel() - mock := new(testing.T) - - if !IsIncreasing(mock, []int{1, 2}) { - t.Error("IsIncreasing should return true") - } - if !IsIncreasing(mock, []int{1, 2, 3, 4, 5}) { - t.Error("IsIncreasing should return true") - } + for tc := range orderErrorMessageCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() - if IsIncreasing(mock, []int{1, 1}) { - t.Error("IsIncreasing should return false") - } + mock := newOutputMock() + result := tc.fn(mock, tc.collection, tc.msgAndArgs...) + if result { + t.Errorf("expected ordering assertion %q to fail on %v", tc.name, tc.collection) - if IsIncreasing(mock, []int{2, 1}) { - t.Error("IsIncreasing should return false") - } + return + } - // Check error report - for currCase := range decreasingFixtures() { - t.Run(fmt.Sprintf("%#v", currCase.collection), func(t *testing.T) { - out := &outputT{buf: bytes.NewBuffer(nil)} - False(t, IsIncreasing(out, currCase.collection)) - Contains(t, out.buf.String(), currCase.msg) + if !strings.Contains(mock.buf.String(), tc.expectedInMsg) { + t.Errorf("expected error message to contain: %s but got %q", tc.expectedInMsg, mock.buf.String()) + } }) } } -func TestOrderIsNonIncreasing(t *testing.T) { +// Test functions for reflection-based and generic assertions + +// Unified test for all order assertions, with different input types +// +// NOTE: Unified testing pattern for ordering assertions. +// Test cases are defined with their intrinsic ordering property (kind). +// Expected pass/fail is determined from the kind + assertion semantics. +// +// Unlike the pattern used in string_test.go, we can't easily use a conversion for slices. +// Therefore, we have to resort to a type switch with a fixed known list of tested slice types. +// +// The matrix of expected assertion semantics is defined by [expectedStatusForAssertion]. +func TestOrder(t *testing.T) { t.Parallel() - mock := new(testing.T) - if !IsNonIncreasing(mock, []int{2, 1}) { - t.Error("IsNonIncreasing should return true") + for tc := range unifiedOrderCases() { + t.Run(tc.name, testAllOrdersWithTypes(tc)) } +} - if !IsNonIncreasing(mock, []int{5, 4, 4, 3, 2, 1}) { - t.Error("IsNonIncreasing should return true") - } +func testAllOrdersWithTypes(tc orderTestCase) func(*testing.T) { + return func(t *testing.T) { + t.Run("with IsIncreasing", func(t *testing.T) { + t.Parallel() - if !IsNonIncreasing(mock, []int{1, 1}) { - t.Error("IsNonIncreasing should return true") - } + shouldPass := expectedStatusForAssertion(increasingKind, tc.kind) + t.Run("with reflection", testOrderReflectBased(IsIncreasing, tc.collection, shouldPass)) + if !tc.reflectionOnly { + t.Run("with generic", testOrderGeneric(increasingKind, tc.collection, shouldPass)) + } + }) - if IsNonIncreasing(mock, []int{1, 2}) { - t.Error("IsNonIncreasing should return false") - } + t.Run("with IsNonIncreasing", func(t *testing.T) { + t.Parallel() - // Check error report - for currCase := range increasingFixtures() { - t.Run(fmt.Sprintf("%#v", currCase.collection), func(t *testing.T) { - out := &outputT{buf: bytes.NewBuffer(nil)} - False(t, IsNonIncreasing(out, currCase.collection)) - Contains(t, out.buf.String(), currCase.msg) + shouldPass := expectedStatusForAssertion(notIncreasingKind, tc.kind) + t.Run("with reflection", testOrderReflectBased(IsNonIncreasing, tc.collection, shouldPass)) + if !tc.reflectionOnly { + t.Run("with generic", testOrderGeneric(notIncreasingKind, tc.collection, shouldPass)) + } }) - } -} -func TestOrderIsDecreasing(t *testing.T) { - t.Parallel() - mock := new(testing.T) + t.Run("with IsDecreasing", func(t *testing.T) { + t.Parallel() - if !IsDecreasing(mock, []int{2, 1}) { - t.Error("IsDecreasing should return true") - } + shouldPass := expectedStatusForAssertion(decreasingKind, tc.kind) + t.Run("with reflection", testOrderReflectBased(IsDecreasing, tc.collection, shouldPass)) + if !tc.reflectionOnly { + t.Run("with generic", testOrderGeneric(decreasingKind, tc.collection, shouldPass)) + } + }) - if !IsDecreasing(mock, []int{5, 4, 3, 2, 1}) { - t.Error("IsDecreasing should return true") - } + t.Run("with IsNonDecreasing", func(t *testing.T) { + t.Parallel() - if IsDecreasing(mock, []int{1, 1}) { - t.Error("IsDecreasing should return false") - } + shouldPass := expectedStatusForAssertion(notDecreasingKind, tc.kind) + t.Run("with reflection", testOrderReflectBased(IsNonDecreasing, tc.collection, shouldPass)) + if !tc.reflectionOnly { + t.Run("with generic", testOrderGeneric(notDecreasingKind, tc.collection, shouldPass)) + } + }) - if IsDecreasing(mock, []int{1, 2}) { - t.Error("IsDecreasing should return false") - } + if tc.reflectionOnly { + return + } + + t.Run("with SortedT", func(t *testing.T) { + t.Parallel() + + shouldPass := expectedStatusForAssertion(sortedKind, tc.kind) + t.Run("with generic only", testOrderGeneric(sortedKind, tc.collection, shouldPass)) + }) - // Check error report - for currCase := range increasingFixtures2() { - t.Run(fmt.Sprintf("%#v", currCase.collection), func(t *testing.T) { - out := &outputT{buf: bytes.NewBuffer(nil)} - False(t, IsDecreasing(out, currCase.collection)) - Contains(t, out.buf.String(), currCase.msg) + t.Run("with NotSortedT", func(t *testing.T) { + t.Parallel() + + shouldPass := expectedStatusForAssertion(notSortedKind, tc.kind) + t.Run("with generic only", testOrderGeneric(notSortedKind, tc.collection, shouldPass)) }) } } -func TestOrderIsNonDecreasing(t *testing.T) { - t.Parallel() - mock := new(testing.T) +// collectionKind represents the ordering property of a collection. +type collectionKind int + +const ( + allEqual collectionKind = iota // all values equal (sorted but not strictly) + strictlyAsc // strictly ascending (each < next) + strictlyDesc // strictly descending (each > next) + nonStrictlyAsc // non-strictly ascending (each <= next, some equal) + nonStrictlyDesc // non-strictly descending (each >= next, some equal) + unsorted // no ordering + passAll // empty or single element collection + errorCase // should fail with error (not panic) +) - if !IsNonDecreasing(mock, []int{1, 2}) { - t.Error("IsNonDecreasing should return true") - } +type orderAssertionKind int + +const ( + increasingKind orderAssertionKind = iota + notIncreasingKind + decreasingKind + notDecreasingKind + sortedKind + notSortedKind +) - if !IsNonDecreasing(mock, []int{1, 1, 2, 3, 4, 5}) { - t.Error("IsNonDecreasing should return true") +// orderTestCase represents a test case that can be used for all ordering assertions. +type orderTestCase struct { + name string + collection any + kind collectionKind + reflectionOnly bool +} + +// Unified test cases for all ordering assertions. +type ( + myFloat float64 + myCollection []myFloat +) + +func unifiedOrderCases() iter.Seq[orderTestCase] { + t0 := time.Now() + t1 := t0.Add(time.Second) + t2 := t1.Add(time.Second) + + // Test types for reflection-only edge cases. + type nonComparableStruct struct { + Value int + Data []int // slices make structs non-comparable } - if !IsNonDecreasing(mock, []int{1, 1}) { - t.Error("IsNonDecreasing should return false") + type structWithUnexportedField struct { + unexported int } - if IsNonDecreasing(mock, []int{2, 1}) { - t.Error("IsNonDecreasing should return false") + return slices.Values([]orderTestCase{ + // Edge cases: nil, empty, single element collections + {"empty/int", []int{}, passAll, false}, + {"nil/int", []int(nil), passAll, false}, + {"single/int", []int{1}, passAll, false}, + + // All equal - both non-strict pass, both strict fail, sorted + {"all-equal/int", []int{2, 2, 2}, allEqual, false}, + {"all-equal/float64", []float64{1.5, 1.5, 1.5}, allEqual, false}, + {"all-equal/~float64", []myFloat{1.5, 1.5, 1.5}, allEqual, false}, + {"all-equal/~[]~float64", myCollection{1.5, 1.5, 1.5}, allEqual, false}, + {"all-equal/string", []string{"a", "a", "a"}, allEqual, false}, + {"all-equal/time.Time", []time.Time{t0, t0, t0}, allEqual, false}, + {"all-equal/[]byte", [][]byte{[]byte("a"), []byte("a"), []byte("a")}, allEqual, false}, + + // Strictly ascending - IsIncreasing passes, IsNonDecreasing passes, sorted + {"strictly-asc/int-short", []int{1, 2, 3}, strictlyAsc, false}, + {"strictly-asc/int-long", []int{1, 2, 3, 4, 5}, strictlyAsc, false}, + {"strictly-asc/int8", []int8{1, 2, 3}, strictlyAsc, false}, + {"strictly-asc/int16", []int16{1, 2, 3}, strictlyAsc, false}, + {"strictly-asc/int32", []int32{1, 2, 3}, strictlyAsc, false}, + {"strictly-asc/int64", []int64{1, 2, 3}, strictlyAsc, false}, + {"strictly-asc/uint", []uint{1, 2, 3}, strictlyAsc, false}, + {"strictly-asc/uint8", []uint8{1, 2, 3}, strictlyAsc, false}, + {"strictly-asc/uint16", []uint16{1, 2, 3}, strictlyAsc, false}, + {"strictly-asc/uint32", []uint32{1, 2, 3}, strictlyAsc, false}, + {"strictly-asc/uint64", []uint64{1, 2, 3}, strictlyAsc, false}, + {"strictly-asc/float32", []float32{1.1, 2.2, 3.3}, strictlyAsc, false}, + {"strictly-asc/float64", []float64{1.1, 2.2, 3.3}, strictlyAsc, false}, + {"strictly-asc/~float64", []myFloat{1.1, 2.2, 3.3}, strictlyAsc, false}, + {"strictly-asc/~[]~float64", myCollection{1.1, 2.2, 3.3}, strictlyAsc, false}, + {"strictly-asc/string", []string{"a", "b", "c"}, strictlyAsc, false}, + {"strictly-asc/time.Time", []time.Time{t0, t1, t2}, strictlyAsc, false}, + {"strictly-asc/[]byte", [][]byte{[]byte("a"), []byte("b"), []byte("c")}, strictlyAsc, false}, + + // Strictly descending - IsDecreasing passes, IsNonIncreasing passes, not sorted + {"strictly-desc/int-short", []int{3, 2, 1}, strictlyDesc, false}, + {"strictly-desc/int-long", []int{5, 4, 3, 2, 1}, strictlyDesc, false}, + {"strictly-desc/float64", []float64{3.3, 2.2, 1.1}, strictlyDesc, false}, + {"strictly-desc/~float64", []myFloat{3.3, 2.2, 1.1}, strictlyDesc, false}, + {"strictly-desc/~[]~float64", myCollection{3.3, 2.2, 1.1}, strictlyDesc, false}, + {"strictly-desc/string", []string{"c", "b", "a"}, strictlyDesc, false}, + {"strictly-desc/time.Time", []time.Time{t2, t1, t0}, strictlyDesc, false}, + {"strictly-desc/[]byte", [][]byte{[]byte("c"), []byte("b"), []byte("a")}, strictlyDesc, false}, + + // Non-strictly ascending - sorted, but not strictly (has equal adjacent) + {"non-strictly-asc/int-with-equal", []int{1, 1, 2, 3}, nonStrictlyAsc, false}, + {"non-strictly-asc/int-with-equal-middle", []int{1, 2, 2, 3}, nonStrictlyAsc, false}, + {"non-strictly-asc/float64", []float64{1.1, 2.2, 2.2, 3.3}, nonStrictlyAsc, false}, + {"non-strictly-asc/~float64", []myFloat{1.1, 1.1, 2.2, 3.3}, nonStrictlyAsc, false}, + {"non-strictly-asc/~[]~float64", myCollection{1.1, 1.1, 2.2, 3.3}, nonStrictlyAsc, false}, + {"non-strictly-asc/string", []string{"a", "a", "b", "c"}, nonStrictlyAsc, false}, + {"non-strictly-asc/time.Time", []time.Time{t0, t0, t1, t2}, nonStrictlyAsc, false}, + {"non-strictly-asc/[]byte", [][]byte{[]byte("a"), []byte("a"), []byte("b"), []byte("c")}, nonStrictlyAsc, false}, + + // Non-strictly descending - not sorted, but consistently >= (has equal adjacent) + {"non-strictly-desc/int-with-equal", []int{3, 2, 2, 1}, nonStrictlyDesc, false}, + {"non-strictly-desc/int-with-equal-start", []int{3, 3, 2, 1}, nonStrictlyDesc, false}, + {"non-strictly-desc/float64", []float64{3.3, 2.2, 2.2, 1.1}, nonStrictlyDesc, false}, + {"non-strictly-desc/~float64", []myFloat{3.3, 2.2, 2.2, 1.1}, nonStrictlyDesc, false}, + {"non-strictly-desc/~[]~float64", myCollection{3.3, 2.2, 2.2, 1.1}, nonStrictlyDesc, false}, + {"non-strictly-desc/string", []string{"c", "b", "b", "a"}, nonStrictlyDesc, false}, + {"non-strictly-desc/time.Time", []time.Time{t2, t1, t1, t0}, nonStrictlyDesc, false}, + {"non-strictly-desc/[]byte", [][]byte{[]byte("c"), []byte("b"), []byte("b"), []byte("a")}, nonStrictlyDesc, false}, + + // Unsorted - no ordering pattern + {"unsorted/int-mixed", []int{1, 4, 2}, unsorted, false}, + {"unsorted/int-up-down-up", []int{1, 3, 2, 4}, unsorted, false}, + {"unsorted/float64", []float64{1.1, 3.3, 2.2}, unsorted, false}, + {"unsorted/~float64", []myFloat{1.1, 3.3, 2.2}, unsorted, false}, + {"unsorted/~[]~float64", myCollection{1.1, 3.3, 2.2}, unsorted, false}, + {"unsorted/string", []string{"b", "a", "c"}, unsorted, false}, + {"unsorted/time.Time", []time.Time{t1, t0, t2}, unsorted, false}, + {"unsorted/[]byte", [][]byte{[]byte("b"), []byte("a"), []byte("c")}, unsorted, false}, + + // Reflection-only edge cases + + // Case 1: Object is not a slice or array (should error) + {"error/not-a-collection", nonComparableStruct{Value: 1, Data: []int{1}}, errorCase, true}, + + // Case 2: Slice of non-comparable elements (should error) + {"error/non-comparable-elements", []nonComparableStruct{ + {Value: 1, Data: []int{1}}, + {Value: 2, Data: []int{2}}, + }, errorCase, true}, + + // Case 3: Slice with unexported fields (triggers panic bug in isStrictlyOrdered) + {"panic/unexported-fields", []structWithUnexportedField{ + {unexported: 1}, + {unexported: 2}, + }, errorCase, true}, + }) +} + +// Determine the expected pass/fail status for each assertion based on ordering kind. +func expectedStatusForAssertion(assertionKind orderAssertionKind, kind collectionKind) bool { + // Error cases always fail (return false) + if kind == errorCase { + return false } - // Check error report - for currCase := range decreasingFixtures2() { - t.Run(fmt.Sprintf("%#v", currCase.collection), func(t *testing.T) { - out := &outputT{buf: bytes.NewBuffer(nil)} - False(t, IsNonDecreasing(out, currCase.collection)) - Contains(t, out.buf.String(), currCase.msg) - }) + switch assertionKind { + case increasingKind: + // IsIncreasing: strictly ascending only + return kind == strictlyAsc || kind == passAll + case notIncreasingKind: + return kind != strictlyAsc && kind != passAll + case decreasingKind: + // IsDecreasing: strictly descending only + return kind == strictlyDesc || kind == passAll + case notDecreasingKind: + return kind != strictlyDesc && kind != passAll + case sortedKind: + // SortedT: passes for sorted (non-strictly ascending, allows equal) + return kind == allEqual || kind == strictlyAsc || kind == nonStrictlyAsc || kind == passAll + case notSortedKind: + // NotSortedT: inverse of SortedT + return kind != allEqual && kind != strictlyAsc && kind != nonStrictlyAsc && kind != passAll + default: + panic(fmt.Errorf("test case configuration error: invalid orderAssertionKind: %d", assertionKind)) } } -func TestOrderMsgAndArgsForwarding(t *testing.T) { - t.Parallel() +func testOrderReflectBased(orderAssertion func(T, any, ...any) bool, collection any, shouldPass bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() - msgAndArgs := []any{"format %s %x", "this", 0xc001} - expectedOutput := "format this c001\n" - collection := []int{1, 2, 1} - funcs := []func(t T){ - func(t T) { IsIncreasing(t, collection, msgAndArgs...) }, - func(t T) { IsNonIncreasing(t, collection, msgAndArgs...) }, - func(t T) { IsDecreasing(t, collection, msgAndArgs...) }, - func(t T) { IsNonDecreasing(t, collection, msgAndArgs...) }, + mock := new(mockT) + result := orderAssertion(mock, collection) + shouldPassOrFail(t, mock, result, shouldPass) } - for _, f := range funcs { - out := &outputT{buf: bytes.NewBuffer(nil)} - f(out) - Contains(t, out.buf.String(), expectedOutput) +} + +func testOrderGeneric(assertionKind orderAssertionKind, collection any, shouldPass bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := testOrderAssertionResult(mock, assertionKind, collection) + shouldPassOrFail(t, mock, result, shouldPass) } } -func decreasingFixtures() iter.Seq[orderedFixture] { - return slices.Values( - []orderedFixture{ - {collection: []string{"b", "a"}, msg: `"b" is not less than "a"`}, - {collection: []int{2, 1}, msg: `"2" is not less than "1"`}, - {collection: []int{2, 1, 3, 4, 5, 6, 7}, msg: `"2" is not less than "1"`}, - {collection: []int{-1, 0, 2, 1}, msg: `"2" is not less than "1"`}, - {collection: []int8{2, 1}, msg: `"2" is not less than "1"`}, - {collection: []int16{2, 1}, msg: `"2" is not less than "1"`}, - {collection: []int32{2, 1}, msg: `"2" is not less than "1"`}, - {collection: []int64{2, 1}, msg: `"2" is not less than "1"`}, - {collection: []uint8{2, 1}, msg: `"2" is not less than "1"`}, - {collection: []uint16{2, 1}, msg: `"2" is not less than "1"`}, - {collection: []uint32{2, 1}, msg: `"2" is not less than "1"`}, - {collection: []uint64{2, 1}, msg: `"2" is not less than "1"`}, - {collection: []float32{2.34, 1.23}, msg: `"2.34" is not less than "1.23"`}, - {collection: []float64{2.34, 1.23}, msg: `"2.34" is not less than "1.23"`}, - {collection: struct{}{}, msg: `object struct {} is not an ordered collection`}, - }, - ) +func testOrderAssertionResult(mock T, assertionKind orderAssertionKind, collection any) bool { + // Type switch to call the appropriate generic function. + // + // This switch doesn't cover ALL variants of ~[]Ordered but is deemed sufficient for the purpose + // of testing ordering. + switch coll := collection.(type) { + case []int: + return testGenericAssertion(mock, assertionKind, coll) + case []int8: + return testGenericAssertion(mock, assertionKind, coll) + case []int16: + return testGenericAssertion(mock, assertionKind, coll) + case []int32: + return testGenericAssertion(mock, assertionKind, coll) + case []int64: + return testGenericAssertion(mock, assertionKind, coll) + case []uint: + return testGenericAssertion(mock, assertionKind, coll) + case []uint8: + return testGenericAssertion(mock, assertionKind, coll) + case []uint16: + return testGenericAssertion(mock, assertionKind, coll) + case []uint32: + return testGenericAssertion(mock, assertionKind, coll) + case []uint64: + return testGenericAssertion(mock, assertionKind, coll) + case []uintptr: + return testGenericAssertion(mock, assertionKind, coll) + case []float32: + return testGenericAssertion(mock, assertionKind, coll) + case []float64: + return testGenericAssertion(mock, assertionKind, coll) + case []myFloat: + return testGenericAssertion(mock, assertionKind, coll) + case myCollection: + return testGenericAssertion(mock, assertionKind, coll) + case [][]byte: + return testGenericAssertion(mock, assertionKind, coll) + case []string: + return testGenericAssertion(mock, assertionKind, coll) + case []time.Time: + return testGenericAssertion(mock, assertionKind, coll) + default: + panic(fmt.Errorf("internal test error: unsupported collection type in test suite: %T", coll)) + } } -func increasingFixtures() iter.Seq[orderedFixture] { - return slices.Values( - []orderedFixture{ - {collection: []string{"a", "b"}, msg: `"a" is not greater than or equal to "b"`}, - {collection: []int{1, 2}, msg: `"1" is not greater than or equal to "2"`}, - {collection: []int{1, 2, 7, 6, 5, 4, 3}, msg: `"1" is not greater than or equal to "2"`}, - {collection: []int{5, 4, 3, 1, 2}, msg: `"1" is not greater than or equal to "2"`}, - {collection: []int8{1, 2}, msg: `"1" is not greater than or equal to "2"`}, - {collection: []int16{1, 2}, msg: `"1" is not greater than or equal to "2"`}, - {collection: []int32{1, 2}, msg: `"1" is not greater than or equal to "2"`}, - {collection: []int64{1, 2}, msg: `"1" is not greater than or equal to "2"`}, - {collection: []uint8{1, 2}, msg: `"1" is not greater than or equal to "2"`}, - {collection: []uint16{1, 2}, msg: `"1" is not greater than or equal to "2"`}, - {collection: []uint32{1, 2}, msg: `"1" is not greater than or equal to "2"`}, - {collection: []uint64{1, 2}, msg: `"1" is not greater than or equal to "2"`}, - {collection: []float32{1.23, 2.34}, msg: `"1.23" is not greater than or equal to "2.34"`}, - {collection: []float64{1.23, 2.34}, msg: `"1.23" is not greater than or equal to "2.34"`}, - {collection: struct{}{}, msg: `object struct {} is not an ordered collection`}, - }, - ) +func testGenericAssertion[Collection ~[]E, E Ordered](mock T, assertionKind orderAssertionKind, collection Collection) bool { + switch assertionKind { + case increasingKind: + return IsIncreasingT(mock, collection) + case notIncreasingKind: + return IsNonIncreasingT(mock, collection) + case decreasingKind: + return IsDecreasingT(mock, collection) + case notDecreasingKind: + return IsNonDecreasingT(mock, collection) + case sortedKind: + return SortedT(mock, collection) + case notSortedKind: + return NotSortedT(mock, collection) + default: + panic(fmt.Errorf("test case configuration error: invalid orderAssertionKind: %d", assertionKind)) + } } -func increasingFixtures2() iter.Seq[orderedFixture] { - return slices.Values( - []orderedFixture{ - {collection: []string{"a", "b"}, msg: `"a" is not greater than "b"`}, - {collection: []int{1, 2}, msg: `"1" is not greater than "2"`}, - {collection: []int{1, 2, 7, 6, 5, 4, 3}, msg: `"1" is not greater than "2"`}, - {collection: []int{5, 4, 3, 1, 2}, msg: `"1" is not greater than "2"`}, - {collection: []int8{1, 2}, msg: `"1" is not greater than "2"`}, - {collection: []int16{1, 2}, msg: `"1" is not greater than "2"`}, - {collection: []int32{1, 2}, msg: `"1" is not greater than "2"`}, - {collection: []int64{1, 2}, msg: `"1" is not greater than "2"`}, - {collection: []uint8{1, 2}, msg: `"1" is not greater than "2"`}, - {collection: []uint16{1, 2}, msg: `"1" is not greater than "2"`}, - {collection: []uint32{1, 2}, msg: `"1" is not greater than "2"`}, - {collection: []uint64{1, 2}, msg: `"1" is not greater than "2"`}, - {collection: []float32{1.23, 2.34}, msg: `"1.23" is not greater than "2.34"`}, - {collection: []float64{1.23, 2.34}, msg: `"1.23" is not greater than "2.34"`}, - {collection: struct{}{}, msg: `object struct {} is not an ordered collection`}, - }, - ) +type errorMessageTestCase struct { + name string + fn func(T, any, ...any) bool + collection any + msgAndArgs []any + expectedInMsg string } -func decreasingFixtures2() iter.Seq[orderedFixture] { - return slices.Values( - []orderedFixture{ - {collection: []string{"b", "a"}, msg: `"b" is not less than or equal to "a"`}, - {collection: []int{2, 1}, msg: `"2" is not less than or equal to "1"`}, - {collection: []int{2, 1, 3, 4, 5, 6, 7}, msg: `"2" is not less than or equal to "1"`}, - {collection: []int{-1, 0, 2, 1}, msg: `"2" is not less than or equal to "1"`}, - {collection: []int8{2, 1}, msg: `"2" is not less than or equal to "1"`}, - {collection: []int16{2, 1}, msg: `"2" is not less than or equal to "1"`}, - {collection: []int32{2, 1}, msg: `"2" is not less than or equal to "1"`}, - {collection: []int64{2, 1}, msg: `"2" is not less than or equal to "1"`}, - {collection: []uint8{2, 1}, msg: `"2" is not less than or equal to "1"`}, - {collection: []uint16{2, 1}, msg: `"2" is not less than or equal to "1"`}, - {collection: []uint32{2, 1}, msg: `"2" is not less than or equal to "1"`}, - {collection: []uint64{2, 1}, msg: `"2" is not less than or equal to "1"`}, - {collection: []float32{2.34, 1.23}, msg: `"2.34" is not less than or equal to "1.23"`}, - {collection: []float64{2.34, 1.23}, msg: `"2.34" is not less than or equal to "1.23"`}, - {collection: struct{}{}, msg: `object struct {} is not an ordered collection`}, - }, +func orderErrorMessageCases() iter.Seq[errorMessageTestCase] { + const ( + format = "format %s %x" + arg1 = "this" + arg2 = 0xc001 + expectedOutput = "format this c001\n" ) + + msgAndArgs := []any{format, arg1, arg2} + + return slices.Values([]errorMessageTestCase{ + // Test msgAndArgs formatting + {"IsIncreasing/with-msgAndArgs", IsIncreasing, []int{1, 2, 1}, msgAndArgs, expectedOutput}, + {"IsNonIncreasing/with-msgAndArgs", IsNonIncreasing, []int{1, 2, 3}, msgAndArgs, expectedOutput}, + {"IsDecreasing/with-msgAndArgs", IsDecreasing, []int{1, 2, 1}, msgAndArgs, expectedOutput}, + {"IsNonDecreasing/with-msgAndArgs", IsNonDecreasing, []int{3, 2, 1}, msgAndArgs, expectedOutput}, + + // Test specific error messages + {"IsIncreasing/string", IsIncreasing, []string{"b", "a"}, nil, `"b" is not less than "a"`}, + {"IsIncreasing/int", IsIncreasing, []int{2, 1}, nil, `"2" is not less than "1"`}, + {"IsIncreasing/int8", IsIncreasing, []int8{2, 1}, nil, `"2" is not less than "1"`}, + {"IsIncreasing/float32", IsIncreasing, []float32{2.34, 1.23}, nil, `"2.34" is not less than "1.23"`}, + {"IsIncreasing/invalid-type", IsIncreasing, struct{}{}, nil, `object struct {} is not an ordered collection`}, + + {"IsNonIncreasing/string", IsNonIncreasing, []string{"a", "b"}, nil, `should not be increasing`}, + {"IsNonIncreasing/int", IsNonIncreasing, []int{1, 2}, nil, `should not be increasing`}, + {"IsNonIncreasing/float64", IsNonIncreasing, []float64{1.23, 2.34}, nil, `should not be increasing`}, + + {"IsDecreasing/string", IsDecreasing, []string{"a", "b"}, nil, `"a" is not greater than "b"`}, + {"IsDecreasing/int", IsDecreasing, []int{1, 2}, nil, `"1" is not greater than "2"`}, + {"IsDecreasing/uint64", IsDecreasing, []uint64{1, 2}, nil, `"1" is not greater than "2"`}, + + {"IsNonDecreasing/string", IsNonDecreasing, []string{"b", "a"}, nil, `should not be decreasing`}, + {"IsNonDecreasing/int", IsNonDecreasing, []int{2, 1}, nil, `should not be decreasing`}, + {"IsNonDecreasing/float32", IsNonDecreasing, []float32{2.34, 1.23}, nil, `should not be decreasing`}, + }) } diff --git a/internal/assertions/string_test.go b/internal/assertions/string_test.go index 3ab379f2e..183b4a881 100644 --- a/internal/assertions/string_test.go +++ b/internal/assertions/string_test.go @@ -10,94 +10,117 @@ import ( "testing" ) -func TestStringEqual(t *testing.T) { - t.Parallel() - - i := 0 - for currCase := range stringEqualCases() { - mock := &bufferT{} +func TestStringRegexpEdgeCases(t *testing.T) { + // check edge cases for reflection-based Regexp, such as unsupported types or nil input or when the input + // is converted using fmt.Sprint. - Equal(mock, currCase.equalWant, currCase.equalGot, currCase.msgAndArgs...) - Regexp(t, regexp.MustCompile(currCase.want), mock.buf.String(), "Case %d", i) - i++ - } -} + t.Run("with unsupported regexp type", func(t *testing.T) { + t.Parallel() -func TestStringEqualFormatting(t *testing.T) { - t.Parallel() + const ( + str = "whatever" + msg = "expected this invalid call to fail (regexp=%v)" + ) - i := 0 - for currCase := range stringEqualFormattingCases() { - mock := &bufferT{} + mock := new(mockT) - Equal(mock, currCase.equalWant, currCase.equalGot, currCase.msgAndArgs...) - Regexp(t, regexp.MustCompile(currCase.want), mock.buf.String(), "Case %d", i) - i++ - } -} - -func TestStringRegexp(t *testing.T) { - t.Parallel() - - // run test cases with all combinations of supported types - for tc := range stringRegexpCases() { - t.Run(tc.name, tc.test) - } + t.Run("should fail (invalid regexp type)", func(t *testing.T) { + invalidRex := struct{ a string }{a: "invalid"} - t.Run("with edge cases", func(t *testing.T) { - t.Run("with unsupported regexp type", func(t *testing.T) { - t.Parallel() + if Regexp(mock, invalidRex, str) { + t.Errorf(msg, invalidRex) + } + if NotRegexp(mock, invalidRex, str) { + t.Errorf(msg, invalidRex) + } + }) - const ( - str = "whatever" - msg = "expected this invalid call to fail (regexp=%v)" - ) + t.Run("should fail (nil regexp)", func(t *testing.T) { + invalidRex := []byte(nil) - mock := new(mockT) + if Regexp(mock, invalidRex, str) { + t.Errorf(msg, invalidRex) + } + if NotRegexp(mock, invalidRex, str) { + t.Errorf(msg, invalidRex) + } + }) + }) - t.Run("should fail (invalid regexp type)", func(t *testing.T) { - invalidRex := struct{ a string }{a: "invalid"} + t.Run("with fmt.Sprint conversion (edge case)", func(t *testing.T) { + t.Parallel() - if Regexp(mock, invalidRex, str) { - t.Errorf(msg, invalidRex) - } - if NotRegexp(mock, invalidRex, str) { - t.Errorf(msg, invalidRex) - } - }) + const ( + numeric = 1234 + msg = "expected %q to match %q" + rex = "^[0-9]+$" + ) - t.Run("should fail (nil regexp)", func(t *testing.T) { - invalidRex := []byte(nil) + mock := new(mockT) - if Regexp(mock, invalidRex, str) { - t.Errorf(msg, invalidRex) - } - if NotRegexp(mock, invalidRex, str) { - t.Errorf(msg, invalidRex) - } - }) + 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) + } }) + }) +} - t.Run("with fmt.Sprint conversion", func(t *testing.T) { - t.Parallel() - - const ( - numeric = 1234 - msg = "expected %q to match %q" - rex = "^[0-9]+$" - ) +func TestStringRegexp(t *testing.T) { + t.Parallel() - mock := new(mockT) + // run test cases with all combinations of supported types + // + // NOTE: testing pattern, focused on the expected result (true/false) and _NOT_ the content of the returned message. + // - stringRegexpCases: loop over generic test cases + // - testAllRegexpWithTypes: dispatch over type combinations of values + // - testAllRegexp: dispatch over the assertion variants (reflection-based, generic, X vs NotX semantics) + // Single assertion test functions: + // - testRegexp + // - testRegexpT + // - testNotRegexp + // - testNotRegexpT + for tc := range stringRegexpCases() { + t.Run(tc.name, tc.test) + } +} - 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) - } - }) - }) +// 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, + )}, }) } @@ -261,136 +284,3 @@ func testRex(rex string) *regexp.Regexp { rx, _ := compileRegex(rex) return rx } - -type stringEqualCase struct { - equalWant string - equalGot string - msgAndArgs []any - want string -} - -func stringEqualCases() iter.Seq[stringEqualCase] { - return slices.Values([]stringEqualCase{ - { - equalWant: "hi, \nmy name is", - equalGot: "what,\nmy name is", - want: "\t[a-z]+.go:\\d+: \n" + // NOTE: the exact file name reported should be asserted in integration tests - "\t+Error Trace:\t\n+" + - "\t+Error:\\s+Not equal:\\s+\n" + - "\\s+expected: \"hi, \\\\nmy name is\"\n" + - "\\s+actual\\s+: " + "\"what,\\\\nmy name is\"\n" + - "\\s+Diff:\n" + - "\\s+-+ Expected\n\\s+\\++ " + - "Actual\n" + - "\\s+@@ -1,2 \\+1,2 @@\n" + - "\\s+-hi, \n\\s+\\+what,\n" + - "\\s+my name is", - }, - }) -} - -func stringEqualFormattingCases() iter.Seq[stringEqualCase] { - return slices.Values([]stringEqualCase{ - { - equalWant: "want", - equalGot: "got", - want: "\t[a-z]+.go:\\d+: \n" + - "\t+Error Trace:\t\n" + - "\t+Error:\\s+Not equal:\\s+\n" + - "\\s+expected: \"want\"\n" + - "\\s+actual\\s+: \"got\"\n" + - "\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ " + - "Actual\n" + - "\\s+@@ -1 \\+1 @@\n" + - "\\s+-want\n" + - "\\s+\\+got\n", - }, - { - equalWant: "want", - equalGot: "got", - msgAndArgs: []any{"hello, %v!", "world"}, - want: "\t[a-z]+.go:[0-9]+: \n" + - "\t+Error Trace:\t\n" + - "\t+Error:\\s+Not equal:\\s+\n" + - "\\s+expected: \"want\"\n" + - "\\s+actual\\s+: \"got\"\n" + - "\\s+Diff:\n" + - "\\s+-+ Expected\n" + - "\\s+\\++ Actual\n" + - "\\s+@@ -1 \\+1 @@\n" + - "\\s+-want\n" + - "\\s+\\+got\n" + - "\\s+Messages:\\s+hello, world!\n", - }, - { - equalWant: "want", - equalGot: "got", - msgAndArgs: []any{123}, - want: "\t[a-z]+.go:[0-9]+: \n" + - "\t+Error Trace:\t\n" + - "\t+Error:\\s+Not equal:\\s+\n" + - "\\s+expected: \"want\"\n" + - "\\s+actual\\s+: \"got\"\n" + - "\\s+Diff:\n" + - "\\s+-+ Expected\n" + - "\\s+\\++ Actual\n" + - "\\s+@@ -1 \\+1 @@\n" + - "\\s+-want\n" + - "\\s+\\+got\n" + - "\\s+Messages:\\s+123\n", - }, - { - equalWant: "want", - equalGot: "got", - msgAndArgs: []any{struct{ a string }{"hello"}}, - want: "\t[a-z]+.go:[0-9]+: \n" + - "\t+Error Trace:\t\n" + - "\t+Error:\\s+Not equal:\\s+\n" + - "\\s+expected: \"want\"\n" + - "\\s+actual\\s+: \"got\"\n" + - "\\s+Diff:\n" + - "\\s+-+ Expected\n" + - "\\s+\\++ Actual\n" + - "\\s+@@ -1 \\+1 @@\n" + - "\\s+-want\n" + - "\\s+\\+got\n" + - "\\s+Messages:\\s+{a:hello}\n", - }, - }) -} - -// 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/type.go b/internal/assertions/type.go index 392a35548..d5c84b75f 100644 --- a/internal/assertions/type.go +++ b/internal/assertions/type.go @@ -83,6 +83,30 @@ func IsType(t T, expectedType, object any, msgAndArgs ...any) bool { return Fail(t, fmt.Sprintf("Object expected to be of type %T, but was %T", expectedType, object), msgAndArgs...) } +// IsOfTypeT asserts that an object is of a given type. +// +// # Usage +// +// assertions.IsOfTypeT[MyType](t,myVar) +// +// # Examples +// +// success: myType(123.123) +// failure: 123.123 +func IsOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool { + // Domain: type + if h, ok := t.(H); ok { + h.Helper() + } + + _, ok := object.(EType) + if ok { + return true + } + + return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %T", reflect.TypeFor[EType](), object), msgAndArgs...) +} + // IsNotType asserts that the specified objects are not of the same type. // // # Usage @@ -104,6 +128,30 @@ func IsNotType(t T, theType, object any, msgAndArgs ...any) bool { return Fail(t, fmt.Sprintf("Object type expected to be different than %T", theType), msgAndArgs...) } +// IsNotOfTypeT asserts that an object is of a given type. +// +// # Usage +// +// assertions.IsOfType[MyType](t,myVar) +// +// # Examples +// +// success: 123.123 +// failure: myType(123.123) +func IsNotOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool { + // Domain: type + if h, ok := t.(H); ok { + h.Helper() + } + + _, ok := object.(EType) + if !ok { + return true + } + + return Fail(t, fmt.Sprintf("Object type expected to be different than %T", reflect.TypeFor[EType]()), msgAndArgs...) +} + // Zero asserts that i is the zero value for its type. // // # Usage diff --git a/internal/assertions/type_test.go b/internal/assertions/type_test.go index 08f09be4e..1f247f1e8 100644 --- a/internal/assertions/type_test.go +++ b/internal/assertions/type_test.go @@ -65,6 +65,20 @@ func TestTypeNotIsType(t *testing.T) { } } +func TestTypeIsOfTypeT(t *testing.T) { + t.Parallel() + + mock := new(mockT) + type myType float64 + var myVar myType = 1.2 + f := 1.2 + + True(t, IsOfTypeT[myType](mock, myVar), "expected myVar to be of type %T", myVar) + False(t, IsNotOfTypeT[myType](mock, myVar), "expected myVar to be of type %T", myVar) + False(t, IsOfTypeT[myType](mock, f), "expected f (%T) not to be of type %T", f, myVar) + True(t, IsNotOfTypeT[myType](mock, f), "expected f (%T) not to be of type %T", f, myVar) +} + func TestTypeZeroWithSliceTooLongToPrint(t *testing.T) { t.Parallel() mock := new(mockT) diff --git a/require/require_assertions.go b/require/require_assertions.go index 6e7036f77..9f8df864f 100644 --- a/require/require_assertions.go +++ b/require/require_assertions.go @@ -2,11 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package require import ( + "iter" "net/http" "net/url" "reflect" @@ -88,6 +89,30 @@ func DirExists(t T, path string, msgAndArgs ...any) { t.FailNow() } +// DirNotExists checks whether a directory does not exist in the given path. +// It fails if the path points to an existing _directory_ only. +// +// # Usage +// +// assertions.DirNotExists(t, "path/to/directory") +// +// # Examples +// +// success: filepath.Join(testDataPath(),"non_existing_dir") +// failure: filepath.Join(testDataPath(),"existing_dir") +// +// Upon failure, the test [T] is marked as failed and stops execution. +func DirNotExists(t T, path string, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.DirNotExists(t, path, msgAndArgs...) { + return + } + + t.FailNow() +} + // 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, // the number of appearances of each of them in both lists should match. @@ -131,7 +156,7 @@ 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...) { + if assertions.ElementsMatchT[E](t, listA, listB, msgAndArgs...) { return } @@ -254,6 +279,38 @@ func EqualExportedValues(t T, expected any, actual any, msgAndArgs ...any) { t.FailNow() } +// EqualT asserts that two objects of the same comparable type are equal. +// +// Pointer variable equality is determined based on the equality of the memory addresses (unlike [Equal], but like [Same]). +// +// Functions, slices and maps are not comparable. See also [ComparisonOperators]. +// +// If you need to compare values of non-comparable types, or compare pointers by the value they point to, +// use [Equal] instead. +// +// # Usage +// +// assertions.EqualT(t, 123, 123) +// +// # Examples +// +// success: 123, 123 +// failure: 123, 456 +// +// Upon failure, the test [T] is marked as failed and stops execution. +// +// [ComparisonOperators]: https://go.dev/ref/spec#Comparison_operators. +func EqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.EqualT[V](t, expected, actual, msgAndArgs...) { + return + } + + t.FailNow() +} + // EqualValues asserts that two objects are equal or convertible to the larger // type and equal. // @@ -571,7 +628,7 @@ func FalseT[B Boolean](t T, value B, msgAndArgs ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.FalseT(t, value, msgAndArgs...) { + if assertions.FalseT[B](t, value, msgAndArgs...) { return } @@ -650,6 +707,30 @@ func FileNotEmpty(t T, path string, msgAndArgs ...any) { t.FailNow() } +// FileNotExists checks whether a file does not exist in a given path. It fails +// if the path points to an existing _file_ only. +// +// # Usage +// +// assertions.FileNotExists(t, "path/to/file") +// +// # Examples +// +// success: filepath.Join(testDataPath(),"non_existing_file") +// failure: filepath.Join(testDataPath(),"existing_file") +// +// Upon failure, the test [T] is marked as failed and stops execution. +func FileNotExists(t T, path string, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.FileNotExists(t, path, msgAndArgs...) { + return + } + + t.FailNow() +} + // 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. @@ -736,7 +817,7 @@ func GreaterOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndA if h, ok := t.(H); ok { h.Helper() } - if assertions.GreaterOrEqualT(t, e1, e2, msgAndArgs...) { + if assertions.GreaterOrEqualT[Orderable](t, e1, e2, msgAndArgs...) { return } @@ -772,7 +853,7 @@ func GreaterT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ... if h, ok := t.(H); ok { h.Helper() } - if assertions.GreaterT(t, e1, e2, msgAndArgs...) { + if assertions.GreaterT[Orderable](t, e1, e2, msgAndArgs...) { return } @@ -1066,7 +1147,7 @@ func InDeltaT[Number Measurable](t T, expected Number, actual Number, delta Numb if h, ok := t.(H); ok { h.Helper() } - if assertions.InDeltaT(t, expected, actual, delta, msgAndArgs...) { + if assertions.InDeltaT[Number](t, expected, actual, delta, msgAndArgs...) { return } @@ -1171,14 +1252,14 @@ func InEpsilonT[Number Measurable](t T, expected Number, actual Number, epsilon if h, ok := t.(H); ok { h.Helper() } - if assertions.InEpsilonT(t, expected, actual, epsilon, msgAndArgs...) { + if assertions.InEpsilonT[Number](t, expected, actual, epsilon, msgAndArgs...) { return } t.FailNow() } -// IsDecreasing asserts that the collection is decreasing. +// IsDecreasing asserts that the collection is strictly decreasing. // // # Usage // @@ -1203,7 +1284,32 @@ func IsDecreasing(t T, object any, msgAndArgs ...any) { t.FailNow() } -// IsIncreasing asserts that the collection is increasing. +// IsDecreasingT asserts that a slice of [Ordered] is strictly decreasing. +// +// # Usage +// +// assertions.IsDecreasingT(t, []int{2, 1, 0}) +// assertions.IsDecreasingT(t, []float{2, 1}) +// assertions.IsDecreasingT(t, []string{"b", "a"}) +// +// # Examples +// +// success: []int{3, 2, 1} +// failure: []int{1, 2, 3} +// +// Upon failure, the test [T] is marked as failed and stops execution. +func IsDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.IsDecreasingT[OrderedSlice, E](t, collection, msgAndArgs...) { + return + } + + t.FailNow() +} + +// IsIncreasing asserts that the collection is strictly increasing. // // # Usage // @@ -1228,7 +1334,32 @@ func IsIncreasing(t T, object any, msgAndArgs ...any) { t.FailNow() } -// IsNonDecreasing asserts that the collection is not decreasing. +// IsIncreasingT asserts that a slice of [Ordered] is strictly increasing. +// +// # Usage +// +// assertions.IsIncreasingT(t, []int{1, 2, 3}) +// assertions.IsIncreasingT(t, []float{1, 2}) +// assertions.IsIncreasingT(t, []string{"a", "b"}) +// +// # Examples +// +// success: []int{1, 2, 3} +// failure: []int{1, 1, 2} +// +// Upon failure, the test [T] is marked as failed and stops execution. +func IsIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.IsIncreasingT[OrderedSlice, E](t, collection, msgAndArgs...) { + return + } + + t.FailNow() +} + +// IsNonDecreasing asserts that the collection is not strictly decreasing. // // # Usage // @@ -1239,7 +1370,7 @@ func IsIncreasing(t T, object any, msgAndArgs ...any) { // # Examples // // success: []int{1, 1, 2} -// failure: []int{2, 1, 1} +// failure: []int{2, 1, 0} // // Upon failure, the test [T] is marked as failed and stops execution. func IsNonDecreasing(t T, object any, msgAndArgs ...any) { @@ -1253,6 +1384,31 @@ func IsNonDecreasing(t T, object any, msgAndArgs ...any) { t.FailNow() } +// IsNonDecreasingT asserts that a slice of [Ordered] is not decreasing. +// +// # Usage +// +// assertions.IsNonDecreasingT(t, []int{1, 1, 2}) +// assertions.IsNonDecreasingT(t, []float{1, 2}) +// assertions.IsNonDecreasingT(t, []string{"a", "b"}) +// +// # Examples +// +// success: []int{1, 1, 2} +// failure: []int{2, 1, 0} +// +// Upon failure, the test [T] is marked as failed and stops execution. +func IsNonDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.IsNonDecreasingT[OrderedSlice, E](t, collection, msgAndArgs...) { + return + } + + t.FailNow() +} + // IsNonIncreasing asserts that the collection is not increasing. // // # Usage @@ -1278,6 +1434,54 @@ func IsNonIncreasing(t T, object any, msgAndArgs ...any) { t.FailNow() } +// IsNonIncreasingT asserts that a slice of [Ordered] is NOT strictly increasing. +// +// # Usage +// +// assertions.IsNonIncreasing(t, []int{2, 1, 1}) +// assertions.IsNonIncreasing(t, []float{2, 1}) +// assertions.IsNonIncreasing(t, []string{"b", "a"}) +// +// # Examples +// +// success: []int{2, 1, 1} +// failure: []int{1, 2, 3} +// +// Upon failure, the test [T] is marked as failed and stops execution. +func IsNonIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.IsNonIncreasingT[OrderedSlice, E](t, collection, msgAndArgs...) { + return + } + + t.FailNow() +} + +// IsNotOfTypeT asserts that an object is of a given type. +// +// # Usage +// +// assertions.IsOfType[MyType](t,myVar) +// +// # Examples +// +// success: 123.123 +// failure: myType(123.123) +// +// Upon failure, the test [T] is marked as failed and stops execution. +func IsNotOfTypeT[EType any](t T, object any, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.IsNotOfTypeT[EType](t, object, msgAndArgs...) { + return + } + + t.FailNow() +} + // IsNotType asserts that the specified objects are not of the same type. // // # Usage @@ -1301,6 +1505,29 @@ func IsNotType(t T, theType any, object any, msgAndArgs ...any) { t.FailNow() } +// IsOfTypeT asserts that an object is of a given type. +// +// # Usage +// +// assertions.IsOfTypeT[MyType](t,myVar) +// +// # Examples +// +// success: myType(123.123) +// failure: 123.123 +// +// Upon failure, the test [T] is marked as failed and stops execution. +func IsOfTypeT[EType any](t T, object any, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.IsOfTypeT[EType](t, object, msgAndArgs...) { + return + } + + t.FailNow() +} + // IsType asserts that the specified objects are of the same type. // // # Usage @@ -1394,7 +1621,7 @@ 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...) { + if assertions.JSONEqT[EDoc, ADoc](t, expected, actual, msgAndArgs...) { return } @@ -1541,7 +1768,7 @@ func LessOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs if h, ok := t.(H); ok { h.Helper() } - if assertions.LessOrEqualT(t, e1, e2, msgAndArgs...) { + if assertions.LessOrEqualT[Orderable](t, e1, e2, msgAndArgs...) { return } @@ -1576,7 +1803,53 @@ 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...) { + if assertions.LessT[Orderable](t, e1, e2, msgAndArgs...) { + return + } + + t.FailNow() +} + +// MapContainsT asserts that the specified map contains a key. +// +// # Usage +// +// assertions.MapContainsT(t, map[string]string{"Hello": "x","World": "y"}, "World") +// +// # Examples +// +// success: map[string]string{"A": "B"}, "A" +// failure: map[string]string{"A": "B"}, "C" +// +// Upon failure, the test [T] is marked as failed and stops execution. +func MapContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.MapContainsT[Map, K, V](t, m, key, msgAndArgs...) { + return + } + + t.FailNow() +} + +// MapNotContainsT asserts that the specified map does not contain a key. +// +// # Usage +// +// assertions.MapNotContainsT(t, map[string]string{"Hello": "x","World": "y"}, "hi") +// +// # Examples +// +// success: map[string]string{"A": "B"}, "C" +// failure: map[string]string{"A": "B"}, "A" +// +// Upon failure, the test [T] is marked as failed and stops execution. +func MapNotContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.MapNotContainsT[Map, K, V](t, m, key, msgAndArgs...) { return } @@ -1624,7 +1897,7 @@ func NegativeT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...an if h, ok := t.(H); ok { h.Helper() } - if assertions.NegativeT(t, e, msgAndArgs...) { + if assertions.NegativeT[SignedNumber](t, e, msgAndArgs...) { return } @@ -1690,30 +1963,6 @@ func Nil(t T, object any, msgAndArgs ...any) { t.FailNow() } -// NoDirExists checks whether a directory does not exist in the given path. -// It fails if the path points to an existing _directory_ only. -// -// # Usage -// -// assertions.NoDirExists(t, "path/to/directory") -// -// # Examples -// -// success: filepath.Join(testDataPath(),"non_existing_dir") -// failure: filepath.Join(testDataPath(),"existing_dir") -// -// Upon failure, the test [T] is marked as failed and stops execution. -func NoDirExists(t T, path string, msgAndArgs ...any) { - if h, ok := t.(H); ok { - h.Helper() - } - if assertions.NoDirExists(t, path, msgAndArgs...) { - return - } - - t.FailNow() -} - // NoError asserts that a function returned a nil error (ie. no error). // // # Usage @@ -1740,30 +1989,6 @@ func NoError(t T, err error, msgAndArgs ...any) { t.FailNow() } -// NoFileExists checks whether a file does not exist in a given path. It fails -// if the path points to an existing _file_ only. -// -// # Usage -// -// assertions.NoFileExists(t, "path/to/file") -// -// # Examples -// -// success: filepath.Join(testDataPath(),"non_existing_file") -// failure: filepath.Join(testDataPath(),"existing_file") -// -// Upon failure, the test [T] is marked as failed and stops execution. -func NoFileExists(t T, path string, msgAndArgs ...any) { - if h, ok := t.(H); ok { - h.Helper() - } - if assertions.NoFileExists(t, path, msgAndArgs...) { - return - } - - t.FailNow() -} - // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // @@ -1839,7 +2064,7 @@ func NotElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...an if h, ok := t.(H); ok { h.Helper() } - if assertions.NotElementsMatchT(t, listA, listB, msgAndArgs...) { + if assertions.NotElementsMatchT[E](t, listA, listB, msgAndArgs...) { return } @@ -1897,6 +2122,31 @@ func NotEqual(t T, expected any, actual any, msgAndArgs ...any) { t.FailNow() } +// NotEqualT asserts that the specified values of the same comparable type are NOT equal. +// +// See [EqualT]. +// +// # Usage +// +// assertions.NotEqualT(t, obj1, obj2) +// +// # Examples +// +// success: 123, 456 +// failure: 123, 123 +// +// Upon failure, the test [T] is marked as failed and stops execution. +func NotEqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.NotEqualT[V](t, expected, actual, msgAndArgs...) { + return + } + + t.FailNow() +} + // NotEqualValues asserts that two objects are not equal even when converted to the same type. // // # Usage @@ -2109,7 +2359,7 @@ func NotRegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...a if h, ok := t.(H); ok { h.Helper() } - if assertions.NotRegexpT(t, rx, actual, msgAndArgs...) { + if assertions.NotRegexpT[Rex, ADoc](t, rx, actual, msgAndArgs...) { return } @@ -2142,6 +2392,58 @@ func NotSame(t T, expected any, actual any, msgAndArgs ...any) { t.FailNow() } +// NotSameT asserts that two pointers do not reference the same object. +// +// See [SameT] +// +// # Usage +// +// assertions.NotSameT(t, ptr1, ptr2) +// +// # Examples +// +// success: &staticVar, ptr("static string") +// failure: &staticVar, staticVarPtr +// +// Upon failure, the test [T] is marked as failed and stops execution. +func NotSameT[P any](t T, expected *P, actual *P, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.NotSameT[P](t, expected, actual, msgAndArgs...) { + return + } + + t.FailNow() +} + +// NotSortedT asserts that the slice of [Ordered] is NOT sorted (i.e. non-strictly increasing). +// +// Unlike [IsDecreasingT], it accepts slices that are neither increasing nor decreasing. +// +// # Usage +// +// assertions.NotSortedT(t, []int{3, 2, 3}) +// assertions.NotSortedT(t, []float{2, 1}) +// assertions.NotSortedT(t, []string{"b", "a"}) +// +// # Examples +// +// success: []int{3, 1, 3} +// failure: []int{1, 4, 8} +// +// Upon failure, the test [T] is marked as failed and stops execution. +func NotSortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.NotSortedT[OrderedSlice, E](t, collection, msgAndArgs...) { + return + } + + t.FailNow() +} + // NotSubset asserts that the list (array, slice, or map) does NOT contain all // elements given in the subset (array, slice, or map). // Map elements are key-value pairs unless compared with an array or slice where @@ -2307,7 +2609,7 @@ func PositiveT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...an if h, ok := t.(H); ok { h.Helper() } - if assertions.PositiveT(t, e, msgAndArgs...) { + if assertions.PositiveT[SignedNumber](t, e, msgAndArgs...) { return } @@ -2358,7 +2660,7 @@ func RegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) if h, ok := t.(H); ok { h.Helper() } - if assertions.RegexpT(t, rx, actual, msgAndArgs...) { + if assertions.RegexpT[Rex, ADoc](t, rx, actual, msgAndArgs...) { return } @@ -2391,6 +2693,244 @@ func Same(t T, expected any, actual any, msgAndArgs ...any) { t.FailNow() } +// SameT asserts that two pointers of the same type reference the same object. +// +// # Usage +// +// assertions.SameT(t, ptr1, ptr2) +// +// # Examples +// +// success: &staticVar, staticVarPtr +// failure: &staticVar, ptr("static string") +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SameT[P any](t T, expected *P, actual *P, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SameT[P](t, expected, actual, msgAndArgs...) { + return + } + + t.FailNow() +} + +// SeqContainsT asserts that the specified iterator contains a comparable element. +// +// # Usage +// +// assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World") +// +// # Examples +// +// success: slices.Values([]string{"A","B"}), "A" +// failure: slices.Values([]string{"A","B"}), "C" +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SeqContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SeqContainsT[E](t, iter, element, msgAndArgs...) { + return + } + + t.FailNow() +} + +// SeqNotContainsT asserts that the specified iterator does not contain a comparable element. +// +// # Usage +// +// assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World") +// +// # Examples +// +// success: slices.Values([]string{"A","B"}), "C" +// failure: slices.Values([]string{"A","B"}), "A" +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SeqNotContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SeqNotContainsT[E](t, iter, element, msgAndArgs...) { + return + } + + t.FailNow() +} + +// SliceContainsT asserts that the specified slice contains a comparable element. +// +// # Usage +// +// assertions.SliceContainsT(t, []{"Hello","World"}, "World") +// +// # Examples +// +// success: []string{"A","B"}, "A" +// failure: []string{"A","B"}, "C" +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SliceContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SliceContainsT[Slice, E](t, s, element, msgAndArgs...) { + return + } + + t.FailNow() +} + +// SliceNotContainsT asserts that the specified slice does not contain a comparable element. +// +// # Usage +// +// assertions.SliceNotContainsT(t, []{"Hello","World"}, "hi") +// +// # Examples +// +// success: []string{"A","B"}, "C" +// failure: []string{"A","B"}, "A" +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SliceNotContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SliceNotContainsT[Slice, E](t, s, element, msgAndArgs...) { + return + } + + t.FailNow() +} + +// SliceNotSubsetT asserts that a slice of comparable elements does not contain all the elements given in the subset. +// +// # Usage +// +// assertions.SliceNotSubsetT(t, []int{1, 2, 3}, []int{1, 4}) +// +// # Examples +// +// success: []int{1, 2, 3}, []int{4, 5} +// failure: []int{1, 2, 3}, []int{1, 2} +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SliceNotSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SliceNotSubsetT[Slice, E](t, list, subset, msgAndArgs...) { + return + } + + t.FailNow() +} + +// SliceSubsetT asserts that a slice of comparable elements contains all the elements given in the subset. +// +// # Usage +// +// assertions.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) +// +// # Examples +// +// success: []int{1, 2, 3}, []int{1, 2} +// failure: []int{1, 2, 3}, []int{4, 5} +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SliceSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SliceSubsetT[Slice, E](t, list, subset, msgAndArgs...) { + return + } + + t.FailNow() +} + +// SortedT asserts that the slice of [Ordered] is sorted (i.e. non-strictly increasing). +// +// Unlike [IsIncreasingT], it accepts elements to be equal. +// +// # Usage +// +// assertions.SortedT(t, []int{1, 2, 3}) +// assertions.SortedT(t, []float{1, 2}) +// assertions.SortedT(t, []string{"a", "b"}) +// +// # Examples +// +// success: []int{1, 1, 3} +// failure: []int{1, 4, 2} +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SortedT[OrderedSlice, E](t, collection, msgAndArgs...) { + return + } + + t.FailNow() +} + +// StringContainsT asserts that a string contains the specified substring. +// +// Strings may be go strings or []byte. +// +// # Usage +// +// assertions.StringContainsT(t, "Hello World", "World") +// +// # Examples +// +// success: "AB", "A" +// failure: "AB", "C" +// +// Upon failure, the test [T] is marked as failed and stops execution. +func StringContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.StringContainsT[ADoc, EDoc](t, str, substring, msgAndArgs...) { + return + } + + t.FailNow() +} + +// StringNotContainsT asserts that a string does not contain the specified substring. +// +// Strings may be go strings or []byte. +// +// # Usage +// +// assertions.StringNotContainsT(t, "Hello World", "hi") +// +// # Examples +// +// success: "AB", "C" +// failure: "AB", "A" +// +// Upon failure, the test [T] is marked as failed and stops execution. +func StringNotContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.StringNotContainsT[ADoc, EDoc](t, str, substring, msgAndArgs...) { + return + } + + t.FailNow() +} + // Subset asserts that the list (array, slice, or map) contains all elements // given in the subset (array, slice, or map). // @@ -2463,7 +3003,7 @@ func TrueT[B Boolean](t T, value B, msgAndArgs ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.TrueT(t, value, msgAndArgs...) { + if assertions.TrueT[B](t, value, msgAndArgs...) { return } @@ -2598,7 +3138,7 @@ func YAMLEqT[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msgAndArgs ...any if h, ok := t.(H); ok { h.Helper() } - if assertions.YAMLEqT(t, expected, actual, msgAndArgs...) { + if assertions.YAMLEqT[EDoc, ADoc](t, expected, actual, msgAndArgs...) { return } diff --git a/require/require_assertions_test.go b/require/require_assertions_test.go index 5436ee3fd..6fdcc9445 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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package require @@ -13,6 +13,7 @@ import ( "net/url" "path/filepath" "reflect" + "slices" "testing" "time" ) @@ -77,6 +78,26 @@ func TestDirExists(t *testing.T) { }) } +func TestDirNotExists(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + DirNotExists(t, filepath.Join(testDataPath(), "non_existing_dir")) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + DirNotExists(mock, filepath.Join(testDataPath(), "existing_dir")) + // require functions don't return a value + if !mock.failed { + t.Error("DirNotExists should call FailNow()") + } + }) +} + func TestElementsMatch(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -197,6 +218,26 @@ func TestEqualExportedValues(t *testing.T) { }) } +func TestEqualT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + EqualT(t, 123, 123) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + EqualT(mock, 123, 456) + // require functions don't return a value + if !mock.failed { + t.Error("EqualT should call FailNow()") + } + }) +} + func TestEqualValues(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -485,6 +526,26 @@ func TestFileNotEmpty(t *testing.T) { }) } +func TestFileNotExists(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + FileNotExists(t, filepath.Join(testDataPath(), "non_existing_file")) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + FileNotExists(mock, filepath.Join(testDataPath(), "existing_file")) + // require functions don't return a value + if !mock.failed { + t.Error("FileNotExists should call FailNow()") + } + }) +} + func TestGreater(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -865,6 +926,26 @@ func TestIsDecreasing(t *testing.T) { }) } +func TestIsDecreasingT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + IsDecreasingT(t, []int{3, 2, 1}) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + IsDecreasingT(mock, []int{1, 2, 3}) + // require functions don't return a value + if !mock.failed { + t.Error("IsDecreasingT should call FailNow()") + } + }) +} + func TestIsIncreasing(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -885,6 +966,26 @@ func TestIsIncreasing(t *testing.T) { }) } +func TestIsIncreasingT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + IsIncreasingT(t, []int{1, 2, 3}) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + IsIncreasingT(mock, []int{1, 1, 2}) + // require functions don't return a value + if !mock.failed { + t.Error("IsIncreasingT should call FailNow()") + } + }) +} + func TestIsNonDecreasing(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -897,7 +998,7 @@ func TestIsNonDecreasing(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - IsNonDecreasing(mock, []int{2, 1, 1}) + IsNonDecreasing(mock, []int{2, 1, 0}) // require functions don't return a value if !mock.failed { t.Error("IsNonDecreasing should call FailNow()") @@ -905,6 +1006,26 @@ func TestIsNonDecreasing(t *testing.T) { }) } +func TestIsNonDecreasingT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + IsNonDecreasingT(t, []int{1, 1, 2}) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + IsNonDecreasingT(mock, []int{2, 1, 0}) + // require functions don't return a value + if !mock.failed { + t.Error("IsNonDecreasingT should call FailNow()") + } + }) +} + func TestIsNonIncreasing(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -925,6 +1046,46 @@ func TestIsNonIncreasing(t *testing.T) { }) } +func TestIsNonIncreasingT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + IsNonIncreasingT(t, []int{2, 1, 1}) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + IsNonIncreasingT(mock, []int{1, 2, 3}) + // require functions don't return a value + if !mock.failed { + t.Error("IsNonIncreasingT should call FailNow()") + } + }) +} + +func TestIsNotOfTypeT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + IsNotOfTypeT[myType](t, 123.123) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + IsNotOfTypeT[myType](mock, myType(123.123)) + // require functions don't return a value + if !mock.failed { + t.Error("IsNotOfTypeT should call FailNow()") + } + }) +} + func TestIsNotType(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -945,6 +1106,26 @@ func TestIsNotType(t *testing.T) { }) } +func TestIsOfTypeT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + IsOfTypeT[myType](t, myType(123.123)) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + IsOfTypeT[myType](mock, 123.123) + // require functions don't return a value + if !mock.failed { + t.Error("IsOfTypeT should call FailNow()") + } + }) +} + func TestIsType(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1145,11 +1326,11 @@ func TestLessT(t *testing.T) { }) } -func TestNegative(t *testing.T) { +func TestMapContainsT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - Negative(t, -1) + MapContainsT(t, map[string]string{"A": "B"}, "A") // require functions don't return a value }) @@ -1157,19 +1338,19 @@ func TestNegative(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - Negative(mock, 1) + MapContainsT(mock, map[string]string{"A": "B"}, "C") // require functions don't return a value if !mock.failed { - t.Error("Negative should call FailNow()") + t.Error("MapContainsT should call FailNow()") } }) } -func TestNegativeT(t *testing.T) { +func TestMapNotContainsT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - NegativeT(t, -1) + MapNotContainsT(t, map[string]string{"A": "B"}, "C") // require functions don't return a value }) @@ -1177,19 +1358,19 @@ func TestNegativeT(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - NegativeT(mock, 1) + MapNotContainsT(mock, map[string]string{"A": "B"}, "A") // require functions don't return a value if !mock.failed { - t.Error("NegativeT should call FailNow()") + t.Error("MapNotContainsT should call FailNow()") } }) } -func TestNever(t *testing.T) { +func TestNegative(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - Never(t, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond) + Negative(t, -1) // require functions don't return a value }) @@ -1197,19 +1378,19 @@ func TestNever(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - Never(mock, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond) + Negative(mock, 1) // require functions don't return a value if !mock.failed { - t.Error("Never should call FailNow()") + t.Error("Negative should call FailNow()") } }) } -func TestNil(t *testing.T) { +func TestNegativeT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - Nil(t, nil) + NegativeT(t, -1) // require functions don't return a value }) @@ -1217,19 +1398,19 @@ func TestNil(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - Nil(mock, "not nil") + NegativeT(mock, 1) // require functions don't return a value if !mock.failed { - t.Error("Nil should call FailNow()") + t.Error("NegativeT should call FailNow()") } }) } -func TestNoDirExists(t *testing.T) { +func TestNever(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - NoDirExists(t, filepath.Join(testDataPath(), "non_existing_dir")) + Never(t, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond) // require functions don't return a value }) @@ -1237,19 +1418,19 @@ func TestNoDirExists(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - NoDirExists(mock, filepath.Join(testDataPath(), "existing_dir")) + Never(mock, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond) // require functions don't return a value if !mock.failed { - t.Error("NoDirExists should call FailNow()") + t.Error("Never should call FailNow()") } }) } -func TestNoError(t *testing.T) { +func TestNil(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - NoError(t, nil) + Nil(t, nil) // require functions don't return a value }) @@ -1257,19 +1438,19 @@ func TestNoError(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - NoError(mock, ErrTest) + Nil(mock, "not nil") // require functions don't return a value if !mock.failed { - t.Error("NoError should call FailNow()") + t.Error("Nil should call FailNow()") } }) } -func TestNoFileExists(t *testing.T) { +func TestNoError(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - NoFileExists(t, filepath.Join(testDataPath(), "non_existing_file")) + NoError(t, nil) // require functions don't return a value }) @@ -1277,10 +1458,10 @@ func TestNoFileExists(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - NoFileExists(mock, filepath.Join(testDataPath(), "existing_file")) + NoError(mock, ErrTest) // require functions don't return a value if !mock.failed { - t.Error("NoFileExists should call FailNow()") + t.Error("NoError should call FailNow()") } }) } @@ -1385,6 +1566,26 @@ func TestNotEqual(t *testing.T) { }) } +func TestNotEqualT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + NotEqualT(t, 123, 456) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + NotEqualT(mock, 123, 123) + // require functions don't return a value + if !mock.failed { + t.Error("NotEqualT should call FailNow()") + } + }) +} + func TestNotEqualValues(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1585,6 +1786,46 @@ func TestNotSame(t *testing.T) { }) } +func TestNotSameT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + NotSameT(t, &staticVar, ptr("static string")) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + NotSameT(mock, &staticVar, staticVarPtr) + // require functions don't return a value + if !mock.failed { + t.Error("NotSameT should call FailNow()") + } + }) +} + +func TestNotSortedT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + NotSortedT(t, []int{3, 1, 3}) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + NotSortedT(mock, []int{1, 4, 8}) + // require functions don't return a value + if !mock.failed { + t.Error("NotSortedT should call FailNow()") + } + }) +} + func TestNotSubset(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1785,6 +2026,206 @@ func TestSame(t *testing.T) { }) } +func TestSameT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + SameT(t, &staticVar, staticVarPtr) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SameT(mock, &staticVar, ptr("static string")) + // require functions don't return a value + if !mock.failed { + t.Error("SameT should call FailNow()") + } + }) +} + +func TestSeqContainsT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + SeqContainsT(t, slices.Values([]string{"A", "B"}), "A") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SeqContainsT(mock, slices.Values([]string{"A", "B"}), "C") + // require functions don't return a value + if !mock.failed { + t.Error("SeqContainsT should call FailNow()") + } + }) +} + +func TestSeqNotContainsT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + SeqNotContainsT(t, slices.Values([]string{"A", "B"}), "C") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SeqNotContainsT(mock, slices.Values([]string{"A", "B"}), "A") + // require functions don't return a value + if !mock.failed { + t.Error("SeqNotContainsT should call FailNow()") + } + }) +} + +func TestSliceContainsT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + SliceContainsT(t, []string{"A", "B"}, "A") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SliceContainsT(mock, []string{"A", "B"}, "C") + // require functions don't return a value + if !mock.failed { + t.Error("SliceContainsT should call FailNow()") + } + }) +} + +func TestSliceNotContainsT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + SliceNotContainsT(t, []string{"A", "B"}, "C") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SliceNotContainsT(mock, []string{"A", "B"}, "A") + // require functions don't return a value + if !mock.failed { + t.Error("SliceNotContainsT should call FailNow()") + } + }) +} + +func TestSliceNotSubsetT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + SliceNotSubsetT(t, []int{1, 2, 3}, []int{4, 5}) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SliceNotSubsetT(mock, []int{1, 2, 3}, []int{1, 2}) + // require functions don't return a value + if !mock.failed { + t.Error("SliceNotSubsetT should call FailNow()") + } + }) +} + +func TestSliceSubsetT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SliceSubsetT(mock, []int{1, 2, 3}, []int{4, 5}) + // require functions don't return a value + if !mock.failed { + t.Error("SliceSubsetT should call FailNow()") + } + }) +} + +func TestSortedT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + SortedT(t, []int{1, 1, 3}) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SortedT(mock, []int{1, 4, 2}) + // require functions don't return a value + if !mock.failed { + t.Error("SortedT should call FailNow()") + } + }) +} + +func TestStringContainsT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + StringContainsT(t, "AB", "A") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + StringContainsT(mock, "AB", "C") + // require functions don't return a value + if !mock.failed { + t.Error("StringContainsT should call FailNow()") + } + }) +} + +func TestStringNotContainsT(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + StringNotContainsT(t, "AB", "C") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + StringNotContainsT(mock, "AB", "A") + // require functions don't return a value + if !mock.failed { + t.Error("StringNotContainsT should call FailNow()") + } + }) +} + func TestSubset(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -2010,3 +2451,5 @@ type dummyError struct { func (d *dummyError) Error() string { return "dummy error" } + +type myType float64 diff --git a/require/require_examples_test.go b/require/require_examples_test.go index 083d4860f..749f60689 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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package require_test @@ -13,6 +13,7 @@ import ( "net/url" "path/filepath" "reflect" + "slices" "testing" "time" @@ -46,6 +47,14 @@ func ExampleDirExists() { // Output: passed } +func ExampleDirNotExists() { + t := new(testing.T) + require.DirNotExists(t, filepath.Join(testDataPath(), "non_existing_dir")) + fmt.Println("passed") + + // Output: passed +} + func ExampleElementsMatch() { t := new(testing.T) require.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) @@ -94,6 +103,14 @@ func ExampleEqualExportedValues() { // Output: passed } +func ExampleEqualT() { + t := new(testing.T) + require.EqualT(t, 123, 123) + fmt.Println("passed") + + // Output: passed +} + func ExampleEqualValues() { t := new(testing.T) require.EqualValues(t, uint32(123), int32(123)) @@ -210,6 +227,14 @@ func ExampleFileNotEmpty() { // Output: passed } +func ExampleFileNotExists() { + t := new(testing.T) + require.FileNotExists(t, filepath.Join(testDataPath(), "non_existing_file")) + fmt.Println("passed") + + // Output: passed +} + func ExampleGreater() { t := new(testing.T) require.Greater(t, 2, 1) @@ -362,6 +387,14 @@ func ExampleIsDecreasing() { // Output: passed } +func ExampleIsDecreasingT() { + t := new(testing.T) + require.IsDecreasingT(t, []int{3, 2, 1}) + fmt.Println("passed") + + // Output: passed +} + func ExampleIsIncreasing() { t := new(testing.T) require.IsIncreasing(t, []int{1, 2, 3}) @@ -370,6 +403,14 @@ func ExampleIsIncreasing() { // Output: passed } +func ExampleIsIncreasingT() { + t := new(testing.T) + require.IsIncreasingT(t, []int{1, 2, 3}) + fmt.Println("passed") + + // Output: passed +} + func ExampleIsNonDecreasing() { t := new(testing.T) require.IsNonDecreasing(t, []int{1, 1, 2}) @@ -378,6 +419,14 @@ func ExampleIsNonDecreasing() { // Output: passed } +func ExampleIsNonDecreasingT() { + t := new(testing.T) + require.IsNonDecreasingT(t, []int{1, 1, 2}) + fmt.Println("passed") + + // Output: passed +} + func ExampleIsNonIncreasing() { t := new(testing.T) require.IsNonIncreasing(t, []int{2, 1, 1}) @@ -386,6 +435,22 @@ func ExampleIsNonIncreasing() { // Output: passed } +func ExampleIsNonIncreasingT() { + t := new(testing.T) + require.IsNonIncreasingT(t, []int{2, 1, 1}) + fmt.Println("passed") + + // Output: passed +} + +func ExampleIsNotOfTypeT() { + t := new(testing.T) + require.IsNotOfTypeT[myType](t, 123.123) + fmt.Println("passed") + + // Output: passed +} + func ExampleIsNotType() { t := new(testing.T) require.IsNotType(t, int32(123), int64(456)) @@ -394,6 +459,14 @@ func ExampleIsNotType() { // Output: passed } +func ExampleIsOfTypeT() { + t := new(testing.T) + require.IsOfTypeT[myType](t, myType(123.123)) + fmt.Println("passed") + + // Output: passed +} + func ExampleIsType() { t := new(testing.T) require.IsType(t, 123, 456) @@ -474,59 +547,59 @@ func ExampleLessT() { // Output: passed } -func ExampleNegative() { +func ExampleMapContainsT() { t := new(testing.T) - require.Negative(t, -1) + require.MapContainsT(t, map[string]string{"A": "B"}, "A") fmt.Println("passed") // Output: passed } -func ExampleNegativeT() { +func ExampleMapNotContainsT() { t := new(testing.T) - require.NegativeT(t, -1) + require.MapNotContainsT(t, map[string]string{"A": "B"}, "C") fmt.Println("passed") // Output: passed } -func ExampleNever() { +func ExampleNegative() { t := new(testing.T) - require.Never(t, func() bool { - return false - }, 100*time.Millisecond, 20*time.Millisecond) + require.Negative(t, -1) fmt.Println("passed") // Output: passed } -func ExampleNil() { +func ExampleNegativeT() { t := new(testing.T) - require.Nil(t, nil) + require.NegativeT(t, -1) fmt.Println("passed") // Output: passed } -func ExampleNoDirExists() { +func ExampleNever() { t := new(testing.T) - require.NoDirExists(t, filepath.Join(testDataPath(), "non_existing_dir")) + require.Never(t, func() bool { + return false + }, 100*time.Millisecond, 20*time.Millisecond) fmt.Println("passed") // Output: passed } -func ExampleNoError() { +func ExampleNil() { t := new(testing.T) - require.NoError(t, nil) + require.Nil(t, nil) fmt.Println("passed") // Output: passed } -func ExampleNoFileExists() { +func ExampleNoError() { t := new(testing.T) - require.NoFileExists(t, filepath.Join(testDataPath(), "non_existing_file")) + require.NoError(t, nil) fmt.Println("passed") // Output: passed @@ -572,6 +645,14 @@ func ExampleNotEqual() { // Output: passed } +func ExampleNotEqualT() { + t := new(testing.T) + require.NotEqualT(t, 123, 456) + fmt.Println("passed") + + // Output: passed +} + func ExampleNotEqualValues() { t := new(testing.T) require.NotEqualValues(t, uint32(123), int32(456)) @@ -653,6 +734,22 @@ func ExampleNotSame() { // Output: passed } +func ExampleNotSameT() { + t := new(testing.T) + require.NotSameT(t, &staticVar, ptr("static string")) + fmt.Println("passed") + + // Output: passed +} + +func ExampleNotSortedT() { + t := new(testing.T) + require.NotSortedT(t, []int{3, 1, 3}) + fmt.Println("passed") + + // Output: passed +} + func ExampleNotSubset() { t := new(testing.T) require.NotSubset(t, []int{1, 2, 3}, []int{4, 5}) @@ -739,6 +836,86 @@ func ExampleSame() { // Output: passed } +func ExampleSameT() { + t := new(testing.T) + require.SameT(t, &staticVar, staticVarPtr) + fmt.Println("passed") + + // Output: passed +} + +func ExampleSeqContainsT() { + t := new(testing.T) + require.SeqContainsT(t, slices.Values([]string{"A", "B"}), "A") + fmt.Println("passed") + + // Output: passed +} + +func ExampleSeqNotContainsT() { + t := new(testing.T) + require.SeqNotContainsT(t, slices.Values([]string{"A", "B"}), "C") + fmt.Println("passed") + + // Output: passed +} + +func ExampleSliceContainsT() { + t := new(testing.T) + require.SliceContainsT(t, []string{"A", "B"}, "A") + fmt.Println("passed") + + // Output: passed +} + +func ExampleSliceNotContainsT() { + t := new(testing.T) + require.SliceNotContainsT(t, []string{"A", "B"}, "C") + fmt.Println("passed") + + // Output: passed +} + +func ExampleSliceNotSubsetT() { + t := new(testing.T) + require.SliceNotSubsetT(t, []int{1, 2, 3}, []int{4, 5}) + fmt.Println("passed") + + // Output: passed +} + +func ExampleSliceSubsetT() { + t := new(testing.T) + require.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) + fmt.Println("passed") + + // Output: passed +} + +func ExampleSortedT() { + t := new(testing.T) + require.SortedT(t, []int{1, 1, 3}) + fmt.Println("passed") + + // Output: passed +} + +func ExampleStringContainsT() { + t := new(testing.T) + require.StringContainsT(t, "AB", "A") + fmt.Println("passed") + + // Output: passed +} + +func ExampleStringNotContainsT() { + t := new(testing.T) + require.StringNotContainsT(t, "AB", "C") + fmt.Println("passed") + + // Output: passed +} + func ExampleSubset() { t := new(testing.T) require.Subset(t, []int{1, 2, 3}, []int{1, 2}) @@ -848,3 +1025,5 @@ type dummyError struct { func (d *dummyError) Error() string { return "dummy error" } + +type myType float64 diff --git a/require/require_format.go b/require/require_format.go index cbf3cfe93..7c547d826 100644 --- a/require/require_format.go +++ b/require/require_format.go @@ -2,11 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package require import ( + "iter" "net/http" "net/url" "reflect" @@ -57,6 +58,20 @@ func DirExistsf(t T, path string, msg string, args ...any) { t.FailNow() } +// DirNotExistsf is the same as [DirNotExists], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func DirNotExistsf(t T, path string, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.DirNotExists(t, path, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // 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. @@ -78,7 +93,7 @@ func ElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, args . if h, ok := t.(H); ok { h.Helper() } - if assertions.ElementsMatchT(t, listA, listB, forwardArgs(msg, args)) { + if assertions.ElementsMatchT[E](t, listA, listB, forwardArgs(msg, args)) { return } @@ -141,6 +156,20 @@ func EqualExportedValuesf(t T, expected any, actual any, msg string, args ...any t.FailNow() } +// EqualTf is the same as [EqualT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func EqualTf[V comparable](t T, expected V, actual V, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.EqualT[V](t, expected, actual, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // 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. @@ -298,7 +327,7 @@ 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)) { + if assertions.FalseT[B](t, value, forwardArgs(msg, args)) { return } @@ -347,6 +376,20 @@ func FileNotEmptyf(t T, path string, msg string, args ...any) { t.FailNow() } +// FileNotExistsf is the same as [FileNotExists], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func FileNotExistsf(t T, path string, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.FileNotExists(t, path, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // 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. @@ -382,7 +425,7 @@ func GreaterOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg st if h, ok := t.(H); ok { h.Helper() } - if assertions.GreaterOrEqualT(t, e1, e2, forwardArgs(msg, args)) { + if assertions.GreaterOrEqualT[Orderable](t, e1, e2, forwardArgs(msg, args)) { return } @@ -396,7 +439,7 @@ func GreaterTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, a if h, ok := t.(H); ok { h.Helper() } - if assertions.GreaterT(t, e1, e2, forwardArgs(msg, args)) { + if assertions.GreaterT[Orderable](t, e1, e2, forwardArgs(msg, args)) { return } @@ -550,7 +593,7 @@ func InDeltaTf[Number Measurable](t T, expected Number, actual Number, delta Num if h, ok := t.(H); ok { h.Helper() } - if assertions.InDeltaT(t, expected, actual, delta, forwardArgs(msg, args)) { + if assertions.InDeltaT[Number](t, expected, actual, delta, forwardArgs(msg, args)) { return } @@ -592,7 +635,7 @@ func InEpsilonTf[Number Measurable](t T, expected Number, actual Number, epsilon if h, ok := t.(H); ok { h.Helper() } - if assertions.InEpsilonT(t, expected, actual, epsilon, forwardArgs(msg, args)) { + if assertions.InEpsilonT[Number](t, expected, actual, epsilon, forwardArgs(msg, args)) { return } @@ -613,6 +656,20 @@ func IsDecreasingf(t T, object any, msg string, args ...any) { t.FailNow() } +// IsDecreasingTf is the same as [IsDecreasingT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func IsDecreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.IsDecreasingT[OrderedSlice, E](t, collection, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // 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. @@ -627,6 +684,20 @@ func IsIncreasingf(t T, object any, msg string, args ...any) { t.FailNow() } +// IsIncreasingTf is the same as [IsIncreasingT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func IsIncreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.IsIncreasingT[OrderedSlice, E](t, collection, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // 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. @@ -641,6 +712,20 @@ func IsNonDecreasingf(t T, object any, msg string, args ...any) { t.FailNow() } +// IsNonDecreasingTf is the same as [IsNonDecreasingT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func IsNonDecreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.IsNonDecreasingT[OrderedSlice, E](t, collection, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // 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. @@ -655,6 +740,34 @@ func IsNonIncreasingf(t T, object any, msg string, args ...any) { t.FailNow() } +// IsNonIncreasingTf is the same as [IsNonIncreasingT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func IsNonIncreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.IsNonIncreasingT[OrderedSlice, E](t, collection, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// IsNotOfTypeTf is the same as [IsNotOfTypeT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func IsNotOfTypeTf[EType any](t T, object any, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.IsNotOfTypeT[EType](t, object, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // 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. @@ -669,6 +782,20 @@ func IsNotTypef(t T, theType any, object any, msg string, args ...any) { t.FailNow() } +// IsOfTypeTf is the same as [IsOfTypeT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func IsOfTypeTf[EType any](t T, object any, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.IsOfTypeT[EType](t, object, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // 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. @@ -718,7 +845,7 @@ func JSONEqTf[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msg string, args if h, ok := t.(H); ok { h.Helper() } - if assertions.JSONEqT(t, expected, actual, forwardArgs(msg, args)) { + if assertions.JSONEqT[EDoc, ADoc](t, expected, actual, forwardArgs(msg, args)) { return } @@ -788,7 +915,7 @@ func LessOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg strin if h, ok := t.(H); ok { h.Helper() } - if assertions.LessOrEqualT(t, e1, e2, forwardArgs(msg, args)) { + if assertions.LessOrEqualT[Orderable](t, e1, e2, forwardArgs(msg, args)) { return } @@ -802,105 +929,105 @@ func LessTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args if h, ok := t.(H); ok { h.Helper() } - if assertions.LessT(t, e1, e2, forwardArgs(msg, args)) { + if assertions.LessT[Orderable](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]. +// MapContainsTf is the same as [MapContainsT], but it 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) { +func MapContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg string, args ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.Negative(t, e, forwardArgs(msg, args)) { + if assertions.MapContainsT[Map, K, V](t, m, key, forwardArgs(msg, args)) { return } t.FailNow() } -// NegativeTf is the same as [NegativeT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// MapNotContainsTf is the same as [MapNotContainsT], but it 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) { +func MapNotContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg string, args ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.NegativeT(t, e, forwardArgs(msg, args)) { + if assertions.MapNotContainsT[Map, K, V](t, m, key, 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]. +// 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 Neverf(t T, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...any) { +func Negativef(t T, e any, msg string, args ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.Never(t, condition, waitFor, tick, forwardArgs(msg, args)) { + if assertions.Negative(t, e, forwardArgs(msg, args)) { return } t.FailNow() } -// Nilf is the same as [Nil], but it 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 Nilf(t T, object any, msg string, args ...any) { +func NegativeTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, args ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.Nil(t, object, forwardArgs(msg, args)) { + if assertions.NegativeT[SignedNumber](t, e, forwardArgs(msg, args)) { return } t.FailNow() } -// NoDirExistsf is the same as [NoDirExists], but it accepts a format msg string to format arguments like [fmt.Printf]. +// 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 NoDirExistsf(t T, path string, msg string, args ...any) { +func Neverf(t T, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.NoDirExists(t, path, forwardArgs(msg, args)) { + if assertions.Never(t, condition, waitFor, tick, forwardArgs(msg, args)) { return } t.FailNow() } -// NoErrorf is the same as [NoError], but it 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 NoErrorf(t T, err error, msg string, args ...any) { +func Nilf(t T, object any, msg string, args ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.NoError(t, err, forwardArgs(msg, args)) { + if assertions.Nil(t, object, forwardArgs(msg, args)) { return } t.FailNow() } -// NoFileExistsf is the same as [NoFileExists], but it 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 NoFileExistsf(t T, path string, msg string, args ...any) { +func NoErrorf(t T, err error, msg string, args ...any) { if h, ok := t.(H); ok { h.Helper() } - if assertions.NoFileExists(t, path, forwardArgs(msg, args)) { + if assertions.NoError(t, err, forwardArgs(msg, args)) { return } @@ -942,7 +1069,7 @@ func NotElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, arg if h, ok := t.(H); ok { h.Helper() } - if assertions.NotElementsMatchT(t, listA, listB, forwardArgs(msg, args)) { + if assertions.NotElementsMatchT[E](t, listA, listB, forwardArgs(msg, args)) { return } @@ -977,6 +1104,20 @@ func NotEqualf(t T, expected any, actual any, msg string, args ...any) { t.FailNow() } +// NotEqualTf is the same as [NotEqualT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func NotEqualTf[V comparable](t T, expected V, actual V, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.NotEqualT[V](t, expected, actual, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // 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. @@ -1096,7 +1237,7 @@ func NotRegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, ar if h, ok := t.(H); ok { h.Helper() } - if assertions.NotRegexpT(t, rx, actual, forwardArgs(msg, args)) { + if assertions.NotRegexpT[Rex, ADoc](t, rx, actual, forwardArgs(msg, args)) { return } @@ -1117,6 +1258,34 @@ func NotSamef(t T, expected any, actual any, msg string, args ...any) { t.FailNow() } +// NotSameTf is the same as [NotSameT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func NotSameTf[P any](t T, expected *P, actual *P, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.NotSameT[P](t, expected, actual, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// NotSortedTf is the same as [NotSortedT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func NotSortedTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.NotSortedT[OrderedSlice, E](t, collection, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // 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. @@ -1208,7 +1377,7 @@ func PositiveTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, arg if h, ok := t.(H); ok { h.Helper() } - if assertions.PositiveT(t, e, forwardArgs(msg, args)) { + if assertions.PositiveT[SignedNumber](t, e, forwardArgs(msg, args)) { return } @@ -1236,7 +1405,7 @@ func RegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, args if h, ok := t.(H); ok { h.Helper() } - if assertions.RegexpT(t, rx, actual, forwardArgs(msg, args)) { + if assertions.RegexpT[Rex, ADoc](t, rx, actual, forwardArgs(msg, args)) { return } @@ -1257,6 +1426,146 @@ func Samef(t T, expected any, actual any, msg string, args ...any) { t.FailNow() } +// SameTf is the same as [SameT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SameTf[P any](t T, expected *P, actual *P, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SameT[P](t, expected, actual, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// SeqContainsTf is the same as [SeqContainsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SeqContainsTf[E comparable](t T, iter iter.Seq[E], element E, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SeqContainsT[E](t, iter, element, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// SeqNotContainsTf is the same as [SeqNotContainsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SeqNotContainsTf[E comparable](t T, iter iter.Seq[E], element E, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SeqNotContainsT[E](t, iter, element, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// SliceContainsTf is the same as [SliceContainsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SliceContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SliceContainsT[Slice, E](t, s, element, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// SliceNotContainsTf is the same as [SliceNotContainsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SliceNotContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SliceNotContainsT[Slice, E](t, s, element, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// SliceNotSubsetTf is the same as [SliceNotSubsetT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SliceNotSubsetTf[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SliceNotSubsetT[Slice, E](t, list, subset, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// SliceSubsetTf is the same as [SliceSubsetT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SliceSubsetTf[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SliceSubsetT[Slice, E](t, list, subset, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// SortedTf is the same as [SortedT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SortedTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SortedT[OrderedSlice, E](t, collection, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// StringContainsTf is the same as [StringContainsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func StringContainsTf[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.StringContainsT[ADoc, EDoc](t, str, substring, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + +// StringNotContainsTf is the same as [StringNotContainsT], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func StringNotContainsTf[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.StringNotContainsT[ADoc, EDoc](t, str, substring, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // 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. @@ -1292,7 +1601,7 @@ 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)) { + if assertions.TrueT[B](t, value, forwardArgs(msg, args)) { return } @@ -1362,7 +1671,7 @@ func YAMLEqTf[EDoc, ADoc Text](t T, expected EDoc, actual ADoc, msg string, args if h, ok := t.(H); ok { h.Helper() } - if assertions.YAMLEqT(t, expected, actual, forwardArgs(msg, args)) { + if assertions.YAMLEqT[EDoc, ADoc](t, expected, actual, forwardArgs(msg, args)) { return } diff --git a/require/require_format_test.go b/require/require_format_test.go index d9874bcc5..483bceff2 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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package require @@ -13,6 +13,7 @@ import ( "net/url" "path/filepath" "reflect" + "slices" "testing" "time" ) @@ -77,6 +78,26 @@ func TestDirExistsf(t *testing.T) { }) } +func TestDirNotExistsf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + DirNotExistsf(t, filepath.Join(testDataPath(), "non_existing_dir"), "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + DirNotExistsf(mock, filepath.Join(testDataPath(), "existing_dir"), "test message") + // require functions don't return a value + if !mock.failed { + t.Error("DirNotExists should call FailNow()") + } + }) +} + func TestElementsMatchf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -197,6 +218,26 @@ func TestEqualExportedValuesf(t *testing.T) { }) } +func TestEqualTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + EqualTf(t, 123, 123, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + EqualTf(mock, 123, 456, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("EqualT should call FailNow()") + } + }) +} + func TestEqualValuesf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -485,6 +526,26 @@ func TestFileNotEmptyf(t *testing.T) { }) } +func TestFileNotExistsf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + FileNotExistsf(t, filepath.Join(testDataPath(), "non_existing_file"), "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + FileNotExistsf(mock, filepath.Join(testDataPath(), "existing_file"), "test message") + // require functions don't return a value + if !mock.failed { + t.Error("FileNotExists should call FailNow()") + } + }) +} + func TestGreaterf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -865,6 +926,26 @@ func TestIsDecreasingf(t *testing.T) { }) } +func TestIsDecreasingTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + IsDecreasingTf(t, []int{3, 2, 1}, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + IsDecreasingTf(mock, []int{1, 2, 3}, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("IsDecreasingT should call FailNow()") + } + }) +} + func TestIsIncreasingf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -885,6 +966,26 @@ func TestIsIncreasingf(t *testing.T) { }) } +func TestIsIncreasingTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + IsIncreasingTf(t, []int{1, 2, 3}, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + IsIncreasingTf(mock, []int{1, 1, 2}, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("IsIncreasingT should call FailNow()") + } + }) +} + func TestIsNonDecreasingf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -897,7 +998,7 @@ func TestIsNonDecreasingf(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - IsNonDecreasingf(mock, []int{2, 1, 1}, "test message") + IsNonDecreasingf(mock, []int{2, 1, 0}, "test message") // require functions don't return a value if !mock.failed { t.Error("IsNonDecreasing should call FailNow()") @@ -905,6 +1006,26 @@ func TestIsNonDecreasingf(t *testing.T) { }) } +func TestIsNonDecreasingTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + IsNonDecreasingTf(t, []int{1, 1, 2}, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + IsNonDecreasingTf(mock, []int{2, 1, 0}, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("IsNonDecreasingT should call FailNow()") + } + }) +} + func TestIsNonIncreasingf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -925,6 +1046,46 @@ func TestIsNonIncreasingf(t *testing.T) { }) } +func TestIsNonIncreasingTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + IsNonIncreasingTf(t, []int{2, 1, 1}, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + IsNonIncreasingTf(mock, []int{1, 2, 3}, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("IsNonIncreasingT should call FailNow()") + } + }) +} + +func TestIsNotOfTypeTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + IsNotOfTypeTf[myType](t, 123.123, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + IsNotOfTypeTf[myType](mock, myType(123.123), "test message") + // require functions don't return a value + if !mock.failed { + t.Error("IsNotOfTypeT should call FailNow()") + } + }) +} + func TestIsNotTypef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -945,6 +1106,26 @@ func TestIsNotTypef(t *testing.T) { }) } +func TestIsOfTypeTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + IsOfTypeTf[myType](t, myType(123.123), "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + IsOfTypeTf[myType](mock, 123.123, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("IsOfTypeT should call FailNow()") + } + }) +} + func TestIsTypef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1145,11 +1326,11 @@ func TestLessTf(t *testing.T) { }) } -func TestNegativef(t *testing.T) { +func TestMapContainsTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - Negativef(t, -1, "test message") + MapContainsTf(t, map[string]string{"A": "B"}, "A", "test message") // require functions don't return a value }) @@ -1157,19 +1338,19 @@ func TestNegativef(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - Negativef(mock, 1, "test message") + MapContainsTf(mock, map[string]string{"A": "B"}, "C", "test message") // require functions don't return a value if !mock.failed { - t.Error("Negative should call FailNow()") + t.Error("MapContainsT should call FailNow()") } }) } -func TestNegativeTf(t *testing.T) { +func TestMapNotContainsTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - NegativeTf(t, -1, "test message") + MapNotContainsTf(t, map[string]string{"A": "B"}, "C", "test message") // require functions don't return a value }) @@ -1177,19 +1358,19 @@ func TestNegativeTf(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - NegativeTf(mock, 1, "test message") + MapNotContainsTf(mock, map[string]string{"A": "B"}, "A", "test message") // require functions don't return a value if !mock.failed { - t.Error("NegativeT should call FailNow()") + t.Error("MapNotContainsT should call FailNow()") } }) } -func TestNeverf(t *testing.T) { +func TestNegativef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - Neverf(t, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond, "test message") + Negativef(t, -1, "test message") // require functions don't return a value }) @@ -1197,19 +1378,19 @@ func TestNeverf(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - Neverf(mock, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond, "test message") + Negativef(mock, 1, "test message") // require functions don't return a value if !mock.failed { - t.Error("Never should call FailNow()") + t.Error("Negative should call FailNow()") } }) } -func TestNilf(t *testing.T) { +func TestNegativeTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - Nilf(t, nil, "test message") + NegativeTf(t, -1, "test message") // require functions don't return a value }) @@ -1217,19 +1398,19 @@ func TestNilf(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - Nilf(mock, "not nil", "test message") + NegativeTf(mock, 1, "test message") // require functions don't return a value if !mock.failed { - t.Error("Nil should call FailNow()") + t.Error("NegativeT should call FailNow()") } }) } -func TestNoDirExistsf(t *testing.T) { +func TestNeverf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - NoDirExistsf(t, filepath.Join(testDataPath(), "non_existing_dir"), "test message") + Neverf(t, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond, "test message") // require functions don't return a value }) @@ -1237,19 +1418,19 @@ func TestNoDirExistsf(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - NoDirExistsf(mock, filepath.Join(testDataPath(), "existing_dir"), "test message") + Neverf(mock, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond, "test message") // require functions don't return a value if !mock.failed { - t.Error("NoDirExists should call FailNow()") + t.Error("Never should call FailNow()") } }) } -func TestNoErrorf(t *testing.T) { +func TestNilf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - NoErrorf(t, nil, "test message") + Nilf(t, nil, "test message") // require functions don't return a value }) @@ -1257,19 +1438,19 @@ func TestNoErrorf(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - NoErrorf(mock, ErrTest, "test message") + Nilf(mock, "not nil", "test message") // require functions don't return a value if !mock.failed { - t.Error("NoError should call FailNow()") + t.Error("Nil should call FailNow()") } }) } -func TestNoFileExistsf(t *testing.T) { +func TestNoErrorf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() - NoFileExistsf(t, filepath.Join(testDataPath(), "non_existing_file"), "test message") + NoErrorf(t, nil, "test message") // require functions don't return a value }) @@ -1277,10 +1458,10 @@ func TestNoFileExistsf(t *testing.T) { t.Parallel() mock := new(mockFailNowT) - NoFileExistsf(mock, filepath.Join(testDataPath(), "existing_file"), "test message") + NoErrorf(mock, ErrTest, "test message") // require functions don't return a value if !mock.failed { - t.Error("NoFileExists should call FailNow()") + t.Error("NoError should call FailNow()") } }) } @@ -1385,6 +1566,26 @@ func TestNotEqualf(t *testing.T) { }) } +func TestNotEqualTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + NotEqualTf(t, 123, 456, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + NotEqualTf(mock, 123, 123, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("NotEqualT should call FailNow()") + } + }) +} + func TestNotEqualValuesf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1585,6 +1786,46 @@ func TestNotSamef(t *testing.T) { }) } +func TestNotSameTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + NotSameTf(t, &staticVar, ptr("static string"), "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + NotSameTf(mock, &staticVar, staticVarPtr, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("NotSameT should call FailNow()") + } + }) +} + +func TestNotSortedTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + NotSortedTf(t, []int{3, 1, 3}, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + NotSortedTf(mock, []int{1, 4, 8}, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("NotSortedT should call FailNow()") + } + }) +} + func TestNotSubsetf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1785,6 +2026,206 @@ func TestSamef(t *testing.T) { }) } +func TestSameTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + SameTf(t, &staticVar, staticVarPtr, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SameTf(mock, &staticVar, ptr("static string"), "test message") + // require functions don't return a value + if !mock.failed { + t.Error("SameT should call FailNow()") + } + }) +} + +func TestSeqContainsTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + SeqContainsTf(t, slices.Values([]string{"A", "B"}), "A", "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SeqContainsTf(mock, slices.Values([]string{"A", "B"}), "C", "test message") + // require functions don't return a value + if !mock.failed { + t.Error("SeqContainsT should call FailNow()") + } + }) +} + +func TestSeqNotContainsTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + SeqNotContainsTf(t, slices.Values([]string{"A", "B"}), "C", "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SeqNotContainsTf(mock, slices.Values([]string{"A", "B"}), "A", "test message") + // require functions don't return a value + if !mock.failed { + t.Error("SeqNotContainsT should call FailNow()") + } + }) +} + +func TestSliceContainsTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + SliceContainsTf(t, []string{"A", "B"}, "A", "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SliceContainsTf(mock, []string{"A", "B"}, "C", "test message") + // require functions don't return a value + if !mock.failed { + t.Error("SliceContainsT should call FailNow()") + } + }) +} + +func TestSliceNotContainsTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + SliceNotContainsTf(t, []string{"A", "B"}, "C", "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SliceNotContainsTf(mock, []string{"A", "B"}, "A", "test message") + // require functions don't return a value + if !mock.failed { + t.Error("SliceNotContainsT should call FailNow()") + } + }) +} + +func TestSliceNotSubsetTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + SliceNotSubsetTf(t, []int{1, 2, 3}, []int{4, 5}, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SliceNotSubsetTf(mock, []int{1, 2, 3}, []int{1, 2}, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("SliceNotSubsetT should call FailNow()") + } + }) +} + +func TestSliceSubsetTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + SliceSubsetTf(t, []int{1, 2, 3}, []int{1, 2}, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SliceSubsetTf(mock, []int{1, 2, 3}, []int{4, 5}, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("SliceSubsetT should call FailNow()") + } + }) +} + +func TestSortedTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + SortedTf(t, []int{1, 1, 3}, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SortedTf(mock, []int{1, 4, 2}, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("SortedT should call FailNow()") + } + }) +} + +func TestStringContainsTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + StringContainsTf(t, "AB", "A", "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + StringContainsTf(mock, "AB", "C", "test message") + // require functions don't return a value + if !mock.failed { + t.Error("StringContainsT should call FailNow()") + } + }) +} + +func TestStringNotContainsTf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + StringNotContainsTf(t, "AB", "C", "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + StringNotContainsTf(mock, "AB", "A", "test message") + // require functions don't return a value + if !mock.failed { + t.Error("StringNotContainsT should call FailNow()") + } + }) +} + func TestSubsetf(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 63b71a553..486b392d1 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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package require @@ -115,6 +115,34 @@ func (a *Assertions) DirExistsf(path string, msg string, args ...any) { a.t.FailNow() } +// DirNotExists is the same as [DirNotExists], as a method rather than a package-level function. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func (a *Assertions) DirNotExists(path string, msgAndArgs ...any) { + if h, ok := a.t.(H); ok { + h.Helper() + } + if assertions.DirNotExists(a.t, path, msgAndArgs...) { + return + } + + a.t.FailNow() +} + +// DirNotExistsf is the same as [Assertions.DirNotExists], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func (a *Assertions) DirNotExistsf(path string, msg string, args ...any) { + if h, ok := a.t.(H); ok { + h.Helper() + } + if assertions.DirNotExists(a.t, path, forwardArgs(msg, args)) { + return + } + + a.t.FailNow() +} + // ElementsMatch is the same as [ElementsMatch], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. @@ -639,6 +667,34 @@ func (a *Assertions) FileNotEmptyf(path string, msg string, args ...any) { a.t.FailNow() } +// FileNotExists is the same as [FileNotExists], as a method rather than a package-level function. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func (a *Assertions) FileNotExists(path string, msgAndArgs ...any) { + if h, ok := a.t.(H); ok { + h.Helper() + } + if assertions.FileNotExists(a.t, path, msgAndArgs...) { + return + } + + a.t.FailNow() +} + +// FileNotExistsf is the same as [Assertions.FileNotExists], but it accepts a format msg string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func (a *Assertions) FileNotExistsf(path string, msg string, args ...any) { + if h, ok := a.t.(H); ok { + h.Helper() + } + if assertions.FileNotExists(a.t, path, forwardArgs(msg, args)) { + return + } + + a.t.FailNow() +} + // Greater is the same as [Greater], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. @@ -1451,34 +1507,6 @@ func (a *Assertions) Nilf(object any, msg string, args ...any) { a.t.FailNow() } -// NoDirExists is the same as [NoDirExists], as a method rather than a package-level function. -// -// Upon failure, the test [T] is marked as failed and stops execution. -func (a *Assertions) NoDirExists(path string, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { - h.Helper() - } - if assertions.NoDirExists(a.t, path, msgAndArgs...) { - return - } - - a.t.FailNow() -} - -// 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) { - if h, ok := a.t.(H); ok { - h.Helper() - } - if assertions.NoDirExists(a.t, path, forwardArgs(msg, args)) { - return - } - - a.t.FailNow() -} - // NoError is the same as [NoError], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. @@ -1507,34 +1535,6 @@ func (a *Assertions) NoErrorf(err error, msg string, args ...any) { a.t.FailNow() } -// NoFileExists is the same as [NoFileExists], as a method rather than a package-level function. -// -// Upon failure, the test [T] is marked as failed and stops execution. -func (a *Assertions) NoFileExists(path string, msgAndArgs ...any) { - if h, ok := a.t.(H); ok { - h.Helper() - } - if assertions.NoFileExists(a.t, path, msgAndArgs...) { - return - } - - a.t.FailNow() -} - -// 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) { - if h, ok := a.t.(H); ok { - h.Helper() - } - if assertions.NoFileExists(a.t, path, forwardArgs(msg, args)) { - return - } - - a.t.FailNow() -} - // NotContains is the same as [NotContains], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and stops execution. diff --git a/require/require_forward_test.go b/require/require_forward_test.go index 8727e84a1..73e8dc239 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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package require @@ -164,6 +164,55 @@ func TestAssertionsDirExistsf(t *testing.T) { }) } +func TestAssertionsDirNotExists(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + + a := New(t) + a.DirNotExists(filepath.Join(testDataPath(), "non_existing_dir")) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + a := New(mock) + a.DirNotExists(filepath.Join(testDataPath(), "existing_dir")) + // require functions don't return a value + if !mock.failed { + t.Error("DirNotExists should call FailNow()") + } + }) +} + +func TestAssertionsDirNotExistsf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + + a := New(t) + a.DirNotExistsf(filepath.Join(testDataPath(), "non_existing_dir"), "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + a := New(mock) + a.DirNotExistsf(filepath.Join(testDataPath(), "existing_dir"), "test message") + // require functions don't return a value + if !mock.failed { + t.Error("Assertions.DirNotExists should mark test as failed") + } + if !mock.failed { + t.Error("Assertions.DirNotExistsf should call FailNow()") + } + }) +} + func TestAssertionsElementsMatch(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1063,6 +1112,55 @@ func TestAssertionsFileNotEmptyf(t *testing.T) { }) } +func TestAssertionsFileNotExists(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + + a := New(t) + a.FileNotExists(filepath.Join(testDataPath(), "non_existing_file")) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + a := New(mock) + a.FileNotExists(filepath.Join(testDataPath(), "existing_file")) + // require functions don't return a value + if !mock.failed { + t.Error("FileNotExists should call FailNow()") + } + }) +} + +func TestAssertionsFileNotExistsf(t *testing.T) { + t.Parallel() + t.Run("success", func(t *testing.T) { + t.Parallel() + + a := New(t) + a.FileNotExistsf(filepath.Join(testDataPath(), "non_existing_file"), "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + a := New(mock) + a.FileNotExistsf(filepath.Join(testDataPath(), "existing_file"), "test message") + // require functions don't return a value + if !mock.failed { + t.Error("Assertions.FileNotExists should mark test as failed") + } + if !mock.failed { + t.Error("Assertions.FileNotExistsf should call FailNow()") + } + }) +} + func TestAssertionsGreater(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -1862,7 +1960,7 @@ func TestAssertionsIsNonDecreasing(t *testing.T) { mock := new(mockFailNowT) a := New(mock) - a.IsNonDecreasing([]int{2, 1, 1}) + a.IsNonDecreasing([]int{2, 1, 0}) // require functions don't return a value if !mock.failed { t.Error("IsNonDecreasing should call FailNow()") @@ -1885,7 +1983,7 @@ func TestAssertionsIsNonDecreasingf(t *testing.T) { mock := new(mockFailNowT) a := New(mock) - a.IsNonDecreasingf([]int{2, 1, 1}, "test message") + a.IsNonDecreasingf([]int{2, 1, 0}, "test message") // require functions don't return a value if !mock.failed { t.Error("Assertions.IsNonDecreasing should mark test as failed") @@ -2484,55 +2582,6 @@ func TestAssertionsNilf(t *testing.T) { }) } -func TestAssertionsNoDirExists(t *testing.T) { - t.Parallel() - t.Run("success", func(t *testing.T) { - t.Parallel() - - a := New(t) - a.NoDirExists(filepath.Join(testDataPath(), "non_existing_dir")) - // require functions don't return a value - }) - - t.Run("failure", func(t *testing.T) { - t.Parallel() - - mock := new(mockFailNowT) - a := New(mock) - a.NoDirExists(filepath.Join(testDataPath(), "existing_dir")) - // require functions don't return a value - if !mock.failed { - t.Error("NoDirExists should call FailNow()") - } - }) -} - -func TestAssertionsNoDirExistsf(t *testing.T) { - t.Parallel() - t.Run("success", func(t *testing.T) { - t.Parallel() - - a := New(t) - a.NoDirExistsf(filepath.Join(testDataPath(), "non_existing_dir"), "test message") - // require functions don't return a value - }) - - t.Run("failure", func(t *testing.T) { - t.Parallel() - - mock := new(mockFailNowT) - a := New(mock) - a.NoDirExistsf(filepath.Join(testDataPath(), "existing_dir"), "test message") - // require functions don't return a value - if !mock.failed { - t.Error("Assertions.NoDirExists should mark test as failed") - } - if !mock.failed { - t.Error("Assertions.NoDirExistsf should call FailNow()") - } - }) -} - func TestAssertionsNoError(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { @@ -2582,55 +2631,6 @@ func TestAssertionsNoErrorf(t *testing.T) { }) } -func TestAssertionsNoFileExists(t *testing.T) { - t.Parallel() - t.Run("success", func(t *testing.T) { - t.Parallel() - - a := New(t) - a.NoFileExists(filepath.Join(testDataPath(), "non_existing_file")) - // require functions don't return a value - }) - - t.Run("failure", func(t *testing.T) { - t.Parallel() - - mock := new(mockFailNowT) - a := New(mock) - a.NoFileExists(filepath.Join(testDataPath(), "existing_file")) - // require functions don't return a value - if !mock.failed { - t.Error("NoFileExists should call FailNow()") - } - }) -} - -func TestAssertionsNoFileExistsf(t *testing.T) { - t.Parallel() - t.Run("success", func(t *testing.T) { - t.Parallel() - - a := New(t) - a.NoFileExistsf(filepath.Join(testDataPath(), "non_existing_file"), "test message") - // require functions don't return a value - }) - - t.Run("failure", func(t *testing.T) { - t.Parallel() - - mock := new(mockFailNowT) - a := New(mock) - a.NoFileExistsf(filepath.Join(testDataPath(), "existing_file"), "test message") - // require functions don't return a value - if !mock.failed { - t.Error("Assertions.NoFileExists should mark test as failed") - } - if !mock.failed { - t.Error("Assertions.NoFileExistsf should call FailNow()") - } - }) -} - func TestAssertionsNotContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { diff --git a/require/require_helpers.go b/require/require_helpers.go index 3c81e56c2..5b376445f 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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package require diff --git a/require/require_helpers_test.go b/require/require_helpers_test.go index 432ad7258..9412da3b8 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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package require diff --git a/require/require_types.go b/require/require_types.go index 073f5bdf0..4688674e8 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-18 (version e12affe) using codegen version v2.1.9-0.20260118112101-e12affef2419+dirty [sha: e12affef24198e72ee13eb6d25018d2c3232629f] +// Generated on 2026-01-20 (version 74d5686) using codegen version v2.1.9-0.20260119232631-74d5686313f0+dirty [sha: 74d5686313f0820ae0e2758b95d598f646cd7ad5] package require @@ -57,7 +57,7 @@ type ( // 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]. + // This is used by [GreaterT], [GreaterOrEqualT], [LessT], [LessOrEqualT], [IsIncreasingT], [IsDecreasingT]. // // NOTE: since [time.Time] is a struct, custom types which redeclare [time.Time] are not supported. Ordered = assertions.Ordered