-
-
Notifications
You must be signed in to change notification settings - Fork 125
Comparing changes
Open a pull request
base repository: thomhurst/TUnit
base: v1.35.2
head repository: thomhurst/TUnit
compare: v1.36.0
- 17 commits
- 140 files changed
- 2 contributors
Commits on Apr 16, 2026
-
fix: don't render test's own trace as Linked Trace in HTML report (#5580
) The engine auto-registers the test's own traceId in TraceRegistry for OTLP cross-process log correlation. HtmlReporter then read it back as an "additional" trace, causing the primary trace to render twice — once filtered under "Trace Timeline", once raw under "Linked Trace: TUnit". Filter out the primary traceId so only user-registered external traces (via TestContext.RegisterTrace) appear as Linked Traces, matching documented behavior.
Configuration menu - View commit details
-
Copy full SHA for 8c823c5 - Browse repository at this point
Copy the full SHA 8c823c5View commit details -
chore(deps): update tunit to 1.35.2 (#5581)
Co-authored-by: Renovate Bot <renovate@whitesourcesoftware.com>
Configuration menu - View commit details
-
Copy full SHA for 7a72f9d - Browse repository at this point
Copy the full SHA 7a72f9dView commit details
Commits on Apr 17, 2026
-
chore(deps): update dependency typescript to ~6.0.3 (#5582)
Co-authored-by: Renovate Bot <renovate@whitesourcesoftware.com>
Configuration menu - View commit details
-
Copy full SHA for 4f1033c - Browse repository at this point
Copy the full SHA 4f1033cView commit details -
Configuration menu - View commit details
-
Copy full SHA for b22d2b6 - Browse repository at this point
Copy the full SHA b22d2b6View commit details -
Configuration menu - View commit details
-
Copy full SHA for e9ed809 - Browse repository at this point
Copy the full SHA e9ed809View commit details -
fix(docs): benchmark index links 404 (#5587)
Bare relative links like `[AsyncTests](AsyncTests)` resolved to `/docs/AsyncTests` instead of `/docs/benchmarks/AsyncTests`. Use `./Name.md` form so Docusaurus resolves to the sibling route. Fixes #5585
Configuration menu - View commit details
-
Copy full SHA for 90bdbd0 - Browse repository at this point
Copy the full SHA 90bdbd0View commit details -
docs: replace repeated 'Detailed performance analysis' with per-bench…
…mark descriptions (#5588)
Configuration menu - View commit details
-
Copy full SHA for 3488eff - Browse repository at this point
Copy the full SHA 3488effView commit details -
docs: expand and clarify distributed tracing setup and troubleshooting (
#5597) Adds symptom-driven troubleshooting to the OpenTelemetry guide and a new Distributed Tracing guide consolidating per-backend setup, correlation strategies, and known limitations. Moves the "use TestWebApplicationFactory" guidance into a top-of-page warning callout so users don't silently lose trace correlation by inheriting from the vanilla WebApplicationFactory. Cross-references issues #5589, #5590, #5591, #5592, #5593, #5594, #5595, and #5596 which track the planned automation that will let later doc revisions trim each manual recipe.
Configuration menu - View commit details
-
Copy full SHA for 5d3df9b - Browse repository at this point
Copy the full SHA 5d3df9bView commit details -
fix: auto-suppress ExecutionContext flow for hosted services (#5589) (#…
…5598) * fix: auto-suppress ExecutionContext flow for hosted services (#5589) `TestWebApplicationFactory<T>` now wraps every registered `IHostedService` so its `StartAsync` runs under `ExecutionContext.SuppressFlow`. Background tasks spawned inside `StartAsync` capture a clean execution context, preventing spans from hosted-service work in test B from being attributed to test A's `TraceId`. The wrapper also implements `IHostedLifecycleService` so the Host's `StartingAsync`/`StartedAsync`/`StoppingAsync`/`StoppedAsync` hooks keep firing for inner services that implement it (the Host uses an `is` check against the registered instance). Override `SuppressHostedServiceExecutionContextFlow` and return `false` to preserve ambient context flow. * fix: wrap StartAsync in Task.Run under SuppressFlow `using var _ = SuppressFlow(); return inner.StartAsync(ct);` only suppresses context capture during the synchronous portion of StartAsync — `Task.Run` after a prior `await` re-captures the test's Activity.Current. Combine SuppressFlow with `Task.Run(() => inner.StartAsync(ct), ct)` so the inner hosted service runs on a thread-pool worker whose ExecutionContext was captured under suppression. Activity.Current starts null and stays null through every await point, so background tasks spawned anywhere inside StartAsync inherit a clean context. Also drops the Limitation xmldoc since it no longer applies, and adds a deep-async test (StartAsync_SuppressesFlow_WhenSpawnIsAfterAwait) proving the fix holds past the first await.
Configuration menu - View commit details
-
Copy full SHA for 76c4d4e - Browse repository at this point
Copy the full SHA 76c4d4eView commit details -
feat: auto-align DistributedContextPropagator to W3C (#5599)
* feat: auto-align DistributedContextPropagator to W3C (#5592) .NET's default LegacyPropagator emits Correlation-Context; the OpenTelemetry SDK's BaggagePropagator only reads W3C baggage. The mismatch silently drops tunit.test.id across processes so test correlation breaks on the SUT side. Add a module initializer in TUnit.Core that swaps the runtime-default LegacyPropagator for CreateW3CPropagator(), leaving user-customised propagators untouched. TestWebApplicationFactory.ConfigureWebHost re-applies the same alignment so SUT startup code cannot accidentally revert it. Opt out via TUNIT_KEEP_LEGACY_PROPAGATOR=1. Docs updated to reflect automatic alignment; manual OTel SetDefaultTextMapPropagator snippet retained only for out-of-process SUTs that don't reference TUnit.Core. * fix: provide W3C propagator on net8/net9 DistributedContextPropagator.CreateW3CPropagator() was added in .NET 10; on net8/net9 supply a minimal in-library W3CBaggagePropagator that delegates traceparent/tracestate to the runtime default and emits/parses W3C baggage. * refactor: dedupe baggage utilities, tighten W3CBaggagePropagator - Promote BaggageHeader constant onto TUnitActivitySource; consume from W3CBaggagePropagator and the ASP.NET Core / Aspire propagation handlers. - Reuse TUnitActivitySource.TryBuildBaggageHeader in W3CBaggagePropagator instead of duplicating the URI-escape/comma-join logic. - Rename PropagatorAlignment.CreateW3CPropagator -> CreateAlignedPropagator to avoid shadowing the BCL static. - Parse baggage via span-walker (no Split allocation), lazy-init the result list so empty headers return null. * refactor: register PropagatorAlignment via IStartupFilter ConfigureWebHost callbacks register builder actions that run before user Program.cs/Startup code; calling AlignIfDefault() there lets SUT startup clobber the propagator again. IStartupFilter runs when the request pipeline is built, after all service registration and startup assignments, so alignment wins. Also drop the "TUnit 0.x (issue #5592)" placeholder from the OpenTelemetry docs — the auto-alignment is just how it works now.
Configuration menu - View commit details
-
Copy full SHA for e7fde9b - Browse repository at this point
Copy the full SHA e7fde9bView commit details -
chore(deps): update dependency coverlet.collector to v10 (#5600)
Co-authored-by: Renovate Bot <renovate@whitesourcesoftware.com>
Configuration menu - View commit details
-
Copy full SHA for e6d42c3 - Browse repository at this point
Copy the full SHA e6d42c3View commit details -
feat: TUnit0064 analyzer + code fix for direct WebApplicationFactory …
…inheritance (#5601) * feat: add TUnit0064 analyzer + code fix for direct WebApplicationFactory inheritance Flags classes inheriting directly from `Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<T>` and offers a code fix to rewrite the base to `TUnit.AspNetCore.TestWebApplicationFactory<T>`, preserving distributed tracing, per-test logging correlation, and `TestContext.Current` resolution inside request handlers. Closes #5596 * fix: detect namespace-scoped TUnit.AspNetCore usings when applying code fix Also guards against ignoring pre-existing top-level usings and adds tests for both cases (top-level + namespace-scoped) so BatchFixer can't emit duplicate directives. * fix: skip analysis when TestWebApplicationFactory<T> is unavailable If `TUnit.AspNetCore` isn't referenced, warning a user to migrate to a type they can't resolve is unhelpful and would make the code fix produce a compile error. Bail out of the analyzer early in that case. Also adds tests for file-scoped-namespace using deduplication and partial class declarations.
Configuration menu - View commit details
-
Copy full SHA for f394eef - Browse repository at this point
Copy the full SHA f394eefView commit details -
feat: auto-propagate test trace context through IHttpClientFactory (#…
…5603) * feat: auto-propagate test trace context through IHttpClientFactory Registers an IHttpMessageHandlerBuilderFilter in TestWebApplicationFactory so that every IHttpClientFactory pipeline built inside the SUT (AddHttpClient<T>(), named clients, typed clients) automatically carries the test's traceparent, baggage, and X-TUnit-TestId headers on outbound calls. Opt out per-test via WebApplicationTestOptions.AutoPropagateHttpClientFactory = false. Closes #5590. * address review: document statelessness + tighten opt-out assertion - Document that both handler types inserted by TUnitHttpClientFilter must remain stateless/thread-safe because IHttpClientFactory caches pipelines and shares handler instances across concurrent parallel-test requests. - Explain the outermost-insert intent so a future refactor doesn't reverse the order. - Opt-out test now also asserts the downstream hop does not carry baggage, mirroring the positive test.
Configuration menu - View commit details
-
Copy full SHA for b8f9bb5 - Browse repository at this point
Copy the full SHA b8f9bb5View commit details -
feat: TUnit.OpenTelemetry zero-config tracing package (#5602)
* chore: scaffold TUnit.OpenTelemetry project * chore: scaffold TUnit.OpenTelemetry.Tests project * feat(otel): add TUnitOpenTelemetry.Configure hook * feat(otel): auto-wire TracerProvider at TestDiscovery with coexistence * test(otel): cover TUNIT_OTEL_AUTOSTART=0 opt-out * test(otel): cover TUnitTestCorrelationProcessor baggage-to-tag behavior * feat(otel): skip auto-wire when no endpoint and no user Configure * feat(otel): set default service.name resource attribute * test(otel): add public API snapshot for TUnit.OpenTelemetry * refactor(aspire): delegate TracerProvider to TUnit.OpenTelemetry * test(otel): end-to-end span export smoke test * docs(otel): document TUnit.OpenTelemetry zero-config setup * chore(otel): wire TUnit.OpenTelemetry into pipeline (pack + test) * refactor(otel): reuse TagTestId constant, extract env-var names, release lock during Build - TUnitTestCorrelationProcessor now references TUnit.Core.TUnitActivitySource.TagTestId instead of duplicating the "tunit.test.id" literal. - AutoStart extracts AutoStartEnvVar / OtlpEndpointEnvVar / ServiceNameEnvVar as internal consts; tests reference them by name rather than raw strings. - Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT") is read once and reused by both the gate check and AddOtlpExporter branch. - TracerProvider.Build() and user Configure callbacks now execute outside the AutoStart lock; a CAS-like re-check inside the lock disposes the loser if two Start calls race. * refactor(otel): address PR review — tighten TOCTOU, drop test-only wrapper - AutoStart.Start moves the HasListeners/endpoint/HasConfiguration checks inside the existing _lock section so the listener probe and the provider assignment are no longer separated by a gap (PR #5602 issue 2). Build() still runs outside the lock; the post-Build re-check disposes the loser on a race. - provider.Dispose() is called unconditionally after Build — the local is known non-null, so the ?. was misleading (issue 3). - TestTraceExporter.CreateTracerProvider is removed — it only existed for one Aspire test that duplicated AddToBuilder_ExportsTracesForRegisteredSource anyway. Production code uses AddToBuilder (issue 4). Issue 1 (AutoStart hard-coding "TUnit.AspNetCore.Http") is acknowledged but not acted on: the plan explicitly requires TUnit.AspNetCore to stay OTel-agnostic, so the suggested self-registration pattern would create the very dep we want to avoid. Users who disagree can already use TUnitOpenTelemetry.Configure(b => b.AddSource(...)) to add/override sources.Configuration menu - View commit details
-
Copy full SHA for b700a16 - Browse repository at this point
Copy the full SHA b700a16View commit details -
fix: restore [Obsolete] members removed in v1.27 (#5539) (#5605)
* fix: restore [Obsolete] members removed in v1.27 (#5539) PR #5384 deleted previously [Obsolete]-marked public APIs in a minor release, breaking semver. Restore them with [Obsolete] reapplied so v1.x consumers can upgrade without compile errors. Actual deletion is deferred to the v2 major bump (tracked in #5604). Restored: - TUnit.Assertions: CountWrapper, LengthWrapper, HasCount/HasLength overloads on CollectionAssertionBase / AssertionExtensions - TUnit.Core: ObjectBag on TestBuilderContext + TestRegisteredContext, Timing record, ITestOutput.Timings + RecordTiming bridged to internal TimingEntry storage with new _timingsLock for user-facing concurrent RecordTiming calls - PublicAPI snapshots regenerated for net8/9/10/472 * refactor: address PR review feedback - Extract GetCount/MapToCount helpers in CountWrapper to remove 6x duplication - Use <inheritdoc cref="StateBag"/> on ObjectBag aliases (drop duplicated XML) - Clarify _timingsLock comment: engine writes are sequential, lock guards user-facing obsolete RecordTiming
Configuration menu - View commit details
-
Copy full SHA for 02d2c96 - Browse repository at this point
Copy the full SHA 02d2c96View commit details -
feat: generalize OTLP receiver for use outside TUnit.Aspire (#5606)
* feat: generalize OTLP receiver for use outside TUnit.Aspire Extracts OtlpReceiver + parsers from TUnit.Aspire to TUnit.OpenTelemetry and auto-starts them at test discovery. Out-of-process SUTs (spawned processes, testcontainers, external services) can now export spans into TUnit's HTML report via OTLP/HTTP without Aspire. ActivityCollector gains RegisterExternalTrace/IngestExternalSpan and a process-wide Current pointer so the receiver can route spans without explicit wiring. Unknown trace IDs are dropped; registered traces are capped at 100 external spans. Opt out with TUNIT_OTEL_RECEIVER=0. Adds OtlpTraceParser (field-by-field protobuf parser, no external dep) so incoming /v1/traces are ingested, not just forwarded. Closes #5595 * docs: address PR review on OTLP receiver generalization - Add OTLP proto spec links and field-name comments to OtlpTraceParser and OtlpLogParser so magic field numbers can be cross-referenced. - Make external span cap overridable via TUNIT_OTEL_MAX_EXTERNAL_SPANS env var and emit a one-time stderr warning when the cap is first hit. * refactor: tidy external span cap helpers - Move TUNIT_OTEL_MAX_EXTERNAL_SPANS into EnvironmentConstants so the env var name isn't duplicated across resolver and warning message. - Collapse the MaxExternalSpansPerTest/MaxExternalSpansPerTrace alias pair into a single MaxExternalSpans field — per-test vs per-trace distinction is already clear at the use sites. - Use Interlocked.CompareExchange for the one-shot warning latch so we don't re-write the flag on every dropped span once the cap is hit. * fix: correlate external spans regardless of hex case OTLP parser emitted uppercase hex IDs (via Convert.ToHexString) while System.Diagnostics.Activity serializes lowercase. That meant external spans landed in a separate trace bucket from in-process spans on the same logical trace, the per-test cap never matched a test case span ID, and users had to know to call .ToUpperInvariant() when registering traces manually. - Parser now emits lowercase via a HexLower helper (uses ToHexStringLower on net9+, falls back to ToLowerInvariant on net8) so IDs round-trip with Activity's format without caller ceremony. - All trace/span ID dictionaries in ActivityCollector are now OrdinalIgnoreCase as defense-in-depth against any remaining mixed-case caller. - SpanType no longer duplicates Name for external spans — it's a TUnit-only classifier and has no OTLP analogue. - Regression test IngestExternalSpan_TraceIdCaseMismatch_StillCorrelates. - Docs and existing tests updated to drop the now-unnecessary ToUpperInvariant. * chore: address PR review stragglers - Log parse failures in ProcessTraces/ProcessLogs via Trace.WriteLine instead of swallowing silently, matching the rest of OtlpReceiver's error paths. - Drop the [EditorBrowsable(Never)] attribute on the internal HasReceiverForTesting property — it's a no-op on internal members. - Expose ActivityCollector.MaxExternalSpans internal so the cap test reads the runtime value instead of a hardcoded 100, keeping it correct when TUNIT_OTEL_MAX_EXTERNAL_SPANS is set in the environment. * chore: update PublicAPI snapshots for OTLP receiver generalization New AutoReceiver class in TUnit.OpenTelemetry and InternalsVisibleTo entries for TUnit.OpenTelemetry.Tests/TUnit.Aspire.Tests. * fix: read proto fixed64 as uint64 before cast Matches proto semantics (fixed64 is unsigned). Bit pattern unchanged; this expresses intent and keeps the cast explicit at the call site.
Configuration menu - View commit details
-
Copy full SHA for 0861740 - Browse repository at this point
Copy the full SHA 0861740View commit details -
+semver:minor - feat: auto-configure OpenTelemetry in TestWebApplicat…
…ionFactory SUT (#5607) * feat: auto-configure OpenTelemetry in TestWebApplicationFactory SUT (#5594) TestWebApplicationFactory<T> now augments the SUT's TracerProvider with TUnit's AspNetCore activity source, TUnitTestCorrelationProcessor, and ASP.NET Core + HttpClient instrumentation. Spans emitted inside the SUT stay queryable per-test (tunit.test.id tag) even when third-party libs break the parent chain. Opt out per-test with WebApplicationTestOptions.AutoConfigureOpenTelemetry = false. TUnitTestCorrelationProcessor moves from TUnit.OpenTelemetry to TUnit.Core (NET-only) so the AspNetCore wrapper can reference it without pulling in the zero-config package. Public namespace (TUnit.OpenTelemetry) is unchanged. * refactor: keep TUnit.Core OTel-free; ref TUnit.OpenTelemetry from AspNetCore.Core Addresses PR review: TUnit.Core should not own an OpenTelemetry dependency (every TUnit user would get OTel transitively). Revert the processor move: TUnitTestCorrelationProcessor stays in TUnit.OpenTelemetry, and TUnit.AspNetCore.Core references that project directly. Also document zero-config + SUT double-processor safety on the helper. * test: update Core public API snapshot for AspNetCoreHttpSourceName Adds the newly introduced public const on TUnitActivitySource to the verified PublicAPI snapshots for net8.0, net9.0, and net10.0.
Configuration menu - View commit details
-
Copy full SHA for 9e6ca6f - Browse repository at this point
Copy the full SHA 9e6ca6fView commit details
This comparison is taking too long to generate.
Unfortunately it looks like we can’t render this comparison for you right now. It might be too big, or there might be something weird with your repository.
You can try running this command locally to see the comparison on your machine:
git diff v1.35.2...v1.36.0