From e65a64c87ea6aac3ef3d958b980935cefa56dfc5 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Fri, 1 Sep 2017 14:54:03 -0400 Subject: [PATCH] Add gotestyourself/poll Signed-off-by: Daniel Nephin Upstream-commit: 683b6226ede1eec766e166b19d77d8613c39e6e3 Component: cli --- components/cli/vendor.conf | 2 +- .../gotestyourself/gotestyourself/README.md | 3 +- .../gotestyourself/golden/golden.go | 8 +- .../gotestyourself/icmd/command.go | 19 ++- .../gotestyourself/icmd/exitcode.go | 6 +- .../gotestyourself/gotestyourself/icmd/ops.go | 4 + .../gotestyourself/poll/poll.go | 133 ++++++++++++++++++ 7 files changed, 155 insertions(+), 20 deletions(-) create mode 100644 components/cli/vendor/github.com/gotestyourself/gotestyourself/icmd/ops.go create mode 100644 components/cli/vendor/github.com/gotestyourself/gotestyourself/poll/poll.go diff --git a/components/cli/vendor.conf b/components/cli/vendor.conf index 004318a681..4e4d165238 100755 --- a/components/cli/vendor.conf +++ b/components/cli/vendor.conf @@ -21,7 +21,7 @@ github.com/gogo/protobuf v0.4 github.com/golang/protobuf 7a211bcf3bce0e3f1d74f9894916e6f116ae83b4 github.com/gorilla/context v1.1 github.com/gorilla/mux v1.1 -github.com/gotestyourself/gotestyourself v1.0.0 +github.com/gotestyourself/gotestyourself v1.1.0 github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 github.com/mattn/go-shellwords v1.0.3 github.com/Microsoft/go-winio v0.4.4 diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/README.md b/components/cli/vendor/github.com/gotestyourself/gotestyourself/README.md index 2648074900..037fba6c77 100644 --- a/components/cli/vendor/github.com/gotestyourself/gotestyourself/README.md +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/README.md @@ -18,10 +18,11 @@ patterns. a program to summarize `go test` output and test failures * [icmd](http://godoc.org/github.com/gotestyourself/gotestyourself/icmd) - execute binaries and test the output +* [poll](http://godoc.org/github.com/gotestyourself/gotestyourself/poll) - + test asynchronous code by polling until a desired state is reached * [skip](http://godoc.org/github.com/gotestyourself/gotestyourself/skip) - skip tests based on conditions - ## Related * [testify/assert](https://godoc.org/github.com/stretchr/testify/assert) and diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/golden/golden.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/golden/golden.go index 230ff685cd..9b1c8ba4d0 100644 --- a/components/cli/vendor/github.com/gotestyourself/gotestyourself/golden/golden.go +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/golden/golden.go @@ -37,8 +37,8 @@ func update(t require.TestingT, filename string, actual []byte) { } // Assert compares the actual content to the expected content in the golden file. -// If `--update-golden` is set then the actual content is written to the golden -// file. +// If the `-test.update-golden` flag is set then the actual content is written +// to the golden file. // Returns whether the assertion was successful (true) or not (false) func Assert(t require.TestingT, actual string, filename string, msgAndArgs ...interface{}) bool { expected := Get(t, filename) @@ -60,8 +60,8 @@ func Assert(t require.TestingT, actual string, filename string, msgAndArgs ...in } // AssertBytes compares the actual result to the expected result in the golden -// file. If `--update-golden` is set then the actual content is written to the -// golden file. +// file. If the `-test.update-golden` flag is set then the actual content is +// written to the golden file. // Returns whether the assertion was successful (true) or not (false) // nolint: lll func AssertBytes(t require.TestingT, actual []byte, filename string, msgAndArgs ...interface{}) bool { diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/icmd/command.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/icmd/command.go index 2470fdc662..8729457b4f 100644 --- a/components/cli/vendor/github.com/gotestyourself/gotestyourself/icmd/command.go +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/icmd/command.go @@ -18,10 +18,8 @@ type testingT interface { Fatalf(string, ...interface{}) } -const ( - // None is a token to inform Result.Assert that the output should be empty - None string = "" -) +// None is a token to inform Result.Assert that the output should be empty +const None string = "[NOTHING]" type lockedBuffer struct { m sync.RWMutex @@ -170,8 +168,7 @@ func (r *Result) Combined() string { return r.outBuffer.String() + r.errBuffer.String() } -// SetExitError sets Error and ExitCode based on Error -func (r *Result) SetExitError(err error) { +func (r *Result) setExitError(err error) { if err == nil { return } @@ -196,7 +193,7 @@ func Command(command string, args ...string) Cmd { } // RunCmd runs a command and returns a Result -func RunCmd(cmd Cmd, cmdOperators ...func(*Cmd)) *Result { +func RunCmd(cmd Cmd, cmdOperators ...CmdOp) *Result { for _, op := range cmdOperators { op(&cmd) } @@ -207,7 +204,7 @@ func RunCmd(cmd Cmd, cmdOperators ...func(*Cmd)) *Result { return WaitOnCmd(cmd.Timeout, result) } -// RunCommand parses a command line and runs it, returning a result +// RunCommand runs a command with default options, and returns a result func RunCommand(command string, args ...string) *Result { return RunCmd(Command(command, args...)) } @@ -218,7 +215,7 @@ func StartCmd(cmd Cmd) *Result { if result.Error != nil { return result } - result.SetExitError(result.Cmd.Start()) + result.setExitError(result.Cmd.Start()) return result } @@ -253,7 +250,7 @@ func buildCmd(cmd Cmd) *Result { // only wait until the timeout. func WaitOnCmd(timeout time.Duration, result *Result) *Result { if timeout == time.Duration(0) { - result.SetExitError(result.Cmd.Wait()) + result.setExitError(result.Cmd.Wait()) return result } @@ -271,7 +268,7 @@ func WaitOnCmd(timeout time.Duration, result *Result) *Result { } result.Timeout = true case err := <-done: - result.SetExitError(err) + result.setExitError(err) } return result } diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/icmd/exitcode.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/icmd/exitcode.go index 32272b4bbb..9356dbcdef 100644 --- a/components/cli/vendor/github.com/gotestyourself/gotestyourself/icmd/exitcode.go +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/icmd/exitcode.go @@ -7,9 +7,9 @@ import ( "github.com/pkg/errors" ) -// GetExitCode returns the ExitStatus of a process from the error returned by +// getExitCode returns the ExitStatus of a process from the error returned by // exec.Run(). If the exit status could not be parsed an error is returned. -func GetExitCode(err error) (int, error) { +func getExitCode(err error) (int, error) { if exiterr, ok := err.(*exec.ExitError); ok { if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok { return procExit.ExitStatus(), nil @@ -22,7 +22,7 @@ func processExitCode(err error) (exitCode int) { if err == nil { return 0 } - exitCode, exiterr := GetExitCode(err) + exitCode, exiterr := getExitCode(err) if exiterr != nil { // TODO: Fix this so we check the error's text. // we've failed to retrieve exit code, so we set it to 127 diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/icmd/ops.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/icmd/ops.go new file mode 100644 index 0000000000..02b1d84023 --- /dev/null +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/icmd/ops.go @@ -0,0 +1,4 @@ +package icmd + +// CmdOp is an operation which modified a Cmd structure used to execute commands +type CmdOp func(*Cmd) diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/poll/poll.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/poll/poll.go new file mode 100644 index 0000000000..3838d38e13 --- /dev/null +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/poll/poll.go @@ -0,0 +1,133 @@ +/*Package poll provides tools for testing asynchronous code. + */ +package poll + +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{}) +} + +// Settings are used to configure the behaviour of WaitOn +type Settings struct { + // Timeout is the maximum time to wait for the condition. Defaults to 10s + Timeout time.Duration + // Delay is the time to sleep between checking the condition. Detaults to + // 1ms + Delay time.Duration +} + +func defaultConfig() *Settings { + return &Settings{Timeout: 10 * time.Second, Delay: time.Millisecond} +} + +// 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. +func WaitOn(t TestingT, check func(t LogT) Result, pollOps ...SettingOp) { + 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() + } + } +}