diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-10-06 22:23:20 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-10-06 23:09:19 +0900 |
| commit | a9e2749f6654d0aa07b274a45c9177d10323f80a (patch) | |
| tree | 291f5b23a67af036cbe5d374b3d3fb0240fc438c /test/internal/testsuite/testsuite.go | |
| parent | 19f36491f2e2a5029ac396c10408d653cad6c81b (diff) | |
internal/testsuite: move from test
This structure is a lot less clumsy than the old nix-centric layout.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'test/internal/testsuite/testsuite.go')
| -rw-r--r-- | test/internal/testsuite/testsuite.go | 290 |
1 files changed, 0 insertions, 290 deletions
diff --git a/test/internal/testsuite/testsuite.go b/test/internal/testsuite/testsuite.go deleted file mode 100644 index 456c36b4..00000000 --- a/test/internal/testsuite/testsuite.go +++ /dev/null @@ -1,290 +0,0 @@ -// Package testsuite provides many quick-and-dirty integration testing utilities. -// -// Attempting to import this package outside testing causes the resulting -// program to panic. -package testsuite - -import ( - "bufio" - "context" - "crypto/sha512" - "errors" - "log" - "os" - "os/exec" - "os/signal" - "sync" - "syscall" - "time" -) - -// ReceiveSignals blocks until a termination signal arrives, and terminates. -func ReceiveSignals() { - s := make(chan os.Signal, 3) - signal.Notify(s, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP) - log.Fatalf("terminating on signal %s", <-s) -} - -// MustRun runs command and terminates the testsuite on error. -func MustRun(cred *syscall.Credential, extraEnv []string, command ...string) { - cmd := exec.Command(command[0], command[1:]...) - cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr - cmd.SysProcAttr = &syscall.SysProcAttr{ - Pdeathsig: syscall.SIGKILL, - Credential: cred, - } - if len(extraEnv) != 0 { - cmd.Env = append(cmd.Environ(), extraEnv...) - } - if err := cmd.Run(); err != nil { - log.Fatal(err) - } -} - -// ErrUnexpectedSuccess is returned for processes expected to exit with a -// non-zero code, but failed to do so. -var ErrUnexpectedSuccess = errors.New("process unexpectedly exited with code 0") - -// MustFail runs command and terminates the testsuite if the program fails to -// start or exits with code 0. -func MustFail(cred *syscall.Credential, extraEnv []string, command ...string) { - cmd := exec.Command(command[0], command[1:]...) - cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr - cmd.SysProcAttr = &syscall.SysProcAttr{ - Pdeathsig: syscall.SIGKILL, - Credential: cred, - } - if len(extraEnv) != 0 { - cmd.Env = append(cmd.Environ(), extraEnv...) - } - if err := cmd.Run(); err == nil { - log.Fatal(ErrUnexpectedSuccess) - } else if e, ok := errors.AsType[*exec.ExitError](err); !ok { - log.Fatal(err) - } else if !e.Exited() { - log.Fatal(e) - } -} - -// MustStart starts cmd and returns a channel delivering its wait error. -func MustStart(cmd *exec.Cmd) (done <-chan error) { - if err := cmd.Start(); err != nil { - log.Fatal(err) - } - d := make(chan error) - go func() { d <- cmd.Wait() }() - return d -} - -// MustStartWith wraps [MustStart] and creates the [exec.Cmd] object internally. -func MustStartWith( - ctx context.Context, - cred *syscall.Credential, - extraEnv []string, - files []*os.File, - command ...string, -) (proc *os.Process, done <-chan error) { - cmd := exec.CommandContext(ctx, command[0], command[1:]...) - cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr - cmd.ExtraFiles = files - cmd.SysProcAttr = &syscall.SysProcAttr{ - Pdeathsig: syscall.SIGTERM, - Credential: cred, - } - if len(extraEnv) != 0 { - cmd.Env = append(cmd.Environ(), extraEnv...) - } - return cmd.Process, MustStart(cmd) -} - -// MustCheckFilter is like [CheckFilter], but terminates the test suite if a -// non-nil error is returned. Otherwise, the tracee is terminated after it -// resumes. -func MustCheckFilter(pid int, sum [sha512.Size]byte) { - // podman installs its own filter - if err := CheckFilter(pid, 1, sum); err != nil { - log.Fatal(err) - } else if err = syscall.Kill(pid, syscall.SIGTERM); err != nil { - log.Fatalf("cannot terminate tracee: %v", err) - } -} - -// FilterTerminated returns a non-nil error if err is not an [exec.ExitError] -// describing a process terminated by a syscall.SIGTERM signal. -func FilterTerminated(err error) error { - if err == nil { - return ErrUnexpectedSuccess - } - - e, ok := errors.AsType[*exec.ExitError](err) - if !ok { - return err - } - - if e.ExitCode() == 0x80+int(syscall.SIGTERM) { - return nil - } - return e -} - -// Poll repeatedly runs command until it succeeds. -func Poll( - d time.Duration, - cred *syscall.Credential, - extraEnv []string, - command ...string, -) { - for range time.NewTicker(d).C { - cmd := exec.Command(command[0], command[1:]...) - cmd.SysProcAttr = &syscall.SysProcAttr{ - Pdeathsig: syscall.SIGKILL, - Credential: cred, - } - if len(extraEnv) != 0 { - cmd.Env = append(cmd.Environ(), extraEnv...) - } - if err := cmd.Run(); err != nil { - if e, ok := errors.AsType[*exec.ExitError](err); ok && e.Exited() { - continue - } - log.Fatal(err) - } - break - } -} - -const ( - // XDGRuntimeDir is the hardcoded XDG runtime directory for the user - // described by [GetUser]. - XDGRuntimeDir = "/var/run/user/1000" - - // XDGRuntimeEnv is the environment variable string for XDG_RUNTIME_DIR. - XDGRuntimeEnv = "XDG_RUNTIME_DIR=" + XDGRuntimeDir -) - -// MustStartSessionBus starts a session bus that is never explicitly terminated. -// The test suite is terminated if the session bus daemon terminates. -func MustStartSessionBus(cred *syscall.Credential) (dbusEnv string) { - r, w, err := os.Pipe() - if err != nil { - log.Fatal(err) - } - - // this is never explicitly terminated - _, done := MustStartWith( - context.Background(), cred, nil, []*os.File{w}, - "dbus-daemon", - "--print-address=3", - "--address=unix:path="+XDGRuntimeDir+"/dbus", - "--session", - "--nofork", - "--nopidfile", - ) - - go func() { - if _err := <-done; _err != nil { - log.Fatal(_err) - } - log.Fatal("session bus terminated unexpectedly") - }() - - dbusEnv, err = bufio.NewReader(r).ReadString('\n') - if err != nil { - log.Fatal(err) - } - dbusEnv = dbusEnv[:len(dbusEnv)-1] - log.Printf("dbus listening on %s", dbusEnv) - dbusEnv = "DBUS_SESSION_BUS_ADDRESS=" + dbusEnv - - if err = r.Close(); err != nil { - log.Fatal(err) - } - return -} - -const ( - // SwayEnv is the environment variable string for the sway IPC socket. - SwayEnv = "SWAYSOCK=" + XDGRuntimeDir + "/sway" - // WaylandEnv is the environment variable string for the wayland display. - WaylandEnv = "WAYLAND_DISPLAY=wayland-1" -) - -// MustStartSway starts the sway wayland display server which must be terminated -// by calling [TerminateSway]. -func MustStartSway( - wg *sync.WaitGroup, - cred *syscall.Credential, - dbusEnv string, -) { - wg.Go(func() { - // this is terminated via swaymsg - _, done := MustStartWith( - context.Background(), cred, []string{ - "WLR_BACKENDS=headless", - XDGRuntimeEnv, - SwayEnv, - dbusEnv, - }, nil, - "sway", - ) - if err := <-done; err != nil { - log.Fatal(err) - } - }) - - Poll( - 50*time.Millisecond, - cred, - []string{SwayEnv}, - "swaymsg", - ) - log.Printf("sway available via %s", SwayEnv) -} - -// TerminateSway requests for the sway server to terminate via sway IPC. -func TerminateSway(cred *syscall.Credential) { - MustFail(cred, []string{SwayEnv}, "swaymsg", "exit") -} - -// MustStartPipeWire starts a PipeWire server that is never explicitly -// terminated. The test suite is terminated if the PipeWire server terminates. -func MustStartPipeWire(cred *syscall.Credential, dbusEnv string) { - // this is never explicitly terminated - _, done := MustStartWith( - context.Background(), cred, []string{ - XDGRuntimeEnv, - dbusEnv, - }, nil, - "pipewire", - ) - - go func() { - if _err := <-done; _err != nil { - log.Fatal(_err) - } - log.Fatal("pipewire terminated unexpectedly") - }() - - Poll(50*time.Millisecond, cred, []string{ - XDGRuntimeEnv, - dbusEnv, - }, - "wpctl", - "status", - ) - - _, _done := MustStartWith( - context.Background(), cred, []string{ - XDGRuntimeEnv, - dbusEnv, - }, nil, - "wireplumber", - ) - - go func() { - if _err := <-_done; _err != nil { - log.Fatal(_err) - } - log.Fatal("wireplumber terminated unexpectedly") - }() -} |
