diff options
| -rw-r--r-- | cmd/hakurei/testsuite/sandbox/main.go | 11 | ||||
| -rw-r--r-- | internal/testsuite/testsuite.go | 134 |
2 files changed, 113 insertions, 32 deletions
diff --git a/cmd/hakurei/testsuite/sandbox/main.go b/cmd/hakurei/testsuite/sandbox/main.go index 31e85331..a85ec485 100644 --- a/cmd/hakurei/testsuite/sandbox/main.go +++ b/cmd/hakurei/testsuite/sandbox/main.go @@ -35,7 +35,7 @@ func mustStart( ) (pid int, done <-chan error) { _serial := strconv.FormatUint(serial, 10) _, done = testsuite.MustStartWith( - ctx, cred, nil, files, + ctx, cred, false, nil, files, "hakurei", "exec", "-a", strconv.Itoa(identity), "sleep", "infinity", _serial, @@ -202,12 +202,12 @@ func main() { for _, p := range []byte{'a', 'b', 'i', 'p'} { testsuite.MustFail( - &cred, nil, + &cred, nil, false, "hakurei", "exec", "capsh", "--has-"+string(p)+"=CAP_SYS_ADMIN", ) } testsuite.MustFail( - &cred, nil, + &cred, nil, false, "hakurei", "exec", "umount", "-R", "/dev", ) }) @@ -287,14 +287,13 @@ func main() { for name, tc := range testdata.All() { wg.Go(func() { _, pid := testsuite.MustStartHakurei( - &wg, &cred, dbusEnv, + ctx, &wg, &cred, dbusEnv, func(err error, output string) { log.Printf("completed test case %s\n%s", name, output) if err != nil { log.Fatalf("test case %s: %v", name, err) } - }, - &tc.Hakurei, + }, true, false, &tc.Hakurei, ) testsuite.MustCheckFilter( diff --git a/internal/testsuite/testsuite.go b/internal/testsuite/testsuite.go index aa03327f..fee4952c 100644 --- a/internal/testsuite/testsuite.go +++ b/internal/testsuite/testsuite.go @@ -110,9 +110,16 @@ 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) { +func MustFail( + cred *syscall.Credential, + extraEnv []string, + quiet bool, + command ...string, +) { cmd := exec.Command(command[0], command[1:]...) - cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr + if !quiet { + cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr + } cmd.SysProcAttr = &syscall.SysProcAttr{ Pdeathsig: syscall.SIGKILL, Credential: cred, @@ -141,12 +148,15 @@ func MustStart(cmd *exec.Cmd) (done <-chan error) { func MustStartWith( ctx context.Context, cred *syscall.Credential, + quiet bool, 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 + if !quiet { + cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr + } cmd.ExtraFiles = files cmd.SysProcAttr = &syscall.SysProcAttr{ Pdeathsig: syscall.SIGTERM, @@ -313,6 +323,10 @@ func SessionEnv(dbusEnv string) []string { } } +// quietServices is initialised from the environment and controls whether +// session services should have their outputs connected. +var quietServices = os.Getenv("HAKUREI_VERBOSE_SERVICES") == "" + // 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) { @@ -323,7 +337,7 @@ func MustStartSessionBus(cred *syscall.Credential) (dbusEnv string) { // this is never explicitly terminated _, done := MustStartWith( - context.Background(), cred, nil, []*os.File{w}, + context.Background(), cred, quietServices, nil, []*os.File{w}, "dbus-daemon", "--print-address=3", "--address=unix:path="+XDGRuntimeDir+"/dbus", @@ -370,7 +384,7 @@ func MustStartSway( wg.Go(func() { // this is terminated via swaymsg _, done := MustStartWith( - context.Background(), cred, []string{ + context.Background(), cred, quietServices, []string{ "WLR_BACKENDS=headless", XDGRuntimeEnv, SwayEnv, @@ -394,7 +408,7 @@ func MustStartSway( // TerminateSway requests for the sway server to terminate via sway IPC. func TerminateSway(cred *syscall.Credential) { - MustFail(cred, []string{SwayEnv}, "swaymsg", "exit") + MustFail(cred, []string{SwayEnv}, quietServices, "swaymsg", "exit") } // MustStartPipeWire starts a PipeWire server that is never explicitly @@ -402,7 +416,7 @@ func TerminateSway(cred *syscall.Credential) { func MustStartPipeWire(cred *syscall.Credential, dbusEnv string) { // this is never explicitly terminated _, done := MustStartWith( - context.Background(), cred, []string{ + context.Background(), cred, quietServices, []string{ XDGRuntimeEnv, dbusEnv, }, nil, @@ -425,7 +439,7 @@ func MustStartPipeWire(cred *syscall.Credential, dbusEnv string) { ) _, _done := MustStartWith( - context.Background(), cred, []string{ + context.Background(), cred, quietServices, []string{ XDGRuntimeEnv, dbusEnv, }, nil, @@ -456,21 +470,29 @@ func MustScanFor(f func(ps *StatScanner) bool) int { return ps.Stat().PID } -// MustStartHakurei starts a hakurei container configured by c. -func MustStartHakurei( +// StartHakurei starts a hakurei container configured by c. +func StartHakurei( + ctx context.Context, wg *sync.WaitGroup, cred *syscall.Credential, dbusEnv string, + processCommand func(cmd *exec.Cmd), onDone func(err error, output string), + scan, wait, verbose bool, c *hst.Config, -) (p hst.State, pid int) { +) (p hst.State, pid int, err error) { s := store.New(check.MustAbs("/tmp/hakurei.0/state")) - cmd := exec.Command( + hakureiFlags := "" + if verbose { + hakureiFlags += " -v" + } + + cmd := exec.CommandContext(ctx, "script", "/dev/null", "-E", "always", "-qec", - "hakurei run "+ + "hakurei"+hakureiFlags+" run "+ "--identifier-fd=5"+ " 4 1>&3", ) @@ -479,44 +501,76 @@ func MustStartHakurei( Credential: cred, } var output strings.Builder - cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, &output, &output + cmd.Stdout, cmd.Stderr = &output, &output cmd.Env = SessionEnv(dbusEnv) - var err error var notify, _notify, _conf, conf, ident, _ident *os.File if notify, _notify, err = os.Pipe(); err != nil { - log.Fatal(err) + return } cmd.ExtraFiles = append(cmd.ExtraFiles, _notify) if _conf, conf, err = os.Pipe(); err != nil { - log.Fatal(err) + _, _ = notify.Close(), _notify.Close() + return } cmd.ExtraFiles = append(cmd.ExtraFiles, _conf) if ident, _ident, err = os.Pipe(); err != nil { - log.Fatal(err) + _, _ = notify.Close(), _notify.Close() + _, _ = conf.Close(), _conf.Close() + return } cmd.ExtraFiles = append(cmd.ExtraFiles, _ident) + if processCommand != nil { + processCommand(cmd) + } done := MustStart(cmd) wg.Go(func() { _err := <-done; onDone(_err, output.String()) }) + if err = errors.Join( + _notify.Close(), + _conf.Close(), + _ident.Close(), + ); err != nil { + _, _, _ = notify.Close(), conf.Close(), ident.Close() + return + } + if err = json.NewEncoder(conf).Encode(c); err != nil { - log.Fatal(err) + _, _, _ = notify.Close(), conf.Close(), ident.Close() + return } else if err = conf.Close(); err != nil { - log.Fatal(err) + _, _ = notify.Close(), ident.Close() + return + } + + if !scan && len(cmd.ExtraFiles) != 3 { + err = errors.Join(notify.Close(), ident.Close()) + return } var id hst.ID if _, err = io.ReadFull(ident, id[:]); err != nil { - log.Fatal(err) + _, _ = notify.Close(), ident.Close() + return } else if err = ident.Close(); err != nil { - log.Fatal(err) + _ = notify.Close() + return } - if _, err = io.ReadFull(notify, make([]byte, 8)); err != nil { - log.Fatal(err) - } else if err = notify.Close(); err != nil { - log.Fatal(err) + if !scan { + err = notify.Close() + return + } + + if wait { + if _, err = io.ReadFull(notify, make([]byte, 8)); err != nil { + _ = notify.Close() + return + } + } + if err = notify.Close(); err != nil { + return } var ok bool @@ -595,3 +649,31 @@ func MustStartHakurei( }) return } + +// MustStartHakurei is like [StartHakurei], but terminates the test suite if +// an error is encountered. +func MustStartHakurei( + ctx context.Context, + wg *sync.WaitGroup, + cred *syscall.Credential, + dbusEnv string, + onDone func(err error, output string), + wait, verbose bool, + c *hst.Config, +) (p hst.State, pid int) { + _ctx, cancel := context.WithCancel(ctx) + + var err error + p, pid, err = StartHakurei( + _ctx, wg, cred, dbusEnv, + nil, func(err error, output string) { + defer cancel() + onDone(err, output) + }, true, wait, verbose, c, + ) + if err != nil { + log.Printf("cannot start hakurei container: %v", err) + cancel() + } + return +} |
