All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m9s
Test / Hakurei (push) Successful in 3m9s
Test / Hpkg (push) Successful in 4m12s
Test / Sandbox (race detector) (push) Successful in 4m29s
Test / Hakurei (race detector) (push) Successful in 5m6s
Test / Flake checks (push) Successful in 1m29s
The file is very difficult to stub. Pass fd instead as it is the value that is actually useful. Signed-off-by: Ophestra <cat@gensokyo.uk>
645 lines
17 KiB
Go
645 lines
17 KiB
Go
package container
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"os"
|
|
"os/exec"
|
|
"reflect"
|
|
"runtime"
|
|
"slices"
|
|
"strings"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
|
|
"hakurei.app/container/seccomp"
|
|
)
|
|
|
|
var errUnique = errors.New("unique error injected by the test suite")
|
|
|
|
type opValidTestCase struct {
|
|
name string
|
|
op Op
|
|
want bool
|
|
}
|
|
|
|
func checkOpsValid(t *testing.T, testCases []opValidTestCase) {
|
|
t.Run("valid", func(t *testing.T) {
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := tc.op.Valid(); got != tc.want {
|
|
t.Errorf("Valid: %v, want %v", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
type opsBuilderTestCase struct {
|
|
name string
|
|
ops *Ops
|
|
want Ops
|
|
}
|
|
|
|
func checkOpsBuilder(t *testing.T, testCases []opsBuilderTestCase) {
|
|
t.Run("build", func(t *testing.T) {
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if !slices.EqualFunc(*tc.ops, tc.want, func(op Op, v Op) bool { return op.Is(v) }) {
|
|
t.Errorf("Ops: %#v, want %#v", tc.ops, tc.want)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
type opIsTestCase struct {
|
|
name string
|
|
op, v Op
|
|
want bool
|
|
}
|
|
|
|
func checkOpIs(t *testing.T, testCases []opIsTestCase) {
|
|
t.Run("is", func(t *testing.T) {
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := tc.op.Is(tc.v); got != tc.want {
|
|
t.Errorf("Is: %v, want %v", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
type opMetaTestCase struct {
|
|
name string
|
|
op Op
|
|
|
|
wantPrefix string
|
|
wantString string
|
|
}
|
|
|
|
func checkOpMeta(t *testing.T, testCases []opMetaTestCase) {
|
|
t.Run("meta", func(t *testing.T) {
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Run("prefix", func(t *testing.T) {
|
|
if got := tc.op.prefix(); got != tc.wantPrefix {
|
|
t.Errorf("prefix: %q, want %q", got, tc.wantPrefix)
|
|
}
|
|
})
|
|
|
|
t.Run("string", func(t *testing.T) {
|
|
if got := tc.op.String(); got != tc.wantString {
|
|
t.Errorf("String: %s, want %s", got, tc.wantString)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
type simpleTestCase struct {
|
|
name string
|
|
f func(k syscallDispatcher) error
|
|
want []kexpect
|
|
wantErr error
|
|
}
|
|
|
|
func checkSimple(t *testing.T, fname string, testCases []simpleTestCase) {
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
k := &kstub{t: t, want: tc.want}
|
|
if err := tc.f(k); !errors.Is(err, tc.wantErr) {
|
|
t.Errorf("%s: error = %v, want %v", fname, err, tc.wantErr)
|
|
}
|
|
if len(k.want) != k.pos {
|
|
t.Errorf("%s: %d calls, want %d", fname, k.pos, len(k.want))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type opBehaviourTestCase struct {
|
|
name string
|
|
params *Params
|
|
op Op
|
|
|
|
early []kexpect
|
|
wantErrEarly error
|
|
|
|
apply []kexpect
|
|
wantErrApply error
|
|
}
|
|
|
|
func checkOpBehaviour(t *testing.T, testCases []opBehaviourTestCase) {
|
|
t.Run("behaviour", func(t *testing.T) {
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
state := &setupState{Params: tc.params}
|
|
k := &kstub{t: t, want: slices.Concat(tc.early, []kexpect{{name: "\x00"}}, tc.apply)}
|
|
errEarly := tc.op.early(state, k)
|
|
k.expect("\x00")
|
|
if !errors.Is(errEarly, tc.wantErrEarly) {
|
|
t.Errorf("early: error = %v, want %v", errEarly, tc.wantErrEarly)
|
|
}
|
|
if errEarly != nil {
|
|
goto out
|
|
}
|
|
|
|
if err := tc.op.apply(state, k); !errors.Is(err, tc.wantErrApply) {
|
|
t.Errorf("apply: error = %v, want %v", err, tc.wantErrApply)
|
|
}
|
|
|
|
out:
|
|
if len(k.want) != k.pos {
|
|
count := k.pos - 1 // separator
|
|
if count < len(tc.early) {
|
|
t.Errorf("early: %d calls, want %d", count, len(tc.early))
|
|
} else {
|
|
t.Errorf("apply: %d calls, want %d", count-len(tc.early), len(tc.apply))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
func newCheckedFile(t *testing.T, name, wantData string, closeErr error) osFile {
|
|
f := &checkedOsFile{t: t, name: name, want: wantData, closeErr: closeErr}
|
|
// check happens in Close, and cleanup is not guaranteed to run, so relying on it for sloppy implementations will cause sporadic test results
|
|
f.cleanup = runtime.AddCleanup(f, func(name string) { f.t.Fatalf("checkedOsFile %s became unreachable without a call to Close", name) }, f.name)
|
|
return f
|
|
}
|
|
|
|
type checkedOsFile struct {
|
|
t *testing.T
|
|
name string
|
|
want string
|
|
closeErr error
|
|
cleanup runtime.Cleanup
|
|
bytes.Buffer
|
|
}
|
|
|
|
func (f *checkedOsFile) Name() string { return f.name }
|
|
func (f *checkedOsFile) Stat() (fs.FileInfo, error) { panic("unreachable") }
|
|
func (f *checkedOsFile) Close() error {
|
|
defer f.cleanup.Stop()
|
|
if f.String() != f.want {
|
|
f.t.Errorf("checkedOsFile:\n%s\nwant\n%s", f.String(), f.want)
|
|
return syscall.ENOTRECOVERABLE
|
|
}
|
|
return f.closeErr
|
|
}
|
|
|
|
func newConstFile(s string) osFile { return &readerOsFile{Reader: strings.NewReader(s)} }
|
|
|
|
type readerOsFile struct {
|
|
closed bool
|
|
io.Reader
|
|
}
|
|
|
|
func (*readerOsFile) Name() string { panic("unreachable") }
|
|
func (*readerOsFile) Write([]byte) (int, error) { panic("unreachable") }
|
|
func (*readerOsFile) Stat() (fs.FileInfo, error) { panic("unreachable") }
|
|
func (r *readerOsFile) Close() error {
|
|
if r.closed {
|
|
return os.ErrClosed
|
|
}
|
|
r.closed = true
|
|
return nil
|
|
}
|
|
|
|
type writeErrOsFile struct{ err error }
|
|
|
|
func (writeErrOsFile) Name() string { panic("unreachable") }
|
|
func (f writeErrOsFile) Write([]byte) (int, error) { return 0, f.err }
|
|
func (writeErrOsFile) Stat() (fs.FileInfo, error) { panic("unreachable") }
|
|
func (writeErrOsFile) Read([]byte) (int, error) { panic("unreachable") }
|
|
func (writeErrOsFile) Close() error { panic("unreachable") }
|
|
|
|
type expectArgs = [5]any
|
|
|
|
type isDirFi bool
|
|
|
|
func (isDirFi) Name() string { panic("unreachable") }
|
|
func (isDirFi) Size() int64 { panic("unreachable") }
|
|
func (isDirFi) Mode() fs.FileMode { panic("unreachable") }
|
|
func (isDirFi) ModTime() time.Time { panic("unreachable") }
|
|
func (fi isDirFi) IsDir() bool { return bool(fi) }
|
|
func (isDirFi) Sys() any { panic("unreachable") }
|
|
|
|
func stubDir(names ...string) []os.DirEntry {
|
|
d := make([]os.DirEntry, len(names))
|
|
for i, name := range names {
|
|
d[i] = nameDentry(name)
|
|
}
|
|
return d
|
|
}
|
|
|
|
type nameDentry string
|
|
|
|
func (e nameDentry) Name() string { return string(e) }
|
|
func (nameDentry) IsDir() bool { panic("unreachable") }
|
|
func (nameDentry) Type() fs.FileMode { panic("unreachable") }
|
|
func (nameDentry) Info() (fs.FileInfo, error) { panic("unreachable") }
|
|
|
|
type kexpect struct {
|
|
name string
|
|
args expectArgs
|
|
ret any
|
|
err error
|
|
}
|
|
|
|
func (k *kexpect) error(ok ...bool) error {
|
|
if !slices.Contains(ok, false) {
|
|
return k.err
|
|
}
|
|
return syscall.ENOTRECOVERABLE
|
|
}
|
|
|
|
type kstub struct {
|
|
t *testing.T
|
|
want []kexpect
|
|
pos int
|
|
}
|
|
|
|
// expect checks name and returns the current kexpect and advances pos.
|
|
func (k *kstub) expect(name string) (expect *kexpect) {
|
|
if len(k.want) == k.pos {
|
|
k.t.Fatal("expect: want too short")
|
|
}
|
|
expect = &k.want[k.pos]
|
|
if name != expect.name {
|
|
if expect.name == "\x00" {
|
|
k.t.Fatalf("expect: func = %s, separator overrun", name)
|
|
}
|
|
if name == "\x00" {
|
|
k.t.Fatalf("expect: separator, want %s", expect.name)
|
|
}
|
|
k.t.Fatalf("expect: func = %s, want %s", name, expect.name)
|
|
}
|
|
k.pos++
|
|
return
|
|
}
|
|
|
|
// checkArg checks an argument comparable with the == operator. Avoid using this with pointers.
|
|
func checkArg[T comparable](k *kstub, arg string, got T, n int) bool {
|
|
if k.pos == 0 {
|
|
panic("invalid call to checkArg")
|
|
}
|
|
expect := k.want[k.pos-1]
|
|
want, ok := expect.args[n].(T)
|
|
if !ok || got != want {
|
|
k.t.Errorf("%s: %s = %#v, want %#v (%d)", expect.name, arg, got, want, k.pos-1)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// checkArgReflect checks an argument of any type.
|
|
func checkArgReflect(k *kstub, arg string, got any, n int) bool {
|
|
if k.pos == 0 {
|
|
panic("invalid call to checkArgReflect")
|
|
}
|
|
expect := k.want[k.pos-1]
|
|
want := expect.args[n]
|
|
if !reflect.DeepEqual(got, want) {
|
|
k.t.Errorf("%s: %s = %#v, want %#v (%d)", expect.name, arg, got, want, k.pos-1)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (k *kstub) lockOSThread() { k.expect("lockOSThread") }
|
|
|
|
func (k *kstub) setPtracer(pid uintptr) error {
|
|
return k.expect("setPtracer").error(
|
|
checkArg(k, "pid", pid, 0))
|
|
}
|
|
|
|
func (k *kstub) setDumpable(dumpable uintptr) error {
|
|
return k.expect("setDumpable").error(
|
|
checkArg(k, "dumpable", dumpable, 0))
|
|
}
|
|
|
|
func (k *kstub) setNoNewPrivs() error { return k.expect("setNoNewPrivs").err }
|
|
func (k *kstub) lastcap() uintptr { return k.expect("setNoNewPrivs").ret.(uintptr) }
|
|
|
|
func (k *kstub) capset(hdrp *capHeader, datap *[2]capData) error {
|
|
return k.expect("capset").error(
|
|
checkArgReflect(k, "hdrp", hdrp, 0),
|
|
checkArgReflect(k, "datap", datap, 1))
|
|
}
|
|
|
|
func (k *kstub) capBoundingSetDrop(cap uintptr) error {
|
|
return k.expect("capBoundingSetDrop").error(
|
|
checkArg(k, "cap", cap, 0))
|
|
}
|
|
|
|
func (k *kstub) capAmbientClearAll() error { return k.expect("capAmbientClearAll").err }
|
|
|
|
func (k *kstub) capAmbientRaise(cap uintptr) error {
|
|
return k.expect("capAmbientRaise").error(
|
|
checkArg(k, "cap", cap, 0))
|
|
}
|
|
|
|
func (k *kstub) isatty(fd int) bool {
|
|
expect := k.expect("isatty")
|
|
if !checkArg(k, "fd", fd, 0) {
|
|
k.t.FailNow()
|
|
}
|
|
return expect.ret.(bool)
|
|
}
|
|
|
|
func (k *kstub) receive(key string, e any, fdp *uintptr) (closeFunc func() error, err error) {
|
|
expect := k.expect("receive")
|
|
return expect.ret.(func() error), expect.error(
|
|
checkArg(k, "key", key, 0),
|
|
checkArgReflect(k, "e", e, 1),
|
|
checkArg(k, "fdp", fdp, 2))
|
|
}
|
|
|
|
func (k *kstub) bindMount(source, target string, flags uintptr, eq bool) error {
|
|
return k.expect("bindMount").error(
|
|
checkArg(k, "source", source, 0),
|
|
checkArg(k, "target", target, 1),
|
|
checkArg(k, "flags", flags, 2),
|
|
checkArg(k, "eq", eq, 3))
|
|
}
|
|
|
|
func (k *kstub) remount(target string, flags uintptr) error {
|
|
return k.expect("remount").error(
|
|
checkArg(k, "target", target, 0),
|
|
checkArg(k, "flags", flags, 1))
|
|
}
|
|
|
|
func (k *kstub) mountTmpfs(fsname, target string, flags uintptr, size int, perm os.FileMode) error {
|
|
return k.expect("mountTmpfs").error(
|
|
checkArg(k, "fsname", fsname, 0),
|
|
checkArg(k, "target", target, 1),
|
|
checkArg(k, "flags", flags, 2),
|
|
checkArg(k, "size", size, 3),
|
|
checkArg(k, "perm", perm, 4))
|
|
}
|
|
|
|
func (k *kstub) ensureFile(name string, perm, pperm os.FileMode) error {
|
|
|
|
return k.expect("ensureFile").error(
|
|
checkArg(k, "name", name, 0),
|
|
checkArg(k, "perm", perm, 1),
|
|
checkArg(k, "pperm", pperm, 2))
|
|
}
|
|
|
|
func (k *kstub) seccompLoad(rules []seccomp.NativeRule, flags seccomp.ExportFlag) error {
|
|
return k.expect("seccompLoad").error(
|
|
checkArgReflect(k, "rules", rules, 0),
|
|
checkArg(k, "flags", flags, 1))
|
|
}
|
|
|
|
func (k *kstub) notify(c chan<- os.Signal, sig ...os.Signal) {
|
|
expect := k.expect("notify")
|
|
if c == nil || expect.error(
|
|
checkArgReflect(k, "sig", sig, 1)) != nil {
|
|
k.t.FailNow()
|
|
}
|
|
|
|
// export channel for external instrumentation
|
|
if chanp, ok := expect.args[0].(*chan<- os.Signal); ok && chanp != nil {
|
|
if *chanp != nil {
|
|
panic(fmt.Sprintf("attempting reuse of %p", chanp))
|
|
}
|
|
*chanp = c
|
|
}
|
|
}
|
|
|
|
func (k *kstub) start(c *exec.Cmd) error {
|
|
return k.expect("start").error(
|
|
checkArg(k, "c.Path", c.Path, 0),
|
|
checkArgReflect(k, "c.Args", c.Args, 1),
|
|
checkArgReflect(k, "c.Env", c.Env, 2),
|
|
checkArg(k, "c.Dir", c.Dir, 3))
|
|
}
|
|
|
|
func (k *kstub) signal(c *exec.Cmd, sig os.Signal) error {
|
|
return k.expect("signal").error(
|
|
checkArg(k, "c.Path", c.Path, 0),
|
|
checkArgReflect(k, "c.Args", c.Args, 1),
|
|
checkArgReflect(k, "c.Env", c.Env, 2),
|
|
checkArg(k, "c.Dir", c.Dir, 3),
|
|
checkArg(k, "sig", sig, 4))
|
|
}
|
|
|
|
func (k *kstub) evalSymlinks(path string) (string, error) {
|
|
expect := k.expect("evalSymlinks")
|
|
return expect.ret.(string), expect.error(
|
|
checkArg(k, "path", path, 0))
|
|
}
|
|
|
|
func (k *kstub) exit(code int) {
|
|
k.expect("exit")
|
|
if !checkArg(k, "code", code, 0) {
|
|
k.t.FailNow()
|
|
}
|
|
}
|
|
|
|
func (k *kstub) getpid() int { return k.expect("getpid").ret.(int) }
|
|
|
|
func (k *kstub) stat(name string) (os.FileInfo, error) {
|
|
expect := k.expect("stat")
|
|
return expect.ret.(os.FileInfo), expect.error(
|
|
checkArg(k, "name", name, 0))
|
|
}
|
|
|
|
func (k *kstub) mkdir(name string, perm os.FileMode) error {
|
|
return k.expect("mkdir").error(
|
|
checkArg(k, "name", name, 0),
|
|
checkArg(k, "perm", perm, 1))
|
|
}
|
|
|
|
func (k *kstub) mkdirTemp(dir, pattern string) (string, error) {
|
|
expect := k.expect("mkdirTemp")
|
|
return expect.ret.(string), expect.error(
|
|
checkArg(k, "dir", dir, 0),
|
|
checkArg(k, "pattern", pattern, 1))
|
|
}
|
|
|
|
func (k *kstub) mkdirAll(path string, perm os.FileMode) error {
|
|
return k.expect("mkdirAll").error(
|
|
checkArg(k, "path", path, 0),
|
|
checkArg(k, "perm", perm, 1))
|
|
}
|
|
|
|
func (k *kstub) readdir(name string) ([]os.DirEntry, error) {
|
|
expect := k.expect("readdir")
|
|
return expect.ret.([]os.DirEntry), expect.error(
|
|
checkArg(k, "name", name, 0))
|
|
}
|
|
|
|
func (k *kstub) openNew(name string) (osFile, error) {
|
|
expect := k.expect("openNew")
|
|
return expect.ret.(osFile), expect.error(
|
|
checkArg(k, "name", name, 0))
|
|
}
|
|
|
|
func (k *kstub) writeFile(name string, data []byte, perm os.FileMode) error {
|
|
return k.expect("writeFile").error(
|
|
checkArg(k, "name", name, 0),
|
|
checkArgReflect(k, "data", data, 1),
|
|
checkArg(k, "perm", perm, 2))
|
|
}
|
|
|
|
func (k *kstub) createTemp(dir, pattern string) (osFile, error) {
|
|
expect := k.expect("createTemp")
|
|
return expect.ret.(osFile), expect.error(
|
|
checkArg(k, "dir", dir, 0),
|
|
checkArg(k, "pattern", pattern, 1))
|
|
}
|
|
|
|
func (k *kstub) remove(name string) error {
|
|
return k.expect("remove").error(
|
|
checkArg(k, "name", name, 0))
|
|
}
|
|
|
|
func (k *kstub) newFile(fd uintptr, name string) *os.File {
|
|
expect := k.expect("newFile")
|
|
if expect.error(
|
|
checkArg(k, "fd", fd, 0),
|
|
checkArg(k, "name", name, 1)) != nil {
|
|
k.t.FailNow()
|
|
}
|
|
return expect.ret.(*os.File)
|
|
}
|
|
|
|
func (k *kstub) symlink(oldname, newname string) error {
|
|
return k.expect("symlink").error(
|
|
checkArg(k, "oldname", oldname, 0),
|
|
checkArg(k, "newname", newname, 1))
|
|
}
|
|
|
|
func (k *kstub) readlink(name string) (string, error) {
|
|
expect := k.expect("readlink")
|
|
return expect.ret.(string), expect.error(
|
|
checkArg(k, "name", name, 0))
|
|
}
|
|
|
|
func (k *kstub) umask(mask int) (oldmask int) {
|
|
expect := k.expect("umask")
|
|
if !checkArg(k, "mask", mask, 0) {
|
|
k.t.FailNow()
|
|
}
|
|
return expect.ret.(int)
|
|
}
|
|
|
|
func (k *kstub) sethostname(p []byte) (err error) {
|
|
return k.expect("sethostname").error(
|
|
checkArgReflect(k, "p", p, 0))
|
|
}
|
|
|
|
func (k *kstub) chdir(path string) (err error) {
|
|
return k.expect("chdir").error(
|
|
checkArg(k, "path", path, 0))
|
|
}
|
|
|
|
func (k *kstub) fchdir(fd int) (err error) {
|
|
return k.expect("fchdir").error(
|
|
checkArg(k, "fd", fd, 0))
|
|
}
|
|
|
|
func (k *kstub) open(path string, mode int, perm uint32) (fd int, err error) {
|
|
expect := k.expect("open")
|
|
return expect.ret.(int), expect.error(
|
|
checkArg(k, "path", path, 0),
|
|
checkArg(k, "mode", mode, 1),
|
|
checkArg(k, "perm", perm, 2))
|
|
}
|
|
|
|
func (k *kstub) close(fd int) (err error) {
|
|
return k.expect("close").error(
|
|
checkArg(k, "fd", fd, 0))
|
|
}
|
|
|
|
func (k *kstub) pivotRoot(newroot, putold string) (err error) {
|
|
return k.expect("pivotRoot").error(
|
|
checkArg(k, "newroot", newroot, 0),
|
|
checkArg(k, "putold", putold, 1))
|
|
}
|
|
|
|
func (k *kstub) mount(source, target, fstype string, flags uintptr, data string) (err error) {
|
|
return k.expect("mount").error(
|
|
checkArg(k, "source", source, 0),
|
|
checkArg(k, "target", target, 1),
|
|
checkArg(k, "fstype", fstype, 2),
|
|
checkArg(k, "flags", flags, 3),
|
|
checkArg(k, "data", data, 4))
|
|
}
|
|
|
|
func (k *kstub) unmount(target string, flags int) (err error) {
|
|
return k.expect("unmount").error(
|
|
checkArg(k, "target", target, 0),
|
|
checkArg(k, "flags", flags, 1))
|
|
}
|
|
|
|
func (k *kstub) wait4(pid int, wstatus *syscall.WaitStatus, options int, rusage *syscall.Rusage) (wpid int, err error) {
|
|
expect := k.expect("wait4")
|
|
return expect.ret.(int), expect.error(
|
|
checkArg(k, "pid", pid, 0),
|
|
checkArg(k, "wstatus", wstatus, 1),
|
|
checkArg(k, "options", options, 2),
|
|
checkArg(k, "rusage", rusage, 3))
|
|
}
|
|
|
|
func (k *kstub) printf(format string, v ...any) {
|
|
if k.expect("printf").error(
|
|
checkArg(k, "format", format, 0),
|
|
checkArgReflect(k, "v", v, 1)) != nil {
|
|
k.t.FailNow()
|
|
}
|
|
}
|
|
|
|
func (k *kstub) fatal(v ...any) {
|
|
if k.expect("fatal").error(
|
|
checkArgReflect(k, "v", v, 0)) != nil {
|
|
k.t.FailNow()
|
|
}
|
|
}
|
|
|
|
func (k *kstub) fatalf(format string, v ...any) {
|
|
if k.expect("fatalf").error(
|
|
checkArg(k, "format", format, 0),
|
|
checkArgReflect(k, "v", v, 1)) != nil {
|
|
k.t.FailNow()
|
|
}
|
|
}
|
|
|
|
func (k *kstub) verbose(v ...any) {
|
|
if k.expect("verbose").error(
|
|
checkArgReflect(k, "v", v, 0)) != nil {
|
|
k.t.FailNow()
|
|
}
|
|
}
|
|
|
|
func (k *kstub) verbosef(format string, v ...any) {
|
|
if k.expect("verbosef").error(
|
|
checkArg(k, "format", format, 0),
|
|
checkArgReflect(k, "v", v, 1)) != nil {
|
|
k.t.FailNow()
|
|
}
|
|
}
|
|
|
|
func (k *kstub) suspend() { k.expect("suspend") }
|
|
func (k *kstub) resume() bool { return k.expect("resume").ret.(bool) }
|
|
func (k *kstub) beforeExit() { k.expect("beforeExit") }
|
|
|
|
func (k *kstub) printBaseErr(err error, fallback string) {
|
|
if k.expect("printBaseErr").error(
|
|
checkArgReflect(k, "err", err, 0),
|
|
checkArg(k, "fallback", fallback, 1)) != nil {
|
|
k.t.FailNow()
|
|
}
|
|
}
|