import (
"bytes"
+ "context"
"errors"
"flag"
"fmt"
tempDir string
tempDirErr error
tempDirSeq int32
+
+ ctx context.Context
+ cancelCtx context.CancelFunc
}
// Short reports whether the -test.short flag is set.
Skipf(format string, args ...any)
Skipped() bool
TempDir() string
+ Context() context.Context
// A private method to prevent users implementing the
// interface and so future additions to it will not
})
}
+// Context returns a context that is canceled just before
+// [T.Cleanup]-registered functions are called.
+//
+// Cleanup functions can wait for any resources
+// that shut down on Context.Done before the test completes.
+func (c *common) Context() context.Context {
+ c.checkFuzzFn("Context")
+ return c.ctx
+}
+
// panicHandling controls the panic handling used by runCleanup.
type panicHandling int
}
}()
+ if c.cancelCtx != nil {
+ c.cancelCtx()
+ }
+
for {
var cleanup func()
c.mu.Lock()
// continue walking the stack into the parent test.
var pc [maxStackLen]uintptr
n := runtime.Callers(2, pc[:])
+
+ // There's no reason to inherit this context from parent. The user's code can't observe
+ // the difference between the background context and the one from the parent test.
+ ctx, cancelCtx := context.WithCancel(context.Background())
t = &T{
common: common{
- barrier: make(chan bool),
- signal: make(chan bool, 1),
- name: testName,
- parent: &t.common,
- level: t.level + 1,
- creator: pc[:n],
- chatty: t.chatty,
+ barrier: make(chan bool),
+ signal: make(chan bool, 1),
+ name: testName,
+ parent: &t.common,
+ level: t.level + 1,
+ creator: pc[:n],
+ chatty: t.chatty,
+ ctx: ctx,
+ cancelCtx: cancelCtx,
},
context: t.context,
}
// to keep trying.
break
}
- ctx := newTestContext(*parallel, newMatcher(matchString, *match, "-test.run", *skip))
- ctx.deadline = deadline
+ ctx, cancelCtx := context.WithCancel(context.Background())
+ tctx := newTestContext(*parallel, newMatcher(matchString, *match, "-test.run", *skip))
+ tctx.deadline = deadline
t := &T{
common: common{
- signal: make(chan bool, 1),
- barrier: make(chan bool),
- w: os.Stdout,
+ signal: make(chan bool, 1),
+ barrier: make(chan bool),
+ w: os.Stdout,
+ ctx: ctx,
+ cancelCtx: cancelCtx,
},
- context: ctx,
+ context: tctx,
}
if Verbose() {
t.chatty = newChattyPrinter(t.w)
import (
"bytes"
+ "context"
+ "errors"
"fmt"
"internal/race"
"internal/testenv"
})
})
}
+
+func TestContext(t *testing.T) {
+ ctx := t.Context()
+ if err := ctx.Err(); err != nil {
+ t.Fatalf("expected non-canceled context, got %v", err)
+ }
+
+ var innerCtx context.Context
+ t.Run("inner", func(t *testing.T) {
+ innerCtx = t.Context()
+ if err := innerCtx.Err(); err != nil {
+ t.Fatalf("expected inner test to not inherit canceled context, got %v", err)
+ }
+ })
+ t.Run("inner2", func(t *testing.T) {
+ if !errors.Is(innerCtx.Err(), context.Canceled) {
+ t.Fatal("expected context of sibling test to be canceled after its test function finished")
+ }
+ })
+
+ t.Cleanup(func() {
+ if !errors.Is(ctx.Err(), context.Canceled) {
+ t.Fatal("expected context canceled before cleanup")
+ }
+ })
+}