package util
import (
"context"
"time"
"github.com/grafana/dskit/backoff"
"github.com/stretchr/testify/require"
)
var backoffRetry = backoff.Config{
MinBackoff: 10 * time.Millisecond,
MaxBackoff: 1 * time.Second,
MaxRetries: 10,
}
func Eventually(t require.TestingT, check func(t require.TestingT)) {
EventuallyWithBackoff(t, check, backoffRetry)
}
func EventuallyWithBackoff(t require.TestingT, check func(t require.TestingT), bc backoff.Config) {
bo := backoff.New(context.Background(), bc)
var (
lastErrors []testError
shouldAbort bool
)
for bo.Ongoing() {
ev := invokeCheck(check)
lastErrors = ev.errors
shouldAbort = ev.aborted
if len(ev.errors) == 0 {
break
}
bo.Wait()
}
if bo.Err() != nil {
for _, err := range lastErrors {
t.Errorf(err.format, err.args...)
}
if shouldAbort {
t.FailNow()
}
}
}
func invokeCheck(check func(t require.TestingT)) (result eventuallyT) {
defer func() {
if err := recover(); err != nil {
if _, ok := err.(testAbort); ok {
return
}
panic(err)
}
}()
check(&result)
return
}
type eventuallyT struct {
errors []testError
aborted bool
}
type (
testError struct {
format string
args []interface{}
}
testAbort struct{}
)
var _ require.TestingT = (*eventuallyT)(nil)
func (et *eventuallyT) Errorf(format string, args ...interface{}) {
et.errors = append(et.errors, testError{
format: format,
args: args,
})
}
func (et *eventuallyT) FailNow() {
et.aborted = true
panic(testAbort{})
}