container/dispatcher: instrument each goroutine individually
All checks were successful
Test / Create distribution (push) Successful in 35s
Test / Sandbox (push) Successful in 44s
Test / Hakurei (push) Successful in 2m33s
Test / Sandbox (race detector) (push) Successful in 2m35s
Test / Hakurei (race detector) (push) Successful in 3m25s
Test / Hpkg (push) Successful in 3m41s
Test / Flake checks (push) Successful in 1m30s
All checks were successful
Test / Create distribution (push) Successful in 35s
Test / Sandbox (push) Successful in 44s
Test / Hakurei (push) Successful in 2m33s
Test / Sandbox (race detector) (push) Successful in 2m35s
Test / Hakurei (race detector) (push) Successful in 3m25s
Test / Hpkg (push) Successful in 3m41s
Test / Flake checks (push) Successful in 1m30s
Scheduler nondeterminism cannot be accounted for, so do this instead. There should not be any performance penalty as these calls are optimised out for direct. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -106,7 +106,7 @@ func checkOpMeta(t *testing.T, testCases []opMetaTestCase) {
|
||||
type simpleTestCase struct {
|
||||
name string
|
||||
f func(k syscallDispatcher) error
|
||||
want []kexpect
|
||||
want [][]kexpect
|
||||
wantErr error
|
||||
}
|
||||
|
||||
@@ -117,9 +117,9 @@ func checkSimple(t *testing.T, fname string, testCases []simpleTestCase) {
|
||||
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))
|
||||
}
|
||||
k.handleIncomplete(func(k *kstub) {
|
||||
t.Errorf("%s: %d calls, want %d (track %d)", fname, k.pos, len(k.want[k.track]), k.track)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -141,7 +141,7 @@ func checkOpBehaviour(t *testing.T, testCases []opBehaviourTestCase) {
|
||||
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)}
|
||||
k := &kstub{t: t, want: [][]kexpect{slices.Concat(tc.early, []kexpect{{name: "\x00"}}, tc.apply)}}
|
||||
errEarly := tc.op.early(state, k)
|
||||
k.expect("\x00")
|
||||
if !errors.Is(errEarly, tc.wantErrEarly) {
|
||||
@@ -156,14 +156,14 @@ func checkOpBehaviour(t *testing.T, testCases []opBehaviourTestCase) {
|
||||
}
|
||||
|
||||
out:
|
||||
if len(k.want) != k.pos {
|
||||
k.handleIncomplete(func(k *kstub) {
|
||||
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))
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -263,17 +263,33 @@ func (k *kexpect) error(ok ...bool) error {
|
||||
}
|
||||
|
||||
type kstub struct {
|
||||
t *testing.T
|
||||
want []kexpect
|
||||
pos int
|
||||
t *testing.T
|
||||
|
||||
want [][]kexpect
|
||||
// pos is the current position in want[track].
|
||||
pos int
|
||||
// track is the current active want.
|
||||
track int
|
||||
// sub stores addresses of kstub created by new.
|
||||
sub []*kstub
|
||||
}
|
||||
|
||||
// handleIncomplete calls f on an incomplete k and all its descendants.
|
||||
func (k *kstub) handleIncomplete(f func(k *kstub)) {
|
||||
if k.want != nil && len(k.want[k.track]) != k.pos {
|
||||
f(k)
|
||||
}
|
||||
for _, sk := range k.sub {
|
||||
sk.handleIncomplete(f)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if len(k.want[k.track]) == k.pos {
|
||||
k.t.Fatal("expect: want too short")
|
||||
}
|
||||
expect = &k.want[k.pos]
|
||||
expect = &k.want[k.track][k.pos]
|
||||
if name != expect.name {
|
||||
if expect.name == "\x00" {
|
||||
k.t.Fatalf("expect: func = %s, separator overrun", name)
|
||||
@@ -292,7 +308,7 @@ 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]
|
||||
expect := k.want[k.track][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)
|
||||
@@ -306,7 +322,7 @@ 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]
|
||||
expect := k.want[k.track][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)
|
||||
@@ -315,6 +331,15 @@ func checkArgReflect(k *kstub, arg string, got any, n int) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (k *kstub) new() syscallDispatcher {
|
||||
k.expect("new")
|
||||
if len(k.want) <= k.track+1 {
|
||||
k.t.Fatalf("new: track overrun")
|
||||
}
|
||||
k.sub = append(k.sub, &kstub{t: k.t, want: k.want, track: k.track + 1})
|
||||
return k.sub[len(k.sub)-1]
|
||||
}
|
||||
|
||||
func (k *kstub) lockOSThread() { k.expect("lockOSThread") }
|
||||
|
||||
func (k *kstub) setPtracer(pid uintptr) error {
|
||||
@@ -328,7 +353,7 @@ func (k *kstub) setDumpable(dumpable uintptr) error {
|
||||
}
|
||||
|
||||
func (k *kstub) setNoNewPrivs() error { return k.expect("setNoNewPrivs").err }
|
||||
func (k *kstub) lastcap() uintptr { return k.expect("setNoNewPrivs").ret.(uintptr) }
|
||||
func (k *kstub) lastcap() uintptr { return k.expect("lastcap").ret.(uintptr) }
|
||||
|
||||
func (k *kstub) capset(hdrp *capHeader, datap *[2]capData) error {
|
||||
return k.expect("capset").error(
|
||||
|
||||
Reference in New Issue
Block a user