mirror of https://github.com/golang/go.git
testing: add Context
Adds a new Context method to testing.T, that returns a context, that is canceled before the end of its test function. Don't inherit parent's text context. Add release notes. Fixes #36532.
This commit is contained in:
parent
27093581b2
commit
1c3fd6c4d8
|
|
@ -0,0 +1,4 @@
|
||||||
|
pkg testing, method (*B) Context() context.Context #36532
|
||||||
|
pkg testing, method (*F) Context() context.Context #36532
|
||||||
|
pkg testing, method (*T) Context() context.Context #36532
|
||||||
|
pkg testing, type TB interface, Context() context.Context #36532
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
The new [T.Context] and [B.Context] methods return a context that's canceled
|
||||||
|
before the end of its associated test or benchmark function.
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
The new [T.Chdir] and [B.Chdir] methods can be used to change the working
|
The new [T.Context] and [B.Context] methods return a context that is canceled
|
||||||
directory for the duration of a test or benchmark.
|
after the test completes and before test cleanup functions run.
|
||||||
|
|
|
||||||
|
|
@ -371,6 +371,7 @@ package testing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -633,6 +634,9 @@ type common struct {
|
||||||
tempDir string
|
tempDir string
|
||||||
tempDirErr error
|
tempDirErr error
|
||||||
tempDirSeq int32
|
tempDirSeq int32
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
cancelCtx context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Short reports whether the -test.short flag is set.
|
// Short reports whether the -test.short flag is set.
|
||||||
|
|
@ -898,6 +902,7 @@ type TB interface {
|
||||||
Skipf(format string, args ...any)
|
Skipf(format string, args ...any)
|
||||||
Skipped() bool
|
Skipped() bool
|
||||||
TempDir() string
|
TempDir() string
|
||||||
|
Context() context.Context
|
||||||
|
|
||||||
// A private method to prevent users implementing the
|
// A private method to prevent users implementing the
|
||||||
// interface and so future additions to it will not
|
// interface and so future additions to it will not
|
||||||
|
|
@ -1351,6 +1356,16 @@ func (c *common) Chdir(dir string) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.
|
// panicHandling controls the panic handling used by runCleanup.
|
||||||
type panicHandling int
|
type panicHandling int
|
||||||
|
|
||||||
|
|
@ -1383,6 +1398,10 @@ func (c *common) runCleanup(ph panicHandling) (panicVal any) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if c.cancelCtx != nil {
|
||||||
|
c.cancelCtx()
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var cleanup func()
|
var cleanup func()
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
|
|
@ -1771,15 +1790,21 @@ func (t *T) Run(name string, f func(t *T)) bool {
|
||||||
// continue walking the stack into the parent test.
|
// continue walking the stack into the parent test.
|
||||||
var pc [maxStackLen]uintptr
|
var pc [maxStackLen]uintptr
|
||||||
n := runtime.Callers(2, pc[:])
|
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{
|
t = &T{
|
||||||
common: common{
|
common: common{
|
||||||
barrier: make(chan bool),
|
barrier: make(chan bool),
|
||||||
signal: make(chan bool, 1),
|
signal: make(chan bool, 1),
|
||||||
name: testName,
|
name: testName,
|
||||||
parent: &t.common,
|
parent: &t.common,
|
||||||
level: t.level + 1,
|
level: t.level + 1,
|
||||||
creator: pc[:n],
|
creator: pc[:n],
|
||||||
chatty: t.chatty,
|
chatty: t.chatty,
|
||||||
|
ctx: ctx,
|
||||||
|
cancelCtx: cancelCtx,
|
||||||
},
|
},
|
||||||
context: t.context,
|
context: t.context,
|
||||||
}
|
}
|
||||||
|
|
@ -2205,15 +2230,18 @@ func runTests(matchString func(pat, str string) (bool, error), tests []InternalT
|
||||||
// to keep trying.
|
// to keep trying.
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
ctx := newTestContext(*parallel, newMatcher(matchString, *match, "-test.run", *skip))
|
ctx, cancelCtx := context.WithCancel(context.Background())
|
||||||
ctx.deadline = deadline
|
tctx := newTestContext(*parallel, newMatcher(matchString, *match, "-test.run", *skip))
|
||||||
|
tctx.deadline = deadline
|
||||||
t := &T{
|
t := &T{
|
||||||
common: common{
|
common: common{
|
||||||
signal: make(chan bool, 1),
|
signal: make(chan bool, 1),
|
||||||
barrier: make(chan bool),
|
barrier: make(chan bool),
|
||||||
w: os.Stdout,
|
w: os.Stdout,
|
||||||
|
ctx: ctx,
|
||||||
|
cancelCtx: cancelCtx,
|
||||||
},
|
},
|
||||||
context: ctx,
|
context: tctx,
|
||||||
}
|
}
|
||||||
if Verbose() {
|
if Verbose() {
|
||||||
t.chatty = newChattyPrinter(t.w)
|
t.chatty = newChattyPrinter(t.w)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ package testing_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"internal/race"
|
"internal/race"
|
||||||
"internal/testenv"
|
"internal/testenv"
|
||||||
|
|
@ -918,3 +920,29 @@ func TestParentRun(t1 *testing.T) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue