From 15ae495194dee4ddc411055222cac01d1fe55bfb Mon Sep 17 00:00:00 2001 From: Tobias Krischer Date: Wed, 14 Sep 2022 01:08:17 +0200 Subject: [PATCH 1/4] add EventuallyWithT assertion --- assert/assertion_format.go | 34 +++++++++++++++ assert/assertion_forward.go | 68 +++++++++++++++++++++++++++++ assert/assertions.go | 86 +++++++++++++++++++++++++++++++++++++ assert/assertions_test.go | 32 ++++++++++++++ require/require.go | 74 +++++++++++++++++++++++++++++++ require/require_forward.go | 68 +++++++++++++++++++++++++++++ 6 files changed, 362 insertions(+) diff --git a/assert/assertion_format.go b/assert/assertion_format.go index 7880b8f94..3fe076cfe 100644 --- a/assert/assertion_format.go +++ b/assert/assertion_format.go @@ -155,6 +155,40 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) } +// EventuallyWithTf 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. +// 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. +// +// falseThenTrue := func(falses int) func() bool { +// count := 0 +// return func() bool { +// if count < falses { +// count++ +// return false +// } +// return true +// } +// } +// f := falseThenTrue(5) +// assert.EventuallyWithTf(t, func(mockT *assert.CollectT) (success bool, "error message %s", "formatted") { +// defer func() { +// r := recover() +// success = (r == nil) +// }() +// assert.True(mockT, f()) +// return +// }, 50*time.Millisecond, 10*time.Millisecond) +func EventuallyWithTf(t TestingT, condition func(collect *CollectT) bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return EventuallyWithT(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) +} + // Exactlyf asserts that two objects are equal in value and type. // // assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") diff --git a/assert/assertion_forward.go b/assert/assertion_forward.go index 339515b8b..7ab795746 100644 --- a/assert/assertion_forward.go +++ b/assert/assertion_forward.go @@ -288,6 +288,74 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti return Eventually(a.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. +// 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. +// +// falseThenTrue := func(falses int) func() bool { +// count := 0 +// return func() bool { +// if count < falses { +// count++ +// return false +// } +// return true +// } +// } +// f := falseThenTrue(5) +// a.EventuallyWithT(func(mockT *assert.CollectT) (success bool) { +// defer func() { +// r := recover() +// success = (r == nil) +// }() +// assert.True(mockT, f()) +// return +// }, 50*time.Millisecond, 10*time.Millisecond) +func (a *Assertions) EventuallyWithT(condition func(collect *CollectT) bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// EventuallyWithTf 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. +// 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. +// +// falseThenTrue := func(falses int) func() bool { +// count := 0 +// return func() bool { +// if count < falses { +// count++ +// return false +// } +// return true +// } +// } +// f := falseThenTrue(5) +// a.EventuallyWithTf(func(mockT *assert.CollectT) (success bool, "error message %s", "formatted") { +// defer func() { +// r := recover() +// success = (r == nil) +// }() +// assert.True(mockT, f()) +// return +// }, 50*time.Millisecond, 10*time.Millisecond) +func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT) bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...) +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // diff --git a/assert/assertions.go b/assert/assertions.go index fa1245b18..71c5c5a4d 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1756,6 +1756,92 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t } } +// CollectT implements the TestingT interface and collects all errors. +type CollectT struct { + errors []error +} + +// Errorf collects the error. +func (c *CollectT) Errorf(format string, args ...interface{}) { + c.errors = append(c.errors, fmt.Errorf(format, args...)) +} + +// FailNow panics. +func (c *CollectT) FailNow() { + panic("Assertion failed") +} + +// Reset clears the collected errors. +func (c *CollectT) Reset() { + c.errors = nil +} + +// Copy copies the collected errors to the supplied t. +func (c *CollectT) Copy(t TestingT) { + for _, err := range c.errors { + t.Errorf("%v", err) + } +} + +// 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. +// 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. +// +// falseThenTrue := func(falses int) func() bool { +// count := 0 +// return func() bool { +// if count < falses { +// count++ +// return false +// } +// return true +// } +// } +// f := falseThenTrue(5) +// assert.EventuallyWithT(t, func(mockT *assert.CollectT) (success bool) { +// defer func() { +// r := recover() +// success = (r == nil) +// }() +// assert.True(mockT, f()) +// return +// }, 50*time.Millisecond, 10*time.Millisecond) +func EventuallyWithT(t TestingT, condition func(collect *CollectT) bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + collect := new(CollectT) + ch := make(chan bool, 1) + + timer := time.NewTimer(waitFor) + defer timer.Stop() + + ticker := time.NewTicker(tick) + defer ticker.Stop() + + for tick := ticker.C; ; { + select { + case <-timer.C: + collect.Copy(t) + return Fail(t, "Condition never satisfied", msgAndArgs...) + case <-tick: + tick = nil + collect.Reset() + go func() { ch <- condition(collect) }() + case v := <-ch: + if v { + return true + } + tick = ticker.C + } + } +} + // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // diff --git a/assert/assertions_test.go b/assert/assertions_test.go index bdab184c1..9c324f1c0 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -2428,6 +2428,38 @@ func TestEventuallyTrue(t *testing.T) { True(t, Eventually(t, condition, 100*time.Millisecond, 20*time.Millisecond)) } +func TestEventuallyWithTFalse(t *testing.T) { + mockT := new(CollectT) + + condition := func(collect *CollectT) bool { + return True(collect, false) + } + + False(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond)) + Len(t, mockT.errors, 2) +} + +func TestEventuallyWithTTrue(t *testing.T) { + mockT := new(CollectT) + + state := 0 + condition := func(collect *CollectT) bool { + defer func() { + state += 1 + }() + + if state == 2 { + True(collect, true) + return true + } + + return True(collect, false) + } + + True(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond)) + Len(t, mockT.errors, 0) +} + func TestNeverFalse(t *testing.T) { condition := func() bool { return false diff --git a/require/require.go b/require/require.go index 880853f5a..79052243f 100644 --- a/require/require.go +++ b/require/require.go @@ -364,6 +364,80 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t 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. +// 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. +// +// falseThenTrue := func(falses int) func() bool { +// count := 0 +// return func() bool { +// if count < falses { +// count++ +// return false +// } +// return true +// } +// } +// f := falseThenTrue(5) +// assert.EventuallyWithT(t, func(mockT *assert.CollectT) (success bool) { +// defer func() { +// r := recover() +// success = (r == nil) +// }() +// assert.True(mockT, f()) +// return +// }, 50*time.Millisecond, 10*time.Millisecond) +func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT) bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EventuallyWithT(t, condition, waitFor, tick, msgAndArgs...) { + return + } + t.FailNow() +} + +// EventuallyWithTf 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. +// 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. +// +// falseThenTrue := func(falses int) func() bool { +// count := 0 +// return func() bool { +// if count < falses { +// count++ +// return false +// } +// return true +// } +// } +// f := falseThenTrue(5) +// assert.EventuallyWithTf(t, func(mockT *assert.CollectT) (success bool, "error message %s", "formatted") { +// defer func() { +// r := recover() +// success = (r == nil) +// }() +// assert.True(mockT, f()) +// return +// }, 50*time.Millisecond, 10*time.Millisecond) +func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT) bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EventuallyWithTf(t, condition, waitFor, tick, msg, args...) { + return + } + t.FailNow() +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // diff --git a/require/require_forward.go b/require/require_forward.go index 960bf6f2c..75cbd4bde 100644 --- a/require/require_forward.go +++ b/require/require_forward.go @@ -289,6 +289,74 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti Eventually(a.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. +// 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. +// +// falseThenTrue := func(falses int) func() bool { +// count := 0 +// return func() bool { +// if count < falses { +// count++ +// return false +// } +// return true +// } +// } +// f := falseThenTrue(5) +// a.EventuallyWithT(func(mockT *assert.CollectT) (success bool) { +// defer func() { +// r := recover() +// success = (r == nil) +// }() +// assert.True(mockT, f()) +// return +// }, 50*time.Millisecond, 10*time.Millisecond) +func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT) bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// EventuallyWithTf 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. +// 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. +// +// falseThenTrue := func(falses int) func() bool { +// count := 0 +// return func() bool { +// if count < falses { +// count++ +// return false +// } +// return true +// } +// } +// f := falseThenTrue(5) +// a.EventuallyWithTf(func(mockT *assert.CollectT) (success bool, "error message %s", "formatted") { +// defer func() { +// r := recover() +// success = (r == nil) +// }() +// assert.True(mockT, f()) +// return +// }, 50*time.Millisecond, 10*time.Millisecond) +func (a *Assertions) EventuallyWithTf(condition func(collect *assert.CollectT) bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...) +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // From 360d14b3ae5c874aa98a1c26b0bc9eba94f4fee4 Mon Sep 17 00:00:00 2001 From: Arik Kfir Date: Thu, 20 Oct 2022 08:25:34 +0200 Subject: [PATCH 2/4] Change "EventuallyWithT" condition acceptance to no-errors raised This change updates the "EventuallyWithT" assertion variants (regular, formatted, requirement) to consider a condition as "met" if no assertion errors were raised in a tick. This allows to write easier conditions which simply contain assertions, without needing to return a bool. The equivalent of a condition returning true in the previous implementation would be a a condition with a single "assert.True(..)" call. --- assert/assertion_format.go | 31 +++++++------------ assert/assertion_forward.go | 62 +++++++++++++------------------------ assert/assertions.go | 36 +++++++++------------ assert/assertions_test.go | 14 +++------ require/require.go | 62 +++++++++++++------------------------ require/require_forward.go | 62 +++++++++++++------------------------ 6 files changed, 96 insertions(+), 171 deletions(-) diff --git a/assert/assertion_format.go b/assert/assertion_format.go index 3fe076cfe..0b946da2d 100644 --- a/assert/assertion_format.go +++ b/assert/assertion_format.go @@ -159,30 +159,21 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick // 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. +// 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. // -// falseThenTrue := func(falses int) func() bool { -// count := 0 -// return func() bool { -// if count < falses { -// count++ -// return false -// } -// return true -// } -// } -// f := falseThenTrue(5) -// assert.EventuallyWithTf(t, func(mockT *assert.CollectT) (success bool, "error message %s", "formatted") { -// defer func() { -// r := recover() -// success = (r == nil) -// }() -// assert.True(mockT, f()) -// return -// }, 50*time.Millisecond, 10*time.Millisecond) -func EventuallyWithTf(t TestingT, condition func(collect *CollectT) bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithTf(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still: %v", externalState) +func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } diff --git a/assert/assertion_forward.go b/assert/assertion_forward.go index 7ab795746..661ab07a3 100644 --- a/assert/assertion_forward.go +++ b/assert/assertion_forward.go @@ -292,30 +292,21 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti // 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. +// 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. // -// falseThenTrue := func(falses int) func() bool { -// count := 0 -// return func() bool { -// if count < falses { -// count++ -// return false -// } -// return true -// } -// } -// f := falseThenTrue(5) -// a.EventuallyWithT(func(mockT *assert.CollectT) (success bool) { -// defer func() { -// r := recover() -// success = (r == nil) -// }() -// assert.True(mockT, f()) -// return -// }, 50*time.Millisecond, 10*time.Millisecond) -func (a *Assertions) EventuallyWithT(condition func(collect *CollectT) bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithT(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } @@ -326,30 +317,21 @@ func (a *Assertions) EventuallyWithT(condition func(collect *CollectT) bool, wai // 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. +// 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. // -// falseThenTrue := func(falses int) func() bool { -// count := 0 -// return func() bool { -// if count < falses { -// count++ -// return false -// } -// return true -// } -// } -// f := falseThenTrue(5) -// a.EventuallyWithTf(func(mockT *assert.CollectT) (success bool, "error message %s", "formatted") { -// defer func() { -// r := recover() -// success = (r == nil) -// }() -// assert.True(mockT, f()) -// return -// }, 50*time.Millisecond, 10*time.Millisecond) -func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT) bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithTf(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still: %v", externalState) +func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } diff --git a/assert/assertions.go b/assert/assertions.go index 71c5c5a4d..b53adfacf 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1787,30 +1787,21 @@ func (c *CollectT) Copy(t TestingT) { // 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. +// 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. // -// falseThenTrue := func(falses int) func() bool { -// count := 0 -// return func() bool { -// if count < falses { -// count++ -// return false -// } -// return true -// } -// } -// f := falseThenTrue(5) -// assert.EventuallyWithT(t, func(mockT *assert.CollectT) (success bool) { -// defer func() { -// r := recover() -// success = (r == nil) -// }() -// assert.True(mockT, f()) -// return -// }, 50*time.Millisecond, 10*time.Millisecond) -func EventuallyWithT(t TestingT, condition func(collect *CollectT) bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithT(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } @@ -1832,7 +1823,10 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT) bool, waitFor case <-tick: tick = nil collect.Reset() - go func() { ch <- condition(collect) }() + go func() { + condition(collect) + ch <- len(collect.errors) == 0 + }() case v := <-ch: if v { return true diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 9c324f1c0..14b657bd9 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -2431,8 +2431,8 @@ func TestEventuallyTrue(t *testing.T) { func TestEventuallyWithTFalse(t *testing.T) { mockT := new(CollectT) - condition := func(collect *CollectT) bool { - return True(collect, false) + condition := func(collect *CollectT) { + True(collect, false) } False(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond)) @@ -2443,17 +2443,11 @@ func TestEventuallyWithTTrue(t *testing.T) { mockT := new(CollectT) state := 0 - condition := func(collect *CollectT) bool { + condition := func(collect *CollectT) { defer func() { state += 1 }() - - if state == 2 { - True(collect, true) - return true - } - - return True(collect, false) + True(collect, state == 2) } True(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond)) diff --git a/require/require.go b/require/require.go index 79052243f..e59c4f4e0 100644 --- a/require/require.go +++ b/require/require.go @@ -368,30 +368,21 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t // 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. +// 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. // -// falseThenTrue := func(falses int) func() bool { -// count := 0 -// return func() bool { -// if count < falses { -// count++ -// return false -// } -// return true -// } -// } -// f := falseThenTrue(5) -// assert.EventuallyWithT(t, func(mockT *assert.CollectT) (success bool) { -// defer func() { -// r := recover() -// success = (r == nil) -// }() -// assert.True(mockT, f()) -// return -// }, 50*time.Millisecond, 10*time.Millisecond) -func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT) bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// require.EventuallyWithT(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } @@ -405,30 +396,21 @@ func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT) bool, // 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. +// 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. // -// falseThenTrue := func(falses int) func() bool { -// count := 0 -// return func() bool { -// if count < falses { -// count++ -// return false -// } -// return true -// } -// } -// f := falseThenTrue(5) -// assert.EventuallyWithTf(t, func(mockT *assert.CollectT) (success bool, "error message %s", "formatted") { -// defer func() { -// r := recover() -// success = (r == nil) -// }() -// assert.True(mockT, f()) -// return -// }, 50*time.Millisecond, 10*time.Millisecond) -func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT) bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// require.EventuallyWithTf(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still: %v", externalState) +func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } diff --git a/require/require_forward.go b/require/require_forward.go index 75cbd4bde..ab0231574 100644 --- a/require/require_forward.go +++ b/require/require_forward.go @@ -293,30 +293,21 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti // 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. +// 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. // -// falseThenTrue := func(falses int) func() bool { -// count := 0 -// return func() bool { -// if count < falses { -// count++ -// return false -// } -// return true -// } -// } -// f := falseThenTrue(5) -// a.EventuallyWithT(func(mockT *assert.CollectT) (success bool) { -// defer func() { -// r := recover() -// success = (r == nil) -// }() -// assert.True(mockT, f()) -// return -// }, 50*time.Millisecond, 10*time.Millisecond) -func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT) bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// require.EventuallyWithT(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } @@ -327,30 +318,21 @@ func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT) bo // 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. +// 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. // -// falseThenTrue := func(falses int) func() bool { -// count := 0 -// return func() bool { -// if count < falses { -// count++ -// return false -// } -// return true -// } -// } -// f := falseThenTrue(5) -// a.EventuallyWithTf(func(mockT *assert.CollectT) (success bool, "error message %s", "formatted") { -// defer func() { -// r := recover() -// success = (r == nil) -// }() -// assert.True(mockT, f()) -// return -// }, 50*time.Millisecond, 10*time.Millisecond) -func (a *Assertions) EventuallyWithTf(condition func(collect *assert.CollectT) bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// require.EventuallyWithTf(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still: %v", externalState) +func (a *Assertions) EventuallyWithTf(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } From 0c7cc6b1f49966740d3ecf637e2871f574827386 Mon Sep 17 00:00:00 2001 From: Arik Kfir Date: Fri, 21 Oct 2022 17:00:28 +0200 Subject: [PATCH 3/4] Declare the "Collect.Copy(T)" method as a testing helper --- assert/assertions.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assert/assertions.go b/assert/assertions.go index b53adfacf..ed9d677f9 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1778,6 +1778,9 @@ func (c *CollectT) Reset() { // Copy copies the collected errors to the supplied t. func (c *CollectT) Copy(t TestingT) { + if tt, ok := t.(tHelper); ok { + tt.Helper() + } for _, err := range c.errors { t.Errorf("%v", err) } From 988ad3449f13cda4740e7f0c5f5a5af61c49fd44 Mon Sep 17 00:00:00 2001 From: Tobias Krischer Date: Tue, 25 Oct 2022 00:31:08 +0200 Subject: [PATCH 4/4] run go generate --- assert/assertion_format.go | 18 +++++++++--------- assert/assertion_forward.go | 36 ++++++++++++++++++------------------ require/require.go | 36 ++++++++++++++++++------------------ require/require_forward.go | 36 ++++++++++++++++++------------------ 4 files changed, 63 insertions(+), 63 deletions(-) diff --git a/assert/assertion_format.go b/assert/assertion_format.go index 0b946da2d..3f350c45d 100644 --- a/assert/assertion_format.go +++ b/assert/assertion_format.go @@ -164,15 +164,15 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick // If the condition is not met before waitFor, the collected errors of // the last tick are copied to t. // -// externalValue := false -// go func() { -// time.Sleep(8*time.Second) -// externalValue = true -// }() -// assert.EventuallyWithTf(t, func(c *assert.CollectT) { -// // add assertions as needed; any assertion failure will fail the current tick -// assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still: %v", externalState) +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/assert/assertion_forward.go b/assert/assertion_forward.go index 661ab07a3..3c8f870b6 100644 --- a/assert/assertion_forward.go +++ b/assert/assertion_forward.go @@ -297,15 +297,15 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti // If the condition is not met before waitFor, the collected errors of // the last tick are copied to t. // -// externalValue := false -// go func() { -// time.Sleep(8*time.Second) -// externalValue = true -// }() -// a.EventuallyWithT(t, func(c *assert.CollectT) { -// // add assertions as needed; any assertion failure will fail the current tick -// assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithT(func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -322,15 +322,15 @@ func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor // If the condition is not met before waitFor, the collected errors of // the last tick are copied to t. // -// externalValue := false -// go func() { -// time.Sleep(8*time.Second) -// externalValue = true -// }() -// a.EventuallyWithTf(t, func(c *assert.CollectT) { -// // add assertions as needed; any assertion failure will fail the current tick -// assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still: %v", externalState) +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/require/require.go b/require/require.go index e59c4f4e0..282499aad 100644 --- a/require/require.go +++ b/require/require.go @@ -373,15 +373,15 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t // If the condition is not met before waitFor, the collected errors of // the last tick are copied to t. // -// externalValue := false -// go func() { -// time.Sleep(8*time.Second) -// externalValue = true -// }() -// require.EventuallyWithT(t, func(c *assert.CollectT) { -// // add assertions as needed; any assertion failure will fail the current tick -// assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithT(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -401,15 +401,15 @@ func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitF // If the condition is not met before waitFor, the collected errors of // the last tick are copied to t. // -// externalValue := false -// go func() { -// time.Sleep(8*time.Second) -// externalValue = true -// }() -// require.EventuallyWithTf(t, func(c *assert.CollectT) { -// // add assertions as needed; any assertion failure will fail the current tick -// assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still: %v", externalState) +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/require/require_forward.go b/require/require_forward.go index ab0231574..4df7b514e 100644 --- a/require/require_forward.go +++ b/require/require_forward.go @@ -298,15 +298,15 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti // If the condition is not met before waitFor, the collected errors of // the last tick are copied to t. // -// externalValue := false -// go func() { -// time.Sleep(8*time.Second) -// externalValue = true -// }() -// require.EventuallyWithT(t, func(c *assert.CollectT) { -// // add assertions as needed; any assertion failure will fail the current tick -// assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithT(func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -323,15 +323,15 @@ func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT), w // If the condition is not met before waitFor, the collected errors of // the last tick are copied to t. // -// externalValue := false -// go func() { -// time.Sleep(8*time.Second) -// externalValue = true -// }() -// require.EventuallyWithTf(t, func(c *assert.CollectT) { -// // add assertions as needed; any assertion failure will fail the current tick -// assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still: %v", externalState) +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") func (a *Assertions) EventuallyWithTf(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper()