helper: stub helper for tests
Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
This commit is contained in:
parent
0e7849fac2
commit
d530a9e9f9
|
@ -0,0 +1,18 @@
|
||||||
|
package helper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// replace execCommand to have the resulting *exec.Cmd launch TestHelperChildStub
|
||||||
|
func ReplaceExecCommand(t *testing.T) {
|
||||||
|
t.Cleanup(func() {
|
||||||
|
execCommand = exec.Command
|
||||||
|
})
|
||||||
|
|
||||||
|
execCommand = func(name string, arg ...string) *exec.Cmd {
|
||||||
|
return exec.Command(os.Args[0], append([]string{"-test.run=TestHelperChildStub", "--", name}, arg...)...)
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,11 @@ var (
|
||||||
ErrStatusRead = errors.New("unexpected status response")
|
ErrStatusRead = errors.New("unexpected status response")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FortifyHelper = "FORTIFY_HELPER"
|
||||||
|
FortifyStatus = "FORTIFY_STATUS"
|
||||||
|
)
|
||||||
|
|
||||||
// Helper wraps *exec.Cmd and manages status and args fd.
|
// Helper wraps *exec.Cmd and manages status and args fd.
|
||||||
// Args is always 3 and status if set is always 4.
|
// Args is always 3 and status if set is always 4.
|
||||||
type Helper struct {
|
type Helper struct {
|
||||||
|
@ -52,15 +57,20 @@ func (h *Helper) StartNotify(ready chan error) error {
|
||||||
h.argsP[0], h.argsP[1] = pr, pw
|
h.argsP[0], h.argsP[1] = pr, pw
|
||||||
}
|
}
|
||||||
// create status pipes if ready signal is requested
|
// create status pipes if ready signal is requested
|
||||||
|
var sv string
|
||||||
if ready != nil {
|
if ready != nil {
|
||||||
if pr, pw, err := os.Pipe(); err != nil {
|
if pr, pw, err := os.Pipe(); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
h.statP[0], h.statP[1] = pr, pw
|
h.statP[0], h.statP[1] = pr, pw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sv = FortifyStatus + "=1"
|
||||||
|
} else {
|
||||||
|
sv = FortifyStatus + "=0"
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare extra files
|
// prepare extra files from caller
|
||||||
el := len(h.ExtraFiles)
|
el := len(h.ExtraFiles)
|
||||||
if ready != nil {
|
if ready != nil {
|
||||||
el += 2
|
el += 2
|
||||||
|
@ -76,6 +86,7 @@ func (h *Helper) StartNotify(ready chan error) error {
|
||||||
|
|
||||||
// prepare and start process
|
// prepare and start process
|
||||||
h.Cmd.ExtraFiles = ef
|
h.Cmd.ExtraFiles = ef
|
||||||
|
h.Cmd.Env = append(h.Cmd.Env, FortifyHelper+"=1", sv)
|
||||||
if err := h.Cmd.Start(); err != nil {
|
if err := h.Cmd.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -175,10 +186,12 @@ func (h *Helper) Start() error {
|
||||||
return h.StartNotify(nil)
|
return h.StartNotify(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var execCommand = exec.Command
|
||||||
|
|
||||||
func New(wt io.WriterTo, name string, arg ...string) *Helper {
|
func New(wt io.WriterTo, name string, arg ...string) *Helper {
|
||||||
if wt == nil {
|
if wt == nil {
|
||||||
panic("attempted to create helper with invalid argument writer")
|
panic("attempted to create helper with invalid argument writer")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Helper{args: wt, Cmd: exec.Command(name, arg...)}
|
return &Helper{args: wt, Cmd: execCommand(name, arg...)}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
package helper_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.ophivana.moe/cat/fortify/helper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHelperChildStub(t *testing.T) {
|
||||||
|
// this test mocks the helper process
|
||||||
|
if os.Getenv(helper.FortifyHelper) != "1" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// simulate args pipe behaviour
|
||||||
|
func() {
|
||||||
|
f := os.NewFile(3, "|0")
|
||||||
|
if f == nil {
|
||||||
|
panic("attempted to start helper without args pipe")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := io.Copy(os.Stdout, f); err != nil {
|
||||||
|
panic("cannot read args: " + err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var wait chan struct{}
|
||||||
|
|
||||||
|
// simulate status pipe behaviour
|
||||||
|
if os.Getenv(helper.FortifyStatus) == "1" {
|
||||||
|
wait = make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
f := os.NewFile(4, "|1")
|
||||||
|
if f == nil {
|
||||||
|
panic("attempted to start with status reporting without status pipe")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := f.Write([]byte{'x'}); err != nil {
|
||||||
|
panic("cannot write to status pipe: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for status pipe close
|
||||||
|
var epoll int
|
||||||
|
if fd, err := syscall.EpollCreate1(0); err != nil {
|
||||||
|
panic("cannot open epoll fd: " + err.Error())
|
||||||
|
} else {
|
||||||
|
defer func() {
|
||||||
|
if err = syscall.Close(fd); err != nil {
|
||||||
|
panic("cannot close epoll fd: " + err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
epoll = fd
|
||||||
|
}
|
||||||
|
if err := syscall.EpollCtl(epoll, syscall.EPOLL_CTL_ADD, int(f.Fd()), &syscall.EpollEvent{}); err != nil {
|
||||||
|
panic("cannot add status pipe to epoll: " + err.Error())
|
||||||
|
}
|
||||||
|
events := make([]syscall.EpollEvent, 1)
|
||||||
|
if _, err := syscall.EpollWait(epoll, events, -1); err != nil {
|
||||||
|
panic("cannot poll status pipe: " + err.Error())
|
||||||
|
}
|
||||||
|
if events[0].Events != syscall.EPOLLERR {
|
||||||
|
panic(strconv.Itoa(int(events[0].Events)))
|
||||||
|
|
||||||
|
}
|
||||||
|
close(wait)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if wait != nil {
|
||||||
|
<-wait
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue