ktesting: support cancellation after WithoutCancel

Not canceling the parent context made sense, but the new context should
be cancelable like any other TContext. Found when passing tCtx.WithoutCancel()
to StartTestServer and the tear-down function got stuck because it couldn't
cancel the context.
This commit is contained in:
Patrick Ohly
2026-03-12 10:10:51 +01:00
parent e04d25c222
commit 3b63fe83a0
2 changed files with 40 additions and 2 deletions

View File

@@ -23,6 +23,7 @@ import (
"testing/synctest"
"time"
"github.com/onsi/gomega"
"github.com/stretchr/testify/assert"
)
@@ -132,3 +133,40 @@ func TestCause(t *testing.T) {
})
}
}
// TestCancel checks how cancellation propagates or doesn't propagate
// when setting up child contexts through WithCancel or WithoutCancel.
func TestCancel(t *testing.T) {
tCtx := Init(t)
tCtx2 := tCtx.WithoutCancel()
tCtx3 := tCtx.WithoutCancel()
tCtx4 := tCtx.WithCancel()
tCtx5 := tCtx.WithCancel()
tCtx.AssertNoError(tCtx.Err())
tCtx.AssertNoError(tCtx2.Err())
tCtx.AssertNoError(tCtx3.Err())
tCtx.AssertNoError(tCtx4.Err())
tCtx.AssertNoError(tCtx5.Err())
tCtx2.Cancel("cancel 2")
tCtx.AssertNoError(tCtx.Err())
tCtx.Assert(context.Cause(tCtx2)).To(gomega.MatchError(gomega.ContainSubstring("cancel 2")))
tCtx.AssertNoError(tCtx3.Err())
tCtx.AssertNoError(tCtx4.Err())
tCtx.AssertNoError(tCtx5.Err())
tCtx4.Cancel("cancel 4")
tCtx.AssertNoError(tCtx.Err())
tCtx.Assert(context.Cause(tCtx2)).To(gomega.MatchError(gomega.ContainSubstring("cancel 2")))
tCtx.AssertNoError(tCtx3.Err())
tCtx.Assert(context.Cause(tCtx4)).To(gomega.MatchError(gomega.ContainSubstring("cancel 4")))
tCtx.AssertNoError(tCtx5.Err())
tCtx.Cancel("cancel root")
tCtx.Assert(context.Cause(tCtx)).To(gomega.MatchError(gomega.ContainSubstring("cancel root")))
tCtx.Assert(context.Cause(tCtx2)).To(gomega.MatchError(gomega.ContainSubstring("cancel 2")))
tCtx.AssertNoError(tCtx3.Err())
tCtx.Assert(context.Cause(tCtx4)).To(gomega.MatchError(gomega.ContainSubstring("cancel 4")))
tCtx.Assert(context.Cause(tCtx5)).To(gomega.MatchError(gomega.ContainSubstring("cancel root")))
}

View File

@@ -41,13 +41,13 @@ func (tCtx TContext) WithCancel() TContext {
}
// WithoutCancel causes the returned context to ignore cancellation of its parent.
// Calling Cancel will not cancel the parent either.
// Calling Cancel will only cancel the new context.
// This matches [context.WithoutCancel].
func (tCtx TContext) WithoutCancel() TContext {
ctx := context.WithoutCancel(tCtx)
tCtx.Context = ctx
tCtx.cancel = nil
tCtx = tCtx.WithCancel() // Re-create a cancelable TContext.
return tCtx
}