summaryrefslogtreecommitdiff
path: root/integrationtests/commandutils.go
diff options
context:
space:
mode:
Diffstat (limited to 'integrationtests/commandutils.go')
-rw-r--r--integrationtests/commandutils.go112
1 files changed, 112 insertions, 0 deletions
diff --git a/integrationtests/commandutils.go b/integrationtests/commandutils.go
new file mode 100644
index 0000000..d2f567f
--- /dev/null
+++ b/integrationtests/commandutils.go
@@ -0,0 +1,112 @@
+package integrationtests
+
+import (
+ "bufio"
+ "context"
+ "fmt"
+ "os"
+ "os/exec"
+ "sync"
+ "syscall"
+ "time"
+)
+
+// The exit code and the Go error of the command terminated.
+type exitPromise func() (int, error)
+
+func runCommand(ctx context.Context, stdoutFile, cmdStr string,
+ args ...string) (int, error) {
+
+ stdinCh, _, exit, err := startCommand(ctx, cmdStr, args...)
+ if err != nil {
+ return -1, err
+ }
+
+ fd, err := os.Create(stdoutFile)
+ if err != nil {
+ return -2, err
+ }
+
+ var wg sync.WaitGroup
+ wg.Add(1)
+ defer wg.Wait()
+
+ go func() {
+ defer fd.Close()
+ defer wg.Done()
+ for line := range stdinCh {
+ fd.WriteString(line)
+ fd.WriteString("\n")
+ }
+ }()
+
+ return exit()
+}
+
+func runCommandRetry(ctx context.Context, retries int, stdoutFile, cmd string,
+ args ...string) (exitCode int, err error) {
+
+ for i := 0; i < retries; i++ {
+ time.Sleep(time.Second)
+ if exitCode, err = runCommand(ctx, stdoutFile, cmd, args...); exitCode == 0 {
+ return
+ }
+ }
+ return
+}
+
+func startCommand(ctx context.Context, cmdStr string,
+ args ...string) (<-chan string, <-chan string, exitPromise, error) {
+
+ stdoutCh := make(chan string)
+ stderrCh := make(chan string)
+
+ if _, err := os.Stat(cmdStr); err != nil {
+ return stdoutCh, stderrCh, nil,
+ fmt.Errorf("no such executable '%s', please compile first: %v", cmdStr, err)
+ }
+
+ cmd := exec.CommandContext(ctx, cmdStr, args...)
+
+ cmdStdout, err := cmd.StdoutPipe()
+ if err != nil {
+ return stdoutCh, stderrCh, nil, err
+ }
+ cmdStderr, err := cmd.StderrPipe()
+ err = cmd.Start()
+ if err != nil {
+ return stdoutCh, stderrCh, nil, err
+ }
+
+ go func() {
+ defer close(stdoutCh)
+ scanner := bufio.NewScanner(cmdStdout)
+ scanner.Split(bufio.ScanLines)
+ for scanner.Scan() {
+ stdoutCh <- scanner.Text()
+ }
+ }()
+ go func() {
+ close(stderrCh)
+ scanner := bufio.NewScanner(cmdStderr)
+ scanner.Split(bufio.ScanLines)
+ for scanner.Scan() {
+ stderrCh <- scanner.Text()
+ }
+ }()
+
+ return stdoutCh, stderrCh, func() (int, error) {
+ err := cmd.Wait()
+ return exitCodeFromError(err), err
+ }, nil
+}
+
+func exitCodeFromError(err error) int {
+ if err != nil {
+ if exitError, ok := err.(*exec.ExitError); ok {
+ ws := exitError.Sys().(syscall.WaitStatus)
+ return ws.ExitStatus()
+ }
+ }
+ return 0
+}