perf(assertions): allocation-free passing path (TUnit.Assertions)#6112
Conversation
Eliminates avoidable allocations and re-enumeration on the passing path of several collection assertions (TUnit.Assertions), addressing issue #6107. - IsOrderedBy / IsOrderedByDescending: drop the eager `ToArray()` + `OrderBy(...).ToArray()` + `SequenceEqual` (O(n log n) and two array allocs even when the collection is already ordered) in favour of a single-pass O(n) adjacent-pair comparison over an enumerator. Zero allocation on the passing path; failure message, index semantics and comparer usage unchanged. - HasSingleItem: the assertion previously called `CheckHasSingleItem` (which uses `Count`) and then `.First()`, double-enumerating non-replayable sequences. Added a `CheckHasSingleItem(..., out item)` overload that captures the single item during one enumeration pass, mirroring `CheckHasSingleItemPredicate`. Failure message ("received N item(s)") preserved. - IsEquivalentTo: cache the formatted expected string in `GetExpectation()` (`_cachedExpectedFormat`) instead of re-running `string.Join` over `_expected` on every call, matching `EqualsAssertion._cachedExpectedFormat`. Closes #6107
Not up to standards ⛔🔴 Issues
|
| Category | Results |
|---|---|
| BestPractice | 1 medium |
| CodeStyle | 1 minor |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
There was a problem hiding this comment.
Code Review
This PR delivers three targeted performance fixes on the passing path of collection assertions. Overall the changes are correct and well-scoped.
IsOrderedBy / IsOrderedByDescending — CollectionAssertions.cs
Correctness: ✅
The replacement of ToArray() + OrderBy().ToArray() + SequenceEqual() with a single-pass enumerator-based adjacent-pair scan is both correct and better:
- O(n log n) → O(n), zero allocations on the passing path.
- The
indexvariable starts at 1 and tracks the 0-based position of the current (non-first) element — exactly matching the oldiinfor (int i = 1; i < enumerated.Length; i++). Failure messages are semantically unchanged. - The
SequenceEqualearly-exit in the old code was slightly misleading: it compared elements (not keys), meaning items with different equality but the same key could cause unnecessary loop execution. The new approach uses only key comparisons throughout, which is more semantically correct. - Equal-key adjacent items:
comparer.Compare(a, a) == 0is neither> 0nor< 0, so equal-key items pass both ascending and descending checks — correct.
HasSingleItem — CollectionChecks.cs + CollectionAssertions.cs
Correctness: ✅ | Design: minor observation
The new CheckHasSingleItem(adapter, out TItem? singleItem) overload correctly fixes the double-enumeration hazard for non-replayable lazy sequences (e.g., yield return generators, EF Core queries). Mirroring the existing CheckHasSingleItemPredicate(..., out item) pattern is a good design choice.
The counter logic is correctly intentional:
if (count == 0) singleItem = item; // capture first
else if (count == 1) singleItem = default; // reset on second (wrong count)
count++;It must count to completion because the failure message needs the true total (received N item(s)). This is clearly explained in the inline comment — good.
One observation: For collections that implement ICollection<T> or IReadOnlyCollection<T> (which EnumerableAdapter.Count fast-paths to O(1)), the old approach — O(1) count check, then conditional O(1) First() — was slightly faster than always iterating the full collection. For test assertion code this doesn't matter, but it's worth noting the trade-off: correctness for lazy sequences in exchange for a full iteration on single-item eager collections.
IsEquivalentTo — IsEquivalentToAssertion.cs
Correctness: ✅
The _cachedExpectedFormat ??= string.Join(", ", _expected) pattern is correct and matches the existing EqualsAssertion._cachedExpectedFormat approach. The null-coalescing assignment is a benign double-compute race in the (unlikely) scenario of concurrent calls, and the result is idempotent.
Deferred Items
The decision to defer the ExpressionBuilder (60+ sites, contract-changing) and the boxing of ICollectionAdapter<T> struct adapters is the right call. Documenting both in the PR description with clear rationale is appreciated — it makes it easy to pick up in a follow-up without re-discovering the same analysis.
Summary
Solid, well-reasoned PR. All three optimizations are correct, the failure semantics are preserved, and the deferred items are properly justified. No blocking concerns.
…ut overload Adds the new `CheckHasSingleItem<TItem>(ICollectionAdapter<TItem>, out TItem?)` overload to the approved API snapshots across all TFMs. The overload is public to match its sibling `CheckHasSingleItemPredicate(..., out)` and the existing parameterless `CheckHasSingleItem` overload on the public CollectionChecks helper. Fixes the RunPublicAPITestsModule failure on Assertions_Library_Has_No_API_Changes.
There was a problem hiding this comment.
Code Review (Follow-up)
The previous review found no blocking concerns; this pass verifies the final state and checks for any lingering edge cases.
/ — CollectionAssertions.cs ✅
Single-pass enumerator approach is correct. Index alignment matches the old for (int i = 1; ...) loop — both start at 1 and refer to the same 0-based position in the original collection. Verified: equal-key adjacent items pass both ascending and descending checks (Compare(a, a) == 0 is neither > 0 nor < 0). No regressions.
HasSingleItem — CollectionChecks.cs + CollectionAssertions.cs ✅
Logic trace for the new overload is correct:
- Empty:
count == 0after loop → Failed,singleItem == default - Single:
singleItemcaptures item atcount == 0, loop ends withcount == 1→ Passed - Multi:
singleItemreset todefaultatcount == 1(second item hit), loop finishes with true total → Failed
One worth-noting trade-off (not a blocker): The new implementation always iterates the entire sequence, even when the collection is larger than 1. On the failing path for large sequences, this is a regression vs. the old adapter.Count (which is O(1) for ICollection<T>-backed adapters). The inline comment acknowledges this. For test assertion code this is acceptable — failure paths are rare in normal usage and correctness for lazy/non-replayable sequences justifies it. If performance on the failure path ever becomes a concern, a short-circuit-after-two-items approach with a separate full-count fallback could be considered, but that complexity isn't warranted here.
IsEquivalentTo — IsEquivalentToAssertion.cs ✅
The ??= caching pattern is idiomatic and matches EqualsAssertion._cachedExpectedFormat. No issues.
Public API Snapshots ✅
All four snapshot files updated consistently. The new CheckHasSingleItem<TItem>(adapter, out TItem? singleItem) overload is a pure addition (non-breaking).
Summary
The PR is in good shape. All three optimizations are correct, semantics are preserved, deferred items are properly documented, and the code is clean. No blocking concerns.
There was a problem hiding this comment.
Code Review (Follow-up)
The previous review found no blocking concerns; this pass verifies the final state and checks for any lingering edge cases.
IsOrderedBy / IsOrderedByDescending — CollectionAssertions.cs
Correctness: pass
Single-pass enumerator approach is correct. Index alignment matches the old for (int i = 1; ...) loop — both start at 1 and refer to the same 0-based position in the original collection. Verified: equal-key adjacent items pass both ascending and descending checks (Compare(a, a) == 0 is neither > 0 nor < 0). No regressions.
HasSingleItem — CollectionChecks.cs + CollectionAssertions.cs
Correctness: pass | Design note
Logic trace for the new overload is correct:
- Empty:
count == 0after loop → Failed,singleItem == default - Single:
singleItemcaptures item atcount == 0, loop ends withcount == 1→ Passed - Multi:
singleItemreset todefaultatcount == 1(second item hit), loop finishes with true total → Failed
Worth-noting trade-off (not a blocker): The new implementation always iterates the entire sequence, even when the collection has more than 1 item. On the failing path for large sequences, this is a regression vs. the old adapter.Count (which is O(1) for ICollection<T>-backed adapters). The inline comment acknowledges this. For test assertion code this is acceptable — failure paths are rare in normal usage and correctness for lazy/non-replayable sequences justifies it. A short-circuit-after-two-items approach with a separate full-count fallback could reduce this cost if it ever matters, but that added complexity isn't warranted here.
IsEquivalentTo — IsEquivalentToAssertion.cs
Correctness: pass
The ??= caching pattern is idiomatic and matches EqualsAssertion._cachedExpectedFormat. The double-compute race under concurrent calls is benign (idempotent result). No issues.
Public API Snapshots
All four snapshot files updated consistently. The new CheckHasSingleItem<TItem>(adapter, out TItem? singleItem) overload is a pure addition (non-breaking).
Summary
The PR is in good shape. All three optimizations are correct, failure semantics are preserved, deferred items are properly documented, and the code is clean. Previous review had no action items and there are no new concerns here.
Updated [Microsoft.Testing.Extensions.CodeCoverage](https://github.com/microsoft/codecoverage) from 18.6.2 to 18.7.0. <details> <summary>Release notes</summary> _Sourced from [Microsoft.Testing.Extensions.CodeCoverage's releases](https://github.com/microsoft/codecoverage/releases)._ No release notes found for this version range. Commits viewable in [compare view](https://github.com/microsoft/codecoverage/commits). </details> Updated [TUnit](https://github.com/thomhurst/TUnit) from 1.44.39 to 1.48.6. <details> <summary>Release notes</summary> _Sourced from [TUnit's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.48.6 <!-- Release notes generated using configuration in .github/release.yml at v1.48.6 --> ## What's Changed ### Other Changes * fix(sourcegen): fully-qualify Linq calls in params array binding (#6140) by @thomhurst in thomhurst/TUnit#6141 ### Dependencies * chore(deps): update tunit to 1.48.0 by @thomhurst in thomhurst/TUnit#6135 * chore(deps): update dependency polyfill to 10.7.1 by @thomhurst in thomhurst/TUnit#6137 * chore(deps): update dependency polyfill to 10.7.1 by @thomhurst in thomhurst/TUnit#6138 * chore(deps): update verify to 31.19.0 by @thomhurst in thomhurst/TUnit#6139 **Full Changelog**: thomhurst/TUnit@v1.48.0...v1.48.6 ## 1.48.0 <!-- Release notes generated using configuration in .github/release.yml at v1.48.0 --> ## What's Changed ### Other Changes * feat(html-report): baked-in C# syntax highlighting on Source tab by @slang25 in thomhurst/TUnit#6132 * feat(analyzers): suppress VSTHRD200 on test and hook methods by @thomhurst in thomhurst/TUnit#6123 * fix(source-gen): correct source location for cross-project inherited tests by @slang25 in thomhurst/TUnit#6133 * feat(assertions): add WasCalled to tunit mocks assertions by @robertcoltheart in thomhurst/TUnit#6126 * feat(arguments): bind array values to a single array test parameter by @thomhurst in thomhurst/TUnit#6122 * fix: populate retry/flaky attempt history in HTML report (#6119) by @thomhurst in thomhurst/TUnit#6124 ### Dependencies * chore(deps): update tunit to 1.47.0 by @thomhurst in thomhurst/TUnit#6115 * chore(deps): update dependency microsoft.visualstudio.threading.analyzers to 17.14.15 by @thomhurst in thomhurst/TUnit#6134 **Full Changelog**: thomhurst/TUnit@v1.47.0...v1.48.0 ## 1.47.0 <!-- Release notes generated using configuration in .github/release.yml at v1.47.0 --> ## What's Changed ### Other Changes * perf(engine): hoist GetParameters and dict-dedup AfterTestDiscovery hooks by @thomhurst in thomhurst/TUnit#6062 * perf(engine): hoist GetParameters and drop LINQ in reflection discovery by @thomhurst in thomhurst/TUnit#6063 * perf(engine): cache treenode filter path on TestMetadata by @thomhurst in thomhurst/TUnit#6064 * perf: use is T pattern in ReflectionExtensions.HasAttribute fallback (#6060) by @thomhurst in thomhurst/TUnit#6066 * perf: replace OrderBy().ToArray() with Array.Sort in ConstraintKeyScheduler by @thomhurst in thomhurst/TUnit#6067 * perf: pool HashSet in WaitingTestIndex.GetCandidatesForReleasedKeys by @thomhurst in thomhurst/TUnit#6069 * perf: collapse OfType chains in JUnitXmlWriter (#6052) by @thomhurst in thomhurst/TUnit#6070 * perf(engine): avoid closure allocation in AfterHookPairTracker.GetOrCreateAfterAssemblyTask (#6041) by @thomhurst in thomhurst/TUnit#6071 * perf: avoid closure allocation in BeforeHookTaskCache.GetOrCreateBeforeAssemblyTask (#6040) by @thomhurst in thomhurst/TUnit#6073 * perf: use TryAdd in TestDependencyResolver dependency dedupe by @thomhurst in thomhurst/TUnit#6068 * perf: replace LINQ Any with foreach in TestGenericTypeResolver (#6044) by @thomhurst in thomhurst/TUnit#6072 * perf: avoid Cast<object>().FirstOrDefault() iterator alloc in CastHelper (#6029) by @thomhurst in thomhurst/TUnit#6074 * perf(engine): avoid string round-trip when building nested type names (#6049) by @thomhurst in thomhurst/TUnit#6075 * perf(engine): replace Select+ToArray with manual Type[] build (#6043) by @thomhurst in thomhurst/TUnit#6076 * perf(core): replace OfType().FirstOrDefault()/.Any() with foreach in ClassConstructorHelper by @thomhurst in thomhurst/TUnit#6078 * perf(engine): avoid FirstOrDefault iterator alloc in TestGenericTypeResolver by @thomhurst in thomhurst/TUnit#6079 * perf(engine): use SearchValues<char> for reporter filename sanitization by @thomhurst in thomhurst/TUnit#6090 * perf: dedupe TestDataFormatter.FormatArguments with pooled StringBuilder by @thomhurst in thomhurst/TUnit#6088 * perf(engine): use MemoryExtensions.Split for path parsing in MetadataFilterMatcher by @thomhurst in thomhurst/TUnit#6085 * perf(engine): use CollectionsMarshal.GetValueRefOrAddDefault for dictionary index builds by @thomhurst in thomhurst/TUnit#6086 * perf(engine): replace LINQ Where closure with inline filter in MetadataDependencyExpander BFS by @thomhurst in thomhurst/TUnit#6084 * perf(engine): pool StringBuilder in DisplayNameBuilder.FormatArguments by @thomhurst in thomhurst/TUnit#6082 * Preserve specialized chaining after null assertions by @thomhurst in thomhurst/TUnit#6008 * perf: use EnumerateLines for line splitting in HtmlReportGenerator by @thomhurst in thomhurst/TUnit#6089 * perf: collapse Replace chain in TestNameFormatter.BuildTestId by @thomhurst in thomhurst/TUnit#6083 * perf: use OrdinalIgnoreCase Contains in HtmlReportGenerator span mapping by @thomhurst in thomhurst/TUnit#6093 * perf(assertions): avoid eager interpolated-string alloc in assertion source ctors by @thomhurst in thomhurst/TUnit#6091 * perf: optimize TestNameFormatter argument and bool formatting by @thomhurst in thomhurst/TUnit#6095 * perf: use FrozenSet/FrozenDictionary for read-only static lookups by @thomhurst in thomhurst/TUnit#6099 * perf: avoid GetCustomAttributes() + LINQ chain for per-property attribute scans by @thomhurst in thomhurst/TUnit#6098 * perf(engine): replace magic-string RequiredAttribute match with type check in ConstructorHelper by @thomhurst in thomhurst/TUnit#6087 * perf(core): replace Select+Func factory chain in DataSourceHelpers by @thomhurst in thomhurst/TUnit#6081 * perf: replace LINQ dependency extraction with manual loop by @thomhurst in thomhurst/TUnit#6096 * perf(core): avoid string[] alloc in ArgumentFormatter.FormatArguments by @thomhurst in thomhurst/TUnit#6080 * perf: use [GeneratedRegex] in MetadataFilterMatcher by @thomhurst in thomhurst/TUnit#6094 * perf: dedupe GetSimpleTypeName into shared TypeNameFormatter by @thomhurst in thomhurst/TUnit#6097 * fix: remove GitVersion MSBuild task, pin local builds to 99.99.99 (#6077) by @thomhurst in thomhurst/TUnit#6101 * HTML Report: source link + code snippet on Source tab (#5993) by @thomhurst in thomhurst/TUnit#6100 * perf(sourcegen): Single-pass attribute classification by @thomhurst in thomhurst/TUnit#6111 * perf(core): eliminate per-test allocations in TestDetails/HookMethod by @thomhurst in thomhurst/TUnit#6109 * perf: hoist char[] alloc in FsCheckPropertyTestExecutor to static SearchValues by @thomhurst in thomhurst/TUnit#6108 * perf(core): de-LINQ data-source expansion by @thomhurst in thomhurst/TUnit#6110 * perf: avoid LINQ chains in TestDependency equality and MethodDataSourceAttribute method matching by @thomhurst in thomhurst/TUnit#6092 * perf(engine): reduce allocations in reflection-mode discovery/execution by @thomhurst in thomhurst/TUnit#6113 * perf(assertions): allocation-free passing path (TUnit.Assertions) by @thomhurst in thomhurst/TUnit#6112 ### Dependencies ... (truncated) ## 1.46.0 <!-- Release notes generated using configuration in .github/release.yml at v1.46.0 --> ## What's Changed ### Other Changes * docs: add Rider VSTest conflict troubleshooting by @smolchanovsky in thomhurst/TUnit#5989 * Populate generated test metadata with full source spans by @Copilot in thomhurst/TUnit#5991 * Add devcontainer configuration by @Copilot in thomhurst/TUnit#5995 * fix: treenode filter pre-filter rejects parenthesised segments (#6026) by @thomhurst in thomhurst/TUnit#6027 * fix(engine): isolate per-session state under MTP server-mode concurrency (#6001) by @thomhurst in thomhurst/TUnit#6025 ### Dependencies * chore(deps): update dependency stackexchange.redis to 2.13.10 by @thomhurst in thomhurst/TUnit#5985 * chore(deps): update tunit to 1.45.29 by @thomhurst in thomhurst/TUnit#5986 * chore(deps): update dependency mockolate to 3.2.1 by @thomhurst in thomhurst/TUnit#5987 * chore(deps): update dependency microsoft.playwright to 1.60.0 by @thomhurst in thomhurst/TUnit#5988 * chore(deps): update dependency messagepack to 3.1.6 by @thomhurst in thomhurst/TUnit#5992 * chore(deps): update dependency polyfill to 10.7.0 by @thomhurst in thomhurst/TUnit#5998 * chore(deps): update dependency polyfill to 10.7.0 by @thomhurst in thomhurst/TUnit#5997 * chore(deps): update verify to 31.17.0 by @thomhurst in thomhurst/TUnit#6000 * chore(deps): update verify to 31.18.0 by @thomhurst in thomhurst/TUnit#6013 * chore(deps): update dependency microsoft.net.test.sdk to 18.6.0 by @thomhurst in thomhurst/TUnit#6016 * chore(deps): update dependency dompurify to v3.4.6 by @thomhurst in thomhurst/TUnit#6015 * chore(deps): update dependency dompurify to v3.4.7 by @thomhurst in thomhurst/TUnit#6019 * chore(deps): update dependency npgsql to 10.0.3 by @thomhurst in thomhurst/TUnit#6020 * chore(deps): update dependency stackexchange.redis to 2.13.17 by @thomhurst in thomhurst/TUnit#6021 * chore(deps): update dependency npgsql.entityframeworkcore.postgresql to 10.0.2 by @thomhurst in thomhurst/TUnit#6022 ## New Contributors * @smolchanovsky made their first contribution in thomhurst/TUnit#5989 **Full Changelog**: thomhurst/TUnit@v1.45.29...v1.46.0 ## 1.45.29 <!-- Release notes generated using configuration in .github/release.yml at v1.45.29 --> ## What's Changed ### Other Changes * Fix shared fixture lifetime for reused discovery instances by @thomhurst in thomhurst/TUnit#5983 * Preserve override accessibility in generated mocks by @thomhurst in thomhurst/TUnit#5984 ### Dependencies * chore(deps): update tunit to 1.45.22 by @thomhurst in thomhurst/TUnit#5974 * chore(deps): update dependency messagepack to 3.1.5 by @thomhurst in thomhurst/TUnit#5978 * chore(deps): update aspire to 13.3.5 by @thomhurst in thomhurst/TUnit#5980 **Full Changelog**: thomhurst/TUnit@v1.45.22...v1.45.29 ## 1.45.22 <!-- Release notes generated using configuration in .github/release.yml at v1.45.22 --> ## What's Changed ### Other Changes * Remove ".NET" from Aspire references by @antmdvs in thomhurst/TUnit#5968 * Fix chained mock setup behavior by @thomhurst in thomhurst/TUnit#5973 ### Dependencies * chore(deps): update tunit to 1.45.8 by @thomhurst in thomhurst/TUnit#5958 * chore(deps): update dependency nunit to 4.6.1 by @thomhurst in thomhurst/TUnit#5961 * chore(deps): update dependency testcontainers.postgresql to 4.12.0 by @thomhurst in thomhurst/TUnit#5963 * chore(deps): update dependency testcontainers.redis to 4.12.0 by @thomhurst in thomhurst/TUnit#5965 * chore(deps): update dependency testcontainers.kafka to 4.12.0 by @thomhurst in thomhurst/TUnit#5962 * chore(deps): update aspire to 13.3.4 by @thomhurst in thomhurst/TUnit#5966 * chore(deps): bump webpack-dev-server from 5.2.2 to 5.2.4 in /docs by @dependabot[bot] in thomhurst/TUnit#5964 ## New Contributors * @antmdvs made their first contribution in thomhurst/TUnit#5968 **Full Changelog**: thomhurst/TUnit@v1.45.8...v1.45.22 ## 1.45.8 <!-- Release notes generated using configuration in .github/release.yml at v1.45.8 --> ## What's Changed ### Other Changes * fix(aspire): route CreateHttpClient through IHttpClientFactory by @thomhurst in thomhurst/TUnit#5957 ### Dependencies * chore(deps): update tunit to 1.45.0 by @thomhurst in thomhurst/TUnit#5949 * chore(deps): update dependency dompurify to v3.4.5 by @thomhurst in thomhurst/TUnit#5951 * chore(deps): update dependency microsoft.testing.extensions.codecoverage to 18.7.0 by @thomhurst in thomhurst/TUnit#5953 * chore(deps): update dependency coverlet.collector to 10.0.1 by @thomhurst in thomhurst/TUnit#5952 * chore(deps): update dependency polyfill to 10.6.0 by @thomhurst in thomhurst/TUnit#5955 * chore(deps): update dependency polyfill to 10.6.0 by @thomhurst in thomhurst/TUnit#5954 **Full Changelog**: thomhurst/TUnit@v1.45.0...v1.45.8 ## 1.45.0 <!-- Release notes generated using configuration in .github/release.yml at v1.45.0 --> ## What's Changed ### Other Changes * fix(generator): place CallerArgumentExpression before params in [GenerateAssertion] emit by @JohnVerheij in thomhurst/TUnit#5940 * fix(sourcegen): drop covariant TActual when [GenerateAssertion] method has its own type parameters by @JohnVerheij in thomhurst/TUnit#5935 * feat(assertions): add CancellationToken overload to WaitsFor and Eventually by @JohnVerheij in thomhurst/TUnit#5938 * fix(html-report): extract categories using MTP Key=name convention by @thomhurst in thomhurst/TUnit#5946 * feat(html-report): rewrite as split-pane design template by @thomhurst in thomhurst/TUnit#5947 ### Dependencies * chore(deps): update microsoft.testing to 2.2.3 by @thomhurst in thomhurst/TUnit#5927 * chore(deps): update mstest to 4.2.3 by @thomhurst in thomhurst/TUnit#5928 * chore(deps): update tunit to 1.44.39 by @thomhurst in thomhurst/TUnit#5929 * chore(deps): update aspire to 13.3.3 by @thomhurst in thomhurst/TUnit#5933 * chore(deps): update dependency dompurify to v3.4.4 by @thomhurst in thomhurst/TUnit#5944 * chore(deps): update dependency qs to v6.15.2 by @thomhurst in thomhurst/TUnit#5941 **Full Changelog**: thomhurst/TUnit@v1.44.39...v1.45.0 Commits viewable in [compare view](thomhurst/TUnit@v1.44.39...v1.48.6). </details> Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Updated [TUnit](https://github.com/thomhurst/TUnit) from 1.45.29 to 1.48.6. <details> <summary>Release notes</summary> _Sourced from [TUnit's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.48.6 <!-- Release notes generated using configuration in .github/release.yml at v1.48.6 --> ## What's Changed ### Other Changes * fix(sourcegen): fully-qualify Linq calls in params array binding (#6140) by @thomhurst in thomhurst/TUnit#6141 ### Dependencies * chore(deps): update tunit to 1.48.0 by @thomhurst in thomhurst/TUnit#6135 * chore(deps): update dependency polyfill to 10.7.1 by @thomhurst in thomhurst/TUnit#6137 * chore(deps): update dependency polyfill to 10.7.1 by @thomhurst in thomhurst/TUnit#6138 * chore(deps): update verify to 31.19.0 by @thomhurst in thomhurst/TUnit#6139 **Full Changelog**: thomhurst/TUnit@v1.48.0...v1.48.6 ## 1.48.0 <!-- Release notes generated using configuration in .github/release.yml at v1.48.0 --> ## What's Changed ### Other Changes * feat(html-report): baked-in C# syntax highlighting on Source tab by @slang25 in thomhurst/TUnit#6132 * feat(analyzers): suppress VSTHRD200 on test and hook methods by @thomhurst in thomhurst/TUnit#6123 * fix(source-gen): correct source location for cross-project inherited tests by @slang25 in thomhurst/TUnit#6133 * feat(assertions): add WasCalled to tunit mocks assertions by @robertcoltheart in thomhurst/TUnit#6126 * feat(arguments): bind array values to a single array test parameter by @thomhurst in thomhurst/TUnit#6122 * fix: populate retry/flaky attempt history in HTML report (#6119) by @thomhurst in thomhurst/TUnit#6124 ### Dependencies * chore(deps): update tunit to 1.47.0 by @thomhurst in thomhurst/TUnit#6115 * chore(deps): update dependency microsoft.visualstudio.threading.analyzers to 17.14.15 by @thomhurst in thomhurst/TUnit#6134 **Full Changelog**: thomhurst/TUnit@v1.47.0...v1.48.0 ## 1.47.0 <!-- Release notes generated using configuration in .github/release.yml at v1.47.0 --> ## What's Changed ### Other Changes * perf(engine): hoist GetParameters and dict-dedup AfterTestDiscovery hooks by @thomhurst in thomhurst/TUnit#6062 * perf(engine): hoist GetParameters and drop LINQ in reflection discovery by @thomhurst in thomhurst/TUnit#6063 * perf(engine): cache treenode filter path on TestMetadata by @thomhurst in thomhurst/TUnit#6064 * perf: use is T pattern in ReflectionExtensions.HasAttribute fallback (#6060) by @thomhurst in thomhurst/TUnit#6066 * perf: replace OrderBy().ToArray() with Array.Sort in ConstraintKeyScheduler by @thomhurst in thomhurst/TUnit#6067 * perf: pool HashSet in WaitingTestIndex.GetCandidatesForReleasedKeys by @thomhurst in thomhurst/TUnit#6069 * perf: collapse OfType chains in JUnitXmlWriter (#6052) by @thomhurst in thomhurst/TUnit#6070 * perf(engine): avoid closure allocation in AfterHookPairTracker.GetOrCreateAfterAssemblyTask (#6041) by @thomhurst in thomhurst/TUnit#6071 * perf: avoid closure allocation in BeforeHookTaskCache.GetOrCreateBeforeAssemblyTask (#6040) by @thomhurst in thomhurst/TUnit#6073 * perf: use TryAdd in TestDependencyResolver dependency dedupe by @thomhurst in thomhurst/TUnit#6068 * perf: replace LINQ Any with foreach in TestGenericTypeResolver (#6044) by @thomhurst in thomhurst/TUnit#6072 * perf: avoid Cast<object>().FirstOrDefault() iterator alloc in CastHelper (#6029) by @thomhurst in thomhurst/TUnit#6074 * perf(engine): avoid string round-trip when building nested type names (#6049) by @thomhurst in thomhurst/TUnit#6075 * perf(engine): replace Select+ToArray with manual Type[] build (#6043) by @thomhurst in thomhurst/TUnit#6076 * perf(core): replace OfType().FirstOrDefault()/.Any() with foreach in ClassConstructorHelper by @thomhurst in thomhurst/TUnit#6078 * perf(engine): avoid FirstOrDefault iterator alloc in TestGenericTypeResolver by @thomhurst in thomhurst/TUnit#6079 * perf(engine): use SearchValues<char> for reporter filename sanitization by @thomhurst in thomhurst/TUnit#6090 * perf: dedupe TestDataFormatter.FormatArguments with pooled StringBuilder by @thomhurst in thomhurst/TUnit#6088 * perf(engine): use MemoryExtensions.Split for path parsing in MetadataFilterMatcher by @thomhurst in thomhurst/TUnit#6085 * perf(engine): use CollectionsMarshal.GetValueRefOrAddDefault for dictionary index builds by @thomhurst in thomhurst/TUnit#6086 * perf(engine): replace LINQ Where closure with inline filter in MetadataDependencyExpander BFS by @thomhurst in thomhurst/TUnit#6084 * perf(engine): pool StringBuilder in DisplayNameBuilder.FormatArguments by @thomhurst in thomhurst/TUnit#6082 * Preserve specialized chaining after null assertions by @thomhurst in thomhurst/TUnit#6008 * perf: use EnumerateLines for line splitting in HtmlReportGenerator by @thomhurst in thomhurst/TUnit#6089 * perf: collapse Replace chain in TestNameFormatter.BuildTestId by @thomhurst in thomhurst/TUnit#6083 * perf: use OrdinalIgnoreCase Contains in HtmlReportGenerator span mapping by @thomhurst in thomhurst/TUnit#6093 * perf(assertions): avoid eager interpolated-string alloc in assertion source ctors by @thomhurst in thomhurst/TUnit#6091 * perf: optimize TestNameFormatter argument and bool formatting by @thomhurst in thomhurst/TUnit#6095 * perf: use FrozenSet/FrozenDictionary for read-only static lookups by @thomhurst in thomhurst/TUnit#6099 * perf: avoid GetCustomAttributes() + LINQ chain for per-property attribute scans by @thomhurst in thomhurst/TUnit#6098 * perf(engine): replace magic-string RequiredAttribute match with type check in ConstructorHelper by @thomhurst in thomhurst/TUnit#6087 * perf(core): replace Select+Func factory chain in DataSourceHelpers by @thomhurst in thomhurst/TUnit#6081 * perf: replace LINQ dependency extraction with manual loop by @thomhurst in thomhurst/TUnit#6096 * perf(core): avoid string[] alloc in ArgumentFormatter.FormatArguments by @thomhurst in thomhurst/TUnit#6080 * perf: use [GeneratedRegex] in MetadataFilterMatcher by @thomhurst in thomhurst/TUnit#6094 * perf: dedupe GetSimpleTypeName into shared TypeNameFormatter by @thomhurst in thomhurst/TUnit#6097 * fix: remove GitVersion MSBuild task, pin local builds to 99.99.99 (#6077) by @thomhurst in thomhurst/TUnit#6101 * HTML Report: source link + code snippet on Source tab (#5993) by @thomhurst in thomhurst/TUnit#6100 * perf(sourcegen): Single-pass attribute classification by @thomhurst in thomhurst/TUnit#6111 * perf(core): eliminate per-test allocations in TestDetails/HookMethod by @thomhurst in thomhurst/TUnit#6109 * perf: hoist char[] alloc in FsCheckPropertyTestExecutor to static SearchValues by @thomhurst in thomhurst/TUnit#6108 * perf(core): de-LINQ data-source expansion by @thomhurst in thomhurst/TUnit#6110 * perf: avoid LINQ chains in TestDependency equality and MethodDataSourceAttribute method matching by @thomhurst in thomhurst/TUnit#6092 * perf(engine): reduce allocations in reflection-mode discovery/execution by @thomhurst in thomhurst/TUnit#6113 * perf(assertions): allocation-free passing path (TUnit.Assertions) by @thomhurst in thomhurst/TUnit#6112 ### Dependencies ... (truncated) ## 1.46.0 <!-- Release notes generated using configuration in .github/release.yml at v1.46.0 --> ## What's Changed ### Other Changes * docs: add Rider VSTest conflict troubleshooting by @smolchanovsky in thomhurst/TUnit#5989 * Populate generated test metadata with full source spans by @Copilot in thomhurst/TUnit#5991 * Add devcontainer configuration by @Copilot in thomhurst/TUnit#5995 * fix: treenode filter pre-filter rejects parenthesised segments (#6026) by @thomhurst in thomhurst/TUnit#6027 * fix(engine): isolate per-session state under MTP server-mode concurrency (#6001) by @thomhurst in thomhurst/TUnit#6025 ### Dependencies * chore(deps): update dependency stackexchange.redis to 2.13.10 by @thomhurst in thomhurst/TUnit#5985 * chore(deps): update tunit to 1.45.29 by @thomhurst in thomhurst/TUnit#5986 * chore(deps): update dependency mockolate to 3.2.1 by @thomhurst in thomhurst/TUnit#5987 * chore(deps): update dependency microsoft.playwright to 1.60.0 by @thomhurst in thomhurst/TUnit#5988 * chore(deps): update dependency messagepack to 3.1.6 by @thomhurst in thomhurst/TUnit#5992 * chore(deps): update dependency polyfill to 10.7.0 by @thomhurst in thomhurst/TUnit#5998 * chore(deps): update dependency polyfill to 10.7.0 by @thomhurst in thomhurst/TUnit#5997 * chore(deps): update verify to 31.17.0 by @thomhurst in thomhurst/TUnit#6000 * chore(deps): update verify to 31.18.0 by @thomhurst in thomhurst/TUnit#6013 * chore(deps): update dependency microsoft.net.test.sdk to 18.6.0 by @thomhurst in thomhurst/TUnit#6016 * chore(deps): update dependency dompurify to v3.4.6 by @thomhurst in thomhurst/TUnit#6015 * chore(deps): update dependency dompurify to v3.4.7 by @thomhurst in thomhurst/TUnit#6019 * chore(deps): update dependency npgsql to 10.0.3 by @thomhurst in thomhurst/TUnit#6020 * chore(deps): update dependency stackexchange.redis to 2.13.17 by @thomhurst in thomhurst/TUnit#6021 * chore(deps): update dependency npgsql.entityframeworkcore.postgresql to 10.0.2 by @thomhurst in thomhurst/TUnit#6022 ## New Contributors * @smolchanovsky made their first contribution in thomhurst/TUnit#5989 **Full Changelog**: thomhurst/TUnit@v1.45.29...v1.46.0 Commits viewable in [compare view](thomhurst/TUnit@v1.45.29...v1.48.6). </details> Updated [TUnit.AspNetCore](https://github.com/thomhurst/TUnit) from 1.45.29 to 1.48.6. <details> <summary>Release notes</summary> _Sourced from [TUnit.AspNetCore's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.48.6 <!-- Release notes generated using configuration in .github/release.yml at v1.48.6 --> ## What's Changed ### Other Changes * fix(sourcegen): fully-qualify Linq calls in params array binding (#6140) by @thomhurst in thomhurst/TUnit#6141 ### Dependencies * chore(deps): update tunit to 1.48.0 by @thomhurst in thomhurst/TUnit#6135 * chore(deps): update dependency polyfill to 10.7.1 by @thomhurst in thomhurst/TUnit#6137 * chore(deps): update dependency polyfill to 10.7.1 by @thomhurst in thomhurst/TUnit#6138 * chore(deps): update verify to 31.19.0 by @thomhurst in thomhurst/TUnit#6139 **Full Changelog**: thomhurst/TUnit@v1.48.0...v1.48.6 ## 1.48.0 <!-- Release notes generated using configuration in .github/release.yml at v1.48.0 --> ## What's Changed ### Other Changes * feat(html-report): baked-in C# syntax highlighting on Source tab by @slang25 in thomhurst/TUnit#6132 * feat(analyzers): suppress VSTHRD200 on test and hook methods by @thomhurst in thomhurst/TUnit#6123 * fix(source-gen): correct source location for cross-project inherited tests by @slang25 in thomhurst/TUnit#6133 * feat(assertions): add WasCalled to tunit mocks assertions by @robertcoltheart in thomhurst/TUnit#6126 * feat(arguments): bind array values to a single array test parameter by @thomhurst in thomhurst/TUnit#6122 * fix: populate retry/flaky attempt history in HTML report (#6119) by @thomhurst in thomhurst/TUnit#6124 ### Dependencies * chore(deps): update tunit to 1.47.0 by @thomhurst in thomhurst/TUnit#6115 * chore(deps): update dependency microsoft.visualstudio.threading.analyzers to 17.14.15 by @thomhurst in thomhurst/TUnit#6134 **Full Changelog**: thomhurst/TUnit@v1.47.0...v1.48.0 ## 1.47.0 <!-- Release notes generated using configuration in .github/release.yml at v1.47.0 --> ## What's Changed ### Other Changes * perf(engine): hoist GetParameters and dict-dedup AfterTestDiscovery hooks by @thomhurst in thomhurst/TUnit#6062 * perf(engine): hoist GetParameters and drop LINQ in reflection discovery by @thomhurst in thomhurst/TUnit#6063 * perf(engine): cache treenode filter path on TestMetadata by @thomhurst in thomhurst/TUnit#6064 * perf: use is T pattern in ReflectionExtensions.HasAttribute fallback (#6060) by @thomhurst in thomhurst/TUnit#6066 * perf: replace OrderBy().ToArray() with Array.Sort in ConstraintKeyScheduler by @thomhurst in thomhurst/TUnit#6067 * perf: pool HashSet in WaitingTestIndex.GetCandidatesForReleasedKeys by @thomhurst in thomhurst/TUnit#6069 * perf: collapse OfType chains in JUnitXmlWriter (#6052) by @thomhurst in thomhurst/TUnit#6070 * perf(engine): avoid closure allocation in AfterHookPairTracker.GetOrCreateAfterAssemblyTask (#6041) by @thomhurst in thomhurst/TUnit#6071 * perf: avoid closure allocation in BeforeHookTaskCache.GetOrCreateBeforeAssemblyTask (#6040) by @thomhurst in thomhurst/TUnit#6073 * perf: use TryAdd in TestDependencyResolver dependency dedupe by @thomhurst in thomhurst/TUnit#6068 * perf: replace LINQ Any with foreach in TestGenericTypeResolver (#6044) by @thomhurst in thomhurst/TUnit#6072 * perf: avoid Cast<object>().FirstOrDefault() iterator alloc in CastHelper (#6029) by @thomhurst in thomhurst/TUnit#6074 * perf(engine): avoid string round-trip when building nested type names (#6049) by @thomhurst in thomhurst/TUnit#6075 * perf(engine): replace Select+ToArray with manual Type[] build (#6043) by @thomhurst in thomhurst/TUnit#6076 * perf(core): replace OfType().FirstOrDefault()/.Any() with foreach in ClassConstructorHelper by @thomhurst in thomhurst/TUnit#6078 * perf(engine): avoid FirstOrDefault iterator alloc in TestGenericTypeResolver by @thomhurst in thomhurst/TUnit#6079 * perf(engine): use SearchValues<char> for reporter filename sanitization by @thomhurst in thomhurst/TUnit#6090 * perf: dedupe TestDataFormatter.FormatArguments with pooled StringBuilder by @thomhurst in thomhurst/TUnit#6088 * perf(engine): use MemoryExtensions.Split for path parsing in MetadataFilterMatcher by @thomhurst in thomhurst/TUnit#6085 * perf(engine): use CollectionsMarshal.GetValueRefOrAddDefault for dictionary index builds by @thomhurst in thomhurst/TUnit#6086 * perf(engine): replace LINQ Where closure with inline filter in MetadataDependencyExpander BFS by @thomhurst in thomhurst/TUnit#6084 * perf(engine): pool StringBuilder in DisplayNameBuilder.FormatArguments by @thomhurst in thomhurst/TUnit#6082 * Preserve specialized chaining after null assertions by @thomhurst in thomhurst/TUnit#6008 * perf: use EnumerateLines for line splitting in HtmlReportGenerator by @thomhurst in thomhurst/TUnit#6089 * perf: collapse Replace chain in TestNameFormatter.BuildTestId by @thomhurst in thomhurst/TUnit#6083 * perf: use OrdinalIgnoreCase Contains in HtmlReportGenerator span mapping by @thomhurst in thomhurst/TUnit#6093 * perf(assertions): avoid eager interpolated-string alloc in assertion source ctors by @thomhurst in thomhurst/TUnit#6091 * perf: optimize TestNameFormatter argument and bool formatting by @thomhurst in thomhurst/TUnit#6095 * perf: use FrozenSet/FrozenDictionary for read-only static lookups by @thomhurst in thomhurst/TUnit#6099 * perf: avoid GetCustomAttributes() + LINQ chain for per-property attribute scans by @thomhurst in thomhurst/TUnit#6098 * perf(engine): replace magic-string RequiredAttribute match with type check in ConstructorHelper by @thomhurst in thomhurst/TUnit#6087 * perf(core): replace Select+Func factory chain in DataSourceHelpers by @thomhurst in thomhurst/TUnit#6081 * perf: replace LINQ dependency extraction with manual loop by @thomhurst in thomhurst/TUnit#6096 * perf(core): avoid string[] alloc in ArgumentFormatter.FormatArguments by @thomhurst in thomhurst/TUnit#6080 * perf: use [GeneratedRegex] in MetadataFilterMatcher by @thomhurst in thomhurst/TUnit#6094 * perf: dedupe GetSimpleTypeName into shared TypeNameFormatter by @thomhurst in thomhurst/TUnit#6097 * fix: remove GitVersion MSBuild task, pin local builds to 99.99.99 (#6077) by @thomhurst in thomhurst/TUnit#6101 * HTML Report: source link + code snippet on Source tab (#5993) by @thomhurst in thomhurst/TUnit#6100 * perf(sourcegen): Single-pass attribute classification by @thomhurst in thomhurst/TUnit#6111 * perf(core): eliminate per-test allocations in TestDetails/HookMethod by @thomhurst in thomhurst/TUnit#6109 * perf: hoist char[] alloc in FsCheckPropertyTestExecutor to static SearchValues by @thomhurst in thomhurst/TUnit#6108 * perf(core): de-LINQ data-source expansion by @thomhurst in thomhurst/TUnit#6110 * perf: avoid LINQ chains in TestDependency equality and MethodDataSourceAttribute method matching by @thomhurst in thomhurst/TUnit#6092 * perf(engine): reduce allocations in reflection-mode discovery/execution by @thomhurst in thomhurst/TUnit#6113 * perf(assertions): allocation-free passing path (TUnit.Assertions) by @thomhurst in thomhurst/TUnit#6112 ### Dependencies ... (truncated) ## 1.46.0 <!-- Release notes generated using configuration in .github/release.yml at v1.46.0 --> ## What's Changed ### Other Changes * docs: add Rider VSTest conflict troubleshooting by @smolchanovsky in thomhurst/TUnit#5989 * Populate generated test metadata with full source spans by @Copilot in thomhurst/TUnit#5991 * Add devcontainer configuration by @Copilot in thomhurst/TUnit#5995 * fix: treenode filter pre-filter rejects parenthesised segments (#6026) by @thomhurst in thomhurst/TUnit#6027 * fix(engine): isolate per-session state under MTP server-mode concurrency (#6001) by @thomhurst in thomhurst/TUnit#6025 ### Dependencies * chore(deps): update dependency stackexchange.redis to 2.13.10 by @thomhurst in thomhurst/TUnit#5985 * chore(deps): update tunit to 1.45.29 by @thomhurst in thomhurst/TUnit#5986 * chore(deps): update dependency mockolate to 3.2.1 by @thomhurst in thomhurst/TUnit#5987 * chore(deps): update dependency microsoft.playwright to 1.60.0 by @thomhurst in thomhurst/TUnit#5988 * chore(deps): update dependency messagepack to 3.1.6 by @thomhurst in thomhurst/TUnit#5992 * chore(deps): update dependency polyfill to 10.7.0 by @thomhurst in thomhurst/TUnit#5998 * chore(deps): update dependency polyfill to 10.7.0 by @thomhurst in thomhurst/TUnit#5997 * chore(deps): update verify to 31.17.0 by @thomhurst in thomhurst/TUnit#6000 * chore(deps): update verify to 31.18.0 by @thomhurst in thomhurst/TUnit#6013 * chore(deps): update dependency microsoft.net.test.sdk to 18.6.0 by @thomhurst in thomhurst/TUnit#6016 * chore(deps): update dependency dompurify to v3.4.6 by @thomhurst in thomhurst/TUnit#6015 * chore(deps): update dependency dompurify to v3.4.7 by @thomhurst in thomhurst/TUnit#6019 * chore(deps): update dependency npgsql to 10.0.3 by @thomhurst in thomhurst/TUnit#6020 * chore(deps): update dependency stackexchange.redis to 2.13.17 by @thomhurst in thomhurst/TUnit#6021 * chore(deps): update dependency npgsql.entityframeworkcore.postgresql to 10.0.2 by @thomhurst in thomhurst/TUnit#6022 ## New Contributors * @smolchanovsky made their first contribution in thomhurst/TUnit#5989 **Full Changelog**: thomhurst/TUnit@v1.45.29...v1.46.0 Commits viewable in [compare view](thomhurst/TUnit@v1.45.29...v1.48.6). </details> Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Updated [TUnit.Core](https://github.com/thomhurst/TUnit) from 1.45.29 to 1.48.6. <details> <summary>Release notes</summary> _Sourced from [TUnit.Core's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.48.6 <!-- Release notes generated using configuration in .github/release.yml at v1.48.6 --> ## What's Changed ### Other Changes * fix(sourcegen): fully-qualify Linq calls in params array binding (#6140) by @thomhurst in thomhurst/TUnit#6141 ### Dependencies * chore(deps): update tunit to 1.48.0 by @thomhurst in thomhurst/TUnit#6135 * chore(deps): update dependency polyfill to 10.7.1 by @thomhurst in thomhurst/TUnit#6137 * chore(deps): update dependency polyfill to 10.7.1 by @thomhurst in thomhurst/TUnit#6138 * chore(deps): update verify to 31.19.0 by @thomhurst in thomhurst/TUnit#6139 **Full Changelog**: thomhurst/TUnit@v1.48.0...v1.48.6 ## 1.48.0 <!-- Release notes generated using configuration in .github/release.yml at v1.48.0 --> ## What's Changed ### Other Changes * feat(html-report): baked-in C# syntax highlighting on Source tab by @slang25 in thomhurst/TUnit#6132 * feat(analyzers): suppress VSTHRD200 on test and hook methods by @thomhurst in thomhurst/TUnit#6123 * fix(source-gen): correct source location for cross-project inherited tests by @slang25 in thomhurst/TUnit#6133 * feat(assertions): add WasCalled to tunit mocks assertions by @robertcoltheart in thomhurst/TUnit#6126 * feat(arguments): bind array values to a single array test parameter by @thomhurst in thomhurst/TUnit#6122 * fix: populate retry/flaky attempt history in HTML report (#6119) by @thomhurst in thomhurst/TUnit#6124 ### Dependencies * chore(deps): update tunit to 1.47.0 by @thomhurst in thomhurst/TUnit#6115 * chore(deps): update dependency microsoft.visualstudio.threading.analyzers to 17.14.15 by @thomhurst in thomhurst/TUnit#6134 **Full Changelog**: thomhurst/TUnit@v1.47.0...v1.48.0 ## 1.47.0 <!-- Release notes generated using configuration in .github/release.yml at v1.47.0 --> ## What's Changed ### Other Changes * perf(engine): hoist GetParameters and dict-dedup AfterTestDiscovery hooks by @thomhurst in thomhurst/TUnit#6062 * perf(engine): hoist GetParameters and drop LINQ in reflection discovery by @thomhurst in thomhurst/TUnit#6063 * perf(engine): cache treenode filter path on TestMetadata by @thomhurst in thomhurst/TUnit#6064 * perf: use is T pattern in ReflectionExtensions.HasAttribute fallback (#6060) by @thomhurst in thomhurst/TUnit#6066 * perf: replace OrderBy().ToArray() with Array.Sort in ConstraintKeyScheduler by @thomhurst in thomhurst/TUnit#6067 * perf: pool HashSet in WaitingTestIndex.GetCandidatesForReleasedKeys by @thomhurst in thomhurst/TUnit#6069 * perf: collapse OfType chains in JUnitXmlWriter (#6052) by @thomhurst in thomhurst/TUnit#6070 * perf(engine): avoid closure allocation in AfterHookPairTracker.GetOrCreateAfterAssemblyTask (#6041) by @thomhurst in thomhurst/TUnit#6071 * perf: avoid closure allocation in BeforeHookTaskCache.GetOrCreateBeforeAssemblyTask (#6040) by @thomhurst in thomhurst/TUnit#6073 * perf: use TryAdd in TestDependencyResolver dependency dedupe by @thomhurst in thomhurst/TUnit#6068 * perf: replace LINQ Any with foreach in TestGenericTypeResolver (#6044) by @thomhurst in thomhurst/TUnit#6072 * perf: avoid Cast<object>().FirstOrDefault() iterator alloc in CastHelper (#6029) by @thomhurst in thomhurst/TUnit#6074 * perf(engine): avoid string round-trip when building nested type names (#6049) by @thomhurst in thomhurst/TUnit#6075 * perf(engine): replace Select+ToArray with manual Type[] build (#6043) by @thomhurst in thomhurst/TUnit#6076 * perf(core): replace OfType().FirstOrDefault()/.Any() with foreach in ClassConstructorHelper by @thomhurst in thomhurst/TUnit#6078 * perf(engine): avoid FirstOrDefault iterator alloc in TestGenericTypeResolver by @thomhurst in thomhurst/TUnit#6079 * perf(engine): use SearchValues<char> for reporter filename sanitization by @thomhurst in thomhurst/TUnit#6090 * perf: dedupe TestDataFormatter.FormatArguments with pooled StringBuilder by @thomhurst in thomhurst/TUnit#6088 * perf(engine): use MemoryExtensions.Split for path parsing in MetadataFilterMatcher by @thomhurst in thomhurst/TUnit#6085 * perf(engine): use CollectionsMarshal.GetValueRefOrAddDefault for dictionary index builds by @thomhurst in thomhurst/TUnit#6086 * perf(engine): replace LINQ Where closure with inline filter in MetadataDependencyExpander BFS by @thomhurst in thomhurst/TUnit#6084 * perf(engine): pool StringBuilder in DisplayNameBuilder.FormatArguments by @thomhurst in thomhurst/TUnit#6082 * Preserve specialized chaining after null assertions by @thomhurst in thomhurst/TUnit#6008 * perf: use EnumerateLines for line splitting in HtmlReportGenerator by @thomhurst in thomhurst/TUnit#6089 * perf: collapse Replace chain in TestNameFormatter.BuildTestId by @thomhurst in thomhurst/TUnit#6083 * perf: use OrdinalIgnoreCase Contains in HtmlReportGenerator span mapping by @thomhurst in thomhurst/TUnit#6093 * perf(assertions): avoid eager interpolated-string alloc in assertion source ctors by @thomhurst in thomhurst/TUnit#6091 * perf: optimize TestNameFormatter argument and bool formatting by @thomhurst in thomhurst/TUnit#6095 * perf: use FrozenSet/FrozenDictionary for read-only static lookups by @thomhurst in thomhurst/TUnit#6099 * perf: avoid GetCustomAttributes() + LINQ chain for per-property attribute scans by @thomhurst in thomhurst/TUnit#6098 * perf(engine): replace magic-string RequiredAttribute match with type check in ConstructorHelper by @thomhurst in thomhurst/TUnit#6087 * perf(core): replace Select+Func factory chain in DataSourceHelpers by @thomhurst in thomhurst/TUnit#6081 * perf: replace LINQ dependency extraction with manual loop by @thomhurst in thomhurst/TUnit#6096 * perf(core): avoid string[] alloc in ArgumentFormatter.FormatArguments by @thomhurst in thomhurst/TUnit#6080 * perf: use [GeneratedRegex] in MetadataFilterMatcher by @thomhurst in thomhurst/TUnit#6094 * perf: dedupe GetSimpleTypeName into shared TypeNameFormatter by @thomhurst in thomhurst/TUnit#6097 * fix: remove GitVersion MSBuild task, pin local builds to 99.99.99 (#6077) by @thomhurst in thomhurst/TUnit#6101 * HTML Report: source link + code snippet on Source tab (#5993) by @thomhurst in thomhurst/TUnit#6100 * perf(sourcegen): Single-pass attribute classification by @thomhurst in thomhurst/TUnit#6111 * perf(core): eliminate per-test allocations in TestDetails/HookMethod by @thomhurst in thomhurst/TUnit#6109 * perf: hoist char[] alloc in FsCheckPropertyTestExecutor to static SearchValues by @thomhurst in thomhurst/TUnit#6108 * perf(core): de-LINQ data-source expansion by @thomhurst in thomhurst/TUnit#6110 * perf: avoid LINQ chains in TestDependency equality and MethodDataSourceAttribute method matching by @thomhurst in thomhurst/TUnit#6092 * perf(engine): reduce allocations in reflection-mode discovery/execution by @thomhurst in thomhurst/TUnit#6113 * perf(assertions): allocation-free passing path (TUnit.Assertions) by @thomhurst in thomhurst/TUnit#6112 ### Dependencies ... (truncated) ## 1.46.0 <!-- Release notes generated using configuration in .github/release.yml at v1.46.0 --> ## What's Changed ### Other Changes * docs: add Rider VSTest conflict troubleshooting by @smolchanovsky in thomhurst/TUnit#5989 * Populate generated test metadata with full source spans by @Copilot in thomhurst/TUnit#5991 * Add devcontainer configuration by @Copilot in thomhurst/TUnit#5995 * fix: treenode filter pre-filter rejects parenthesised segments (#6026) by @thomhurst in thomhurst/TUnit#6027 * fix(engine): isolate per-session state under MTP server-mode concurrency (#6001) by @thomhurst in thomhurst/TUnit#6025 ### Dependencies * chore(deps): update dependency stackexchange.redis to 2.13.10 by @thomhurst in thomhurst/TUnit#5985 * chore(deps): update tunit to 1.45.29 by @thomhurst in thomhurst/TUnit#5986 * chore(deps): update dependency mockolate to 3.2.1 by @thomhurst in thomhurst/TUnit#5987 * chore(deps): update dependency microsoft.playwright to 1.60.0 by @thomhurst in thomhurst/TUnit#5988 * chore(deps): update dependency messagepack to 3.1.6 by @thomhurst in thomhurst/TUnit#5992 * chore(deps): update dependency polyfill to 10.7.0 by @thomhurst in thomhurst/TUnit#5998 * chore(deps): update dependency polyfill to 10.7.0 by @thomhurst in thomhurst/TUnit#5997 * chore(deps): update verify to 31.17.0 by @thomhurst in thomhurst/TUnit#6000 * chore(deps): update verify to 31.18.0 by @thomhurst in thomhurst/TUnit#6013 * chore(deps): update dependency microsoft.net.test.sdk to 18.6.0 by @thomhurst in thomhurst/TUnit#6016 * chore(deps): update dependency dompurify to v3.4.6 by @thomhurst in thomhurst/TUnit#6015 * chore(deps): update dependency dompurify to v3.4.7 by @thomhurst in thomhurst/TUnit#6019 * chore(deps): update dependency npgsql to 10.0.3 by @thomhurst in thomhurst/TUnit#6020 * chore(deps): update dependency stackexchange.redis to 2.13.17 by @thomhurst in thomhurst/TUnit#6021 * chore(deps): update dependency npgsql.entityframeworkcore.postgresql to 10.0.2 by @thomhurst in thomhurst/TUnit#6022 ## New Contributors * @smolchanovsky made their first contribution in thomhurst/TUnit#5989 **Full Changelog**: thomhurst/TUnit@v1.45.29...v1.46.0 Commits viewable in [compare view](thomhurst/TUnit@v1.45.29...v1.48.6). </details> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Closes #6107
What
Removes avoidable allocations and re-enumeration on the passing path of several
TUnit.Assertionscollection assertions.1.
IsOrderedBy/IsOrderedByDescending(CollectionAssertions.cs)Previously did
value.ToArray()thenOrderBy(...).ToArray()thenSequenceEqual— O(n log n) plus two array allocations even when the collection is already ordered, followed by an adjacent-pair loop only on failure. Replaced with a single-pass O(n) adjacent-pair comparison over an enumerator (keep previous key, compare to current, fail on inversion). Zero allocation on the passing path.3.
HasSingleItem(CollectionAssertions.cs+CollectionChecks.cs)The assertion called
CheckHasSingleItem(which readsCount) and thenadapter.AsEnumerable().First()— double-enumerating non-replayable sequences. Added aCheckHasSingleItem(adapter, out item)overload that captures the single item during one enumeration pass, mirroring the existingCheckHasSingleItemPredicate(..., out item).4.
IsEquivalentTo(IsEquivalentToAssertion.cs)GetExpectation()re-ranstring.Join(", ", _expected)(re-enumerating_expected) on every call. Now cached in_cachedExpectedFormat, matching theEqualsAssertion._cachedExpectedFormatpattern.Why
These run on the success path of common collection assertions; the allocations/re-enumeration are pure overhead and the re-enumeration is a latent correctness hazard for lazy/non-replayable sequences.
Failure semantics
Failure messages, index semantics, and comparer usage are unchanged. The ordering loop reports the same 1-based index and the same keys;
HasSingleItemstill reportsreceived N item(s)with the true total.Validation
dotnet build TUnit.Assertionsacrossnetstandard2.0;net8.0;net9.0;net10.0— 0 errors, 0 new warnings (pre-existing analyzer/source-gen warnings only).EnumerableTests(ordering + single item),CollectionStructuralEquivalenceTests,Old/EquivalentAssertionTests,ListAssertionTests,IsEquivalentTo_TypeProperty_Tests,IgnoringTypeEquivalentTests,MemberCollectionAssertionTests,MemoryAssertionTests,FuncCollectionAssertionTests,DictionaryAssertionTests— all green.Deferred
ExpressionBuilderacross 60+ sites) — intentionally deferred to a follow-up. It is contract-changing and too broad/risky to combine with this parallel change, as noted in the issue.ICollectionAdapter<T>) — verified to be real boxing: all adapters (EnumerableAdapter<T>etc.) arereadonly structs and are boxed when passed to theICollectionAdapter<TItem>parameter ofCollectionChecks.Check*. The clean fix (generic<TAdapter, TItem> where TAdapter : ICollectionAdapter<TItem>) is not applied here because C# cannot inferTItemfrom the constraint, forcing explicit type arguments at 24+ call sites and changing the publicCollectionChecksAPI surface — the same invasiveness class the issue flags for the deferred item. Documenting it here and deferring to a follow-up rather than taking that risk in this PR.