From c6223771dbc9cf8a4a44424759591adb496f2ce5 Mon Sep 17 00:00:00 2001 From: Ophestra Umiker Date: Tue, 8 Oct 2024 14:02:54 +0900 Subject: [PATCH] helper: generalise helper.Helper test For testing the upcoming bwrap implementation of helper.Helper as it must have identical behaviour. Signed-off-by: Ophestra Umiker --- helper/args_test.go | 2 +- helper/direct_test.go | 159 +++--------------------------------------- helper/helper_test.go | 147 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 151 insertions(+), 157 deletions(-) diff --git a/helper/args_test.go b/helper/args_test.go index 5675a87..e8383c7 100644 --- a/helper/args_test.go +++ b/helper/args_test.go @@ -10,7 +10,7 @@ import ( ) func Test_argsFD_String(t *testing.T) { - wantString := strings.Join(want, " ") + wantString := strings.Join(wantArgs, " ") if got := argsWt.(fmt.Stringer).String(); got != wantString { t.Errorf("String(): got %v; want %v", got, wantString) diff --git a/helper/direct_test.go b/helper/direct_test.go index fc583b6..d2b764c 100644 --- a/helper/direct_test.go +++ b/helper/direct_test.go @@ -2,18 +2,13 @@ package helper_test import ( "errors" - "io" "os" - "strings" "testing" - "time" "git.ophivana.moe/cat/fortify/helper" ) -func TestHelper_StartNotify_Close_Wait(t *testing.T) { - helper.InternalReplaceExecCommand(t) - +func TestDirect(t *testing.T) { t.Run("start non-existent helper path", func(t *testing.T) { h := helper.New(argsWt, "/nonexistent", argF) @@ -23,161 +18,27 @@ func TestHelper_StartNotify_Close_Wait(t *testing.T) { } }) - t.Run("start helper with status channel", func(t *testing.T) { - h := helper.New(argsWt, "crash-test-dummy", argFStatus) - ready := make(chan error, 1) - cmd := h.Unwrap() - - stdout, stderr := new(strings.Builder), new(strings.Builder) - cmd.Stdout, cmd.Stderr = stdout, stderr - - t.Run("wait not yet started helper", func(t *testing.T) { - wantErr := "exec: not started" - if err := h.Wait(); err != nil && err.Error() != wantErr { - t.Errorf("Wait(%v) error = %v, wantErr %v", - ready, - err, wantErr) - return - } - }) - - t.Log("starting helper stub") - if err := h.StartNotify(ready); err != nil { - t.Errorf("StartNotify(%v) error = %v", - ready, - err) - return - } - - t.Run("start already started helper", func(t *testing.T) { - wantErr := "exec: already started" - if err := h.StartNotify(ready); err != nil && err.Error() != wantErr { - t.Errorf("StartNotify(%v) error = %v, wantErr %v", - ready, - err, wantErr) - return - } - }) - - t.Log("waiting on status channel with timeout") - select { - case <-time.NewTimer(5 * time.Second).C: - t.Errorf("never got a ready response") - t.Errorf("stdout:\n%s", stdout.String()) - t.Errorf("stderr:\n%s", stderr.String()) - if err := cmd.Process.Kill(); err != nil { - panic(err.Error()) - } - return - case err := <-ready: - if err != nil { - t.Errorf("StartNotify(%v) latent error = %v", - ready, - err) - } - } - - t.Log("closing status pipe") - if err := h.Close(); err != nil { - t.Errorf("Close() error = %v", - err) - } - - t.Log("waiting on helper") - if err := h.Wait(); err != nil { - t.Errorf("Wait() err = %v stderr = %s", - err, stderr) - } - - t.Run("wait already finalised helper", func(t *testing.T) { - wantErr := "exec: Wait was already called" - if err := h.Wait(); err != nil && err.Error() != wantErr { - t.Errorf("Wait(%v) error = %v, wantErr %v", - ready, - err, wantErr) - return - } - }) - - if got := stdout.String(); !strings.HasPrefix(got, wantPayload) { - t.Errorf("StartNotify(%v) stdout = %v, want %v", - ready, - got, wantPayload) - } - }) -} -func TestHelper_Start_Close_Wait(t *testing.T) { - helper.InternalReplaceExecCommand(t) - - var wt io.WriterTo - if a, err := helper.NewCheckedArgs(want); err != nil { - t.Errorf("NewCheckedArgs(%q) error = %v", - want, - err) - return - } else { - wt = a - } - - t.Run("start helper", func(t *testing.T) { - h := helper.New(wt, "crash-test-dummy", argF) - cmd := h.Unwrap() - - stdout, stderr := new(strings.Builder), new(strings.Builder) - cmd.Stdout, cmd.Stderr = stdout, stderr - - if err := h.Start(); err != nil { - t.Errorf("Start() error = %v", - err) - return - } - - t.Run("close helper without status pipe", func(t *testing.T) { - defer func() { - wantPanic := "attempted to close helper with no status pipe" - if r := recover(); r != wantPanic { - t.Errorf("Close() panic = %v, wantPanic %v", - r, wantPanic) - } - }() - if err := h.Close(); err != nil { - t.Errorf("Close() error = %v", - err) - return - } - }) - - if err := h.Wait(); err != nil { - t.Errorf("Wait() err = %v stderr = %s", - err, stderr) - } - - if got := stdout.String(); !strings.HasPrefix(got, wantPayload) { - t.Errorf("Start() stdout = %v, want %v", - got, wantPayload) - } - }) -} - -func TestNew(t *testing.T) { t.Run("valid new helper nil check", func(t *testing.T) { - swt, _ := helper.NewCheckedArgs(make([]string, 1)) - if got := helper.New(swt, "fortify", argF); got == nil { + if got := helper.New(argsWt, "fortify", argF); got == nil { t.Errorf("New(%q, %q) got nil", - swt, "fortify") + argsWt, "fortify") return } }) t.Run("invalid new helper panic", func(t *testing.T) { defer func() { - want := "attempted to create helper with invalid argument writer" - if r := recover(); r != want { + wantPanic := "attempted to create helper with invalid argument writer" + if r := recover(); r != wantPanic { t.Errorf("New: panic = %q, want %q", - r, want) + r, wantPanic) } }() helper.New(nil, "fortify", argF) }) + + t.Run("implementation compliance", func(t *testing.T) { + testHelper(t, func() helper.Helper { return helper.New(argsWt, "crash-test-dummy", argF) }) + }) } diff --git a/helper/helper_test.go b/helper/helper_test.go index e97ed02..23cff66 100644 --- a/helper/helper_test.go +++ b/helper/helper_test.go @@ -3,12 +3,14 @@ package helper_test import ( "strconv" "strings" + "testing" + "time" "git.ophivana.moe/cat/fortify/helper" ) var ( - want = []string{ + wantArgs = []string{ "unix:path=/run/dbus/system_bus_socket", "/tmp/fortify.1971/12622d846cc3fe7b4c10359d01f0eb47/system_bus_socket", "--filter", @@ -17,14 +19,145 @@ var ( "--talk=org.freedesktop.UPower", } - wantPayload = strings.Join(want, "\x00") + "\x00" - argsWt = helper.MustNewCheckedArgs(want) + wantPayload = strings.Join(wantArgs, "\x00") + "\x00" + argsWt = helper.MustNewCheckedArgs(wantArgs) ) -func argF(argsFD int, _ int) []string { - return []string{"--args", strconv.Itoa(argsFD)} +func argF(argsFD int, statFD int) []string { + if argsFD == -1 { + panic("invalid args fd") + } + + if statFD == -1 { + return []string{"--args", strconv.Itoa(argsFD)} + } else { + return []string{"--args", strconv.Itoa(argsFD), "--fd", strconv.Itoa(statFD)} + } } -func argFStatus(argsFD int, statFD int) []string { - return []string{"--args", strconv.Itoa(argsFD), "--fd", strconv.Itoa(statFD)} +// this function tests an implementation of the helper.Helper interface +func testHelper(t *testing.T, createHelper func() helper.Helper) { + helper.InternalReplaceExecCommand(t) + + t.Run("start helper with status channel and wait", func(t *testing.T) { + h := createHelper() + ready := make(chan error, 1) + cmd := h.Unwrap() + + stdout, stderr := new(strings.Builder), new(strings.Builder) + cmd.Stdout, cmd.Stderr = stdout, stderr + + t.Run("wait not yet started helper", func(t *testing.T) { + wantErr := "exec: not started" + if err := h.Wait(); err != nil && err.Error() != wantErr { + t.Errorf("Wait(%v) error = %v, wantErr %v", + ready, + err, wantErr) + return + } + }) + + t.Log("starting helper stub") + if err := h.StartNotify(ready); err != nil { + t.Errorf("StartNotify(%v) error = %v", + ready, + err) + return + } + + t.Run("start already started helper", func(t *testing.T) { + wantErr := "exec: already started" + if err := h.StartNotify(ready); err != nil && err.Error() != wantErr { + t.Errorf("StartNotify(%v) error = %v, wantErr %v", + ready, + err, wantErr) + return + } + }) + + t.Log("waiting on status channel with timeout") + select { + case <-time.NewTimer(5 * time.Second).C: + t.Errorf("never got a ready response") + t.Errorf("stdout:\n%s", stdout.String()) + t.Errorf("stderr:\n%s", stderr.String()) + if err := cmd.Process.Kill(); err != nil { + panic(err.Error()) + } + return + case err := <-ready: + if err != nil { + t.Errorf("StartNotify(%v) latent error = %v", + ready, + err) + } + } + + t.Log("closing status pipe") + if err := h.Close(); err != nil { + t.Errorf("Close() error = %v", + err) + } + + t.Log("waiting on helper") + if err := h.Wait(); err != nil { + t.Errorf("Wait() err = %v stderr = %s", + err, stderr) + } + + t.Run("wait already finalised helper", func(t *testing.T) { + wantErr := "exec: Wait was already called" + if err := h.Wait(); err != nil && err.Error() != wantErr { + t.Errorf("Wait(%v) error = %v, wantErr %v", + ready, + err, wantErr) + return + } + }) + + if got := stdout.String(); !strings.HasPrefix(got, wantPayload) { + t.Errorf("StartNotify(%v) stdout = %v, want %v", + ready, + got, wantPayload) + } + }) + + t.Run("start helper and wait", func(t *testing.T) { + h := createHelper() + cmd := h.Unwrap() + + stdout, stderr := new(strings.Builder), new(strings.Builder) + cmd.Stdout, cmd.Stderr = stdout, stderr + + if err := h.Start(); err != nil { + t.Errorf("Start() error = %v", + err) + return + } + + t.Run("close helper without status pipe", func(t *testing.T) { + defer func() { + wantPanic := "attempted to close helper with no status pipe" + if r := recover(); r != wantPanic { + t.Errorf("Close() panic = %v, wantPanic %v", + r, wantPanic) + } + }() + if err := h.Close(); err != nil { + t.Errorf("Close() error = %v", + err) + return + } + }) + + if err := h.Wait(); err != nil { + t.Errorf("Wait() err = %v stderr = %s", + err, stderr) + } + + if got := stdout.String(); !strings.HasPrefix(got, wantPayload) { + t.Errorf("Start() stdout = %v, want %v", + got, wantPayload) + } + }) }