2017-08-24 16:56:31 -04:00
|
|
|
/*Package poll provides tools for testing asynchronous code.
|
|
|
|
*/
|
2018-06-08 12:09:51 -04:00
|
|
|
package poll // import "gotest.tools/poll"
|
2017-08-24 16:56:31 -04:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TestingT is the subset of testing.T used by WaitOn
|
|
|
|
type TestingT interface {
|
|
|
|
LogT
|
|
|
|
Fatalf(format string, args ...interface{})
|
|
|
|
}
|
|
|
|
|
|
|
|
// LogT is a logging interface that is passed to the WaitOn check function
|
|
|
|
type LogT interface {
|
|
|
|
Log(args ...interface{})
|
|
|
|
Logf(format string, args ...interface{})
|
|
|
|
}
|
|
|
|
|
2018-01-16 17:20:43 -05:00
|
|
|
type helperT interface {
|
|
|
|
Helper()
|
|
|
|
}
|
|
|
|
|
2017-08-24 16:56:31 -04:00
|
|
|
// Settings are used to configure the behaviour of WaitOn
|
|
|
|
type Settings struct {
|
2018-06-08 12:09:51 -04:00
|
|
|
// Timeout is the maximum time to wait for the condition. Defaults to 10s.
|
2017-08-24 16:56:31 -04:00
|
|
|
Timeout time.Duration
|
2018-06-08 12:09:51 -04:00
|
|
|
// Delay is the time to sleep between checking the condition. Defaults to
|
|
|
|
// 100ms.
|
2017-08-24 16:56:31 -04:00
|
|
|
Delay time.Duration
|
|
|
|
}
|
|
|
|
|
|
|
|
func defaultConfig() *Settings {
|
2018-06-08 12:09:51 -04:00
|
|
|
return &Settings{Timeout: 10 * time.Second, Delay: 100 * time.Millisecond}
|
2017-08-24 16:56:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// SettingOp is a function which accepts and modifies Settings
|
|
|
|
type SettingOp func(config *Settings)
|
|
|
|
|
|
|
|
// WithDelay sets the delay to wait between polls
|
|
|
|
func WithDelay(delay time.Duration) SettingOp {
|
|
|
|
return func(config *Settings) {
|
|
|
|
config.Delay = delay
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithTimeout sets the timeout
|
|
|
|
func WithTimeout(timeout time.Duration) SettingOp {
|
|
|
|
return func(config *Settings) {
|
|
|
|
config.Timeout = timeout
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Result of a check performed by WaitOn
|
|
|
|
type Result interface {
|
|
|
|
// Error indicates that the check failed and polling should stop, and the
|
|
|
|
// the has failed
|
|
|
|
Error() error
|
|
|
|
// Done indicates that polling should stop, and the test should proceed
|
|
|
|
Done() bool
|
|
|
|
// Message provides the most recent state when polling has not completed
|
|
|
|
Message() string
|
|
|
|
}
|
|
|
|
|
|
|
|
type result struct {
|
|
|
|
done bool
|
|
|
|
message string
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r result) Done() bool {
|
|
|
|
return r.done
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r result) Message() string {
|
|
|
|
return r.message
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r result) Error() error {
|
|
|
|
return r.err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Continue returns a Result that indicates to WaitOn that it should continue
|
|
|
|
// polling. The message text will be used as the failure message if the timeout
|
|
|
|
// is reached.
|
|
|
|
func Continue(message string, args ...interface{}) Result {
|
|
|
|
return result{message: fmt.Sprintf(message, args...)}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Success returns a Result where Done() returns true, which indicates to WaitOn
|
|
|
|
// that it should stop polling and exit without an error.
|
|
|
|
func Success() Result {
|
|
|
|
return result{done: true}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error returns a Result that indicates to WaitOn that it should fail the test
|
|
|
|
// and stop polling.
|
|
|
|
func Error(err error) Result {
|
|
|
|
return result{err: err}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WaitOn a condition or until a timeout. Poll by calling check and exit when
|
|
|
|
// check returns a done Result. To fail a test and exit polling with an error
|
|
|
|
// return a error result.
|
2019-04-05 11:02:23 -04:00
|
|
|
func WaitOn(t TestingT, check Check, pollOps ...SettingOp) {
|
2018-01-16 17:20:43 -05:00
|
|
|
if ht, ok := t.(helperT); ok {
|
|
|
|
ht.Helper()
|
|
|
|
}
|
2017-08-24 16:56:31 -04:00
|
|
|
config := defaultConfig()
|
|
|
|
for _, pollOp := range pollOps {
|
|
|
|
pollOp(config)
|
|
|
|
}
|
|
|
|
|
|
|
|
var lastMessage string
|
|
|
|
after := time.After(config.Timeout)
|
|
|
|
chResult := make(chan Result)
|
|
|
|
for {
|
|
|
|
go func() {
|
|
|
|
chResult <- check(t)
|
|
|
|
}()
|
|
|
|
select {
|
|
|
|
case <-after:
|
|
|
|
if lastMessage == "" {
|
|
|
|
lastMessage = "first check never completed"
|
|
|
|
}
|
|
|
|
t.Fatalf("timeout hit after %s: %s", config.Timeout, lastMessage)
|
|
|
|
case result := <-chResult:
|
|
|
|
switch {
|
|
|
|
case result.Error() != nil:
|
|
|
|
t.Fatalf("polling check failed: %s", result.Error())
|
|
|
|
case result.Done():
|
|
|
|
return
|
|
|
|
}
|
|
|
|
time.Sleep(config.Delay)
|
|
|
|
lastMessage = result.Message()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|