Repository navigation
feat(engine): add dispose spans for shared data-source objects - #7396
Conversation
Shared data-source objects already get an 'initialize <Type>' span. Wrap their disposal in a matching 'dispose <Type>' span on the lifecycle source, parented and tagged by the same scope rules, so fixture teardown cost shows in the HTML report and OTel backends.
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Shared-object activity parent context src/TUnit.Core/TUnitActivitySource.cs, src/TUnit.Engine/Services/ObjectLifecycleService.cs |
TUnitActivitySource.GetSharedObjectParentContext selects the parent context for shared-object lifecycle spans. Initialization now uses this method instead of a local helper. |
Disposal span instrumentation and validation src/TUnit.Core/Tracking/ObjectTracker.cs, tests/TUnit.UnitTests/SharedObjectDisposeSpanTests.cs, docs/docs/examples/opentelemetry.md |
ObjectTracker passes test context through disposal and emits a tagged disposal span for registered shared objects when listeners are present. Tests cover per-class, per-session, failing, and per-test disposal. The documentation describes span placement and scope tags. |
Estimated code review effort: 3 (Moderate) | ~20 minutes
Change: Feature · Severity of issue fixed: Medium
Possibly related PRs
- thomhurst/TUnit#7248: It changes shared-instance reference counting, cache eviction, and disposal in
ObjectTracker, which is the disposal path instrumented here. - thomhurst/TUnit#7391: It changes when cached shared instances are retired and disposed, which determines when the new disposal spans run.
Merge Risk: 
Minimal · up to b1814
-
The disposal-span change is ready to merge after normal checks; no merge-blocking issue was established.
Pre-merge checks: 
4 · 
1
-
Failed checks: 1 warning Passed checks: 4 passed-
Full details: Title check
-
Explanation
The title "feat(engine): add dispose spans for shared data-source objects" matches the main change. The PR adds a
dispose <Type>span for disposal of disposable objects with PerClass, PerAssembly, PerTestSession, and Keyed shared scopes. The changeset includes the ObjectTracker disposal path, the shared parent-context helper in TUnitActivitySource, the docs update, and the new tests. The title is short, specific, and uses a conventional commit prefix.
Full details: Linked Issues check
-
Explanation
Issue
#7287is the only active direct link. Its coding requirement is a dispose <Type> span around shared-object disposal, parented by the initialize rules, tagged with tunit.trace.scope. The head implements this. In ObjectTracker.UntrackObject (src/TUnit.Core/Tracking/ObjectTracker.cs), a disposable object with a registered shared scope is disposed through DisposeWithSpanAsync. That method runs on TUnitActivitySource.LifecycleSource with the namedispose {type}, the parent from TUnitActivitySource.GetSharedObjectParentContext, and the tunit.trace.scope tag. The parent helper keeps the rules from the issue: PerTestSession, PerAssembly, and Keyed use the assembly activity; PerClass uses the class activity. The helper was moved from ObjectLifecycleService, so initialize and dispose share one implementation. The TODO that named this gap was removed. The expected 20-second dispose span follows from wrapping DisposeAsync in the span. Per-test objects keep the old path, which the issue does not require to change. The issue does not require tests, and the PR-reported test results were not re-run here.
Full details: Out of Scope Changes check
-
Explanation
All changed files connect to objective
#7287. ObjectTracker.cs adds the dispose span and passes the TestContext needed for parenting. TUnitActivitySource.cs gains GetSharedObjectParentContext, which both init and dispose use. ObjectLifecycleService.cs removes its local copy of the helper and calls the shared one, which avoids duplicated parent rules. SharedObjectDisposeSpanTests.cs covers the new span behaviour. docs/docs/examples/opentelemetry.md adds a note on the new spans. No public API change is reported, and the diff shows only internal members. The root span for UntrackObjectAsync (static properties, no test context) follows from the null context. The issue does not specify this path, so it is not a scope violation. No unrelated change was found.
-
Full details: Docstring Coverage
-
Explanation
Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 9 functions across 4 files. (1 skipped: 1 unsupported.)
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
-
Finishing Touches
-
Generate docstrings
-
- Commit to this branch
- Create a new PR
Generate unit tests (beta)
-
- Commit to this branch
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
A rabbit traced a fixture's trail,
And marked its teardown without fail.
A class span here, a session there,
Scope tags now follow through the air.
When disposal errs, the span records,
Then hops away to document the chores.
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Review:
|
|
Codex Review: Didn't find any major issues. Breezy! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
[Low impact] The PR appears safe to merge; the previous test gap is fixed and no new actionable issue was found. SummaryAdds
Reviews (2) · Last reviewed commit: "test: assert dispose spans have explicit..." · Reviewed by Greptile |
The parent assertions compared against the running test's class and assembly activities, which are null without a listener, so both sides fell back to default and a span with no parent passed. Build isolated contexts with explicit activities and assert the parent is non-default.
|
Dispositions for the non-blocking notes in the Claude review:
The Greptile thread (parent assertions could pass with no parent) is fixed in b18141d. |
|
@codex review |
Review of PR 7396: dispose spans for shared data-source objectsI found no blocking issues. This reads as a clean, well-scoped change. What I checked
Minor, non-blocking suggestions
Good work. Closing out the long-standing TODO with a shared helper is the right design. |
|
Codex Review: Didn't find any major issues. Nice work! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
Dispositions for the non-blocking notes in the second Claude review (head b18141d):
|
Review:
|
Summary
Shared data-source objects get an
initialize <Type>span, but their disposal had none (see the TODO inObjectTracker.UntrackObject). This PR wraps that disposal in a matchingdispose <Type>span:TUnit.Lifecyclesource. It applies only to disposable objects registered inTraceScopeRegistrywith a shared scope (PerClass,PerAssembly,PerTestSession,Keyed), and only when that source has a listener. Per-test objects and runs with no listener take the old path unchanged.ObjectLifecycleServicetoTUnitActivitySource.GetSharedObjectParentContext, so init and dispose share one copy.tunit.trace.scopeandtunit.test.class. A failed dispose records the error on the span and still propagates to the caller.UntrackObjects, so the span can find its parent.UntrackObjectAsync(static properties) has no context, so its span is a root span carrying only the scope tag.HtmlReportGeneratoralready treats thedisposeprefix as a lifecycle span, so the report shows init and dispose cost side by side per fixture. I also added a short note on these spans todocs/docs/examples/opentelemetry.md.Execution after metadata collection is shared, so source-generated and reflection modes get the same behaviour.
Validation
tests/TUnit.UnitTests/SharedObjectDisposeSpanTests.cs(4 tests): per-class span parent, scope tag and class tag; session span under the assembly activity; a failing dispose gives an error-status span and still returns the exception; per-test objects get no span.TUnit.UnitTestsnet10.0: 689 passed, 4 skipped, 0 failed.TUnit.OpenTelemetry.Testsnet10.0: 42 passed.TUnit.EngineRelease build, all TFMs: 0 warnings.Closes #7287
Summary by CodeRabbit
New Features
Documentation