-
-
Notifications
You must be signed in to change notification settings - Fork 136
Comparing changes
Open a pull request
base repository: thomhurst/TUnit
base: v1.29.0
head repository: thomhurst/TUnit
compare: v1.30.0
- 18 commits
- 114 files changed
- 2 contributors
Commits on Apr 7, 2026
-
chore(deps): update tunit to 1.29.0 (#5446)
Co-authored-by: Renovate Bot <renovate@whitesourcesoftware.com>
Configuration menu - View commit details
-
Copy full SHA for fe7002a - Browse repository at this point
Copy the full SHA fe7002aView commit details -
perf: eliminate locks from mock invocation and verification hot paths (…
…#5422) * perf: eliminate locks from mock invocation and verification hot paths Replace the shared MockEngine.Lock with per-member lock-free call recording and lock-free verification reads. This targets the three benchmark categories where TUnit.Mocks loses to source-generated competitors (Imposter, Mockolate): **Callback (60% slower than Imposter)** - StoreCallRecord no longer acquires MockEngine.Lock on the hot path - CallRecordBuffer uses a lightweight per-buffer lock instead of the shared engine lock, eliminating contention between setup and invocation - Buffers are pre-created during setup so invocations never allocate or initialize under lock **Setup (18% slower than Mockolate)** - AddBehavior uses Interlocked.CompareExchange for the common single-behavior case, avoiding Lock object allocation and lock acquisition entirely **Verification (13% slower than Imposter)** - Single-pass verification: count and mark verified in one iteration instead of two. Only unmarks on failure (rare path). - GetCallBufferFor returns the internal buffer directly, eliminating the .ToArray() copy allocation on the verification happy path - GetCallsFor, MarkCallsVerified, GetAllCalls, GetUnverifiedCalls are now lock-free reads All 822 mock tests pass (761 + 31 + 30). * fix: address review feedback on lock-free hot paths - Replace lock(this) with private _syncRoot object in CallRecordBuffer - Add GetSnapshot() to cache array reference for tight iteration loops, eliminating N volatile reads in CountAndMarkBuffer - Remove misleading AggressiveInlining on Add (JIT can't inline lock bodies) - Add double-check after CAS in AddBehavior to prevent race with concurrent AddBehaviorSlow on the same MethodSetup - Fix inaccurate "single-writer"/"lock-free" comment on _callsByMemberId - Remove unnecessary WHAT-style doc comments * fix: address PR review — bundle volatile refs, fix snapshot ordering, restore count-first verification - Bundle _callsByMemberId/_callCountByMemberId into single volatile CallArrays reference to eliminate TOCTOU race between the two fields - Fix GetSnapshot() read order: items before count ensures count <= items.Length after Grow() swaps the array - Restore count-first, mark-on-success verification pattern to avoid corrupting prior verifications on assertion failure * docs: address remaining review items — volatility rationale and snapshot safety - Document why _singleBehavior/_behaviors aren't volatile (CS0420 with Interlocked.CompareExchange) and that all accesses use Volatile.Read/Write - Document CAS full-barrier safety in AddBehavior fast path - Document CollectCallRecords snapshot semantics for concurrent capacity growth * fix: correct GetSnapshot read order, fix XML docs, remove dead guard - GetSnapshot: read count before items (count-first is safe because writer updates items/Grow before incrementing count — seeing new count guarantees items.Length >= count) - Fix duplicate <summary> tag → <remarks> - Remove dead _matchers.Length == 0 guard in CountMatchingBuffer/MarkMatchingBuffer (already handled by the fast path in WasCalled) * perf: use System.Threading.Lock for CallRecordBuffer per-buffer lock Consistent with MockEngine; faster on .NET 9+ and better diagnostics.
Configuration menu - View commit details
-
Copy full SHA for 630182a - Browse repository at this point
Copy the full SHA 630182aView commit details
Commits on Apr 8, 2026
-
Configuration menu - View commit details
-
Copy full SHA for 83840f3 - Browse repository at this point
Copy the full SHA 83840f3View commit details -
Configuration menu - View commit details
-
Copy full SHA for 8da52cd - Browse repository at this point
Copy the full SHA 8da52cdView commit details -
chore(deps): update react to ^19.2.5 (#5457)
Co-authored-by: Renovate Bot <renovate@whitesourcesoftware.com>
Configuration menu - View commit details
-
Copy full SHA for a6a7cee - Browse repository at this point
Copy the full SHA a6a7ceeView commit details -
chore(deps): update opentelemetry to 1.15.2 (#5456)
Co-authored-by: Renovate Bot <renovate@whitesourcesoftware.com>
Configuration menu - View commit details
-
Copy full SHA for 9500f16 - Browse repository at this point
Copy the full SHA 9500f16View commit details -
chore(deps): update dependency qs to v6.15.1 (#5458)
Co-authored-by: Renovate Bot <renovate@whitesourcesoftware.com>
Configuration menu - View commit details
-
Copy full SHA for 526a540 - Browse repository at this point
Copy the full SHA 526a540View commit details -
feat: TUnit0074 analyzer for redundant hook attributes on overrides (#…
…5459) * feat: TUnit0074 analyzer for redundant hook attributes on overrides When a method declares [Before(Test)] / [After(Test)] and overrides a base method that already declares the same hook attribute, both registrations are invoked via virtual dispatch on the same instance — the override's body runs twice per test. The previous fix (#5428 / #5441) deduplicated this at runtime via MethodInfo.GetBaseDefinition(), which silently hid the duplication instead of surfacing it to the author and didn't address the [InheritsTests] variant in #5450. Replace the runtime dedup with a compile-time analyzer (TUnit0074, Error) plus a code fix that removes the redundant attribute. The analyzer walks the full IMethodSymbol.OverriddenMethod chain so transitive cases (gap in the middle of the override chain) are also caught, and the [InheritsTests] + abstract intermediate shape from #5450 fails to compile at the intermediate's override. Revert the runtime dedup: drop InstanceHookMethod.BaseDefinition, ResolveBaseDefinition / IsOverriddenByMoreDerivedHook from HookDelegateBuilder, and the GetBaseDefinition() init in ReflectionHookDiscoveryService. PublicAPI snapshots updated accordingly. Regression tests (VirtualHookOverrideTests + Bugs/5450) rewritten to the "attribute only on base, override without attribute" shape — the only shape TUnit0074 still allows. Assertions are inlined inside the hook bodies because After-hooks are sorted derived-class-first across the type hierarchy, so a separate verification hook on the derived class would run before the base teardown. * address review: combine null guards in analyzer + KeepLeadingTrivia in code fix - Merge the IsStandardHook/HookLevel/hookType checks into one if so the compiler tracks nullability without needing the null-forgiving operator. - Use KeepLeadingTrivia when removing the attribute list so any leading comment above the attribute is preserved on the method declaration.
Configuration menu - View commit details
-
Copy full SHA for dd333ee - Browse repository at this point
Copy the full SHA dd333eeView commit details -
fix(mocks): respect generic type argument accessibility (#5453) (#5460)
Extends the effective-accessibility check from #5426 to also walk generic type arguments and array element types. Previously, mocking a public generic interface closed over an internal type argument (e.g. Mock.Of<ILogger<InternalClass>>()) emitted a public wrapper whose base signature leaked the internal type, producing CS9338/CS0051. IsEffectivelyPublic now switches on the type kind and recurses into TypeArguments/ElementType, collapsing the previous two helpers into one.
Configuration menu - View commit details
-
Copy full SHA for 617c4f0 - Browse repository at this point
Copy the full SHA 617c4f0View commit details -
fix(mocks): skip inaccessible internal accessors when mocking Azure.R…
…esponse (#5461) Azure.Response.IsError is declared `public virtual bool IsError { get; internal set; }`. The mock generator was emitting `override bool IsError { get; set; }` because it only checked symbol presence (`property.SetMethod is not null`), not accessor-level accessibility. External assemblies can't see the internal setter, producing CS0115: 'ResponseMockImpl.IsError.set': no suitable method found to override. Adds an `IsAccessorAccessible` helper to `MemberDiscovery` and threads the compilation assembly through `CreatePropertyModel`, `CreateIndexerModel`, and `MergePropertyAccessors` so `HasGetter`/`HasSetter`/`SetterMemberId` reflect whether each accessor is actually reachable from the user's assembly, across both class- and interface-discovery paths. Fixes #5455
Configuration menu - View commit details
-
Copy full SHA for 538572d - Browse repository at this point
Copy the full SHA 538572dView commit details -
docs: fix hook execution order in OpenTelemetry guide
Before(TestDiscovery) runs before Before(TestSession), not the other way around.
Configuration menu - View commit details
-
Copy full SHA for 782c9ce - Browse repository at this point
Copy the full SHA 782c9ceView commit details -
Configuration menu - View commit details
-
Copy full SHA for 269521b - Browse repository at this point
Copy the full SHA 269521bView commit details -
docs: fix API drift found in documentation audit
Corrects stale property names, missing API references, and incorrect assertion method names across the docs. Also adds a single-arg InconclusiveTestException constructor so the documented usage compiles.
Configuration menu - View commit details
-
Copy full SHA for e1a1ab1 - Browse repository at this point
Copy the full SHA e1a1ab1View commit details
Commits on Apr 9, 2026
-
docs: fix API drift across docs from audit
Parallel audit of all docs against source uncovered fabricated APIs, reversed semantics, and stale signatures. Fixes include: - writing-tests/generic-attributes.md: rewrite DataSourceGeneratorAttribute, AsyncDataSourceGeneratorAttribute, and TypedDataSourceAttribute sections to match real abstract method signatures - guides/performance.md: correct reversed [ParallelGroup] semantics (same group runs in parallel, different groups run sequentially) and replace invalid [Arguments] matrix-explosion example with [MatrixDataSource] - examples/fsharp-interactive.md: replace fabricated TUnitRunner API with real TestApplication.CreateBuilderAsync + AddTUnit entry point - examples/aspnet.md: move body-capture options to AddHttpExchangeCapture service registration; HttpCapture.All -> Exchanges - migration/testcontext-interface-organization.md: correct ITestOutput, ITestExecution, ITestMetadata, ITestDependencies interface snippets - migration/nunit.md, mstest.md: IsInAscendingOrder -> IsInOrder, AllSatisfy(predicate) -> All().Satisfy(src => ...) - assertions/regex-assertions.md: remove fabricated .When/.AtIndex/.Length chaining, fix RegexOptions usage - assertions/combining-assertions.md: warn that .And/.Or cannot be mixed - assertions/collections.md: IsStructurallyEqualTo -> IsEquivalentTo - assertions/datetime.md: WithinDays fluent form, .NET 8+ framework gate - reference/command-line-flags.md: add missing --output-json, --report-html, --junit-output-path, --detailed-stacktrace flags - reference/environment-variables.md: add TUNIT_EXECUTION_MODE, TUNIT_DISCOVERY_DIAGNOSTICS, TUNIT_DIAGNOSTIC_CAST - execution/ci-cd-reporting.md: replace fabricated CI environment-detection table with real GITHUB_ACTIONS/GITLAB_CI/CI_SERVER vars; replace VSTest --logger syntax with MTP --output Detailed - benchmarks/methodology.md: fix [Matrix] usage to require [MatrixDataSource] with parameter-level attributes - writing-tests/artifacts.md: testContext.Result -> testContext.Execution.Result, TestState.TimedOut -> Timeout - extending/display-names.md, exception-handling.md, extension-points.md: remove fake \$\$ escape, use public Metadata.TestDetails accessor, fix TestClass -> ClassType
Configuration menu - View commit details
-
Copy full SHA for db13cfc - Browse repository at this point
Copy the full SHA db13cfcView commit details -
chore(engine): remove unused parallelism-strategy and adaptive-metric…
…s flags Both --parallelism-strategy and --adaptive-metrics CLI flags were registered with command-line validation but their values were never read anywhere in the engine. The ParallelismStrategy enum had no consumers either. Removes: - ParallelismStrategyCommandProvider - AdaptiveMetricsCommandProvider - ParallelismStrategy enum - AddProvider registration calls in TestApplicationBuilderExtensions
Configuration menu - View commit details
-
Copy full SHA for 65f94d9 - Browse repository at this point
Copy the full SHA 65f94d9View commit details -
Configuration menu - View commit details
-
Copy full SHA for 0892b46 - Browse repository at this point
Copy the full SHA 0892b46View commit details -
Configuration menu - View commit details
-
Copy full SHA for 447b4ed - Browse repository at this point
Copy the full SHA 447b4edView commit details -
+semver:minor - fix: apply CultureAttribute and STAThreadExecutorAttr…
…ibute to hooks (#5452) (#5463) * fix: apply CultureAttribute and STAThreadExecutorAttribute to hooks (#5452) CultureAttribute, STAThreadExecutorAttribute and TestExecutorAttribute were only wired into the test executor pipeline. Lifecycle hooks (Before/After Test, Class, Assembly, Session) ran on the default executor regardless of scope, so a class-level [Culture] never affected its hooks. Fix: - Implement IHookRegisteredEventReceiver on all three attributes so class/assembly/session hooks get the custom executor. - Also call SetHookExecutor in OnTestRegistered so per-test hooks share the same executor as the test body. - Add settable HookExecutor on HookRegisteredContext, applied back to the hook method by EventReceiverOrchestrator (mirrors the Timeout pattern). - Cache one executor instance per attribute so the test and hook executor roles share the same object instead of allocating twice. Also fixes a precedence bug surfaced by the change: BeforeTestHookMethod, AfterTestHookMethod and HookTimeoutHelper preferred TestContext.CustomHookExecutor over the hook's own HookExecutor unconditionally. A hook with an explicit [HookExecutor<T>] now wins; CustomHookExecutor is only a fallback when the hook is still on DefaultExecutor. This preserves the #2666 SetHookExecutor scenario without overriding explicit method-level declarations. Follow-up #5462 tracks the inverse gap: HookExecutorAttribute at class or assembly level is still ignored by discovery. * refactor: extract hook executor precedence helper and fix reflection-mode sentinel Consolidates the duplicated "hook's own HookExecutor wins over CustomHookExecutor" precedence logic into HookMethod.ResolveEffectiveExecutor, collapsing BeforeTestHookMethod / AfterTestHookMethod / HookTimeoutHelper to single-line executor calls. Also fixes a pre-existing reflection-mode regression: the precedence check uses ReferenceEquals(_, DefaultExecutor.Instance), but ReflectionHookDiscoveryService was returning a fresh DefaultHookExecutor per hook, breaking the #2666 SetHookExecutor scenario under --reflection. Reusing DefaultExecutor.Instance restores parity with source-gen. * fix: align instance hook executor precedence and assert After(Class) culture Instance hooks went through a separate execution path in HookDelegateBuilder.CreateInstanceHookDelegateAsync that unconditionally preferred CustomHookExecutor, letting it silently override an explicit [HookExecutor<T>] on an instance before/after hook (the opposite of the static path's precedence). Route InstanceHookMethod.ExecuteAsync through ResolveEffectiveExecutor and collapse the delegate builder's custom-executor branch so both static and instance hooks share one precedence rule — and, as a bonus, instance hooks now honour Timeout when a custom hook executor is in use. Also assert CultureInfo.CurrentCulture inside CultureHookTests_ClassLevel's After(Class) hook so the After(Class) path is actually covered (no subsequent test can read a field captured after the class tears down). * docs: note single-test invariant in CultureHookTests_MethodLevelOverride The BeforeTest assertion only holds because this fixture contains exactly one test with [Culture("fr-FR")]. Adding a test without the override would cause BeforeTest to fail against de-AT. Cross-reference the inherits-class fixture to keep the two cases obvious.
Configuration menu - View commit details
-
Copy full SHA for 96c1214 - Browse repository at this point
Copy the full SHA 96c1214View 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.29.0...v1.30.0