kubo/test/cli/harness/run.go

157 lines
3.1 KiB
Go

package harness
import (
"fmt"
"io"
"os/exec"
"strings"
)
// Runner is a process runner which can run subprocesses and aggregate output.
type Runner struct {
Env map[string]string
Dir string
Verbose bool
}
type (
CmdOpt func(*exec.Cmd)
RunFunc func(*exec.Cmd) error
)
var RunFuncStart = (*exec.Cmd).Start
type RunRequest struct {
Path string
Args []string
// Options that are applied to the exec.Cmd just before running it
CmdOpts []CmdOpt
// Function to use to run the command.
// If not specified, defaults to cmd.Run
RunFunc func(*exec.Cmd) error
Verbose bool
}
type RunResult struct {
Stdout *Buffer
Stderr *Buffer
Err error
ExitErr *exec.ExitError
Cmd *exec.Cmd
}
func (r *RunResult) ExitCode() int {
return r.Cmd.ProcessState.ExitCode()
}
func environToMap(environ []string) map[string]string {
m := map[string]string{}
for _, e := range environ {
kv := strings.Split(e, "=")
m[kv[0]] = kv[1]
}
return m
}
func (r *Runner) Run(req RunRequest) *RunResult {
cmd := exec.Command(req.Path, req.Args...)
stdout := &Buffer{}
stderr := &Buffer{}
cmd.Stdout = stdout
cmd.Stderr = stderr
cmd.Dir = r.Dir
for k, v := range r.Env {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v))
}
for _, o := range req.CmdOpts {
o(cmd)
}
if req.RunFunc == nil {
req.RunFunc = (*exec.Cmd).Run
}
log.Debugf("running %v", cmd.Args)
err := req.RunFunc(cmd)
result := RunResult{
Stdout: stdout,
Stderr: stderr,
Cmd: cmd,
Err: err,
}
if exitErr, ok := err.(*exec.ExitError); ok {
result.ExitErr = exitErr
}
return &result
}
// MustRun runs the command and fails the test if the command fails.
func (r *Runner) MustRun(req RunRequest) *RunResult {
result := r.Run(req)
r.AssertNoError(result)
return result
}
func (r *Runner) AssertNoError(result *RunResult) {
if result.ExitErr != nil {
log.Panicf("'%s' returned error, code: %d, err: %s\nstdout:%s\nstderr:%s\n",
result.Cmd.Args, result.ExitErr.ExitCode(), result.ExitErr.Error(), result.Stdout.String(), result.Stderr.String())
}
if result.Err != nil {
log.Panicf("unable to run %s: %s", result.Cmd.Path, result.Err)
}
}
func RunWithEnv(env map[string]string) CmdOpt {
return func(cmd *exec.Cmd) {
for k, v := range env {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v))
}
}
}
func RunWithPath(path string) CmdOpt {
return func(cmd *exec.Cmd) {
var newEnv []string
for _, env := range cmd.Env {
e := strings.Split(env, "=")
if e[0] == "PATH" {
paths := strings.Split(e[1], ":")
paths = append(paths, path)
e[1] = strings.Join(paths, ":")
fmt.Printf("path: %s\n", strings.Join(e, "="))
}
newEnv = append(newEnv, strings.Join(e, "="))
}
cmd.Env = newEnv
}
}
func RunWithStdin(reader io.Reader) CmdOpt {
return func(cmd *exec.Cmd) {
cmd.Stdin = reader
}
}
func RunWithStdinStr(s string) CmdOpt {
return RunWithStdin(strings.NewReader(s))
}
func RunWithStdout(writer io.Writer) CmdOpt {
return func(cmd *exec.Cmd) {
cmd.Stdout = io.MultiWriter(writer, cmd.Stdout)
}
}
func RunWithStderr(writer io.Writer) CmdOpt {
return func(cmd *exec.Cmd) {
cmd.Stderr = io.MultiWriter(writer, cmd.Stdout)
}
}