aboutsummaryrefslogtreecommitdiffhomepage
path: root/container
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-03-17 16:09:14 +0900
committerOphestra <cat@gensokyo.uk>2026-03-17 16:09:14 +0900
commitbac583f89edd1360150143ff51f7264c01aa69ec (patch)
tree47914efe1ae9fae256ef93725097bdb9255c03d5 /container
parent722989c68256469963aee963bed9c6ab4bf55b67 (diff)
internal/stub: move from container
This package solves a very specific stubbing use case, in a less than elegant manner. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'container')
-rw-r--r--container/autoetc_test.go2
-rw-r--r--container/autoroot_test.go2
-rw-r--r--container/dispatcher_test.go2
-rw-r--r--container/errors_test.go2
-rw-r--r--container/init_test.go2
-rw-r--r--container/initbind_test.go2
-rw-r--r--container/initdaemon_test.go2
-rw-r--r--container/initdev_test.go2
-rw-r--r--container/initmkdir_test.go2
-rw-r--r--container/initoverlay_test.go2
-rw-r--r--container/initplace_test.go2
-rw-r--r--container/initproc_test.go2
-rw-r--r--container/initremount_test.go2
-rw-r--r--container/initsymlink_test.go2
-rw-r--r--container/inittmpfs_test.go2
-rw-r--r--container/mount_test.go2
-rw-r--r--container/stub/call.go37
-rw-r--r--container/stub/call_test.go27
-rw-r--r--container/stub/errors.go25
-rw-r--r--container/stub/errors_test.go42
-rw-r--r--container/stub/exit.go44
-rw-r--r--container/stub/exit_test.go111
-rw-r--r--container/stub/stub.go153
-rw-r--r--container/stub/stub_test.go335
24 files changed, 16 insertions, 790 deletions
diff --git a/container/autoetc_test.go b/container/autoetc_test.go
index 232413b5..d76309fd 100644
--- a/container/autoetc_test.go
+++ b/container/autoetc_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"hakurei.app/check"
- "hakurei.app/container/stub"
+ "hakurei.app/internal/stub"
)
func TestAutoEtcOp(t *testing.T) {
diff --git a/container/autoroot_test.go b/container/autoroot_test.go
index 282a41fb..0692f743 100644
--- a/container/autoroot_test.go
+++ b/container/autoroot_test.go
@@ -7,7 +7,7 @@ import (
"hakurei.app/check"
"hakurei.app/container/std"
- "hakurei.app/container/stub"
+ "hakurei.app/internal/stub"
"hakurei.app/message"
)
diff --git a/container/dispatcher_test.go b/container/dispatcher_test.go
index fe0c4dbc..ee2c3f19 100644
--- a/container/dispatcher_test.go
+++ b/container/dispatcher_test.go
@@ -18,7 +18,7 @@ import (
"hakurei.app/container/seccomp"
"hakurei.app/container/std"
- "hakurei.app/container/stub"
+ "hakurei.app/internal/stub"
"hakurei.app/message"
)
diff --git a/container/errors_test.go b/container/errors_test.go
index 27dafbcd..1cede407 100644
--- a/container/errors_test.go
+++ b/container/errors_test.go
@@ -9,7 +9,7 @@ import (
"testing"
"hakurei.app/check"
- "hakurei.app/container/stub"
+ "hakurei.app/internal/stub"
"hakurei.app/vfs"
)
diff --git a/container/init_test.go b/container/init_test.go
index 4f067fd5..02531e99 100644
--- a/container/init_test.go
+++ b/container/init_test.go
@@ -10,7 +10,7 @@ import (
"hakurei.app/check"
"hakurei.app/container/seccomp"
"hakurei.app/container/std"
- "hakurei.app/container/stub"
+ "hakurei.app/internal/stub"
)
func TestInitEntrypoint(t *testing.T) {
diff --git a/container/initbind_test.go b/container/initbind_test.go
index 7d1cc36b..02438105 100644
--- a/container/initbind_test.go
+++ b/container/initbind_test.go
@@ -8,7 +8,7 @@ import (
"hakurei.app/check"
"hakurei.app/container/std"
- "hakurei.app/container/stub"
+ "hakurei.app/internal/stub"
)
func TestBindMountOp(t *testing.T) {
diff --git a/container/initdaemon_test.go b/container/initdaemon_test.go
index 262e6bde..0f75498e 100644
--- a/container/initdaemon_test.go
+++ b/container/initdaemon_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"hakurei.app/check"
- "hakurei.app/container/stub"
+ "hakurei.app/internal/stub"
"hakurei.app/message"
)
diff --git a/container/initdev_test.go b/container/initdev_test.go
index 0566e9af..bcb7c751 100644
--- a/container/initdev_test.go
+++ b/container/initdev_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"hakurei.app/check"
- "hakurei.app/container/stub"
+ "hakurei.app/internal/stub"
)
func TestMountDevOp(t *testing.T) {
diff --git a/container/initmkdir_test.go b/container/initmkdir_test.go
index 65da693d..e9d04c25 100644
--- a/container/initmkdir_test.go
+++ b/container/initmkdir_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"hakurei.app/check"
- "hakurei.app/container/stub"
+ "hakurei.app/internal/stub"
)
func TestMkdirOp(t *testing.T) {
diff --git a/container/initoverlay_test.go b/container/initoverlay_test.go
index 4047e4f6..30dd3237 100644
--- a/container/initoverlay_test.go
+++ b/container/initoverlay_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"hakurei.app/check"
- "hakurei.app/container/stub"
+ "hakurei.app/internal/stub"
)
func TestMountOverlayOp(t *testing.T) {
diff --git a/container/initplace_test.go b/container/initplace_test.go
index dd2f982e..0640400b 100644
--- a/container/initplace_test.go
+++ b/container/initplace_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"hakurei.app/check"
- "hakurei.app/container/stub"
+ "hakurei.app/internal/stub"
)
func TestTmpfileOp(t *testing.T) {
diff --git a/container/initproc_test.go b/container/initproc_test.go
index 51311f02..dd8d93ec 100644
--- a/container/initproc_test.go
+++ b/container/initproc_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"hakurei.app/check"
- "hakurei.app/container/stub"
+ "hakurei.app/internal/stub"
)
func TestMountProcOp(t *testing.T) {
diff --git a/container/initremount_test.go b/container/initremount_test.go
index 3855c18d..bb06e863 100644
--- a/container/initremount_test.go
+++ b/container/initremount_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"hakurei.app/check"
- "hakurei.app/container/stub"
+ "hakurei.app/internal/stub"
)
func TestRemountOp(t *testing.T) {
diff --git a/container/initsymlink_test.go b/container/initsymlink_test.go
index 3a4144d3..6dd065a5 100644
--- a/container/initsymlink_test.go
+++ b/container/initsymlink_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"hakurei.app/check"
- "hakurei.app/container/stub"
+ "hakurei.app/internal/stub"
)
func TestSymlinkOp(t *testing.T) {
diff --git a/container/inittmpfs_test.go b/container/inittmpfs_test.go
index e8a36602..b016c79d 100644
--- a/container/inittmpfs_test.go
+++ b/container/inittmpfs_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"hakurei.app/check"
- "hakurei.app/container/stub"
+ "hakurei.app/internal/stub"
)
func TestMountTmpfsOp(t *testing.T) {
diff --git a/container/mount_test.go b/container/mount_test.go
index ffacf18e..36dc5ad9 100644
--- a/container/mount_test.go
+++ b/container/mount_test.go
@@ -5,7 +5,7 @@ import (
"syscall"
"testing"
- "hakurei.app/container/stub"
+ "hakurei.app/internal/stub"
"hakurei.app/vfs"
)
diff --git a/container/stub/call.go b/container/stub/call.go
deleted file mode 100644
index 585a69ef..00000000
--- a/container/stub/call.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package stub
-
-import (
- "slices"
-)
-
-// ExpectArgs is an array primarily for storing expected function arguments.
-// Its actual use is defined by the implementation.
-type ExpectArgs = [5]any
-
-// An Expect stores expected calls of a goroutine.
-type Expect struct {
- Calls []Call
-
- // Tracks are handed out to descendant goroutines in order.
- Tracks []Expect
-}
-
-// A Call holds expected arguments of a function call and its outcome.
-type Call struct {
- // Name is the function Name of this call. Must be unique.
- Name string
- // Args are the expected arguments of this Call.
- Args ExpectArgs
- // Ret is the return value of this Call.
- Ret any
- // Err is the returned error of this Call.
- Err error
-}
-
-// Error returns [Call.Err] if all arguments are true, or [ErrCheck] otherwise.
-func (k *Call) Error(ok ...bool) error {
- if !slices.Contains(ok, false) {
- return k.Err
- }
- return ErrCheck
-}
diff --git a/container/stub/call_test.go b/container/stub/call_test.go
deleted file mode 100644
index db0b59dc..00000000
--- a/container/stub/call_test.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package stub_test
-
-import (
- "reflect"
- "testing"
-
- "hakurei.app/container/stub"
-)
-
-func TestCallError(t *testing.T) {
- t.Parallel()
-
- t.Run("contains false", func(t *testing.T) {
- t.Parallel()
- if err := new(stub.Call).Error(true, false, true); !reflect.DeepEqual(err, stub.ErrCheck) {
- t.Errorf("Error: %#v, want %#v", err, stub.ErrCheck)
- }
- })
-
- t.Run("passthrough", func(t *testing.T) {
- t.Parallel()
- wantErr := stub.UniqueError(0xbabe)
- if err := (&stub.Call{Err: wantErr}).Error(true); !reflect.DeepEqual(err, wantErr) {
- t.Errorf("Error: %#v, want %#v", err, wantErr)
- }
- })
-}
diff --git a/container/stub/errors.go b/container/stub/errors.go
deleted file mode 100644
index d97b6256..00000000
--- a/container/stub/errors.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package stub
-
-import (
- "errors"
- "strconv"
-)
-
-var (
- ErrCheck = errors.New("one or more arguments did not match")
-)
-
-// UniqueError is an error that only equivalates to other [UniqueError] with the same magic value.
-type UniqueError uintptr
-
-func (e UniqueError) Error() string {
- return "unique error " + strconv.FormatUint(uint64(e), 10) + " injected by the test suite"
-}
-
-func (e UniqueError) Is(target error) bool {
- var u UniqueError
- if !errors.As(target, &u) {
- return false
- }
- return e == u
-}
diff --git a/container/stub/errors_test.go b/container/stub/errors_test.go
deleted file mode 100644
index 604841ac..00000000
--- a/container/stub/errors_test.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package stub_test
-
-import (
- "errors"
- "syscall"
- "testing"
-
- "hakurei.app/container/stub"
-)
-
-func TestUniqueError(t *testing.T) {
- t.Parallel()
-
- t.Run("format", func(t *testing.T) {
- t.Parallel()
- want := "unique error 2989 injected by the test suite"
- if got := stub.UniqueError(0xbad).Error(); got != want {
- t.Errorf("Error: %q, want %q", got, want)
- }
- })
-
- t.Run("is", func(t *testing.T) {
- t.Parallel()
-
- t.Run("type", func(t *testing.T) {
- t.Parallel()
- if errors.Is(stub.UniqueError(0), syscall.ENOTRECOVERABLE) {
- t.Error("Is: unexpected true")
- }
- })
-
- t.Run("val", func(t *testing.T) {
- t.Parallel()
- if errors.Is(stub.UniqueError(0), stub.UniqueError(1)) {
- t.Error("Is: unexpected true")
- }
- if !errors.Is(stub.UniqueError(0xbad), stub.UniqueError(0xbad)) {
- t.Error("Is: unexpected false")
- }
- })
- })
-}
diff --git a/container/stub/exit.go b/container/stub/exit.go
deleted file mode 100644
index 062ee2e0..00000000
--- a/container/stub/exit.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package stub
-
-import "testing"
-
-// PanicExit is a magic panic value treated as a simulated exit.
-const PanicExit = 0xdead
-
-const (
- panicFailNow = 0xcafe0 + iota
- panicFatal
- panicFatalf
-)
-
-// HandleExit must be deferred before calling with the stub.
-func HandleExit(t testing.TB) {
- switch r := recover(); r {
- case PanicExit:
- break
-
- case panicFailNow:
- t.FailNow()
-
- case panicFatal, panicFatalf, nil:
- break
-
- default:
- panic(r)
- }
-}
-
-// handleExitNew handles exits from goroutines created by [Stub.New].
-func handleExitNew(t testing.TB) {
- switch r := recover(); r {
- case PanicExit, panicFatal, panicFatalf, nil:
- break
-
- case panicFailNow:
- t.Fail()
- break
-
- default:
- panic(r)
- }
-}
diff --git a/container/stub/exit_test.go b/container/stub/exit_test.go
deleted file mode 100644
index 3e71c580..00000000
--- a/container/stub/exit_test.go
+++ /dev/null
@@ -1,111 +0,0 @@
-package stub_test
-
-import (
- "testing"
- _ "unsafe" // for go:linkname
-
- "hakurei.app/container/stub"
-)
-
-// Made available here to check panic recovery behaviour.
-//
-//go:linkname handleExitNew hakurei.app/container/stub.handleExitNew
-func handleExitNew(t testing.TB)
-
-// overrideTFailNow overrides the Fail and FailNow method.
-type overrideTFailNow struct {
- *testing.T
- failNow bool
- fail bool
-}
-
-func (o *overrideTFailNow) FailNow() {
- if o.failNow {
- o.Errorf("attempted to FailNow twice")
- }
- o.failNow = true
-}
-
-func (o *overrideTFailNow) Fail() {
- if o.fail {
- o.Errorf("attempted to Fail twice")
- }
- o.fail = true
-}
-
-func TestHandleExit(t *testing.T) {
- t.Parallel()
-
- t.Run("exit", func(t *testing.T) {
- t.Parallel()
- defer stub.HandleExit(t)
- panic(stub.PanicExit)
- })
-
- t.Run("goexit", func(t *testing.T) {
- t.Parallel()
-
- t.Run("FailNow", func(t *testing.T) {
- t.Parallel()
-
- ot := &overrideTFailNow{T: t}
- defer func() {
- if !ot.failNow {
- t.Errorf("FailNow was never called")
- }
- }()
- defer stub.HandleExit(ot)
- panic(0xcafe0)
- })
-
- t.Run("Fail", func(t *testing.T) {
- t.Parallel()
-
- ot := &overrideTFailNow{T: t}
- defer func() {
- if !ot.fail {
- t.Errorf("Fail was never called")
- }
- }()
- defer handleExitNew(ot)
- panic(0xcafe0)
- })
- })
-
- t.Run("nil", func(t *testing.T) {
- t.Parallel()
- defer stub.HandleExit(t)
- })
-
- t.Run("passthrough", func(t *testing.T) {
- t.Parallel()
-
- t.Run("toplevel", func(t *testing.T) {
- t.Parallel()
-
- defer func() {
- want := 0xcafe
- if r := recover(); r != want {
- t.Errorf("recover: %v, want %v", r, want)
- }
-
- }()
- defer stub.HandleExit(t)
- panic(0xcafe)
- })
-
- t.Run("new", func(t *testing.T) {
- t.Parallel()
-
- defer func() {
- want := 0xcafe
- if r := recover(); r != want {
- t.Errorf("recover: %v, want %v", r, want)
- }
-
- }()
- defer handleExitNew(t)
- panic(0xcafe)
- })
- })
-}
diff --git a/container/stub/stub.go b/container/stub/stub.go
deleted file mode 100644
index a945fc0a..00000000
--- a/container/stub/stub.go
+++ /dev/null
@@ -1,153 +0,0 @@
-// Package stub provides function call level stubbing and validation
-// for library functions that are impossible to check otherwise.
-package stub
-
-import (
- "reflect"
- "sync"
- "testing"
-)
-
-// this should prevent stub from being inadvertently imported outside tests
-var _ = func() {
- if !testing.Testing() {
- panic("stub imported while not in a test")
- }
-}
-
-const (
- // A CallSeparator denotes an injected separation between two groups of calls.
- CallSeparator = "\x00"
-)
-
-// A Stub is a collection of tracks of expected calls.
-type Stub[K any] struct {
- testing.TB
-
- // makeK creates a new K for a descendant [Stub].
- // This function may be called concurrently.
- makeK func(s *Stub[K]) K
-
- // want is a hierarchy of expected calls.
- want Expect
- // pos is the current position in [Expect.Calls].
- pos int
- // goroutine counts the number of goroutines created by this [Stub].
- goroutine int
- // sub stores the addresses of descendant [Stub] created by New.
- sub []*Stub[K]
- // wg waits for all descendants to complete.
- wg *sync.WaitGroup
-}
-
-// New creates a root [Stub].
-func New[K any](tb testing.TB, makeK func(s *Stub[K]) K, want Expect) *Stub[K] {
- return &Stub[K]{TB: tb, makeK: makeK, want: want, wg: new(sync.WaitGroup)}
-}
-
-func (s *Stub[K]) FailNow() { s.Helper(); panic(panicFailNow) }
-func (s *Stub[K]) Fatal(args ...any) { s.Helper(); s.Error(args...); panic(panicFatal) }
-func (s *Stub[K]) Fatalf(format string, args ...any) {
- s.Helper()
- s.Errorf(format, args...)
- panic(panicFatalf)
-}
-
-func (s *Stub[K]) SkipNow() { s.Helper(); panic("invalid call to SkipNow") }
-func (s *Stub[K]) Skip(...any) { s.Helper(); panic("invalid call to Skip") }
-func (s *Stub[K]) Skipf(string, ...any) { s.Helper(); panic("invalid call to Skipf") }
-
-// New calls f in a new goroutine
-func (s *Stub[K]) New(f func(k K)) {
- s.Helper()
-
- s.Expects("New")
- if len(s.want.Tracks) <= s.goroutine {
- s.Fatal("New: track overrun")
- }
- ds := &Stub[K]{TB: s.TB, makeK: s.makeK, want: s.want.Tracks[s.goroutine], wg: s.wg}
- s.goroutine++
- s.sub = append(s.sub, ds)
- s.wg.Add(1)
- go func() {
- s.Helper()
-
- defer s.wg.Done()
- defer handleExitNew(s.TB)
- f(s.makeK(ds))
- }()
-}
-
-// Pos returns the current position of [Stub] in its [Expect.Calls]
-func (s *Stub[K]) Pos() int { return s.pos }
-
-// Len returns the length of [Expect.Calls].
-func (s *Stub[K]) Len() int { return len(s.want.Calls) }
-
-// VisitIncomplete calls f on an incomplete s and all its descendants.
-func (s *Stub[K]) VisitIncomplete(f func(s *Stub[K])) {
- s.Helper()
- s.wg.Wait()
-
- if s.want.Calls != nil && len(s.want.Calls) != s.pos {
- f(s)
- }
- for _, ds := range s.sub {
- ds.VisitIncomplete(f)
- }
-}
-
-// Expects checks the name of and returns the current [Call] and advances pos.
-func (s *Stub[K]) Expects(name string) (expect *Call) {
- s.Helper()
-
- if len(s.want.Calls) == s.pos {
- s.Fatal("Expects: advancing beyond expected calls")
- }
- expect = &s.want.Calls[s.pos]
- if name != expect.Name {
- if expect.Name == CallSeparator {
- s.Fatalf("Expects: func = %s, separator overrun", name)
- }
- if name == CallSeparator {
- s.Fatalf("Expects: separator, want %s", expect.Name)
- }
- s.Fatalf("Expects: func = %s, want %s", name, expect.Name)
- }
- s.pos++
- return
-}
-
-// CheckArg checks an argument comparable with the == operator. Avoid using this with pointers.
-func CheckArg[T comparable, K any](s *Stub[K], arg string, got T, n int) bool {
- s.Helper()
-
- pos := s.pos - 1
- if pos < 0 || pos >= len(s.want.Calls) {
- panic("invalid call to CheckArg")
- }
- expect := s.want.Calls[pos]
- want, ok := expect.Args[n].(T)
- if !ok || got != want {
- s.Errorf("%s: %s = %#v, want %#v (%d)", expect.Name, arg, got, want, pos)
- return false
- }
- return true
-}
-
-// CheckArgReflect checks an argument of any type.
-func CheckArgReflect[K any](s *Stub[K], arg string, got any, n int) bool {
- s.Helper()
-
- pos := s.pos - 1
- if pos < 0 || pos >= len(s.want.Calls) {
- panic("invalid call to CheckArgReflect")
- }
- expect := s.want.Calls[pos]
- want := expect.Args[n]
- if !reflect.DeepEqual(got, want) {
- s.Errorf("%s: %s = %#v, want %#v (%d)", expect.Name, arg, got, want, pos)
- return false
- }
- return true
-}
diff --git a/container/stub/stub_test.go b/container/stub/stub_test.go
deleted file mode 100644
index 9c9b0eb8..00000000
--- a/container/stub/stub_test.go
+++ /dev/null
@@ -1,335 +0,0 @@
-package stub
-
-import (
- "reflect"
- "sync/atomic"
- "testing"
-)
-
-// stubHolder embeds [Stub].
-type stubHolder struct{ *Stub[stubHolder] }
-
-// overrideT allows some methods of [testing.T] to be overridden.
-type overrideT struct {
- *testing.T
-
- error atomic.Pointer[func(args ...any)]
- errorf atomic.Pointer[func(format string, args ...any)]
-}
-
-func (t *overrideT) Error(args ...any) {
- fp := t.error.Load()
- if fp == nil || *fp == nil {
- t.T.Error(args...)
- return
- }
- (*fp)(args...)
-}
-
-func (t *overrideT) Errorf(format string, args ...any) {
- fp := t.errorf.Load()
- if fp == nil || *fp == nil {
- t.T.Errorf(format, args...)
- return
- }
- (*fp)(format, args...)
-}
-
-func TestStub(t *testing.T) {
- t.Parallel()
-
- t.Run("goexit", func(t *testing.T) {
- t.Parallel()
-
- t.Run("FailNow", func(t *testing.T) {
- t.Parallel()
-
- defer func() {
- if r := recover(); r != panicFailNow {
- t.Errorf("recover: %v", r)
- }
- }()
- stubHolder{&Stub[stubHolder]{TB: t}}.FailNow()
- })
-
- t.Run("SkipNow", func(t *testing.T) {
- t.Parallel()
-
- defer func() {
- want := "invalid call to SkipNow"
- if r := recover(); r != want {
- t.Errorf("recover: %v, want %v", r, want)
- }
- }()
- stubHolder{&Stub[stubHolder]{TB: t}}.SkipNow()
- })
-
- t.Run("Skip", func(t *testing.T) {
- t.Parallel()
-
- defer func() {
- want := "invalid call to Skip"
- if r := recover(); r != want {
- t.Errorf("recover: %v, want %v", r, want)
- }
- }()
- stubHolder{&Stub[stubHolder]{TB: t}}.Skip()
- })
-
- t.Run("Skipf", func(t *testing.T) {
- t.Parallel()
-
- defer func() {
- want := "invalid call to Skipf"
- if r := recover(); r != want {
- t.Errorf("recover: %v, want %v", r, want)
- }
- }()
- stubHolder{&Stub[stubHolder]{TB: t}}.Skipf("")
- })
- })
-
- t.Run("new", func(t *testing.T) {
- t.Parallel()
-
- t.Run("success", func(t *testing.T) {
- t.Parallel()
-
- s := New(t, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{Calls: []Call{
- {"New", ExpectArgs{}, nil, nil},
- }, Tracks: []Expect{{Calls: []Call{
- {"done", ExpectArgs{0xbabe}, nil, nil},
- }}}})
-
- s.New(func(k stubHolder) {
- expect := k.Expects("done")
- if expect.Name != "done" {
- t.Errorf("New: Name = %s, want done", expect.Name)
- }
- if expect.Args != (ExpectArgs{0xbabe}) {
- t.Errorf("New: Args = %#v", expect.Args)
- }
- if expect.Ret != nil {
- t.Errorf("New: Ret = %#v", expect.Ret)
- }
- if expect.Err != nil {
- t.Errorf("New: Err = %#v", expect.Err)
- }
- })
-
- if pos := s.Pos(); pos != 1 {
- t.Errorf("Pos: %d, want 1", pos)
- }
- if l := s.Len(); l != 1 {
- t.Errorf("Len: %d, want 1", l)
- }
-
- s.VisitIncomplete(func(s *Stub[stubHolder]) { panic("unreachable") })
- })
-
- t.Run("overrun", func(t *testing.T) {
- t.Parallel()
-
- ot := &overrideT{T: t}
- ot.error.Store(checkError(t, "New: track overrun"))
- s := New(ot, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{Calls: []Call{
- {"New", ExpectArgs{}, nil, nil},
- {"panic", ExpectArgs{"unreachable"}, nil, nil},
- }})
- func() { defer HandleExit(t); s.New(func(k stubHolder) { panic("unreachable") }) }()
-
- var visit int
- s.VisitIncomplete(func(s *Stub[stubHolder]) {
- visit++
- if visit > 1 {
- panic("unexpected visit count")
- }
-
- want := Call{"panic", ExpectArgs{"unreachable"}, nil, nil}
- if got := s.want.Calls[s.pos]; !reflect.DeepEqual(got, want) {
- t.Errorf("VisitIncomplete: %#v, want %#v", got, want)
- }
- })
- })
-
- t.Run("expects", func(t *testing.T) {
- t.Parallel()
-
- t.Run("overrun", func(t *testing.T) {
- t.Parallel()
-
- ot := &overrideT{T: t}
- ot.error.Store(checkError(t, "Expects: advancing beyond expected calls"))
- s := New(ot, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{})
- func() { defer HandleExit(t); s.Expects("unreachable") }()
- })
-
- t.Run("separator", func(t *testing.T) {
- t.Parallel()
-
- t.Run("overrun", func(t *testing.T) {
- t.Parallel()
-
- ot := &overrideT{T: t}
- ot.errorf.Store(checkErrorf(t, "Expects: func = %s, separator overrun", "meow"))
- s := New(ot, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{Calls: []Call{
- {CallSeparator, ExpectArgs{}, nil, nil},
- }})
- func() { defer HandleExit(t); s.Expects("meow") }()
- })
-
- t.Run("mismatch", func(t *testing.T) {
- t.Parallel()
-
- ot := &overrideT{T: t}
- ot.errorf.Store(checkErrorf(t, "Expects: separator, want %s", "panic"))
- s := New(ot, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{Calls: []Call{
- {"panic", ExpectArgs{}, nil, nil},
- }})
- func() { defer HandleExit(t); s.Expects(CallSeparator) }()
- })
- })
-
- t.Run("mismatch", func(t *testing.T) {
- t.Parallel()
-
- ot := &overrideT{T: t}
- ot.errorf.Store(checkErrorf(t, "Expects: func = %s, want %s", "meow", "nya"))
- s := New(ot, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{Calls: []Call{
- {"nya", ExpectArgs{}, nil, nil},
- }})
- func() { defer HandleExit(t); s.Expects("meow") }()
- })
- })
- })
-}
-
-func TestCheckArg(t *testing.T) {
- t.Run("oob negative", func(t *testing.T) {
- t.Parallel()
-
- defer func() {
- want := "invalid call to CheckArg"
- if r := recover(); r != want {
- t.Errorf("recover: %v, want %v", r, want)
- }
- }()
- s := New(t, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{})
- CheckArg(s, "unreachable", struct{}{}, 0)
- })
-
- ot := &overrideT{T: t}
- s := New(ot, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{Calls: []Call{
- {"panic", ExpectArgs{PanicExit}, nil, nil},
- {"meow", ExpectArgs{-1}, nil, nil},
- }})
-
- t.Run("match", func(t *testing.T) {
- s.Expects("panic")
- if !CheckArg(s, "v", PanicExit, 0) {
- t.Errorf("CheckArg: unexpected false")
- }
- })
-
- t.Run("mismatch", func(t *testing.T) {
- defer HandleExit(t)
- s.Expects("meow")
- ot.errorf.Store(checkErrorf(t, "%s: %s = %#v, want %#v (%d)", "meow", "time", 0, -1, 1))
- if CheckArg(s, "time", 0, 0) {
- t.Errorf("CheckArg: unexpected true")
- }
- })
-
- t.Run("oob", func(t *testing.T) {
- s.pos++
- defer func() {
- want := "invalid call to CheckArg"
- if r := recover(); r != want {
- t.Errorf("recover: %v, want %v", r, want)
- }
- }()
- CheckArg(s, "unreachable", struct{}{}, 0)
- })
-}
-
-func TestCheckArgReflect(t *testing.T) {
- t.Parallel()
-
- t.Run("oob lower", func(t *testing.T) {
- t.Parallel()
-
- defer func() {
- want := "invalid call to CheckArgReflect"
- if r := recover(); r != want {
- t.Errorf("recover: %v, want %v", r, want)
- }
- }()
- s := New(t, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{})
- CheckArgReflect(s, "unreachable", struct{}{}, 0)
- })
-
- ot := &overrideT{T: t}
- s := New(ot, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{Calls: []Call{
- {"panic", ExpectArgs{PanicExit}, nil, nil},
- {"meow", ExpectArgs{-1}, nil, nil},
- }})
- t.Run("match", func(t *testing.T) {
- s.Expects("panic")
- if !CheckArgReflect(s, "v", PanicExit, 0) {
- t.Errorf("CheckArgReflect: unexpected false")
- }
- })
- t.Run("mismatch", func(t *testing.T) {
- defer HandleExit(t)
- s.Expects("meow")
- ot.errorf.Store(checkErrorf(t, "%s: %s = %#v, want %#v (%d)", "meow", "time", 0, -1, 1))
- if CheckArgReflect(s, "time", 0, 0) {
- t.Errorf("CheckArgReflect: unexpected true")
- }
- })
- t.Run("oob", func(t *testing.T) {
- s.pos++
- defer func() {
- want := "invalid call to CheckArgReflect"
- if r := recover(); r != want {
- t.Errorf("recover: %v, want %v", r, want)
- }
- }()
- CheckArgReflect(s, "unreachable", struct{}{}, 0)
- })
-}
-
-func checkError(t *testing.T, wantArgs ...any) *func(args ...any) {
- var called bool
- f := func(args ...any) {
- if called {
- panic("invalid call to error")
- }
- called = true
-
- if !reflect.DeepEqual(args, wantArgs) {
- t.Errorf("Error: %#v, want %#v", args, wantArgs)
- }
- panic(PanicExit)
- }
- return &f
-}
-
-func checkErrorf(t *testing.T, wantFormat string, wantArgs ...any) *func(format string, args ...any) {
- var called bool
- f := func(format string, args ...any) {
- if called {
- panic("invalid call to errorf")
- }
- called = true
-
- if format != wantFormat {
- t.Errorf("Errorf: format = %q, want %q", format, wantFormat)
- }
- if !reflect.DeepEqual(args, wantArgs) {
- t.Errorf("Errorf: args = %#v, want %#v", args, wantArgs)
- }
- panic(PanicExit)
- }
- return &f
-}