helper: export internal stub functions for cross-package testing

Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
This commit is contained in:
Ophestra Umiker 2024-09-29 15:22:35 +09:00
parent 3bf456da65
commit 7e7327ebf8
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
4 changed files with 93 additions and 85 deletions

View File

@ -1,18 +0,0 @@
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...)...)
}
}

View File

@ -35,7 +35,7 @@ func prepareArgs() {
}
func TestHelper_StartNotify_Close_Wait(t *testing.T) {
helper.ReplaceExecCommand(t)
helper.InternalReplaceExecCommand(t)
argsOnce.Do(prepareArgs)
t.Run("start helper with status channel", func(t *testing.T) {
@ -106,7 +106,7 @@ func TestHelper_StartNotify_Close_Wait(t *testing.T) {
})
}
func TestHelper_Start_Close_Wait(t *testing.T) {
helper.ReplaceExecCommand(t)
helper.InternalReplaceExecCommand(t)
argsOnce.Do(prepareArgs)
var wt io.WriterTo

90
helper/stub.go Normal file
View File

@ -0,0 +1,90 @@
package helper
import (
"io"
"os"
"os/exec"
"strconv"
"syscall"
"testing"
)
// InternalChildStub is an internal function but exported because it is cross-package;
// it is part of the implementation of the helper stub.
func InternalChildStub() {
// this test mocks the helper process
if os.Getenv(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(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
}
}
// InternalReplaceExecCommand is an internal function but exported because it is cross-package;
// it is part of the implementation of the helper stub.
func InternalReplaceExecCommand(t *testing.T) {
t.Cleanup(func() {
execCommand = exec.Command
})
// replace execCommand to have the resulting *exec.Cmd launch TestHelperChildStub
execCommand = func(name string, arg ...string) *exec.Cmd {
return exec.Command(os.Args[0], append([]string{"-test.run=TestHelperChildStub", "--", name}, arg...)...)
}
}

View File

@ -1,75 +1,11 @@
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
}
helper.InternalChildStub()
}