From 8bedbde111bb44081f21d5295a28f9af6e7e536a Mon Sep 17 00:00:00 2001 From: Vladimir Kochnev Date: Sun, 8 Oct 2023 21:08:46 +0300 Subject: [PATCH] assert: collect.FailNow() should not panic Fixes https://github.com/stretchr/testify/issues/1396 Fixes https://github.com/stretchr/testify/issues/1457 --- assert/assertions.go | 19 +++++++++++-------- assert/assertions_test.go | 11 +++++++++++ require/requirements_test.go | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index c276316b4..0f353fc5f 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1864,17 +1864,20 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t // CollectT implements the TestingT interface and collects all errors. type CollectT struct { + failed bool errors []error } // Errorf collects the error. func (c *CollectT) Errorf(format string, args ...interface{}) { + c.failed = true c.errors = append(c.errors, fmt.Errorf(format, args...)) } -// FailNow panics. -func (*CollectT) FailNow() { - panic("Assertion failed") +// FailNow stops the execution by calling runtime.Goexit. +func (c *CollectT) FailNow() { + c.failed = true + runtime.Goexit() } // Deprecated: That was a method for internal usage that should not have been published. Now just panics. @@ -1911,7 +1914,7 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time } var lastFinishedTickErrs []error - ch := make(chan []error, 1) + ch := make(chan *CollectT, 1) timer := time.NewTimer(waitFor) defer timer.Stop() @@ -1931,16 +1934,16 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time go func() { collect := new(CollectT) defer func() { - ch <- collect.errors + ch <- collect }() condition(collect) }() - case errs := <-ch: - if len(errs) == 0 { + 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 = errs + lastFinishedTickErrs = collect.errors tick = ticker.C } } diff --git a/assert/assertions_test.go b/assert/assertions_test.go index e1245b421..47c34a444 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -2838,6 +2838,17 @@ func TestEventuallyWithT_ReturnsTheLatestFinishedConditionErrors(t *testing.T) { Len(t, mockT.errors, 2) } +func TestEventuallyWithTFailNow(t *testing.T) { + mockT := new(CollectT) + + condition := func(collect *CollectT) { + collect.FailNow() + } + + False(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond)) + Len(t, mockT.errors, 1) +} + func TestNeverFalse(t *testing.T) { condition := func() bool { return false diff --git a/require/requirements_test.go b/require/requirements_test.go index febf0c187..c1636ca2e 100644 --- a/require/requirements_test.go +++ b/require/requirements_test.go @@ -5,6 +5,8 @@ import ( "errors" "testing" "time" + + "github.com/stretchr/testify/assert" ) // AssertionTesterInterface defines an interface to be used for testing assertion methods @@ -681,3 +683,33 @@ func TestErrorAssertionFunc(t *testing.T) { }) } } + +func TestEventuallyWithTFalse(t *testing.T) { + mockT := new(MockT) + + condition := func(collect *assert.CollectT) { + True(collect, false) + } + + EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestEventuallyWithTTrue(t *testing.T) { + mockT := new(MockT) + + state := 0 + condition := func(collect *assert.CollectT) { + defer func() { + state += 1 + }() + True(collect, state == 2) + } + + EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond) + if mockT.Failed { + t.Error("Check should pass") + } +}