helper: test child process handling

The stub child process simulates reading from the argument fd and copies the entire payload unmodified to stdout. If status pipe is enabled it will simulate sync fd behaviour as well.

Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
This commit is contained in:
Ophestra Umiker 2024-09-29 14:59:39 +09:00
parent 61ba841c88
commit 3bf456da65
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
2 changed files with 211 additions and 0 deletions

29
helper/args_test.go Normal file
View File

@ -0,0 +1,29 @@
package helper_test
import (
"errors"
"fmt"
"strings"
"testing"
"git.ophivana.moe/cat/fortify/helper"
)
func Test_argsFD_String(t *testing.T) {
argsOnce.Do(prepareArgs)
wantString := strings.Join(want, " ")
if got := argsWt.(fmt.Stringer).String(); got != wantString {
t.Errorf("String(): got %v; want %v",
got, wantString)
}
}
func TestNewCheckedArgs(t *testing.T) {
args := []string{"\x00"}
if _, err := helper.NewCheckedArgs(args); !errors.Is(err, helper.ErrContainsNull) {
t.Errorf("NewCheckedArgs(%q) error = %v, wantErr %v",
args,
err, helper.ErrContainsNull)
}
}

182
helper/helper_test.go Normal file
View File

@ -0,0 +1,182 @@
package helper_test
import (
"io"
"strings"
"sync"
"testing"
"git.ophivana.moe/cat/fortify/helper"
)
var (
want = []string{
"unix:path=/run/dbus/system_bus_socket",
"/tmp/fortify.1971/12622d846cc3fe7b4c10359d01f0eb47/system_bus_socket",
"--filter",
"--talk=org.bluez",
"--talk=org.freedesktop.Avahi",
"--talk=org.freedesktop.UPower",
}
wantPayload string
argsWt io.WriterTo
argsOnce sync.Once
)
func prepareArgs() {
wantPayload = strings.Join(want, "\x00") + "\x00"
if a, err := helper.NewCheckedArgs(want); err != nil {
panic(err.Error())
} else {
argsWt = a
}
}
func TestHelper_StartNotify_Close_Wait(t *testing.T) {
helper.ReplaceExecCommand(t)
argsOnce.Do(prepareArgs)
t.Run("start helper with status channel", func(t *testing.T) {
h := helper.New(argsWt, "crash-test-dummy", "--args=3", "--fd=4")
ready := make(chan error, 1)
stdout, stderr := new(strings.Builder), new(strings.Builder)
h.Stdout, h.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
}
})
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
}
})
if err := <-ready; err != nil {
t.Errorf("StartNotify(%v) latent error = %v",
ready,
err)
}
if err := h.Close(); err != nil {
t.Errorf("Close() error = %v",
err)
}
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.ReplaceExecCommand(t)
argsOnce.Do(prepareArgs)
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", "--args=3")
stdout, stderr := new(strings.Builder), new(strings.Builder)
h.Stdout, h.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"); got == nil {
t.Errorf("New(%q, %q) got nil",
swt, "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 {
t.Errorf("New: panic = %q, want %q",
r, want)
}
}()
helper.New(nil, "fortify")
})
}