internal/http3: use bubbled context in synctest tests

Avoid using T.Context within a synctest bubble. If the Context's
Done channel is created outside the bubble, waiting on it within
the bubble is durably blocking. If it's created within the bubble,
the testing package encounters a panic when closing it after
CL 671960.

Instead, create our own Context within the bubble and cancel it
before the bubble is destroyed.

This will be entirely obviated by synctest.Test, which creates
a testing.T that returns a properly bubbled context.

Change-Id: Iff93c296ccbc1ece8172cb0a60e626ea1bd895ad
Reviewed-on: https://go-review.googlesource.com/c/net/+/675615
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Damien Neil <dneil@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
This commit is contained in:
Damien Neil
2025-05-22 09:29:25 -07:00
committed by Gopher Robot
parent 919c6bc7ad
commit ef33bc08f8

View File

@@ -7,6 +7,7 @@
package http3
import (
"context"
"slices"
"testing"
"testing/synctest"
@@ -14,9 +15,16 @@ import (
// runSynctest runs f in a synctest.Run bubble.
// It arranges for t.Cleanup functions to run within the bubble.
// TODO: Replace with synctest.Test, which handles all this properly.
func runSynctest(t *testing.T, f func(t testing.TB)) {
synctest.Run(func() {
ct := &cleanupT{T: t}
// Create a context within the bubble, rather than using t.Context.
ctx, cancel := context.WithCancel(context.Background())
ct := &cleanupT{
T: t,
ctx: ctx,
cancel: cancel,
}
defer ct.done()
f(ct)
})
@@ -33,6 +41,8 @@ func runSynctestSubtest(t *testing.T, name string, f func(t testing.TB)) {
// Used to execute cleanup functions within a synctest bubble.
type cleanupT struct {
*testing.T
ctx context.Context
cancel context.CancelFunc
cleanups []func()
}
@@ -41,7 +51,13 @@ func (t *cleanupT) Cleanup(f func()) {
t.cleanups = append(t.cleanups, f)
}
// Context replaces T.Context.
func (t *cleanupT) Context() context.Context {
return t.ctx
}
func (t *cleanupT) done() {
t.cancel()
for _, f := range slices.Backward(t.cleanups) {
f()
}