mirror of
https://github.com/stretchr/testify.git
synced 2025-04-28 13:59:07 +00:00
fix: make EventuallyWithT concurrency safe
This commit is contained in:
parent
11a6452626
commit
4ed68e1bca
@ -1873,23 +1873,18 @@ func (c *CollectT) Errorf(format string, args ...interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FailNow panics.
|
// FailNow panics.
|
||||||
func (c *CollectT) FailNow() {
|
func (*CollectT) FailNow() {
|
||||||
panic("Assertion failed")
|
panic("Assertion failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset clears the collected errors.
|
// Deprecated: That was a method for internal usage that should not have been published. Now just panics.
|
||||||
func (c *CollectT) Reset() {
|
func (*CollectT) Reset() {
|
||||||
c.errors = nil
|
panic("Reset() is deprecated")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy copies the collected errors to the supplied t.
|
// Deprecated: That was a method for internal usage that should not have been published. Now just panics.
|
||||||
func (c *CollectT) Copy(t TestingT) {
|
func (*CollectT) Copy(TestingT) {
|
||||||
if tt, ok := t.(tHelper); ok {
|
panic("Copy() is deprecated")
|
||||||
tt.Helper()
|
|
||||||
}
|
|
||||||
for _, err := range c.errors {
|
|
||||||
t.Errorf("%v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EventuallyWithT asserts that given condition will be met in waitFor time,
|
// EventuallyWithT asserts that given condition will be met in waitFor time,
|
||||||
@ -1915,8 +1910,8 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time
|
|||||||
h.Helper()
|
h.Helper()
|
||||||
}
|
}
|
||||||
|
|
||||||
collect := new(CollectT)
|
var lastFinishedTickErrs []error
|
||||||
ch := make(chan bool, 1)
|
ch := make(chan []error, 1)
|
||||||
|
|
||||||
timer := time.NewTimer(waitFor)
|
timer := time.NewTimer(waitFor)
|
||||||
defer timer.Stop()
|
defer timer.Stop()
|
||||||
@ -1927,19 +1922,23 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time
|
|||||||
for tick := ticker.C; ; {
|
for tick := ticker.C; ; {
|
||||||
select {
|
select {
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
collect.Copy(t)
|
for _, err := range lastFinishedTickErrs {
|
||||||
|
t.Errorf("%v", err)
|
||||||
|
}
|
||||||
return Fail(t, "Condition never satisfied", msgAndArgs...)
|
return Fail(t, "Condition never satisfied", msgAndArgs...)
|
||||||
case <-tick:
|
case <-tick:
|
||||||
tick = nil
|
tick = nil
|
||||||
collect.Reset()
|
|
||||||
go func() {
|
go func() {
|
||||||
|
collect := new(CollectT)
|
||||||
condition(collect)
|
condition(collect)
|
||||||
ch <- len(collect.errors) == 0
|
ch <- collect.errors
|
||||||
}()
|
}()
|
||||||
case v := <-ch:
|
case errs := <-ch:
|
||||||
if v {
|
if len(errs) == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
// Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached.
|
||||||
|
lastFinishedTickErrs = errs
|
||||||
tick = ticker.C
|
tick = ticker.C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2766,11 +2766,22 @@ func TestEventuallyTrue(t *testing.T) {
|
|||||||
True(t, Eventually(t, condition, 100*time.Millisecond, 20*time.Millisecond))
|
True(t, Eventually(t, condition, 100*time.Millisecond, 20*time.Millisecond))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// errorsCapturingT is a mock implementation of TestingT that captures errors reported with Errorf.
|
||||||
|
type errorsCapturingT struct {
|
||||||
|
errors []error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *errorsCapturingT) Errorf(format string, args ...interface{}) {
|
||||||
|
t.errors = append(t.errors, fmt.Errorf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *errorsCapturingT) Helper() {}
|
||||||
|
|
||||||
func TestEventuallyWithTFalse(t *testing.T) {
|
func TestEventuallyWithTFalse(t *testing.T) {
|
||||||
mockT := new(CollectT)
|
mockT := new(errorsCapturingT)
|
||||||
|
|
||||||
condition := func(collect *CollectT) {
|
condition := func(collect *CollectT) {
|
||||||
True(collect, false)
|
Fail(collect, "condition fixed failure")
|
||||||
}
|
}
|
||||||
|
|
||||||
False(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond))
|
False(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond))
|
||||||
@ -2778,7 +2789,7 @@ func TestEventuallyWithTFalse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEventuallyWithTTrue(t *testing.T) {
|
func TestEventuallyWithTTrue(t *testing.T) {
|
||||||
mockT := new(CollectT)
|
mockT := new(errorsCapturingT)
|
||||||
|
|
||||||
state := 0
|
state := 0
|
||||||
condition := func(collect *CollectT) {
|
condition := func(collect *CollectT) {
|
||||||
@ -2792,6 +2803,41 @@ func TestEventuallyWithTTrue(t *testing.T) {
|
|||||||
Len(t, mockT.errors, 0)
|
Len(t, mockT.errors, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEventuallyWithT_ConcurrencySafe(t *testing.T) {
|
||||||
|
mockT := new(errorsCapturingT)
|
||||||
|
|
||||||
|
condition := func(collect *CollectT) {
|
||||||
|
Fail(collect, "condition fixed failure")
|
||||||
|
}
|
||||||
|
|
||||||
|
// To trigger race conditions, we run EventuallyWithT with a nanosecond tick.
|
||||||
|
False(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, time.Nanosecond))
|
||||||
|
Len(t, mockT.errors, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEventuallyWithT_ReturnsTheLatestFinishedConditionErrors(t *testing.T) {
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
mockT := new(errorsCapturingT)
|
||||||
|
False(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond))
|
||||||
|
Len(t, mockT.errors, 2)
|
||||||
|
}
|
||||||
|
|
||||||
func TestNeverFalse(t *testing.T) {
|
func TestNeverFalse(t *testing.T) {
|
||||||
condition := func() bool {
|
condition := func() bool {
|
||||||
return false
|
return false
|
||||||
|
Loading…
x
Reference in New Issue
Block a user