-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Eventually
/EventuallyWithT
should only use one goroutine
#1439
Comments
Here's the implementation I have in mind. We could similarly implement EventuallyWithT. I will open a PR if this is desirable: func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
ctx, done := context.WithTimeout(context.Background(), waitFor)
defer done()
ch := make(chan bool, 1)
defer close(ch)
go func() {
ticker := time.NewTicker(tick)
defer ticker.Stop()
for {
if condition() {
ch <- true
return
}
select {
case <-ticker.C:
case <-ctx.Done():
return
}
}
}()
select {
case <-ch:
return true
case <-ctx.Done():
return Fail(t, "Condition never satisfied", msgAndArgs...)
}
} |
I like the idea of not overlapping the executions of the condition. However this will change the behavior. We need feedback from the community. |
The behavior will change as described in #1424 (namely, that the condition will be checked before waiting for one tick) -- are there more ways in which this will change the behavior that should be enumerated here for completeness? |
This comment suggests that func TestEventuallyCallsEveryTick(t *testing.T) {
mockT := new(testing.T)
n := 0
condition := func() bool {
n++
time.Sleep(250 * time.Millisecond)
return false
}
False(t, Eventually(mockT, condition, 500*time.Millisecond, 100*time.Millisecond))
// If called every tick, should be 5 because 500ms/100ms = 5
Equal(t, 5, n)
} With result:
|
Eventually
/EventuallyWithT
should only use one goroutine
@cszczepaniak Thanks for this analysis. Here is a more verbose test running on go.dev/play: https://go.dev/play/p/-Dyc4bZRCTk func TestEventuallyCallsEveryTick(t *testing.T) {
n := 0
condition := func() bool {
m := n
t.Logf("cond%d %v", m, time.Now())
n++
time.Sleep(250 * time.Millisecond)
t.Logf("cond%d %v", m, time.Now())
return false
}
assert.False(t, assert.Eventually(t, condition, 500*time.Millisecond, 100*time.Millisecond))
// If called every tick, should be 5 because 500ms/100ms = 5
assert.Equal(t, 5, n)
}
|
Thanks @dolmen, today I learned that the Go playground can run tests! |
I hadn't noticed the nil ticker. Given the current actual behaviour does not overlap ticks I'm quite happy with such a change. We should also bring the documentation into line with the real behaviour. |
I just merged #1481 which uses |
Currently,
Eventually
andEventuallyWithT
spin up a goroutine per condition check. It also sets the ticker channel tonil
while the condition is being checked, which makes it hard to understand to readers that theselect
will never hitcase <-tick
while the condition is running.A better implementation may be to spin up a single goroutine which polls the condition repeatedly while the function waits on the result or a timeout.
This would also solve #1424 if implemented correctly.
The text was updated successfully, but these errors were encountered: