Case Study
← All case studiesTimeout Inheritance Change in NUnit
PR #5192 did not add Thread.Sleep in async code. The real case-study value is subtler: a release-branch merge changed timeout attribute inheritance, silently changing how derived test fixtures receive cancellation and timeout behavior.
70
files changed
37
commits merged
0
GCI0016 matches
What changed
PR #5192 was a release/4.6 merge to main. Among platform filter updates and framework additions, two attribute declarations changed Inherited = falseto Inherited = true: CancelAfterAttributeand TimeoutAttribute.
That means derived fixtures can begin inheriting cancellation or timeout behavior from a base class. The change may be correct, but it is externally observable for any test suite that depends on NUnit inheritance behavior.
Why this is risky
Timeout and cancellation metadata controls execution, not just documentation. A base fixture decorated with [CancelAfter]can now change the runtime behavior of every derived test. Long-running async tests may start receiving cancellation that they previously ignored.
The diff is deceptively small. A reviewer scanning for blocking calls will not see Thread.Sleepor .Wait(). The risk is encoded in attribute metadata.
What the original thin page got wrong
The old page claimed PR #5192 introduced Thread.Sleepand static mutable state. The verified diff does not support that. The closest file uses an existing AutoResetEvent.WaitOne()pattern in tests, not a newly added sleep.
Keeping this as a case study is still useful if it is framed honestly: it documents a rule-design gap and a class of behavior change that deserves a future detector.
Diff evidence
What a better detector would ask
Coverage note Signal : a one-token attribute metadata change alters derived fixture execution behavior Reality : current GCI0016 would not fire; no Thread.Sleep, .Wait(), .Result, lock(this), or async void was added Lesson : some high-impact behavior changes are API metadata changes, not obvious code-body hazards
Important context
- Current GCI0016 would not fire on this PR; there was no added Thread.Sleep, async blocking call, lock(this), or async void pattern.
- The inheritance change appears intentional and may be a bug fix, not a regression.
- This page is now a coverage-gap case study rather than a claim that GauntletCI would have blocked PR #5192.
Recommended review steps
- Review inherited timeout/cancellation behavior with derived fixture tests before merging framework changes.
- Add release notes for behavior changes caused by attribute metadata, even when method bodies do not change.
- Consider a future rule for high-impact AttributeUsage changes on test framework attributes.
Sources
Eric Cogen -- Founder, GauntletCI
Eric Cogen is a senior .NET engineer with twenty years in production. He has shipped payments systems, internal platforms, and critical line-of-business applications — the kind where a 2 a.m. alert wasn't an emergency, it was a regular Tuesday. GauntletCI is the pre-commit checklist he wishes he had run before every commit.
