fix: resolve inherited instance data source members for MethodDataSource#6178
Conversation
Not up to standards ⛔🔴 Issues
|
| Category | Results |
|---|---|
| Security | 1 critical |
🟢 Metrics 0 complexity
Metric Results Complexity 0
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 is a solid, well-scoped fix for #6162. The root cause analysis in the PR description is accurate, and both failure modes (source-gen and reflection) are addressed. A few things worth discussing:
Source generator fix (TestMetadataGenerator.cs) ✅
Walking the base-type chain with searchType.BaseType is the correct approach here — Roslyn's GetMembers intentionally doesn't include inherited members, so this is the right fix. No issues.
Reflection fix — fragile property copying
The conversion from MethodDataSourceAttribute to InstanceMethodDataSourceAttribute manually copies two properties:
converted.Arguments = methodDataSource.Arguments;
converted.SkipIfEmpty = methodDataSource.SkipIfEmpty;This will silently drop any new settable property added to MethodDataSourceAttribute in the future (e.g., an Order, Timeout, or SharedType equivalent). The Factory property is intentionally skipped here (it's only set by the source generator, so it's always null in reflection mode — that's correct), but the pattern is still brittle.
Suggested improvement: Add a protected or internal copy/clone method on MethodDataSourceAttribute, or a constructor overload that accepts the source attribute, so the upgrade path is self-contained and doesn't require callers to know which properties matter:
// Option A: virtual factory method on MethodDataSourceAttribute
public virtual MethodDataSourceAttribute AsInstanceVariant()
{
var attr = ClassProvidingDataSource is { } t
? new InstanceMethodDataSourceAttribute(t, MethodNameProvidingDataSource)
: new InstanceMethodDataSourceAttribute(MethodNameProvidingDataSource);
attr.Arguments = Arguments;
attr.SkipIfEmpty = SkipIfEmpty;
return attr;
}This way, when new properties are added to the base attribute they only need to be handled in one place. The current code scatters the copy logic in ReflectionAttributeExtractor, which is a poor home for it.
AmbiguousMatchException handling — misleading comment
catch (AmbiguousMatchException)
{
// Ambiguous overloads - leave the attribute as-is and let runtime resolution handle it
return false;
}The comment says "let runtime resolution handle it" — but returning false skips the InstanceMethodDataSourceAttribute conversion, so if the overloaded method is an instance member, the runtime will hit the exact Activator.CreateInstance failure this PR was written to fix. The comment creates false confidence that the skip is safe.
Possible options:
- If overloaded instance data source methods are not a supported use case, change the comment to say so explicitly.
- If they should work, iterate over
targetType.GetMethods(flags).Where(m => m.Name == memberName)and returntrueif any overload is non-static (conservative: if any overload is instance, treat it as instance).
At minimum the comment should be updated to: // Overloaded name — cannot determine statically; may fail at runtime if the member is an instance method.
Duplicated BindingFlags constant
The comment // Must stay in sync with MethodDataSourceAttribute.BindingFlags highlights a maintenance risk. Since MethodDataSourceAttribute.BindingFlags is private, the extractor has to duplicate it. If they ever diverge, the pre-check in TargetsInstanceMember would disagree with what GetDataRowsAsync resolves at runtime.
Suggested improvement: Promote the constant to internal in MethodDataSourceAttribute and reference it directly from ReflectionAttributeExtractor, eliminating the sync requirement.
Test coverage ✅
The regression test in TUnit.TestProject/Bugs/6162/Tests.cs accurately reproduces the reported scenario (abstract base with instance property data source, [InheritsTests], DI-constructed derived class with no parameterless constructor). It's well-structured and clearly documented.
Summary
The fix is correct and the overall approach is right — mirroring the source generator's compile-time conversion in reflection mode at the single choke point in BuildTestMetadata is a clean design. The three issues above are worth addressing, with the property-copy fragility being the most architecturally significant. Happy to approve once the AmbiguousMatchException comment is at least updated to be accurate.
|
Addressed all three review items in 8240009:
Public API snapshots updated: the only diff is the compiler-generated async state machine name ( Re-verified: repro passes both modes, reflection-mode |
There was a problem hiding this comment.
Code Review: fix/6162-inherited-instance-method-data-source
This is a clean, well-targeted bug fix with accurate root-cause analysis and good test coverage. The dual-mode fix is correctly structured — source-gen and reflection paths are each fixed independently, which is the right approach.
Source Generator Fix (TestMetadataGenerator.cs)
The change from GetMembers to a base-type walk is correct — Roslyn's ITypeSymbol.GetMembers(name) is declared-members-only, so the inherited member was invisible before. The fix is minimal and focused. Taking members[0] is consistent with the pre-existing FirstOrDefault() behaviour.
One minor point: if someone has overloaded methods with the same name where some are static and some instance (contrived, but possible), only members[0] is checked for the isStatic decision. This was already the case before this PR, and the source generator later emits both the type and method name into the Factory lambda, so the right overload is ultimately resolved. Not a regression, just worth a note.
Reflection Fix (ReflectionAttributeExtractor.cs)
The TargetsInstanceMember design is sound. Using Type.GetMember with BindingFlags.Instance | FlattenHierarchy correctly traverses the full inheritance chain for instance members — instance members are already traversed by the Instance flag, and FlattenHierarchy additionally pulls in inherited static members. The conservative approach (any matching instance member → treat as instance-targeting) is the right call for overload ambiguity.
The and not InstanceMethodDataSourceAttribute guard in ExtractMethodDataSources correctly skips already-upgraded attributes, and since ExtractDataSources returns a new array, the in-place mutation is safe for concurrency.
ToInstanceVariant()
All user-settable state (Arguments, SkipIfEmpty) is copied. Factory is correctly excluded — it is always null in reflection mode since it is source-generator-populated. The internal visibility is appropriate.
Public API Snapshots
The d__21 → d__22 state-machine number bump is expected: the new ToInstanceVariant method shifts the compiler-generated async state machine number for GetDataRowsAsync. The snapshot updates are correct.
Regression Test
The repro in TUnit.TestProject/Bugs/6162/Tests.cs precisely targets the reported failure scenario (abstract base with an instance property data source, sealed subclass constructed via DI with no parameterless constructor). [EngineTest(ExpectedResult.Pass)] ensures it runs in both modes.
Overall, this is a solid, well-reasoned fix. No issues found. Good work.
…rce (#6162) [MethodDataSource] targeting an instance member combined with [InheritsTests] and a class-level DI data source crashed with "No parameterless constructor defined" because the engine fell back to Activator.CreateInstance. Source-gen mode: GenerateMethodDataSourceAttribute resolved the data source member via GetMembers on the derived test class only, which misses members declared on base classes. The member lookup now walks the base-type chain so inherited members get the InstanceMethodDataSourceAttribute conversion and a compiled Factory, the same as members declared directly on the test class. Reflection mode: plain MethodDataSourceAttributes targeting an instance member were never upgraded to InstanceMethodDataSourceAttribute, so the engine never pre-created a properly-constructed instance. ExtractMethodDataSources now performs the same conversion the source generator does at compile time. Adds a regression test reproducing the issue: an abstract base class with an instance property data source, inherited by a sealed class whose instances are produced by a DependencyInjectionDataSourceAttribute. Fixes #6162
- Move the InstanceMethodDataSourceAttribute conversion onto MethodDataSourceAttribute (internal ToInstanceVariant) so property copying lives with the type instead of in the extractor. - Promote MethodDataSourceAttribute.BindingFlags to internal and reference it from ReflectionAttributeExtractor, removing the duplicated constant. - Replace the GetMethod + AmbiguousMatchException catch with GetMember enumeration: overloaded names no longer skip the conversion, and any instance overload conservatively triggers it. - Public API snapshots: compiler-generated async state machine renamed d__21 -> d__22 (member ordinal shift from the new internal method); no public surface change.
8240009 to
64f49e7
Compare
There was a problem hiding this comment.
Code Review — Round 3
Both items raised in previous reviews (property-copy fragility, AmbiguousMatchException risk) have been addressed cleanly in the follow-up refactor commit.
Previous feedback — confirmed addressed
Property-copy fragility → ToInstanceVariant() on the attribute itself
Moving the conversion onto MethodDataSourceAttribute is the right call. New settable properties only need handling in one place, next to the type that owns them. The explicit Factory exclusion with inline documentation is clear and correct — it is always null in reflection mode.
AmbiguousMatchException risk → Type.GetMember enumeration
GetMember with the shared BindingFlags constant handles overloads without throwing. The conservative "any instance member → treat as instance-targeting" approach is appropriate. The BindingFlags constant is correctly promoted to internal and referenced from both sites rather than duplicated.
Current state
Everything looks good:
- The base-type walk in the source-gen path uses Roslyn's declared-members-only
GetMembers, which is the correct API to iterate explicitly. ExtractMethodDataSourcesguards withand not InstanceMethodDataSourceAttributebefore upgrading — no double-conversion possible.ArgumentsinToInstanceVariant()is a reference copy rather than a clone, but attribute argument arrays are set once at construction and never mutated in this codebase, so this is acceptable.- The public API snapshot changes (
d__21→d__22) are an expected compiler artefact from the new internal method shifting the async state machine ordinal — not a public surface change. - Regression test precisely targets the reported failure path (abstract base + instance property data source + sealed derived class with no parameterless constructor + DI data source).
No further concerns. Solid fix.
Updated [TUnit](https://github.com/thomhurst/TUnit) from 1.48.6 to 1.51.0. <details> <summary>Release notes</summary> _Sourced from [TUnit's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.51.0 <!-- Release notes generated using configuration in .github/release.yml at v1.51.0 --> ## What's Changed ### Other Changes * fix(mocks): WasCalled/WasNeverCalled assertions via [GenerateAssertion] for all ICallVerification types by @thomhurst in thomhurst/TUnit#6176 * fix: create fresh non-shared instances per CombinedDataSources combination by @thomhurst in thomhurst/TUnit#6179 * fix: assign TestDetails before TestContext is published to ClassHookContext.Tests by @thomhurst in thomhurst/TUnit#6182 * fix: resolve inherited instance data source members for MethodDataSource by @thomhurst in thomhurst/TUnit#6178 * feat(mocks): per-element matchers for params array parameters by @thomhurst in thomhurst/TUnit#6181 * fix: invoke inner Func for TestDataRow<Func<T>> data sources (#6161) by @thomhurst in thomhurst/TUnit#6183 ### Dependencies * chore(deps): update _tunitpolyfillversion to 10.8.0 by @thomhurst in thomhurst/TUnit#6167 * chore(deps): update dependency azure.storage.blobs to 12.29.0 by @thomhurst in thomhurst/TUnit#6168 * chore(deps): update aspire by @thomhurst in thomhurst/TUnit#6165 * chore(deps): update dependency cliwrap to 3.10.2 by @thomhurst in thomhurst/TUnit#6166 * chore(deps): update dependency streamjsonrpc to 2.25.25 by @thomhurst in thomhurst/TUnit#6170 * chore(deps): update dependency polyfill to 10.8.0 by @thomhurst in thomhurst/TUnit#6169 * chore(deps): update tunit to 1.5* by @thomhurst in thomhurst/TUnit#6171 * chore(deps): update _tunitpolyfillversion to 10.8.1 by @thomhurst in thomhurst/TUnit#6174 * chore(deps): update dependency polyfill to 10.8.1 by @thomhurst in thomhurst/TUnit#6175 **Full Changelog**: thomhurst/TUnit@v1.50.0...v1.51.0 ## 1.50.0 <!-- Release notes generated using configuration in .github/release.yml at v1.50.0 --> ## What's Changed ### Other Changes * fix(analyzers): decouple code fixers from Rules to prevent MissingFieldException in VS by @thomhurst in thomhurst/TUnit#6158 * Fix mock wrappers for indexers and generic methods by @thomhurst in thomhurst/TUnit#6163 * Add global mock default mode by @thomhurst in thomhurst/TUnit#6164 **Full Changelog**: thomhurst/TUnit@v1.49.0...v1.50.0 ## 1.49.0 <!-- Release notes generated using configuration in .github/release.yml at v1.49.0 --> ## What's Changed ### Other Changes * docs: benchmark page descriptions + promote Benchmarks in sidebar by @thomhurst in thomhurst/TUnit#6143 * feat(mocks): discriminate generic-method mocks by type argument by @thomhurst in thomhurst/TUnit#6153 * fix(source-gen): jagged array data fails to compile (#6150) by @thomhurst in thomhurst/TUnit#6152 * fix: dispose shared fixtures when only a subset of consuming tests runs by @thomhurst in thomhurst/TUnit#6156 ### Dependencies * chore(deps): update tunit to 1.48.6 by @thomhurst in thomhurst/TUnit#6142 * chore(deps): update react to ^19.2.7 by @thomhurst in thomhurst/TUnit#6144 * chore(deps): update aspire to 13.4.0 by @thomhurst in thomhurst/TUnit#6145 * chore(deps): update dependency nunit.analyzers to 4.14.0 by @thomhurst in thomhurst/TUnit#6146 * chore(deps): update dependency polyfill to 10.7.2 by @thomhurst in thomhurst/TUnit#6148 * chore(deps): update dependency polyfill to 10.7.2 by @thomhurst in thomhurst/TUnit#6149 * chore(deps): update dependency dompurify to v3.4.8 by @thomhurst in thomhurst/TUnit#6155 **Full Changelog**: thomhurst/TUnit@v1.48.6...v1.49.0 Commits viewable in [compare view](thomhurst/TUnit@v1.48.6...v1.51.0). </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>
Updated [TUnit](https://github.com/thomhurst/TUnit) from 1.45.0 to 1.51.0. <details> <summary>Release notes</summary> _Sourced from [TUnit's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.51.0 <!-- Release notes generated using configuration in .github/release.yml at v1.51.0 --> ## What's Changed ### Other Changes * fix(mocks): WasCalled/WasNeverCalled assertions via [GenerateAssertion] for all ICallVerification types by @thomhurst in thomhurst/TUnit#6176 * fix: create fresh non-shared instances per CombinedDataSources combination by @thomhurst in thomhurst/TUnit#6179 * fix: assign TestDetails before TestContext is published to ClassHookContext.Tests by @thomhurst in thomhurst/TUnit#6182 * fix: resolve inherited instance data source members for MethodDataSource by @thomhurst in thomhurst/TUnit#6178 * feat(mocks): per-element matchers for params array parameters by @thomhurst in thomhurst/TUnit#6181 * fix: invoke inner Func for TestDataRow<Func<T>> data sources (#6161) by @thomhurst in thomhurst/TUnit#6183 ### Dependencies * chore(deps): update _tunitpolyfillversion to 10.8.0 by @thomhurst in thomhurst/TUnit#6167 * chore(deps): update dependency azure.storage.blobs to 12.29.0 by @thomhurst in thomhurst/TUnit#6168 * chore(deps): update aspire by @thomhurst in thomhurst/TUnit#6165 * chore(deps): update dependency cliwrap to 3.10.2 by @thomhurst in thomhurst/TUnit#6166 * chore(deps): update dependency streamjsonrpc to 2.25.25 by @thomhurst in thomhurst/TUnit#6170 * chore(deps): update dependency polyfill to 10.8.0 by @thomhurst in thomhurst/TUnit#6169 * chore(deps): update tunit to 1.5* by @thomhurst in thomhurst/TUnit#6171 * chore(deps): update _tunitpolyfillversion to 10.8.1 by @thomhurst in thomhurst/TUnit#6174 * chore(deps): update dependency polyfill to 10.8.1 by @thomhurst in thomhurst/TUnit#6175 **Full Changelog**: thomhurst/TUnit@v1.50.0...v1.51.0 ## 1.50.0 <!-- Release notes generated using configuration in .github/release.yml at v1.50.0 --> ## What's Changed ### Other Changes * fix(analyzers): decouple code fixers from Rules to prevent MissingFieldException in VS by @thomhurst in thomhurst/TUnit#6158 * Fix mock wrappers for indexers and generic methods by @thomhurst in thomhurst/TUnit#6163 * Add global mock default mode by @thomhurst in thomhurst/TUnit#6164 **Full Changelog**: thomhurst/TUnit@v1.49.0...v1.50.0 ## 1.49.0 <!-- Release notes generated using configuration in .github/release.yml at v1.49.0 --> ## What's Changed ### Other Changes * docs: benchmark page descriptions + promote Benchmarks in sidebar by @thomhurst in thomhurst/TUnit#6143 * feat(mocks): discriminate generic-method mocks by type argument by @thomhurst in thomhurst/TUnit#6153 * fix(source-gen): jagged array data fails to compile (#6150) by @thomhurst in thomhurst/TUnit#6152 * fix: dispose shared fixtures when only a subset of consuming tests runs by @thomhurst in thomhurst/TUnit#6156 ### Dependencies * chore(deps): update tunit to 1.48.6 by @thomhurst in thomhurst/TUnit#6142 * chore(deps): update react to ^19.2.7 by @thomhurst in thomhurst/TUnit#6144 * chore(deps): update aspire to 13.4.0 by @thomhurst in thomhurst/TUnit#6145 * chore(deps): update dependency nunit.analyzers to 4.14.0 by @thomhurst in thomhurst/TUnit#6146 * chore(deps): update dependency polyfill to 10.7.2 by @thomhurst in thomhurst/TUnit#6148 * chore(deps): update dependency polyfill to 10.7.2 by @thomhurst in thomhurst/TUnit#6149 * chore(deps): update dependency dompurify to v3.4.8 by @thomhurst in thomhurst/TUnit#6155 **Full Changelog**: thomhurst/TUnit@v1.48.6...v1.49.0 ## 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 Commits viewable in [compare view](thomhurst/TUnit@v1.45.0...v1.51.0). </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>
Updated [TUnit](https://github.com/thomhurst/TUnit) from 1.49.0 to 1.51.0. <details> <summary>Release notes</summary> _Sourced from [TUnit's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.51.0 <!-- Release notes generated using configuration in .github/release.yml at v1.51.0 --> ## What's Changed ### Other Changes * fix(mocks): WasCalled/WasNeverCalled assertions via [GenerateAssertion] for all ICallVerification types by @thomhurst in thomhurst/TUnit#6176 * fix: create fresh non-shared instances per CombinedDataSources combination by @thomhurst in thomhurst/TUnit#6179 * fix: assign TestDetails before TestContext is published to ClassHookContext.Tests by @thomhurst in thomhurst/TUnit#6182 * fix: resolve inherited instance data source members for MethodDataSource by @thomhurst in thomhurst/TUnit#6178 * feat(mocks): per-element matchers for params array parameters by @thomhurst in thomhurst/TUnit#6181 * fix: invoke inner Func for TestDataRow<Func<T>> data sources (#6161) by @thomhurst in thomhurst/TUnit#6183 ### Dependencies * chore(deps): update _tunitpolyfillversion to 10.8.0 by @thomhurst in thomhurst/TUnit#6167 * chore(deps): update dependency azure.storage.blobs to 12.29.0 by @thomhurst in thomhurst/TUnit#6168 * chore(deps): update aspire by @thomhurst in thomhurst/TUnit#6165 * chore(deps): update dependency cliwrap to 3.10.2 by @thomhurst in thomhurst/TUnit#6166 * chore(deps): update dependency streamjsonrpc to 2.25.25 by @thomhurst in thomhurst/TUnit#6170 * chore(deps): update dependency polyfill to 10.8.0 by @thomhurst in thomhurst/TUnit#6169 * chore(deps): update tunit to 1.5* by @thomhurst in thomhurst/TUnit#6171 * chore(deps): update _tunitpolyfillversion to 10.8.1 by @thomhurst in thomhurst/TUnit#6174 * chore(deps): update dependency polyfill to 10.8.1 by @thomhurst in thomhurst/TUnit#6175 **Full Changelog**: thomhurst/TUnit@v1.50.0...v1.51.0 ## 1.50.0 <!-- Release notes generated using configuration in .github/release.yml at v1.50.0 --> ## What's Changed ### Other Changes * fix(analyzers): decouple code fixers from Rules to prevent MissingFieldException in VS by @thomhurst in thomhurst/TUnit#6158 * Fix mock wrappers for indexers and generic methods by @thomhurst in thomhurst/TUnit#6163 * Add global mock default mode by @thomhurst in thomhurst/TUnit#6164 **Full Changelog**: thomhurst/TUnit@v1.49.0...v1.50.0 Commits viewable in [compare view](thomhurst/TUnit@v1.49.0...v1.51.0). </details> Updated [TUnit.AspNetCore](https://github.com/thomhurst/TUnit) from 1.49.0 to 1.51.0. <details> <summary>Release notes</summary> _Sourced from [TUnit.AspNetCore's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.51.0 <!-- Release notes generated using configuration in .github/release.yml at v1.51.0 --> ## What's Changed ### Other Changes * fix(mocks): WasCalled/WasNeverCalled assertions via [GenerateAssertion] for all ICallVerification types by @thomhurst in thomhurst/TUnit#6176 * fix: create fresh non-shared instances per CombinedDataSources combination by @thomhurst in thomhurst/TUnit#6179 * fix: assign TestDetails before TestContext is published to ClassHookContext.Tests by @thomhurst in thomhurst/TUnit#6182 * fix: resolve inherited instance data source members for MethodDataSource by @thomhurst in thomhurst/TUnit#6178 * feat(mocks): per-element matchers for params array parameters by @thomhurst in thomhurst/TUnit#6181 * fix: invoke inner Func for TestDataRow<Func<T>> data sources (#6161) by @thomhurst in thomhurst/TUnit#6183 ### Dependencies * chore(deps): update _tunitpolyfillversion to 10.8.0 by @thomhurst in thomhurst/TUnit#6167 * chore(deps): update dependency azure.storage.blobs to 12.29.0 by @thomhurst in thomhurst/TUnit#6168 * chore(deps): update aspire by @thomhurst in thomhurst/TUnit#6165 * chore(deps): update dependency cliwrap to 3.10.2 by @thomhurst in thomhurst/TUnit#6166 * chore(deps): update dependency streamjsonrpc to 2.25.25 by @thomhurst in thomhurst/TUnit#6170 * chore(deps): update dependency polyfill to 10.8.0 by @thomhurst in thomhurst/TUnit#6169 * chore(deps): update tunit to 1.5* by @thomhurst in thomhurst/TUnit#6171 * chore(deps): update _tunitpolyfillversion to 10.8.1 by @thomhurst in thomhurst/TUnit#6174 * chore(deps): update dependency polyfill to 10.8.1 by @thomhurst in thomhurst/TUnit#6175 **Full Changelog**: thomhurst/TUnit@v1.50.0...v1.51.0 ## 1.50.0 <!-- Release notes generated using configuration in .github/release.yml at v1.50.0 --> ## What's Changed ### Other Changes * fix(analyzers): decouple code fixers from Rules to prevent MissingFieldException in VS by @thomhurst in thomhurst/TUnit#6158 * Fix mock wrappers for indexers and generic methods by @thomhurst in thomhurst/TUnit#6163 * Add global mock default mode by @thomhurst in thomhurst/TUnit#6164 **Full Changelog**: thomhurst/TUnit@v1.49.0...v1.50.0 Commits viewable in [compare view](thomhurst/TUnit@v1.49.0...v1.51.0). </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.48.6 to 1.51.0. <details> <summary>Release notes</summary> _Sourced from [TUnit.Core's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.51.0 <!-- Release notes generated using configuration in .github/release.yml at v1.51.0 --> ## What's Changed ### Other Changes * fix(mocks): WasCalled/WasNeverCalled assertions via [GenerateAssertion] for all ICallVerification types by @thomhurst in thomhurst/TUnit#6176 * fix: create fresh non-shared instances per CombinedDataSources combination by @thomhurst in thomhurst/TUnit#6179 * fix: assign TestDetails before TestContext is published to ClassHookContext.Tests by @thomhurst in thomhurst/TUnit#6182 * fix: resolve inherited instance data source members for MethodDataSource by @thomhurst in thomhurst/TUnit#6178 * feat(mocks): per-element matchers for params array parameters by @thomhurst in thomhurst/TUnit#6181 * fix: invoke inner Func for TestDataRow<Func<T>> data sources (#6161) by @thomhurst in thomhurst/TUnit#6183 ### Dependencies * chore(deps): update _tunitpolyfillversion to 10.8.0 by @thomhurst in thomhurst/TUnit#6167 * chore(deps): update dependency azure.storage.blobs to 12.29.0 by @thomhurst in thomhurst/TUnit#6168 * chore(deps): update aspire by @thomhurst in thomhurst/TUnit#6165 * chore(deps): update dependency cliwrap to 3.10.2 by @thomhurst in thomhurst/TUnit#6166 * chore(deps): update dependency streamjsonrpc to 2.25.25 by @thomhurst in thomhurst/TUnit#6170 * chore(deps): update dependency polyfill to 10.8.0 by @thomhurst in thomhurst/TUnit#6169 * chore(deps): update tunit to 1.5* by @thomhurst in thomhurst/TUnit#6171 * chore(deps): update _tunitpolyfillversion to 10.8.1 by @thomhurst in thomhurst/TUnit#6174 * chore(deps): update dependency polyfill to 10.8.1 by @thomhurst in thomhurst/TUnit#6175 **Full Changelog**: thomhurst/TUnit@v1.50.0...v1.51.0 ## 1.50.0 <!-- Release notes generated using configuration in .github/release.yml at v1.50.0 --> ## What's Changed ### Other Changes * fix(analyzers): decouple code fixers from Rules to prevent MissingFieldException in VS by @thomhurst in thomhurst/TUnit#6158 * Fix mock wrappers for indexers and generic methods by @thomhurst in thomhurst/TUnit#6163 * Add global mock default mode by @thomhurst in thomhurst/TUnit#6164 **Full Changelog**: thomhurst/TUnit@v1.49.0...v1.50.0 ## 1.49.0 <!-- Release notes generated using configuration in .github/release.yml at v1.49.0 --> ## What's Changed ### Other Changes * docs: benchmark page descriptions + promote Benchmarks in sidebar by @thomhurst in thomhurst/TUnit#6143 * feat(mocks): discriminate generic-method mocks by type argument by @thomhurst in thomhurst/TUnit#6153 * fix(source-gen): jagged array data fails to compile (#6150) by @thomhurst in thomhurst/TUnit#6152 * fix: dispose shared fixtures when only a subset of consuming tests runs by @thomhurst in thomhurst/TUnit#6156 ### Dependencies * chore(deps): update tunit to 1.48.6 by @thomhurst in thomhurst/TUnit#6142 * chore(deps): update react to ^19.2.7 by @thomhurst in thomhurst/TUnit#6144 * chore(deps): update aspire to 13.4.0 by @thomhurst in thomhurst/TUnit#6145 * chore(deps): update dependency nunit.analyzers to 4.14.0 by @thomhurst in thomhurst/TUnit#6146 * chore(deps): update dependency polyfill to 10.7.2 by @thomhurst in thomhurst/TUnit#6148 * chore(deps): update dependency polyfill to 10.7.2 by @thomhurst in thomhurst/TUnit#6149 * chore(deps): update dependency dompurify to v3.4.8 by @thomhurst in thomhurst/TUnit#6155 **Full Changelog**: thomhurst/TUnit@v1.48.6...v1.49.0 Commits viewable in [compare view](thomhurst/TUnit@v1.48.6...v1.51.0). </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>
Summary
Fixes #6162 —
[MethodDataSource]targeting an instance member, combined with[InheritsTests]and a class-levelDependencyInjectionDataSourceAttribute, failed with:Root cause
Both modes failed to recognise the data source as instance-targeting, so the engine never pre-created a properly-constructed test class instance (via the class-level data source) and
MethodDataSourceAttributefell back toActivator.CreateInstance:GenerateMethodDataSourceAttributeresolved the data source member withGetMemberson the test class symbol only — Roslyn'sGetMembersdoes not include inherited members. With[InheritsTests]the member lives on a base class, so the lookup missed, and the generator emitted a plainMethodDataSourceAttributewithout aFactoryand without theInstanceMethodDataSourceAttributeconversion that triggers early instance creation.InstanceMethodDataSourceAttribute(the source generator normally does this at compile time), so the sameActivator.CreateInstancefallback was hit — independent of[InheritsTests].Changes
TestMetadataGenerator.cs: walk the base-type chain (most-derived first) when resolving the data source member.ReflectionAttributeExtractor.cs: newExtractMethodDataSourcesupgrades plainMethodDataSourceAttributes targeting an instance member toInstanceMethodDataSourceAttribute, mirroring the source generator's compile-time conversion. Method-level only — class-level data sources keep their existing circular-dependency guard.ReflectionTestDataCollector.cs: use the new extraction for method-level data sources (single choke point — all reflection paths that build method-level data sources route throughBuildTestMetadata).TUnit.TestProject/Bugs/6162/Tests.cs: regression test — abstract base with an instance property data source, inherited by a sealed class constructed via a DI data source.Verification
*MethodDataSource*sweep (85 tests): identical results tomainin both modes (1 pre-existing Bug3266 failure, stash-verified)*Inherit*sweep: 36/36 in both modes.received.txtproducedTUnit.EngineandTUnit.Core.SourceGeneratorbuild clean across all TFMs, no new IL trim warnings