From e6b0793ba519fb22dc1887392e1465649a5a95ff Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Sun, 4 Jan 2026 20:48:31 +0100 Subject: [PATCH 1/2] fix(Eventually): fixed go routine leak issues Reference (original issue report): https://github.com/stretchr/testify/issues/1611 This fix involves a complete rewrite of assertions that run background checks, with more idiomatic go routine codes and use of context.Context. Affected assertions: * Eventually * Never * EventuallyWithT We no longer run go routines that are not waited for. Timeouts and early exits are handled via context.Context. CollecT.FailNow() now also cancels the context of the caller instead. Significant changes: * callers should not expect Eventually and others to complete in the case of a blocking condition function. In particular, the condition function should never wait for an event that is triggered after Eventually. Complex or long-running condition function may use t.Context() to exit early if the test is failed. * callers may safely assume that only one go routine is executing the condition function (no race on writes) * test: asserted stretch/testify#480 is fixed Signed-off-by: Frederic BIDON --- internal/assertions/compare_test.go | 19 - internal/assertions/condition.go | 502 +++++++++++++++++++------- internal/assertions/condition_test.go | 443 ++++++++++++++++------- internal/assertions/equal_test.go | 14 + internal/assertions/ifaces.go | 6 + internal/assertions/mock_test.go | 65 ++++ 6 files changed, 759 insertions(+), 290 deletions(-) diff --git a/internal/assertions/compare_test.go b/internal/assertions/compare_test.go index f26bac831..aadc61b37 100644 --- a/internal/assertions/compare_test.go +++ b/internal/assertions/compare_test.go @@ -5,7 +5,6 @@ package assertions import ( "bytes" - "fmt" "iter" "runtime" "slices" @@ -194,24 +193,6 @@ func TestCompareMsgAndArgsForwarding(t *testing.T) { } } -type outputT struct { - buf *bytes.Buffer - helpers map[string]struct{} -} - -// Implements T. -func (t *outputT) Errorf(format string, args ...any) { - s := fmt.Sprintf(format, args...) - t.buf.WriteString(s) -} - -func (t *outputT) Helper() { - if t.helpers == nil { - t.helpers = make(map[string]struct{}) - } - t.helpers[callerName(1)] = struct{}{} -} - // 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 { diff --git a/internal/assertions/condition.go b/internal/assertions/condition.go index fef478a76..1f77025b1 100644 --- a/internal/assertions/condition.go +++ b/internal/assertions/condition.go @@ -4,12 +4,16 @@ package assertions import ( + "context" + "errors" "fmt" "runtime" + "sync" + "sync/atomic" "time" ) -// Condition uses a Comparison to assert a complex condition. +// Condition uses a [Comparison] to assert a complex condition. // // # Usage // @@ -24,19 +28,37 @@ func Condition(t T, comp Comparison, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } + result := comp() if !result { - Fail(t, "Condition failed!", msgAndArgs...) + Fail(t, "condition failed", msgAndArgs...) } + return result } -// Eventually asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. +// Eventually asserts that the given condition will be met in waitFor time, +// periodically checking the target function on each tick. +// +// [Eventually] waits until the condition returns true, for at most waitFor, +// or until the parent context of the test is cancelled. +// +// If the condition takes longer than waitFor to complete, [Eventually] fails +// but waits for the current condition execution to finish before returning. +// +// For long-running conditions to be interrupted early, check [testing.T.Context] +// which is cancelled on test failure. // // # Usage // -// assertions.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +// assertions.Eventually(t, func() bool { return true }, time.Second, 10*time.Millisecond) +// +// # Concurrency +// +// The condition function is never executed in parallel: only one goroutine executes it. +// It may write to variables outside its scope without triggering race conditions. +// +// A blocking condition will cause [Eventually] to hang until it returns. // // # Examples // @@ -48,82 +70,54 @@ func Eventually(t T, condition func() bool, waitFor time.Duration, tick time.Dur h.Helper() } - ch := make(chan bool, 1) - checkCond := func() { ch <- condition() } - - timer := time.NewTimer(waitFor) - defer timer.Stop() - - ticker := time.NewTicker(tick) - defer ticker.Stop() - - var tickC <-chan time.Time - - // Check the condition once first on the initial call. - go checkCond() - - for { - select { - case <-timer.C: - return Fail(t, "Condition never satisfied", msgAndArgs...) - case <-tickC: - tickC = nil - go checkCond() - case v := <-ch: - if v { - return true - } - tickC = ticker.C - } - } + return eventually(t, condition, waitFor, tick, msgAndArgs...) } -// CollectT implements the [T] interface and collects all errors. -type CollectT struct { +// Never asserts that the given condition is never satisfied within waitFor time, +// periodically checking the target function at each tick. +// +// [Never] is the opposite of [Eventually]. It succeeds if the waitFor timeout +// is reached without the condition ever returning true. +// +// If the parent context is cancelled before the timeout, [Never] fails. +// +// # Usage +// +// assertions.Never(t, func() bool { return false }, time.Second, 10*time.Millisecond) +// +// # Concurrency +// +// The condition function is never executed in parallel: only one goroutine executes it. +// It may write to variables outside its scope without triggering race conditions. +// +// A blocking condition will cause [Never] to hang until it returns. +// +// # Examples +// +// success: func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond +// failure: func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond +func Never(t T, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { // Domain: condition - // - // Maintainer: - // 1. we should verify if the use of runtime.GoExit is correct in this context. - // 2. deprecated methods removed. - - // A slice of errors. Non-nil slice denotes a failure. - // If it's non-nil but len(c.errors) == 0, this is also a failure - // obtained by direct c.FailNow() call. - errors []error -} - -// Helper is like [testing.T.Helper] but does nothing. -func (CollectT) Helper() {} - -// Errorf collects the error. -func (c *CollectT) Errorf(format string, args ...any) { - c.errors = append(c.errors, fmt.Errorf(format, args...)) -} - -// FailNow stops execution by calling runtime.Goexit. -func (c *CollectT) FailNow() { - c.fail() - runtime.Goexit() -} - -func (c *CollectT) fail() { - if !c.failed() { - c.errors = []error{} // Make it non-nil to mark a failure. + if h, ok := t.(H); ok { + h.Helper() } -} -func (c *CollectT) failed() bool { - return c.errors != nil + return never(t, condition, waitFor, tick, msgAndArgs...) } -// EventuallyWithT asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. In contrast to Eventually, -// it supplies a CollectT to the condition function, so that the condition -// function can use the CollectT to call other assertions. +// EventuallyWithT asserts that the given condition will be met in waitFor time, +// periodically checking the target function at each tick. +// +// In contrast to [Eventually], the condition function is supplied with a [CollectT] +// to accumulate errors from calling other assertions. +// // The condition is considered "met" if no errors are raised in a tick. -// The supplied CollectT collects all errors from one tick (if there are any). -// If the condition is not met before waitFor, the collected errors of -// the last tick are copied to t. +// The supplied [CollectT] collects all errors from one tick. +// +// If the condition is not met before waitFor, the collected errors from the +// last tick are copied to t. +// +// Calling [CollectT.FailNow] cancels the condition immediately and fails the assertion. // // # Usage // @@ -132,11 +126,17 @@ func (c *CollectT) failed() bool { // time.Sleep(8*time.Second) // externalValue = true // }() +// // assertions.EventuallyWithT(t, func(c *assertions.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assertions.True(c, externalValue, "expected 'externalValue' to be true") // }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") // +// # Concurrency +// +// The condition function is never executed in parallel: only one goroutine executes it. +// It may write to variables outside its scope without triggering race conditions. +// // # Examples // // success: func(c *CollectT) { True(c,true) }, 100*time.Millisecond, 20*time.Millisecond @@ -147,92 +147,320 @@ func EventuallyWithT(t T, condition func(collect *CollectT), waitFor time.Durati h.Helper() } - var lastFinishedTickErrs []error - ch := make(chan *CollectT, 1) + return eventuallyWithT(t, condition, waitFor, tick, msgAndArgs...) +} - checkCond := func() { - collect := new(CollectT) - defer func() { - ch <- collect - }() - condition(collect) +func eventually(t T, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() } - timer := time.NewTimer(waitFor) - defer timer.Stop() + return pollCondition(t, + condition, waitFor, tick, + pollOptions{ + mode: pollUntilTrue, + failMessage: "condition never satisfied", + }, + msgAndArgs..., + ) +} - ticker := time.NewTicker(tick) - defer ticker.Stop() +func never(t T, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } - var tickC <-chan time.Time + return pollCondition(t, + condition, waitFor, tick, + pollOptions{ + mode: pollUntilTimeout, + failMessage: "condition satisfied", + }, + msgAndArgs..., + ) +} - // Check the condition once first on the initial call. - go checkCond() +func eventuallyWithT(t T, collectCondition func(collector *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } - for { - select { - case <-timer.C: - for _, err := range lastFinishedTickErrs { - t.Errorf("%v", err) - } - return Fail(t, "Condition never satisfied", msgAndArgs...) - case <-tickC: - tickC = nil - go checkCond() - case collect := <-ch: - if !collect.failed() { - return true - } - // Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached. - lastFinishedTickErrs = collect.errors - tickC = ticker.C + var lastCollectedErrors []error + var cancelFunc func() // will be set by pollCondition via onSetup + + condition := func() bool { + collector := new(CollectT).withCancelFunc(cancelFunc) + collectCondition(collector) + if collector.failed() { + lastCollectedErrors = collector.collected() + return false + } + + return true + } + + copyCollected := func(tt T) { + for _, err := range lastCollectedErrors { + tt.Errorf("%v", err) } } + + return pollCondition(t, + condition, waitFor, tick, + pollOptions{ + mode: pollUntilTrue, + failMessage: "condition never satisfied", + onFailure: copyCollected, + onSetup: func(cancel func()) { cancelFunc = cancel }, + }, + msgAndArgs..., + ) } -// Never asserts that the given condition doesn't satisfy in waitFor time, -// periodically checking the target function each tick. -// -// # Usage -// -// assertions.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) -// -// # Examples -// -// success: func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond -// failure: func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond -func Never(t T, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { - // Domain: condition +// pollMode determines how the condition polling should behave. +type pollMode int + +const ( + // pollUntilTrue succeeds when condition returns true (for Eventually). + pollUntilTrue pollMode = iota + // pollUntilTimeout succeeds when timeout is reached without condition being true (for Never). + pollUntilTimeout +) + +// pollOptions configures the condition polling behavior. +type pollOptions struct { + mode pollMode + failMessage string // error message added at the end of the stack + onFailure func(t T) // called on failure (e.g., to copy collected errors) + onSetup func(cancel func()) // called after context setup to expose cancel function +} + +// pollCondition is the common implementation for eventually, never, and eventuallyWithT. +// It polls a condition function at regular intervals until success or timeout. +func pollCondition(t T, condition func() bool, waitFor, tick time.Duration, opts pollOptions, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } - ch := make(chan bool, 1) - checkCond := func() { ch <- condition() } + var parentCtx context.Context + if withContext, ok := t.(contextualizer); ok { + parentCtx = withContext.Context() + } + if parentCtx == nil { + parentCtx = context.Background() + } + + // For pollUntilTimeout (Never), we detach from parent cancellation + // so that timeout reaching is a success, not a failure. + var ctx context.Context + var cancel context.CancelFunc + if opts.mode == pollUntilTimeout { + ctx, cancel = context.WithTimeout(context.WithoutCancel(parentCtx), waitFor) + } else { + ctx, cancel = context.WithTimeout(parentCtx, waitFor) + } + defer cancel() - timer := time.NewTimer(waitFor) - defer timer.Stop() + // Allow caller to capture the cancel function (for eventuallyWithT's CollectT) + if opts.onSetup != nil { + opts.onSetup(cancel) + } + + var reported atomic.Bool + failFunc := func(reason string) { + if reported.CompareAndSwap(false, true) { + if reason != "" { + t.Errorf("%s", reason) + } + Fail(t, opts.failMessage, msgAndArgs...) + } + } + + conditionChan := make(chan func() bool, 1) + doneChan := make(chan struct{}) ticker := time.NewTicker(tick) defer ticker.Stop() - var tickC <-chan time.Time - // Check the condition once first on the initial call. - go checkCond() - - for { - select { - case <-timer.C: - return true - case <-tickC: - tickC = nil - go checkCond() - case v := <-ch: - if v { - return Fail(t, "Condition satisfied", msgAndArgs...) + conditionChan <- condition + + var wg sync.WaitGroup + + // Goroutine 1: Poll for the condition at every tick + wg.Add(1) + go func() { + defer wg.Done() + + for { + if opts.mode == pollUntilTimeout { + // For Never: check parent context separately + select { + case <-parentCtx.Done(): + failFunc(parentCtx.Err().Error()) + return + case <-ctx.Done(): + return // timeout reached = success for Never + case <-doneChan: + return + case <-ticker.C: + select { + case <-parentCtx.Done(): + failFunc(parentCtx.Err().Error()) + return + case <-ctx.Done(): + return + case <-doneChan: + return + case conditionChan <- condition: + } + } + } else { + // For Eventually: parent cancellation flows through ctx + select { + case <-ctx.Done(): + failFunc(ctx.Err().Error()) + return + case <-doneChan: + return + case <-ticker.C: + select { + case <-ctx.Done(): + failFunc(ctx.Err().Error()) + return + case <-doneChan: + return + case conditionChan <- condition: + } + } + } + } + }() + + // Goroutine 2: Execute the condition and check results + wg.Add(1) + go func() { + defer wg.Done() + + for { + if opts.mode == pollUntilTimeout { + select { + case <-parentCtx.Done(): + failFunc(parentCtx.Err().Error()) + return + case <-ctx.Done(): + return // timeout = success + case fn := <-conditionChan: + if fn() { + close(doneChan) // condition true = failure for Never + return + } + } + } else { + select { + case <-ctx.Done(): + failFunc(ctx.Err().Error()) + return + case fn := <-conditionChan: + if fn() { + close(doneChan) // condition true = success + return + } + } + } + } + }() + + wg.Wait() + + // Determine success based on mode + select { + case <-doneChan: + if opts.mode == pollUntilTimeout { + // For Never: doneChan closed means condition became true + // But if timeout was reached first (ctx.Err != nil), it's still a success + if ctx.Err() != nil { + return true } - tickC = ticker.C + // Condition became true before timeout = failure + failFunc("") + return false + } + // For Eventually: doneChan closed means condition became true + if ctx.Err() != nil { + // Timeout occurred before or during success + if opts.onFailure != nil { + opts.onFailure(t) + } + return false + } + return true + default: + // doneChan not closed + if opts.mode == pollUntilTimeout { + // For Never: timeout reached without condition being true = success + // We should return a success, unless the parent context has failed. + return parentCtx.Err() == nil } + + // opts.mode = pollUntilTrue + // For Eventually: should not reach here (failFunc already called) + if opts.onFailure != nil { + opts.onFailure(t) + } + + return false } } + +// CollectT implements the [T] interface and collects all errors. +// +// [CollectT] is specifically intended to be used with [EventuallyWithT] and +// should not be used outside of that context. +type CollectT struct { + // Domain: condition + // + // Maintainer: + // 1. FailNow() no longer just exits the go routine, but cancels the context of the caller instead before exiting. + // 2. We no longer establish the distinction between c.error nil or empty. Non-empty is an error, full stop. + // 2. Deprecated methods have been removed. + + // A slice of errors. Non-empty slice denotes a failure. + // A c.FailNow() will thee lose accumulated errors + errors []error + + // cancelContext cancels the parent context on FailNow() + cancelContext func() +} + +// Helper is like [testing.T.Helper] but does nothing. +func (*CollectT) Helper() {} + +// Errorf collects the error. +func (c *CollectT) Errorf(format string, args ...any) { + c.errors = append(c.errors, fmt.Errorf(format, args...)) +} + +// FailNow records a failure and cancels the parent [EventuallyWithT] context, +// before exiting the current go routine with [runtime.Goexit]. +// +// This causes the assertion to fail immediately without waiting for a timeout. +func (c *CollectT) FailNow() { + c.cancelContext() + c.errors = append(c.errors, errors.New("failed now")) // so c.failed() is true (currently lost as not owned by another go routine) + runtime.Goexit() +} + +func (c *CollectT) failed() bool { + return len(c.errors) != 0 +} + +func (c *CollectT) collected() []error { + return c.errors +} + +func (c *CollectT) withCancelFunc(cancel func()) *CollectT { + c.cancelContext = cancel + + return c +} diff --git a/internal/assertions/condition_test.go b/internal/assertions/condition_test.go index 8c4982925..159b9bfd4 100644 --- a/internal/assertions/condition_test.go +++ b/internal/assertions/condition_test.go @@ -4,22 +4,39 @@ package assertions import ( - "fmt" + "context" + "sort" + "strings" + "sync" "testing" "time" ) +const ( + testTimeout = 100 * time.Millisecond + testTick = 20 * time.Millisecond +) + func TestCondition(t *testing.T) { t.Parallel() - mock := new(testing.T) - if !Condition(mock, func() bool { return true }, "Truth") { - t.Error("Condition should return true") - } + t.Run("condition should be true", func(t *testing.T) { + t.Parallel() + + mock := new(testing.T) + if !Condition(mock, func() bool { return true }, "Truth") { + t.Error("condition should return true") + } + }) + + t.Run("condition should be false", func(t *testing.T) { + t.Parallel() - if Condition(mock, func() bool { return false }, "Lie") { - t.Error("Condition should return false") - } + mock := new(testing.T) + if Condition(mock, func() bool { return false }, "Lie") { + t.Error("condition should return false") + } + }) } func TestConditionEventually(t *testing.T) { @@ -27,18 +44,17 @@ func TestConditionEventually(t *testing.T) { t.Run("condition should Eventually be false", func(t *testing.T) { t.Parallel() - mock := new(testing.T) + mock := new(errorsCapturingT) condition := func() bool { return false } - False(t, Eventually(mock, condition, 100*time.Millisecond, 20*time.Millisecond)) + False(t, Eventually(mock, condition, testTimeout, testTick)) }) t.Run("condition should Eventually be true", func(t *testing.T) { t.Parallel() - mock := new(testing.T) state := 0 condition := func() bool { @@ -48,181 +64,340 @@ func TestConditionEventually(t *testing.T) { return state == 2 } - True(t, Eventually(mock, condition, 100*time.Millisecond, 20*time.Millisecond)) + True(t, Eventually(t, condition, testTimeout, testTick)) }) } -func TestConditionEventuallyWithTFalse(t *testing.T) { +// Check that a long running condition doesn't block Eventually. +// +// See issue 805 (and its long tail of following issues). +func TestConditionEventuallyTimeout(t *testing.T) { t.Parallel() - mock := new(errorsCapturingT) - condition := func(collect *CollectT) { - Fail(collect, "condition fixed failure") - } + t.Run("should fail on timeout", func(t *testing.T) { + t.Parallel() - False(t, EventuallyWithT(mock, condition, 100*time.Millisecond, 20*time.Millisecond)) - Len(t, mock.errors, 2) -} + mock := new(errorsCapturingT) + // A condition function that returns after the Eventually timeout + condition := func() bool { + time.Sleep(5 * time.Millisecond) + return true + } -func TestConditionEventuallyWithTTrue(t *testing.T) { - t.Parallel() - mock := new(errorsCapturingT) + False(t, Eventually(mock, condition, time.Millisecond, time.Microsecond)) + }) + + t.Run("should fail on parent test failed", func(t *testing.T) { + t.Parallel() - counter := 0 - condition := func(collect *CollectT) { - counter++ - True(collect, counter == 2) - } + parentCtx, failParent := context.WithCancel(context.WithoutCancel(t.Context())) + mock := new(errorsCapturingT).WithContext(parentCtx) - True(t, EventuallyWithT(mock, condition, 100*time.Millisecond, 20*time.Millisecond)) - Len(t, mock.errors, 0) - Equal(t, 2, counter, "Condition is expected to be called 2 times") + condition := func() bool { + time.Sleep(testTick) + failParent() // this cancels the parent context (e.g. mocks failing the parent test) + time.Sleep(2 * testTick) + + return true + } + + False(t, Eventually(mock, condition, testTimeout, testTick)) + + t.Run("reported errors should include the context cancellation", func(t *testing.T) { + // assert how this failure is reported + Len(t, mock.errors, 2, "expected to have 2 error messages: 1 for the context canceled, 1 for the never met condition") + + var hasContextCancelled, hasFailedCondition bool + for _, err := range mock.errors { + msg := err.Error() + switch { + case strings.Contains(msg, "context canceled"): + hasContextCancelled = true + case strings.Contains(msg, "never satisfied"): + hasFailedCondition = true + } + } + True(t, hasContextCancelled, "expected a context cancelled error") + True(t, hasFailedCondition, "expected a condition never satisfied error") + }) + }) } -func TestConditionEventuallyWithT_ConcurrencySafe(t *testing.T) { +func TestConditionEventuallySucceedQuickly(t *testing.T) { t.Parallel() - mock := new(errorsCapturingT) - condition := func(collect *CollectT) { - Fail(collect, "condition fixed failure") - } + t.Run("should succeed before the first tick", func(t *testing.T) { + mock := new(errorsCapturingT) + condition := func() bool { return true } - // To trigger race conditions, we run EventuallyWithT with a nanosecond tick. - False(t, EventuallyWithT(mock, condition, 100*time.Millisecond, time.Nanosecond)) - Len(t, mock.errors, 2) + // By making the tick longer than the total duration, we expect that this test would fail if + // we didn't check the condition before the first tick elapses. + True(t, Eventually(mock, condition, testTimeout, 1*time.Second)) + }) } -func TestConditionEventuallyWithT_ReturnsTheLatestFinishedConditionErrors(t *testing.T) { +func TestConditionEventuallyNoLeak(t *testing.T) { t.Parallel() - mock := new(errorsCapturingT) - // We'll use a channel to control whether a condition should sleep or not. - mustSleep := make(chan bool, 2) - mustSleep <- false - mustSleep <- true - close(mustSleep) + t.Run("should output messages in a determined order", func(t *testing.T) { + t.Parallel() - condition := func(collect *CollectT) { - if <-mustSleep { - // Sleep to ensure that the second condition runs longer than timeout. - time.Sleep(time.Second) - return + /* Original output (replaced by integers) from https://github.com/stretchr/testify/issues/1611 + condition_test.go:150: 2026-01-11 12:11:49.34854116 +0100 CET m=+0.000641595 Condition: inEventually = true + condition_test.go:152: 2026-01-11 12:11:49.84944055 +0100 CET m=+0.501540975 Condition: inEventually = true + condition_test.go:147: 2026-01-11 12:11:49.849484723 +0100 CET m=+0.501585149 Condition: end. + condition_test.go:160: 2026-01-11 12:11:49.849500022 +0100 CET m=+0.501600447 Eventually done + condition_test.go:163: 2026-01-11 12:11:49.849508218 +0100 CET m=+0.501608643 End of TestConditionEventuallyNoLeak/should_output_messages_in_a_determined_order + */ + mock := new(errorsCapturingT) + done := make(chan struct{}, 1) + recordedActions := make([]int, 0, 5) + var mx sync.Mutex + record := func(action int) { + mx.Lock() + defer mx.Unlock() + + recordedActions = append(recordedActions, action) } - // The first condition will fail. We expect to get this error as a result. - Fail(collect, "condition fixed failure") - } + inEventually := true + Eventually(mock, + func() bool { + defer func() { + record(2) + done <- struct{}{} + }() + if inEventually { + record(0) + } + time.Sleep(5 * testTimeout) + if inEventually { + record(1) + } + return true + }, + testTimeout, + testTick, + ) + + inEventually = false + record(3) + + <-done + record(4) + record(5) + + const expectedActions = 6 + Len(t, recordedActions, expectedActions, "expected 6 actions to be recorded during this execution", "got:", len(recordedActions)) + True(t, sort.IntsAreSorted(recordedActions), "expected recorded actions to be ordered") + }) - False(t, EventuallyWithT(mock, condition, 100*time.Millisecond, 20*time.Millisecond)) - Len(t, mock.errors, 2) + t.Run("should not leak a go routine for condition execution", func(t *testing.T) { + t.Parallel() + + mock := new(errorsCapturingT) + done := make(chan bool, 1) + + inEventually := true + Eventually(mock, + func() bool { + defer func() { + done <- inEventually + }() + time.Sleep(5 * testTimeout) + + return true + }, + testTimeout, + testTick, + ) + + inEventually = false + result := <-done + True(t, result, "Condition should end while Eventually still runs.") + }) } -func TestConditionEventuallyWithTFailNow(t *testing.T) { +func TestConditionEventuallyWithT(t *testing.T) { t.Parallel() - mock := new(CollectT) - condition := func(collect *CollectT) { - collect.FailNow() - } + t.Run("should complete with false", func(t *testing.T) { + t.Parallel() - False(t, EventuallyWithT(mock, condition, 100*time.Millisecond, 20*time.Millisecond)) - Len(t, mock.errors, 1) -} + mock := new(errorsCapturingT) + counter := 0 + condition := func(collect *CollectT) { + counter++ + Fail(collect, "condition fixed failure") + Fail(collect, "another condition fixed failure") + } -// Check that a long running condition doesn't block Eventually. -// See issue 805 (and its long tail of following issues). -func TestConditionEventuallyTimeout(t *testing.T) { - t.Parallel() - mock := new(testing.T) + False(t, EventuallyWithT(mock, condition, testTimeout, testTick)) - NotPanics(t, func() { - done, done2 := make(chan struct{}), make(chan struct{}) + const expectedErrors = 4 + Len(t, mock.errors, expectedErrors, "expected 2 errors from the condition, and 2 additional errors from Eventually") - // A condition function that returns after the Eventually timeout - condition := func() bool { - // Wait until Eventually times out and terminates - <-done - close(done2) - return true + expectedCalls := int(testTimeout / testTick) + if counter != expectedCalls && counter != expectedCalls+1 { // it may be 5 or 6 depending on how the test schedules + t.Errorf("expected %d calls to the condition, but got %d", expectedCalls, counter) } + }) - False(t, Eventually(mock, condition, time.Millisecond, time.Microsecond)) + t.Run("should complete with true", func(t *testing.T) { + t.Parallel() + + mock := new(errorsCapturingT) + counter := 0 + condition := func(collect *CollectT) { + counter++ + True(collect, counter == 2) + } - close(done) - <-done2 + True(t, EventuallyWithT(mock, condition, testTimeout, testTick)) + Len(t, mock.errors, 0) + const expectedCalls = 2 + Equal(t, expectedCalls, counter, "Condition is expected to have been called 2 times") }) -} -func TestConditionEventuallySucceedQuickly(t *testing.T) { - t.Parallel() - mock := new(testing.T) + t.Run("should complete with fail, on a nanosecond tick", func(t *testing.T) { + t.Parallel() - condition := func() bool { return true } + mock := new(errorsCapturingT) + condition := func(collect *CollectT) { + Fail(collect, "condition fixed failure") + } - // By making the tick longer than the total duration, we expect that this test would fail if - // we didn't check the condition before the first tick elapses. - True(t, Eventually(mock, condition, 100*time.Millisecond, time.Second)) -} + // To trigger race conditions, we run EventuallyWithT with a nanosecond tick. + False(t, EventuallyWithT(mock, condition, testTimeout, time.Nanosecond)) + const expectedErrors = 3 + Len(t, mock.errors, expectedErrors, "expected 1 errors from the condition, and 2 additional errors from Eventually") + }) -func TestConditionEventuallyWithTSucceedQuickly(t *testing.T) { - t.Parallel() - mock := new(testing.T) + t.Run("should complete with fail, with latest failed condition", func(t *testing.T) { + t.Parallel() - condition := func(*CollectT) {} + mock := new(errorsCapturingT) + // We'll use a channel to control whether a condition should sleep or not. + mustSleep := make(chan bool, 2) + mustSleep <- false + mustSleep <- true + close(mustSleep) + + condition := func(collect *CollectT) { + if <-mustSleep { + // Sleep to ensure that the second condition runs longer than timeout. + time.Sleep(time.Second) + return + } + + // The first condition will fail. We expect to get this error as a result. + Fail(collect, "condition fixed failure") + } - // By making the tick longer than the total duration, we expect that this test would fail if - // we didn't check the condition before the first tick elapses. - True(t, EventuallyWithT(mock, condition, 100*time.Millisecond, time.Second)) -} + False(t, EventuallyWithT(mock, condition, testTimeout, testTick)) + const expectedErrors = 3 + Len(t, mock.errors, expectedErrors, "expected 1 errors from the condition, and 2 additional errors from Eventually") + }) -func TestConditionNeverFalse(t *testing.T) { - t.Parallel() + t.Run("should complete with success, with the ticker never used", func(t *testing.T) { + t.Parallel() - condition := func() bool { - return false - } + mock := new(errorsCapturingT) + condition := func(*CollectT) {} - True(t, Never(t, condition, 100*time.Millisecond, 20*time.Millisecond)) -} + // By making the tick longer than the total duration, we expect that this test would fail if + // we didn't check the condition before the first tick elapses. + True(t, EventuallyWithT(mock, condition, testTimeout, time.Second)) + }) -// TestNeverTrue checks Never with a condition that returns true on second call. -func TestConditionNeverTrue(t *testing.T) { - t.Parallel() - mock := new(testing.T) + t.Run("should fail with a call to collect.FailNow", func(t *testing.T) { + t.Parallel() - // A list of values returned by condition. - // Channel protects against concurrent access. - returns := make(chan bool, 2) - returns <- false - returns <- true - defer close(returns) + mock := new(errorsCapturingT) + counter := 0 - // Will return true on second call. - condition := func() bool { - return <-returns - } + // The call to FailNow cancels the execution context of EventuallyWithT. + // so we don't have to wait for the timeout. + condition := func(collect *CollectT) { + counter++ + collect.FailNow() + } - False(t, Never(mock, condition, 100*time.Millisecond, 20*time.Millisecond)) + False(t, EventuallyWithT(mock, condition, 30*time.Minute, testTick)) + const expectedErrors = 2 + Len(t, mock.errors, expectedErrors) // we have 0 accumulated error + 2 errors from EventuallyWithT (includes the timeout) + if counter != 1 { + t.Errorf("expected the condition function to have been called only once, but got: %d", counter) + } + }) } -func TestConditionNeverFailQuickly(t *testing.T) { +func TestConditionNever(t *testing.T) { t.Parallel() - mock := new(testing.T) - // By making the tick longer than the total duration, we expect that this test would fail if - // we didn't check the condition before the first tick elapses. - condition := func() bool { return true } - False(t, Never(mock, condition, 100*time.Millisecond, time.Second)) -} + t.Run("should never be true", func(t *testing.T) { + t.Parallel() -// errorsCapturingT is a mock implementation of TestingT that captures errors reported with Errorf. -type errorsCapturingT struct { - errors []error -} + mock := new(errorsCapturingT) + condition := func() bool { + return false + } -// Helper is like [testing.T.Helper] but does nothing. -func (errorsCapturingT) Helper() {} + True(t, Never(mock, condition, testTimeout, testTick)) + }) + + t.Run("should never be true, on timeout", func(t *testing.T) { + t.Parallel() + + mock := new(errorsCapturingT) + condition := func() bool { + time.Sleep(2 * testTick) + // eventually returns true, after timeout + return true + } -func (t *errorsCapturingT) Errorf(format string, args ...any) { - t.errors = append(t.errors, fmt.Errorf(format, args...)) + True(t, Never(mock, condition, testTick, 1*time.Millisecond)) + }) + + t.Run("should never be true fails", func(t *testing.T) { + // checks Never with a condition that returns true on second call. + t.Parallel() + + mock := new(errorsCapturingT) + // A list of values returned by condition. + // Channel protects against concurrent access. + returns := make(chan bool, 2) + returns <- false + returns <- true + defer close(returns) + + // Will return true on second call. + condition := func() bool { + return <-returns + } + + False(t, Never(mock, condition, testTimeout, testTick)) + }) + + t.Run("should never be true fails, with ticker never triggered", func(t *testing.T) { + t.Parallel() + + mock := new(errorsCapturingT) + // By making the tick longer than the total duration, we expect that this test would fail if + // we didn't check the condition before the first tick elapses. + condition := func() bool { return true } + False(t, Never(mock, condition, testTimeout, time.Second)) + }) + + t.Run("should never be true fails, with parent test failing", func(t *testing.T) { + t.Parallel() + + parentCtx, failParent := context.WithCancel(context.WithoutCancel(t.Context())) + mock := new(errorsCapturingT).WithContext(parentCtx) + condition := func() bool { + failParent() // cancels the parent context, which results in Never to fail + return false + } + False(t, Never(mock, condition, testTimeout, time.Second)) + }) } diff --git a/internal/assertions/equal_test.go b/internal/assertions/equal_test.go index 9765180f4..d5a6ebae9 100644 --- a/internal/assertions/equal_test.go +++ b/internal/assertions/equal_test.go @@ -380,6 +380,9 @@ func panicCases() iter.Seq[panicCase] { type structWithUnexportedMapWithArrayKey struct { m any } + type s struct { + f map[[1]byte]int + } return slices.Values([]panicCase{ { @@ -444,6 +447,17 @@ func panicCases() iter.Seq[panicCase] { }, expectEqual: false, }, + { + name: "panic behavior on map with array key", + value1: s{ + f: map[[1]byte]int{ + {0x1}: 0, + {0x2}: 0, + }, + }, + value2: s{}, + expectEqual: false, + }, }) } diff --git a/internal/assertions/ifaces.go b/internal/assertions/ifaces.go index 22d4847b3..eab6737d0 100644 --- a/internal/assertions/ifaces.go +++ b/internal/assertions/ifaces.go @@ -3,6 +3,8 @@ package assertions +import "context" + // T is an interface wrapper around [testing.T]. type T interface { Errorf(format string, args ...any) @@ -21,3 +23,7 @@ type failNower interface { type namer interface { Name() string } + +type contextualizer interface { + Context() context.Context +} diff --git a/internal/assertions/mock_test.go b/internal/assertions/mock_test.go index efbb3e91c..5112bd52a 100644 --- a/internal/assertions/mock_test.go +++ b/internal/assertions/mock_test.go @@ -5,6 +5,7 @@ package assertions import ( "bytes" + "context" "fmt" "regexp" "runtime" @@ -12,6 +13,17 @@ import ( "testing" ) +var ( + _ T = &mockT{} + _ T = &mockFailNowT{} + _ failNower = &mockFailNowT{} + _ T = &captureT{} + _ T = &bufferT{} + _ T = &dummyT{} + _ T = &errorsCapturingT{} + _ T = &outputT{} +) + type mockT struct { errorFmt string args []any @@ -49,6 +61,59 @@ func (m *mockFailNowT) FailNow() { m.failed = true } +type dummyT struct{} + +func (dummyT) Errorf(string, ...any) {} + +func (dummyT) Context() context.Context { + return context.Background() +} + +// errorsCapturingT is a mock implementation of TestingT that captures errors reported with Errorf. +type errorsCapturingT struct { + errors []error + ctx context.Context //nolint:containedctx // this is ok to support context injection tests +} + +// Helper is like [testing.T.Helper] but does nothing. +func (errorsCapturingT) Helper() {} + +func (t errorsCapturingT) Context() context.Context { + if t.ctx == nil { + return context.Background() + } + + return t.ctx +} + +func (t *errorsCapturingT) WithContext(ctx context.Context) *errorsCapturingT { + t.ctx = ctx + + return t +} + +func (t *errorsCapturingT) Errorf(format string, args ...any) { + t.errors = append(t.errors, fmt.Errorf(format, args...)) +} + +type outputT struct { + buf *bytes.Buffer + helpers map[string]struct{} +} + +// Implements T. +func (t *outputT) Errorf(format string, args ...any) { + s := fmt.Sprintf(format, args...) + t.buf.WriteString(s) +} + +func (t *outputT) Helper() { + if t.helpers == nil { + t.helpers = make(map[string]struct{}) + } + t.helpers[callerName(1)] = struct{}{} +} + type captureT struct { failed bool msg string From 73531db79f0e30606a83143a5d250390744819a4 Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Sun, 11 Jan 2026 16:47:36 +0100 Subject: [PATCH 2/2] chore: regenerated packages and docs Signed-off-by: Frederic BIDON --- assert/assert_assertions.go | 71 ++++++++++++++++++++------ assert/assert_assertions_test.go | 4 +- assert/assert_examples_test.go | 4 +- assert/assert_format.go | 4 +- assert/assert_format_test.go | 4 +- assert/assert_forward.go | 4 +- assert/assert_forward_test.go | 4 +- assert/assert_helpers.go | 4 +- assert/assert_helpers_test.go | 4 +- assert/assert_types.go | 7 ++- docs/doc-site/api/_index.md | 8 +-- docs/doc-site/api/boolean.md | 8 +-- docs/doc-site/api/collection.md | 8 +-- docs/doc-site/api/common.md | 8 +-- docs/doc-site/api/comparison.md | 8 +-- docs/doc-site/api/condition.md | 81 ++++++++++++++++++++++-------- docs/doc-site/api/equality.md | 8 +-- docs/doc-site/api/error.md | 8 +-- docs/doc-site/api/file.md | 8 +-- docs/doc-site/api/http.md | 8 +-- docs/doc-site/api/json.md | 8 +-- docs/doc-site/api/number.md | 8 +-- docs/doc-site/api/ordering.md | 8 +-- docs/doc-site/api/panic.md | 8 +-- docs/doc-site/api/string.md | 8 +-- docs/doc-site/api/testing.md | 8 +-- docs/doc-site/api/time.md | 8 +-- docs/doc-site/api/type.md | 8 +-- docs/doc-site/api/yaml.md | 8 +-- internal/assertions/condition.go | 2 + require/require_assertions.go | 71 ++++++++++++++++++++------ require/require_assertions_test.go | 4 +- require/require_examples_test.go | 4 +- require/require_format.go | 4 +- require/require_format_test.go | 4 +- require/require_forward.go | 4 +- require/require_forward_test.go | 4 +- require/require_helpers.go | 4 +- require/require_helpers_test.go | 4 +- require/require_types.go | 7 ++- 40 files changed, 286 insertions(+), 161 deletions(-) diff --git a/assert/assert_assertions.go b/assert/assert_assertions.go index ff5120051..b7de4ac76 100644 --- a/assert/assert_assertions.go +++ b/assert/assert_assertions.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package assert @@ -14,7 +14,7 @@ import ( "github.com/go-openapi/testify/v2/internal/assertions" ) -// Condition uses a Comparison to assert a complex condition. +// Condition uses a [Comparison] to assert a complex condition. // // # Usage // @@ -299,12 +299,28 @@ func ErrorIs(t T, err error, target error, msgAndArgs ...any) bool { return assertions.ErrorIs(t, err, target, msgAndArgs...) } -// Eventually asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. +// Eventually asserts that the given condition will be met in waitFor time, +// periodically checking the target function on each tick. +// +// [Eventually] waits until the condition returns true, for at most waitFor, +// or until the parent context of the test is cancelled. +// +// If the condition takes longer than waitFor to complete, [Eventually] fails +// but waits for the current condition execution to finish before returning. +// +// For long-running conditions to be interrupted early, check [testing.T.Context] +// which is cancelled on test failure. // // # Usage // -// assertions.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +// assertions.Eventually(t, func() bool { return true }, time.Second, 10*time.Millisecond) +// +// # Concurrency +// +// The condition function is never executed in parallel: only one goroutine executes it. +// It may write to variables outside its scope without triggering race conditions. +// +// A blocking condition will cause [Eventually] to hang until it returns. // // # Examples // @@ -319,14 +335,19 @@ func Eventually(t T, condition func() bool, waitFor time.Duration, tick time.Dur return assertions.Eventually(t, condition, waitFor, tick, msgAndArgs...) } -// EventuallyWithT asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. In contrast to Eventually, -// it supplies a CollectT to the condition function, so that the condition -// function can use the CollectT to call other assertions. +// EventuallyWithT asserts that the given condition will be met in waitFor time, +// periodically checking the target function at each tick. +// +// In contrast to [Eventually], the condition function is supplied with a [CollectT] +// to accumulate errors from calling other assertions. +// // The condition is considered "met" if no errors are raised in a tick. -// The supplied CollectT collects all errors from one tick (if there are any). -// If the condition is not met before waitFor, the collected errors of -// the last tick are copied to t. +// The supplied [CollectT] collects all errors from one tick. +// +// If the condition is not met before waitFor, the collected errors from the +// last tick are copied to t. +// +// Calling [CollectT.FailNow] cancels the condition immediately and fails the assertion. // // # Usage // @@ -335,11 +356,17 @@ func Eventually(t T, condition func() bool, waitFor time.Duration, tick time.Dur // time.Sleep(8*time.Second) // externalValue = true // }() +// // assertions.EventuallyWithT(t, func(c *assertions.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assertions.True(c, externalValue, "expected 'externalValue' to be true") // }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") // +// # Concurrency +// +// The condition function is never executed in parallel: only one goroutine executes it. +// It may write to variables outside its scope without triggering race conditions. +// // # Examples // // success: func(c *CollectT) { True(c,true) }, 100*time.Millisecond, 20*time.Millisecond @@ -1021,12 +1048,24 @@ func Negative(t T, e any, msgAndArgs ...any) bool { return assertions.Negative(t, e, msgAndArgs...) } -// Never asserts that the given condition doesn't satisfy in waitFor time, -// periodically checking the target function each tick. +// Never asserts that the given condition is never satisfied within waitFor time, +// periodically checking the target function at each tick. +// +// [Never] is the opposite of [Eventually]. It succeeds if the waitFor timeout +// is reached without the condition ever returning true. +// +// If the parent context is cancelled before the timeout, [Never] fails. // // # Usage // -// assertions.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) +// assertions.Never(t, func() bool { return false }, time.Second, 10*time.Millisecond) +// +// # Concurrency +// +// The condition function is never executed in parallel: only one goroutine executes it. +// It may write to variables outside its scope without triggering race conditions. +// +// A blocking condition will cause [Never] to hang until it returns. // // # Examples // diff --git a/assert/assert_assertions_test.go b/assert/assert_assertions_test.go index 68cc33de2..a67d757e6 100644 --- a/assert/assert_assertions_test.go +++ b/assert/assert_assertions_test.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package assert diff --git a/assert/assert_examples_test.go b/assert/assert_examples_test.go index 82cb71f11..b2ee0c8e8 100644 --- a/assert/assert_examples_test.go +++ b/assert/assert_examples_test.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package assert_test diff --git a/assert/assert_format.go b/assert/assert_format.go index b6b901ed8..a64852e87 100644 --- a/assert/assert_format.go +++ b/assert/assert_format.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package assert diff --git a/assert/assert_format_test.go b/assert/assert_format_test.go index e5e318a87..17e3d4f7f 100644 --- a/assert/assert_format_test.go +++ b/assert/assert_format_test.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package assert diff --git a/assert/assert_forward.go b/assert/assert_forward.go index 3861c5cbb..1a6f39228 100644 --- a/assert/assert_forward.go +++ b/assert/assert_forward.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package assert diff --git a/assert/assert_forward_test.go b/assert/assert_forward_test.go index 839495b1e..bdf9950ee 100644 --- a/assert/assert_forward_test.go +++ b/assert/assert_forward_test.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package assert diff --git a/assert/assert_helpers.go b/assert/assert_helpers.go index ea9205e9e..c1792ebd9 100644 --- a/assert/assert_helpers.go +++ b/assert/assert_helpers.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package assert diff --git a/assert/assert_helpers_test.go b/assert/assert_helpers_test.go index c86490fe1..3c7385fb7 100644 --- a/assert/assert_helpers_test.go +++ b/assert/assert_helpers_test.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package assert diff --git a/assert/assert_types.go b/assert/assert_types.go index 0bb0068e9..625423771 100644 --- a/assert/assert_types.go +++ b/assert/assert_types.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package assert @@ -25,6 +25,9 @@ type ( BoolAssertionFunc = assertions.BoolAssertionFunc // CollectT implements the [T] interface and collects all errors. + // + // [CollectT] is specifically intended to be used with [EventuallyWithT] and + // should not be used outside of that context. CollectT = assertions.CollectT // Comparison is a custom function that returns true on success and false on failure. diff --git a/docs/doc-site/api/_index.md b/docs/doc-site/api/_index.md index 367e8cd2b..d806fda12 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-02 +modified: 2026-01-11 --- **Go testing assertions for the rest of us** @@ -52,7 +52,7 @@ The `testify` API is organized in 18 domains shown below. --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -64,7 +64,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/boolean.md b/docs/doc-site/api/boolean.md index 2d0abeec5..ac630d6d7 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-02 +modified: 2026-01-11 weight: 1 domains: - "boolean" @@ -121,7 +121,7 @@ True asserts that the specified value is true. --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -131,7 +131,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/collection.md b/docs/doc-site/api/collection.md index 356c2b7ef..6eef2b795 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-02 +modified: 2026-01-11 weight: 2 domains: - "collection" @@ -407,7 +407,7 @@ only the map key is evaluated. --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -417,7 +417,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/common.md b/docs/doc-site/api/common.md index 8156537da..3bd243d4c 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-02 +modified: 2026-01-11 weight: 18 domains: - "common" @@ -145,7 +145,7 @@ values are equal. --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -155,7 +155,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/comparison.md b/docs/doc-site/api/comparison.md index 2217f9646..bf88a6209 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-02 +modified: 2026-01-11 weight: 3 domains: - "comparison" @@ -329,7 +329,7 @@ Positive asserts that the specified element is strictly positive. --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -339,7 +339,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/condition.md b/docs/doc-site/api/condition.md index 7c8ef82b4..4457c3714 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-02 +modified: 2026-01-11 weight: 4 domains: - "condition" @@ -29,7 +29,7 @@ This domain exposes 4 functionalities. ### Condition -Condition uses a Comparison to assert a complex condition. +Condition uses a [Comparison] to assert a complex condition. {{% expand title="Examples" %}} {{< tabs >}} @@ -70,20 +70,36 @@ Condition uses a Comparison to assert a complex condition. |--|--| | [`assertions.Condition(t T, comp Comparison, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Condition) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Condition](https://github.com/go-openapi/testify/blob/master/internal/assertions/condition.go#L22) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Condition](https://github.com/go-openapi/testify/blob/master/internal/assertions/condition.go#L26) {{% /tab %}} {{< /tabs >}} ### Eventually -Eventually asserts that given condition will be met in waitFor time, -periodically checking target function each tick. +Eventually asserts that the given condition will be met in waitFor time, +periodically checking the target function on each tick. + +[Eventually] waits until the condition returns true, for at most waitFor, +or until the parent context of the test is cancelled. + +If the condition takes longer than waitFor to complete, [Eventually] fails +but waits for the current condition execution to finish before returning. + +For long-running conditions to be interrupted early, check [testing.T.Context](https://pkg.go.dev/testing#T.Context) +which is cancelled on test failure. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go - assertions.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) + assertions.Eventually(t, func() bool { return true }, time.Second, 10*time.Millisecond) +``` +{{< /tab >}} +{{% tab title="Concurrency" %}} +```go +The condition function is never executed in parallel: only one goroutine executes it. +It may write to variables outside its scope without triggering race conditions. +A blocking condition will cause [Eventually] to hang until it returns. ``` {{< /tab >}} {{% tab title="Examples" %}} @@ -118,20 +134,25 @@ periodically checking target function each tick. |--|--| | [`assertions.Eventually(t T, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Eventually) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Eventually](https://github.com/go-openapi/testify/blob/master/internal/assertions/condition.go#L45) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Eventually](https://github.com/go-openapi/testify/blob/master/internal/assertions/condition.go#L67) {{% /tab %}} {{< /tabs >}} ### EventuallyWithT -EventuallyWithT asserts that given condition will be met in waitFor time, -periodically checking target function each tick. In contrast to Eventually, -it supplies a CollectT to the condition function, so that the condition -function can use the CollectT to call other assertions. +EventuallyWithT asserts that the given condition will be met in waitFor time, +periodically checking the target function at each tick. + +In contrast to [Eventually], the condition function is supplied with a [CollectT] +to accumulate errors from calling other assertions. + The condition is considered "met" if no errors are raised in a tick. -The supplied CollectT collects all errors from one tick (if there are any). -If the condition is not met before waitFor, the collected errors of -the last tick are copied to t. +The supplied [CollectT] collects all errors from one tick. + +If the condition is not met before waitFor, the collected errors from the +last tick are copied to t. + +Calling [CollectT.FailNow](https://pkg.go.dev/CollectT#FailNow) cancels the condition immediately and fails the assertion. {{% expand title="Examples" %}} {{< tabs >}} @@ -148,6 +169,12 @@ the last tick are copied to t. }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") ``` {{< /tab >}} +{{% tab title="Concurrency" %}} +```go +The condition function is never executed in parallel: only one goroutine executes it. +It may write to variables outside its scope without triggering race conditions. +``` +{{< /tab >}} {{% tab title="Examples" %}} ```go success: func(c *CollectT) { True(c,true) }, 100*time.Millisecond, 20*time.Millisecond @@ -186,14 +213,26 @@ the last tick are copied to t. ### Never -Never asserts that the given condition doesn't satisfy in waitFor time, -periodically checking the target function each tick. +Never asserts that the given condition is never satisfied within waitFor time, +periodically checking the target function at each tick. + +[Never] is the opposite of [Eventually]. It succeeds if the waitFor timeout +is reached without the condition ever returning true. + +If the parent context is cancelled before the timeout, [Never] fails. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go - assertions.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) + assertions.Never(t, func() bool { return false }, time.Second, 10*time.Millisecond) +``` +{{< /tab >}} +{{% tab title="Concurrency" %}} +```go +The condition function is never executed in parallel: only one goroutine executes it. +It may write to variables outside its scope without triggering race conditions. +A blocking condition will cause [Never] to hang until it returns. ``` {{< /tab >}} {{% tab title="Examples" %}} @@ -228,7 +267,7 @@ periodically checking the target function each tick. |--|--| | [`assertions.Never(t T, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Never) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Never](https://github.com/go-openapi/testify/blob/master/internal/assertions/condition.go#L204) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Never](https://github.com/go-openapi/testify/blob/master/internal/assertions/condition.go#L99) {{% /tab %}} {{< /tabs >}} @@ -236,7 +275,7 @@ periodically checking the target function each tick. --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -246,7 +285,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/equality.md b/docs/doc-site/api/equality.md index 0f76c0fea..e27ebc10d 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-02 +modified: 2026-01-11 weight: 5 domains: - "equality" @@ -644,7 +644,7 @@ determined based on the equality of both type and value. --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -654,7 +654,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/error.md b/docs/doc-site/api/error.md index a9b8f8342..51f9a1fe9 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-02 +modified: 2026-01-11 weight: 6 domains: - "error" @@ -430,7 +430,7 @@ This is a wrapper for [errors.Is](https://pkg.go.dev/errors#Is). --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -440,7 +440,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/file.md b/docs/doc-site/api/file.md index 4e311c9e9..f024e3789 100644 --- a/docs/doc-site/api/file.md +++ b/docs/doc-site/api/file.md @@ -1,7 +1,7 @@ --- title: "File" description: "Asserting OS Files" -modified: 2026-01-02 +modified: 2026-01-11 weight: 7 domains: - "file" @@ -323,7 +323,7 @@ if the path points to an existing _file_ only. --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -333,7 +333,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/http.md b/docs/doc-site/api/http.md index 2a7605cbe..b0a9ff4ca 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-02 +modified: 2026-01-11 weight: 8 domains: - "http" @@ -361,7 +361,7 @@ It returns the empty string if building a new request fails. --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -371,7 +371,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/json.md b/docs/doc-site/api/json.md index b1d4ba3ee..edb9af0bc 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-02 +modified: 2026-01-11 weight: 9 domains: - "json" @@ -121,7 +121,7 @@ JSONEqBytes asserts that two JSON byte slices are equivalent. --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -131,7 +131,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/number.md b/docs/doc-site/api/number.md index b0b4f71be..89240172c 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-02 +modified: 2026-01-11 weight: 10 domains: - "number" @@ -268,7 +268,7 @@ InEpsilonSlice is the same as InEpsilon, except it compares each value from two --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -278,7 +278,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/ordering.md b/docs/doc-site/api/ordering.md index b33fba1b9..51e1800b8 100644 --- a/docs/doc-site/api/ordering.md +++ b/docs/doc-site/api/ordering.md @@ -1,7 +1,7 @@ --- title: "Ordering" description: "Asserting How Collections Are Ordered" -modified: 2026-01-02 +modified: 2026-01-11 weight: 11 domains: - "ordering" @@ -227,7 +227,7 @@ IsNonIncreasing asserts that the collection is not increasing. --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -237,7 +237,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/panic.md b/docs/doc-site/api/panic.md index ab51a4752..979d89d41 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-02 +modified: 2026-01-11 weight: 12 domains: - "panic" @@ -222,7 +222,7 @@ the recovered panic value equals the expected panic value. --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -232,7 +232,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/string.md b/docs/doc-site/api/string.md index e7c41a4d8..20d95f8d9 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-02 +modified: 2026-01-11 weight: 13 domains: - "string" @@ -123,7 +123,7 @@ Regexp asserts that a specified regexp matches a string. --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -133,7 +133,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/testing.md b/docs/doc-site/api/testing.md index 6e2b491de..c22d3df75 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-02 +modified: 2026-01-11 weight: 14 domains: - "testing" @@ -119,7 +119,7 @@ FailNow fails test. --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -129,7 +129,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/time.md b/docs/doc-site/api/time.md index 7aa6c20c8..4b398bf03 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-02 +modified: 2026-01-11 weight: 15 domains: - "time" @@ -121,7 +121,7 @@ WithinRange asserts that a time is within a time range (inclusive). --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -131,7 +131,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/type.md b/docs/doc-site/api/type.md index 82705771f..2f05b97ae 100644 --- a/docs/doc-site/api/type.md +++ b/docs/doc-site/api/type.md @@ -1,7 +1,7 @@ --- title: "Type" description: "Asserting Types Rather Than Values" -modified: 2026-01-02 +modified: 2026-01-11 weight: 16 domains: - "type" @@ -317,7 +317,7 @@ Zero asserts that i is the zero value for its type. --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -327,7 +327,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/docs/doc-site/api/yaml.md b/docs/doc-site/api/yaml.md index 3003c95cf..dc64b84c0 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-02 +modified: 2026-01-11 weight: 17 domains: - "yaml" @@ -82,7 +82,7 @@ YAMLEq asserts that the first documents in the two YAML strings are equivalent. --- -Generated with github.com/go-openapi/testify/v2/codegen +Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 @@ -92,7 +92,7 @@ SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers SPDX-License-Identifier: Apache-2.0 -Document generated by github.com/go-openapi/testify/v2/codegen DO NOT EDIT. +Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] --> diff --git a/internal/assertions/condition.go b/internal/assertions/condition.go index 1f77025b1..b6d040254 100644 --- a/internal/assertions/condition.go +++ b/internal/assertions/condition.go @@ -237,6 +237,8 @@ type pollOptions struct { // pollCondition is the common implementation for eventually, never, and eventuallyWithT. // It polls a condition function at regular intervals until success or timeout. +// +//nolint:gocognit,gocyclo,cyclop // A refactoring is planned for this complex function. func pollCondition(t T, condition func() bool, waitFor, tick time.Duration, opts pollOptions, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() diff --git a/require/require_assertions.go b/require/require_assertions.go index e509c8078..2dcdeee9d 100644 --- a/require/require_assertions.go +++ b/require/require_assertions.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package require @@ -14,7 +14,7 @@ import ( "github.com/go-openapi/testify/v2/internal/assertions" ) -// Condition uses a Comparison to assert a complex condition. +// Condition uses a [Comparison] to assert a complex condition. // // # Usage // @@ -351,12 +351,28 @@ func ErrorIs(t T, err error, target error, msgAndArgs ...any) { t.FailNow() } -// Eventually asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. +// Eventually asserts that the given condition will be met in waitFor time, +// periodically checking the target function on each tick. +// +// [Eventually] waits until the condition returns true, for at most waitFor, +// or until the parent context of the test is cancelled. +// +// If the condition takes longer than waitFor to complete, [Eventually] fails +// but waits for the current condition execution to finish before returning. +// +// For long-running conditions to be interrupted early, check [testing.T.Context] +// which is cancelled on test failure. // // # Usage // -// assertions.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +// assertions.Eventually(t, func() bool { return true }, time.Second, 10*time.Millisecond) +// +// # Concurrency +// +// The condition function is never executed in parallel: only one goroutine executes it. +// It may write to variables outside its scope without triggering race conditions. +// +// A blocking condition will cause [Eventually] to hang until it returns. // // # Examples // @@ -375,14 +391,19 @@ func Eventually(t T, condition func() bool, waitFor time.Duration, tick time.Dur t.FailNow() } -// EventuallyWithT asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. In contrast to Eventually, -// it supplies a CollectT to the condition function, so that the condition -// function can use the CollectT to call other assertions. +// EventuallyWithT asserts that the given condition will be met in waitFor time, +// periodically checking the target function at each tick. +// +// In contrast to [Eventually], the condition function is supplied with a [CollectT] +// to accumulate errors from calling other assertions. +// // The condition is considered "met" if no errors are raised in a tick. -// The supplied CollectT collects all errors from one tick (if there are any). -// If the condition is not met before waitFor, the collected errors of -// the last tick are copied to t. +// The supplied [CollectT] collects all errors from one tick. +// +// If the condition is not met before waitFor, the collected errors from the +// last tick are copied to t. +// +// Calling [CollectT.FailNow] cancels the condition immediately and fails the assertion. // // # Usage // @@ -391,11 +412,17 @@ func Eventually(t T, condition func() bool, waitFor time.Duration, tick time.Dur // time.Sleep(8*time.Second) // externalValue = true // }() +// // assertions.EventuallyWithT(t, func(c *assertions.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assertions.True(c, externalValue, "expected 'externalValue' to be true") // }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") // +// # Concurrency +// +// The condition function is never executed in parallel: only one goroutine executes it. +// It may write to variables outside its scope without triggering race conditions. +// // # Examples // // success: func(c *CollectT) { True(c,true) }, 100*time.Millisecond, 20*time.Millisecond @@ -1209,12 +1236,24 @@ func Negative(t T, e any, msgAndArgs ...any) { t.FailNow() } -// Never asserts that the given condition doesn't satisfy in waitFor time, -// periodically checking the target function each tick. +// Never asserts that the given condition is never satisfied within waitFor time, +// periodically checking the target function at each tick. +// +// [Never] is the opposite of [Eventually]. It succeeds if the waitFor timeout +// is reached without the condition ever returning true. +// +// If the parent context is cancelled before the timeout, [Never] fails. // // # Usage // -// assertions.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) +// assertions.Never(t, func() bool { return false }, time.Second, 10*time.Millisecond) +// +// # Concurrency +// +// The condition function is never executed in parallel: only one goroutine executes it. +// It may write to variables outside its scope without triggering race conditions. +// +// A blocking condition will cause [Never] to hang until it returns. // // # Examples // diff --git a/require/require_assertions_test.go b/require/require_assertions_test.go index 5e6f53a5e..48d725061 100644 --- a/require/require_assertions_test.go +++ b/require/require_assertions_test.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package require diff --git a/require/require_examples_test.go b/require/require_examples_test.go index bc1a05118..18eeb4555 100644 --- a/require/require_examples_test.go +++ b/require/require_examples_test.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package require_test diff --git a/require/require_format.go b/require/require_format.go index 41fdeac1f..b704fa554 100644 --- a/require/require_format.go +++ b/require/require_format.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package require diff --git a/require/require_format_test.go b/require/require_format_test.go index a4da38239..a8c1b7528 100644 --- a/require/require_format_test.go +++ b/require/require_format_test.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package require diff --git a/require/require_forward.go b/require/require_forward.go index 941b876e7..416048630 100644 --- a/require/require_forward.go +++ b/require/require_forward.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package require diff --git a/require/require_forward_test.go b/require/require_forward_test.go index e43372a0c..76a3d146a 100644 --- a/require/require_forward_test.go +++ b/require/require_forward_test.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package require diff --git a/require/require_helpers.go b/require/require_helpers.go index 3bc26dfed..4a49efac6 100644 --- a/require/require_helpers.go +++ b/require/require_helpers.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package require diff --git a/require/require_helpers_test.go b/require/require_helpers_test.go index b507f90c3..f3fd8b9a5 100644 --- a/require/require_helpers_test.go +++ b/require/require_helpers_test.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package require diff --git a/require/require_types.go b/require/require_types.go index 80e19f708..a15626596 100644 --- a/require/require_types.go +++ b/require/require_types.go @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -// Code generated with github.com/go-openapi/testify/v2/codegen; DO NOT EDIT. -// Generated on 2026-01-02 (version v1.2.2-760-g97c29e3) using codegen version master [sha: 97c29e3dbfc40800a080863ceea81db0cfd6e858] +// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. +// Generated on 2026-01-11 (version e6b0793) using codegen version v2.1.9-0.20260111152118-e6b0793ba519+dirty [sha: e6b0793ba519fb22dc1887392e1465649a5a95ff] package require @@ -25,6 +25,9 @@ type ( BoolAssertionFunc func(T, bool, ...any) // CollectT implements the [T] interface and collects all errors. + // + // [CollectT] is specifically intended to be used with [EventuallyWithT] and + // should not be used outside of that context. CollectT = assertions.CollectT // Comparison is a custom function that returns true on success and false on failure.