mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Add gotestyourself to vendor
Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
parent
2cea2f5469
commit
0a7ff351b7
3 changed files with 310 additions and 0 deletions
274
vendor/github.com/gotestyourself/gotestyourself/icmd/command.go
generated
vendored
Normal file
274
vendor/github.com/gotestyourself/gotestyourself/icmd/command.go
generated
vendored
Normal file
|
@ -0,0 +1,274 @@
|
|||
/*Package icmd executes binaries and provides convenient assertions for testing the results.
|
||||
*/
|
||||
package icmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type testingT interface {
|
||||
Fatalf(string, ...interface{})
|
||||
}
|
||||
|
||||
// None is a token to inform Result.Assert that the output should be empty
|
||||
const None string = "[NOTHING]"
|
||||
|
||||
type lockedBuffer struct {
|
||||
m sync.RWMutex
|
||||
buf bytes.Buffer
|
||||
}
|
||||
|
||||
func (buf *lockedBuffer) Write(b []byte) (int, error) {
|
||||
buf.m.Lock()
|
||||
defer buf.m.Unlock()
|
||||
return buf.buf.Write(b)
|
||||
}
|
||||
|
||||
func (buf *lockedBuffer) String() string {
|
||||
buf.m.RLock()
|
||||
defer buf.m.RUnlock()
|
||||
return buf.buf.String()
|
||||
}
|
||||
|
||||
// Result stores the result of running a command
|
||||
type Result struct {
|
||||
Cmd *exec.Cmd
|
||||
ExitCode int
|
||||
Error error
|
||||
// Timeout is true if the command was killed because it ran for too long
|
||||
Timeout bool
|
||||
outBuffer *lockedBuffer
|
||||
errBuffer *lockedBuffer
|
||||
}
|
||||
|
||||
// Assert compares the Result against the Expected struct, and fails the test if
|
||||
// any of the expectations are not met.
|
||||
func (r *Result) Assert(t testingT, exp Expected) *Result {
|
||||
err := r.Compare(exp)
|
||||
if err == nil {
|
||||
return r
|
||||
}
|
||||
_, file, line, ok := runtime.Caller(1)
|
||||
if ok {
|
||||
t.Fatalf("at %s:%d - %s\n", filepath.Base(file), line, err.Error())
|
||||
} else {
|
||||
t.Fatalf("(no file/line info) - %s", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compare returns a formatted error with the command, stdout, stderr, exit
|
||||
// code, and any failed expectations
|
||||
// nolint: gocyclo
|
||||
func (r *Result) Compare(exp Expected) error {
|
||||
errors := []string{}
|
||||
add := func(format string, args ...interface{}) {
|
||||
errors = append(errors, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
if exp.ExitCode != r.ExitCode {
|
||||
add("ExitCode was %d expected %d", r.ExitCode, exp.ExitCode)
|
||||
}
|
||||
if exp.Timeout != r.Timeout {
|
||||
if exp.Timeout {
|
||||
add("Expected command to timeout")
|
||||
} else {
|
||||
add("Expected command to finish, but it hit the timeout")
|
||||
}
|
||||
}
|
||||
if !matchOutput(exp.Out, r.Stdout()) {
|
||||
add("Expected stdout to contain %q", exp.Out)
|
||||
}
|
||||
if !matchOutput(exp.Err, r.Stderr()) {
|
||||
add("Expected stderr to contain %q", exp.Err)
|
||||
}
|
||||
switch {
|
||||
// If a non-zero exit code is expected there is going to be an error.
|
||||
// Don't require an error message as well as an exit code because the
|
||||
// error message is going to be "exit status <code> which is not useful
|
||||
case exp.Error == "" && exp.ExitCode != 0:
|
||||
case exp.Error == "" && r.Error != nil:
|
||||
add("Expected no error")
|
||||
case exp.Error != "" && r.Error == nil:
|
||||
add("Expected error to contain %q, but there was no error", exp.Error)
|
||||
case exp.Error != "" && !strings.Contains(r.Error.Error(), exp.Error):
|
||||
add("Expected error to contain %q", exp.Error)
|
||||
}
|
||||
|
||||
if len(errors) == 0 {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("%s\nFailures:\n%s", r, strings.Join(errors, "\n"))
|
||||
}
|
||||
|
||||
func matchOutput(expected string, actual string) bool {
|
||||
switch expected {
|
||||
case None:
|
||||
return actual == ""
|
||||
default:
|
||||
return strings.Contains(actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Result) String() string {
|
||||
var timeout string
|
||||
if r.Timeout {
|
||||
timeout = " (timeout)"
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`
|
||||
Command: %s
|
||||
ExitCode: %d%s
|
||||
Error: %v
|
||||
Stdout: %v
|
||||
Stderr: %v
|
||||
`,
|
||||
strings.Join(r.Cmd.Args, " "),
|
||||
r.ExitCode,
|
||||
timeout,
|
||||
r.Error,
|
||||
r.Stdout(),
|
||||
r.Stderr())
|
||||
}
|
||||
|
||||
// Expected is the expected output from a Command. This struct is compared to a
|
||||
// Result struct by Result.Assert().
|
||||
type Expected struct {
|
||||
ExitCode int
|
||||
Timeout bool
|
||||
Error string
|
||||
Out string
|
||||
Err string
|
||||
}
|
||||
|
||||
// Success is the default expected result. A Success result is one with a 0
|
||||
// ExitCode.
|
||||
var Success = Expected{}
|
||||
|
||||
// Stdout returns the stdout of the process as a string
|
||||
func (r *Result) Stdout() string {
|
||||
return r.outBuffer.String()
|
||||
}
|
||||
|
||||
// Stderr returns the stderr of the process as a string
|
||||
func (r *Result) Stderr() string {
|
||||
return r.errBuffer.String()
|
||||
}
|
||||
|
||||
// Combined returns the stdout and stderr combined into a single string
|
||||
func (r *Result) Combined() string {
|
||||
return r.outBuffer.String() + r.errBuffer.String()
|
||||
}
|
||||
|
||||
func (r *Result) setExitError(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
r.Error = err
|
||||
r.ExitCode = processExitCode(err)
|
||||
}
|
||||
|
||||
// Cmd contains the arguments and options for a process to run as part of a test
|
||||
// suite.
|
||||
type Cmd struct {
|
||||
Command []string
|
||||
Timeout time.Duration
|
||||
Stdin io.Reader
|
||||
Stdout io.Writer
|
||||
Dir string
|
||||
Env []string
|
||||
}
|
||||
|
||||
// Command create a simple Cmd with the specified command and arguments
|
||||
func Command(command string, args ...string) Cmd {
|
||||
return Cmd{Command: append([]string{command}, args...)}
|
||||
}
|
||||
|
||||
// RunCmd runs a command and returns a Result
|
||||
func RunCmd(cmd Cmd, cmdOperators ...CmdOp) *Result {
|
||||
for _, op := range cmdOperators {
|
||||
op(&cmd)
|
||||
}
|
||||
result := StartCmd(cmd)
|
||||
if result.Error != nil {
|
||||
return result
|
||||
}
|
||||
return WaitOnCmd(cmd.Timeout, result)
|
||||
}
|
||||
|
||||
// RunCommand runs a command with default options, and returns a result
|
||||
func RunCommand(command string, args ...string) *Result {
|
||||
return RunCmd(Command(command, args...))
|
||||
}
|
||||
|
||||
// StartCmd starts a command, but doesn't wait for it to finish
|
||||
func StartCmd(cmd Cmd) *Result {
|
||||
result := buildCmd(cmd)
|
||||
if result.Error != nil {
|
||||
return result
|
||||
}
|
||||
result.setExitError(result.Cmd.Start())
|
||||
return result
|
||||
}
|
||||
|
||||
func buildCmd(cmd Cmd) *Result {
|
||||
var execCmd *exec.Cmd
|
||||
switch len(cmd.Command) {
|
||||
case 1:
|
||||
execCmd = exec.Command(cmd.Command[0])
|
||||
default:
|
||||
execCmd = exec.Command(cmd.Command[0], cmd.Command[1:]...)
|
||||
}
|
||||
outBuffer := new(lockedBuffer)
|
||||
errBuffer := new(lockedBuffer)
|
||||
|
||||
execCmd.Stdin = cmd.Stdin
|
||||
execCmd.Dir = cmd.Dir
|
||||
execCmd.Env = cmd.Env
|
||||
if cmd.Stdout != nil {
|
||||
execCmd.Stdout = io.MultiWriter(outBuffer, cmd.Stdout)
|
||||
} else {
|
||||
execCmd.Stdout = outBuffer
|
||||
}
|
||||
execCmd.Stderr = errBuffer
|
||||
return &Result{
|
||||
Cmd: execCmd,
|
||||
outBuffer: outBuffer,
|
||||
errBuffer: errBuffer,
|
||||
}
|
||||
}
|
||||
|
||||
// WaitOnCmd waits for a command to complete. If timeout is non-nil then
|
||||
// only wait until the timeout.
|
||||
func WaitOnCmd(timeout time.Duration, result *Result) *Result {
|
||||
if timeout == time.Duration(0) {
|
||||
result.setExitError(result.Cmd.Wait())
|
||||
return result
|
||||
}
|
||||
|
||||
done := make(chan error, 1)
|
||||
// Wait for command to exit in a goroutine
|
||||
go func() {
|
||||
done <- result.Cmd.Wait()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(timeout):
|
||||
killErr := result.Cmd.Process.Kill()
|
||||
if killErr != nil {
|
||||
fmt.Printf("failed to kill (pid=%d): %v\n", result.Cmd.Process.Pid, killErr)
|
||||
}
|
||||
result.Timeout = true
|
||||
case err := <-done:
|
||||
result.setExitError(err)
|
||||
}
|
||||
return result
|
||||
}
|
32
vendor/github.com/gotestyourself/gotestyourself/icmd/exitcode.go
generated
vendored
Normal file
32
vendor/github.com/gotestyourself/gotestyourself/icmd/exitcode.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
package icmd
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"syscall"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// 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) {
|
||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||
if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||
return procExit.ExitStatus(), nil
|
||||
}
|
||||
}
|
||||
return 0, errors.Wrap(err, "failed to get exit code")
|
||||
}
|
||||
|
||||
func processExitCode(err error) (exitCode int) {
|
||||
if err == nil {
|
||||
return 0
|
||||
}
|
||||
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
|
||||
return 127
|
||||
}
|
||||
return exitCode
|
||||
}
|
4
vendor/github.com/gotestyourself/gotestyourself/icmd/ops.go
generated
vendored
Normal file
4
vendor/github.com/gotestyourself/gotestyourself/icmd/ops.go
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
package icmd
|
||||
|
||||
// CmdOp is an operation which modified a Cmd structure used to execute commands
|
||||
type CmdOp func(*Cmd)
|
Loading…
Reference in a new issue