debian-forge-cli/internal/testutil/testutil.go
Michael Vogt bb45b89d84 testutil: remove unnecessary testutil.MockCmd.Restore()
While looking over the code I noticed that the `Restore()` helper
is not needed (and arguably wrong as it does not reset PATH).
We already use `t.TempDir()` and `t.Setenv()` as part of the
command setup so manually cleanup is not neccessary (and is today
even incomplete). So YAGNI and we can remove Restore().
2025-07-17 06:44:29 +00:00

124 lines
2.2 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) Calls() [][]string {
b, err := os.ReadFile(mc.Path() + ".run")
if os.IsNotExist(err) {
return nil
}
if err != nil {
panic(err)
}
var calls [][]string
for _, line := range strings.Split(string(b), "\n") {
if line == "" {
continue
}
call := strings.Split(line, "\000")
calls = append(calls, call[0:len(call)-1])
}
return calls
}
// 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()
}