The current MockCmd.Calls() is a bit ambiguous so follow the example of pytest here and rename to the cleaner CallArgsList() that captures more closely what the helper is actually doing.
124 lines
2.3 KiB
Go
124 lines
2.3 KiB
Go
package testutil
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type MockCmd struct {
|
|
binDir string
|
|
name string
|
|
}
|
|
|
|
func MockCommand(t *testing.T, name string, script string) *MockCmd {
|
|
mockCmd := &MockCmd{
|
|
binDir: t.TempDir(),
|
|
name: name,
|
|
}
|
|
|
|
fullScript := `#!/bin/bash -e
|
|
for arg in "$@"; do
|
|
echo -e -n "$arg\x0" >> "$0".run
|
|
done
|
|
echo >> "$0".run
|
|
` + script
|
|
|
|
t.Setenv("PATH", mockCmd.binDir+":"+os.Getenv("PATH"))
|
|
err := os.WriteFile(filepath.Join(mockCmd.binDir, name), []byte(fullScript), 0755)
|
|
require.NoError(t, err)
|
|
|
|
return mockCmd
|
|
}
|
|
|
|
func (mc *MockCmd) Path() string {
|
|
return filepath.Join(mc.binDir, mc.name)
|
|
}
|
|
|
|
func (mc *MockCmd) CallArgsList() [][]string {
|
|
b, err := os.ReadFile(mc.Path() + ".run")
|
|
if os.IsNotExist(err) {
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
var callArgsList [][]string
|
|
for _, line := range strings.Split(string(b), "\n") {
|
|
if line == "" {
|
|
continue
|
|
}
|
|
call := strings.Split(line, "\000")
|
|
callArgsList = append(callArgsList, call[0:len(call)-1])
|
|
}
|
|
return callArgsList
|
|
}
|
|
|
|
// CaptureStdio runs the given function f() in an environment that
|
|
// captures stdout, stderr and returns the the result as string.
|
|
//
|
|
// Use this very targeted to avoid real stdout/stderr output
|
|
// from being displayed.
|
|
func CaptureStdio(t *testing.T, f func()) (string, string) {
|
|
saved1 := os.Stdout
|
|
saved2 := os.Stderr
|
|
|
|
r1, w1, err := os.Pipe()
|
|
require.NoError(t, err)
|
|
defer r1.Close()
|
|
defer w1.Close()
|
|
r2, w2, err := os.Pipe()
|
|
require.NoError(t, err)
|
|
defer r2.Close()
|
|
defer w2.Close()
|
|
|
|
var wg sync.WaitGroup
|
|
var stdout, stderr bytes.Buffer
|
|
wg.Add(1)
|
|
// this needs to be a go-routines or we could deadlock
|
|
// when the pipe is full
|
|
go func() {
|
|
defer wg.Done()
|
|
io.Copy(&stdout, r1)
|
|
}()
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
io.Copy(&stderr, r2)
|
|
}()
|
|
|
|
os.Stdout = w1
|
|
os.Stderr = w2
|
|
defer func() {
|
|
os.Stdout = saved1
|
|
os.Stderr = saved2
|
|
}()
|
|
|
|
f()
|
|
w1.Close()
|
|
w2.Close()
|
|
wg.Wait()
|
|
return stdout.String(), stderr.String()
|
|
}
|
|
|
|
func Chdir(t *testing.T, dir string, f func()) {
|
|
cwd, err := os.Getwd()
|
|
if err != nil {
|
|
t.Errorf("%s", err.Error())
|
|
}
|
|
defer func() {
|
|
os.Chdir(cwd)
|
|
}()
|
|
|
|
err = os.Chdir(dir)
|
|
if err != nil {
|
|
t.Errorf("%s", err.Error())
|
|
}
|
|
f()
|
|
}
|