Fix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset builds - #9504

Merged
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu
Jun 29, 2026
Merged

Fix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset builds#9504
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jun 29, 2026

Copy link
Copy Markdown
Member

Problem

main has been red across many consecutive official builds (e.g. build 3010746).

The failing test is MSTest.Acceptance.IntegrationTests.AssemblyFixtureProviderTests.AssemblyFixtureProvider_FromReferencedLibrary_RunsAssemblyInitializeAndCleanup. It fails in its ClassInitialize because the source-gen build variant of the AssemblyFixtureProviderAcceptance asset fails to compile with MSB3277.

Root cause (a harness bug, not a product bug)

TestAssetFixtureBase.InitializeAsync builds each acceptance asset twice in the same directory: first the reflection build (default bin/Release), then the source-gen build (with bin/obj redirected to a *SourceGen sub-folder).

For a multi-project asset — AssemblyFixtureProviderAcceptance's test project references ProviderLibrary — the first (reflection) build leaves ProviderLibrary/bin/Release/<tfm>/*.dll on disk. When the later source-gen build then evaluates the host project, the SDK's default item globs pull those stray DLLs in as None items (confirmed in the binlog: AddItem None -> ProviderLibrary\bin\Release\net10.0\MSTest.TestFramework.Extensions.dll). RAR then treats the wrong-TFM copy (a net10.0 MSTest.TestFramework.Extensions.dll, assembly version 9.0.0.0) as a candidate for the net8.0 leg:

error MSB3277: Found conflicts between different versions of "System.Runtime" ... 8.0.0.0 vs 9.0.0.0
...ProviderLibrary\bin\Release\net10.0\MSTest.TestFramework.Extensions.dll

MSBuildTreatWarningsAsErrors=true (passed only to the source-gen build) promotes this to an error, so ClassInitialize throws.

The injected props already re-excluded bin/obj, but only at the asset root (bin/**;obj/**), which does not match the nested ProviderLibrary/bin/**. The reflection build (#1) is unaffected because at its evaluation time the referenced project's bin is still empty — only the later source-gen build (#2) runs in a directory polluted by #1.

Fix

Broaden the injected DefaultItemExcludes in AcceptanceSourceGen to also exclude nested bin/obj (**/obj/**;**/bin/**), so a referenced project's leftover reflection outputs stay out of the source-gen build's globs. This is the proper harness fix and keeps the existing redirect-based design.

With the harness corrected, the AssemblyFixtureProvider opt-out is unnecessary, so the asset is restored to source-gen coverage.

Verification

  • Reproduced the original MSB3277 from the CI binlog (binlog-mcp).
  • Ran the AssemblyFixtureProvider acceptance tests (which drive both the reflection and source-gen builds via the fixture) — all 3 pass across net462/net8.0/net10.0.

The AssemblyFixtureProviderAcceptance asset has a multi-targeting
ProjectReference (ProviderLibrary). In the source-gen build variant, RAR
for the net8.0 leg resolves ProviderLibrary's transitive
MSTest.TestFramework.Extensions.dll from the net10.0 reflection output
instead of the net8.0 one, producing MSB3277 System.* version conflicts
(8.0.0.0 vs 9.0.0.0). The source-gen build promotes these to errors via
MSBuildTreatWarningsAsErrors, failing the fixture's ClassInitialize and
breaking the main build.
The test only exercises the reflection build, so opt the asset out of the
source-gen build (matching FrameworkOnlyTests).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 29, 2026 13:46

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a persistent main build break by preventing an unused acceptance-test asset build variant (source-generation) from compiling, avoiding MSBuild reference-resolution conflicts caused by a multi-targeting ProjectReference in the asset.

Changes:

  • Opt the AssemblyFixtureProviderAcceptance asset out of source-generation builds by overriding SourceGenMetadataModes to an empty set.
  • Add an inline comment documenting the RAR/MSB3277 conflict scenario and why reflection-only is sufficient for this test.
Show a summary per file
FileDescription
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.csOverrides the asset fixture’s SourceGenMetadataModes to skip source-gen builds that are not exercised by the test and currently fail due to assembly version conflicts.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Low

@Evangelink

This comment has been minimized.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

Expert Review — PR #9504

Scope: Single file change in test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.cs. The change adds a SourceGenMetadataModes => [] override and explanatory comment to TestAssetFixture, opting the AssemblyFixtureProviderAcceptance asset out of the source-gen build.


Pattern Verification

The escape hatch used here (protected override IReadOnlyList<MetadataMode> SourceGenMetadataModes => []) is the documented, established opt-out mechanism. It is applied identically in FrameworkOnlyTests.TestAssetFixture (which also carries an explanatory comment stating the reason). The TestAssetFixtureBase XML doc on SourceGenMetadataModes explicitly names this as the opt-out path for assets that "genuinely cannot build under source generation."

Confirmed that TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.TestProjectName, tfm) is called without a metadataMode argument — so the reflection build is the only one exercised by the test. Opting out of building the source-gen variant does not remove any test execution coverage.


22-Dimension Summary

#DimensionVerdictNotes
1Algorithmic Correctness✅ LGTMFix matches root cause: source-gen build fails, test never uses source-gen variant; opting out is correct, not a symptom patch.
2Threading & ConcurrencyN/ANo new concurrent code.
3Security & IPC Contract SafetyN/ANo security boundary touched.
4Public API & Binary CompatibilityN/ANo public API changes.
5Performance & AllocationsN/ATest fixture code; not a hot path.
6Cross-TFM CompatibilityN/AThe fix avoids the cross-TFM RAR issue by not building the failing variant. The => [] collection expression is valid on all targeted TFMs.
7Resource & IDisposable ManagementN/ANo new disposable resources.
8Defensive Coding at BoundariesN/ANo boundary logic changed.
9Localization & ResourcesN/ANo user-facing strings.
10Test Isolation✅ LGTMSingle test method in class — no shared mutable asset across methods, so [DoNotParallelize] is not required.
11Assertion QualityN/ANo assertion changes.
12Flakiness Patterns✅ LGTMThis PR fixes a reliability failure; no new flakiness introduced.
13Test Completeness & Coverage✅ LGTMThe source-gen variant was never exercised by any test method. No coverage is lost. See observation below regarding a potential follow-up.
14Data-Driven Test CoverageN/ANo data rows changed.
15Code Structure & Simplification✅ LGTMMinimal, clean override consistent with FrameworkOnlyTests.
16Naming & Conventions✅ LGTMConsistent with existing pattern.
17Documentation Accuracy✅ LGTMComment accurately explains the RAR resolution issue, the exact error (MSB3277), the promoter (MSBuildTreatWarningsAsErrors), and the justification. Matches the FrameworkOnlyTests comment style.
18Analyzer & Code Fix QualityN/ANo src/Analyzers/ changes.
19IPC Wire CompatibilityN/ANo serialization changes.
20Build Infrastructure & DependenciesN/ANo eng/ or package reference changes.
21Scope & PR Discipline✅ LGTMSingle-concern change; PR description is thorough with root-cause analysis and verification steps.
22PowerShell Scripting HygieneN/ANo .ps1 changes.

22/22 dimensions clean.


💡 Optional Follow-up Observation (non-blocking)

The underlying MSBuild/RAR issue — where the source-gen build's isolated bin/obj redirection causes the net8.0 leg of a multi-targeting ProjectReference to resolve transitive assemblies from the net10.0 output — will silently affect any other acceptance asset that has a similar multi-targeting ProjectReference. Opting this asset out is the right short-term fix to unblock main, but it may be worth filing a tracking issue to investigate whether the source-gen build infrastructure should be hardened to pass -p:TargetFrameworks=... or a comparable property to pin transitive reference resolution to the correct TFM subfolder. If such an issue already exists, linking it from the comment would help future readers.


Verdict: This is a correct, minimal, well-documented fix that follows the established pattern. Ready to merge.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jun 29, 2026
…t globs
Root cause of the broken main build: the acceptance source-gen build harness
let a multi-project asset's leftover reflection outputs pollute the later
source-gen build.
TestAssetFixtureBase builds each asset twice in the same directory: first the
reflection build (default bin/Release), then the source-gen build (bin/obj
redirected to a *SourceGen sub-folder). For a multi-project asset (e.g.
AssemblyFixtureProviderAcceptance, whose test project references ProviderLibrary),
the reflection build leaves ProviderLibrary/bin/Release/<tfm>/*.dll on disk. When
the source-gen build then evaluates the host project, the SDK's default item globs
pulled those stray DLLs in as None items, and RAR treated a wrong-TFM copy (a
net10.0 MSTest.TestFramework.Extensions.dll, assembly version 9.0.0.0) as a
candidate for the net8.0 leg -> MSB3277, which MSBuildTreatWarningsAsErrors
promotes to an error, failing the fixture's ClassInitialize.
The injected props already re-excluded bin/obj, but only at the asset root
(bin/**;obj/**), which does not match the nested ProviderLibrary/bin/**. Broaden
the re-exclusion to **/obj/**;**/bin/** so a referenced project's leftover
reflection outputs stay out of the source-gen build's globs.
The reflection build (#1) is unaffected because at its evaluation time the
referenced project's bin is still empty; only the later source-gen build (#2)
runs in a directory polluted by #1.
This is the proper harness fix; the AssemblyFixtureProvider opt-out is no longer
needed, so the asset is restored to source-gen coverage. Verified by running the
AssemblyFixtureProvider acceptance tests (reflection + source-gen builds) green
across net462/net8.0/net10.0.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@EvangelinkAmaury Levé (Evangelink) changed the title Fix broken main: opt AssemblyFixtureProvider acceptance asset out of source-gen buildFix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset buildsJun 29, 2026
@Evangelink

Copy link
Copy Markdown
MemberAuthor

🧪 Test quality grade — PR #9504

No new or modified test methods were identified in the changed regions
of this PR. Nothing to grade.

The only changed file (test/Utilities/Microsoft.Testing.TestInfrastructure/AcceptanceSourceGen.cs) is infrastructure/utility code that generates MSBuild props content — it contains no test methods.

Re-run with /grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 93.1 AIC · ⌖ 13.1 AIC · ⊞ 45.7K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) merged commit 4ea34a2 into mainJun 29, 2026
39 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu branch June 29, 2026 16:17
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Fix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset builds - #9504

Merged
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu
Jun 29, 2026
Merged

Fix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset builds#9504
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jun 29, 2026

Copy link
Copy Markdown
Member

Problem

main has been red across many consecutive official builds (e.g. build 3010746).

The failing test is MSTest.Acceptance.IntegrationTests.AssemblyFixtureProviderTests.AssemblyFixtureProvider_FromReferencedLibrary_RunsAssemblyInitializeAndCleanup. It fails in its ClassInitialize because the source-gen build variant of the AssemblyFixtureProviderAcceptance asset fails to compile with MSB3277.

Root cause (a harness bug, not a product bug)

TestAssetFixtureBase.InitializeAsync builds each acceptance asset twice in the same directory: first the reflection build (default bin/Release), then the source-gen build (with bin/obj redirected to a *SourceGen sub-folder).

For a multi-project asset — AssemblyFixtureProviderAcceptance's test project references ProviderLibrary — the first (reflection) build leaves ProviderLibrary/bin/Release/<tfm>/*.dll on disk. When the later source-gen build then evaluates the host project, the SDK's default item globs pull those stray DLLs in as None items (confirmed in the binlog: AddItem None -> ProviderLibrary\bin\Release\net10.0\MSTest.TestFramework.Extensions.dll). RAR then treats the wrong-TFM copy (a net10.0 MSTest.TestFramework.Extensions.dll, assembly version 9.0.0.0) as a candidate for the net8.0 leg:

error MSB3277: Found conflicts between different versions of "System.Runtime" ... 8.0.0.0 vs 9.0.0.0
...ProviderLibrary\bin\Release\net10.0\MSTest.TestFramework.Extensions.dll

MSBuildTreatWarningsAsErrors=true (passed only to the source-gen build) promotes this to an error, so ClassInitialize throws.

The injected props already re-excluded bin/obj, but only at the asset root (bin/**;obj/**), which does not match the nested ProviderLibrary/bin/**. The reflection build (#1) is unaffected because at its evaluation time the referenced project's bin is still empty — only the later source-gen build (#2) runs in a directory polluted by #1.

Fix

Broaden the injected DefaultItemExcludes in AcceptanceSourceGen to also exclude nested bin/obj (**/obj/**;**/bin/**), so a referenced project's leftover reflection outputs stay out of the source-gen build's globs. This is the proper harness fix and keeps the existing redirect-based design.

With the harness corrected, the AssemblyFixtureProvider opt-out is unnecessary, so the asset is restored to source-gen coverage.

Verification

  • Reproduced the original MSB3277 from the CI binlog (binlog-mcp).
  • Ran the AssemblyFixtureProvider acceptance tests (which drive both the reflection and source-gen builds via the fixture) — all 3 pass across net462/net8.0/net10.0.

The AssemblyFixtureProviderAcceptance asset has a multi-targeting
ProjectReference (ProviderLibrary). In the source-gen build variant, RAR
for the net8.0 leg resolves ProviderLibrary's transitive
MSTest.TestFramework.Extensions.dll from the net10.0 reflection output
instead of the net8.0 one, producing MSB3277 System.* version conflicts
(8.0.0.0 vs 9.0.0.0). The source-gen build promotes these to errors via
MSBuildTreatWarningsAsErrors, failing the fixture's ClassInitialize and
breaking the main build.
The test only exercises the reflection build, so opt the asset out of the
source-gen build (matching FrameworkOnlyTests).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 29, 2026 13:46

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a persistent main build break by preventing an unused acceptance-test asset build variant (source-generation) from compiling, avoiding MSBuild reference-resolution conflicts caused by a multi-targeting ProjectReference in the asset.

Changes:

  • Opt the AssemblyFixtureProviderAcceptance asset out of source-generation builds by overriding SourceGenMetadataModes to an empty set.
  • Add an inline comment documenting the RAR/MSB3277 conflict scenario and why reflection-only is sufficient for this test.
Show a summary per file
FileDescription
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.csOverrides the asset fixture’s SourceGenMetadataModes to skip source-gen builds that are not exercised by the test and currently fail due to assembly version conflicts.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Low

@Evangelink

This comment has been minimized.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

Expert Review — PR #9504

Scope: Single file change in test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.cs. The change adds a SourceGenMetadataModes => [] override and explanatory comment to TestAssetFixture, opting the AssemblyFixtureProviderAcceptance asset out of the source-gen build.


Pattern Verification

The escape hatch used here (protected override IReadOnlyList<MetadataMode> SourceGenMetadataModes => []) is the documented, established opt-out mechanism. It is applied identically in FrameworkOnlyTests.TestAssetFixture (which also carries an explanatory comment stating the reason). The TestAssetFixtureBase XML doc on SourceGenMetadataModes explicitly names this as the opt-out path for assets that "genuinely cannot build under source generation."

Confirmed that TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.TestProjectName, tfm) is called without a metadataMode argument — so the reflection build is the only one exercised by the test. Opting out of building the source-gen variant does not remove any test execution coverage.


22-Dimension Summary

#DimensionVerdictNotes
1Algorithmic Correctness✅ LGTMFix matches root cause: source-gen build fails, test never uses source-gen variant; opting out is correct, not a symptom patch.
2Threading & ConcurrencyN/ANo new concurrent code.
3Security & IPC Contract SafetyN/ANo security boundary touched.
4Public API & Binary CompatibilityN/ANo public API changes.
5Performance & AllocationsN/ATest fixture code; not a hot path.
6Cross-TFM CompatibilityN/AThe fix avoids the cross-TFM RAR issue by not building the failing variant. The => [] collection expression is valid on all targeted TFMs.
7Resource & IDisposable ManagementN/ANo new disposable resources.
8Defensive Coding at BoundariesN/ANo boundary logic changed.
9Localization & ResourcesN/ANo user-facing strings.
10Test Isolation✅ LGTMSingle test method in class — no shared mutable asset across methods, so [DoNotParallelize] is not required.
11Assertion QualityN/ANo assertion changes.
12Flakiness Patterns✅ LGTMThis PR fixes a reliability failure; no new flakiness introduced.
13Test Completeness & Coverage✅ LGTMThe source-gen variant was never exercised by any test method. No coverage is lost. See observation below regarding a potential follow-up.
14Data-Driven Test CoverageN/ANo data rows changed.
15Code Structure & Simplification✅ LGTMMinimal, clean override consistent with FrameworkOnlyTests.
16Naming & Conventions✅ LGTMConsistent with existing pattern.
17Documentation Accuracy✅ LGTMComment accurately explains the RAR resolution issue, the exact error (MSB3277), the promoter (MSBuildTreatWarningsAsErrors), and the justification. Matches the FrameworkOnlyTests comment style.
18Analyzer & Code Fix QualityN/ANo src/Analyzers/ changes.
19IPC Wire CompatibilityN/ANo serialization changes.
20Build Infrastructure & DependenciesN/ANo eng/ or package reference changes.
21Scope & PR Discipline✅ LGTMSingle-concern change; PR description is thorough with root-cause analysis and verification steps.
22PowerShell Scripting HygieneN/ANo .ps1 changes.

22/22 dimensions clean.


💡 Optional Follow-up Observation (non-blocking)

The underlying MSBuild/RAR issue — where the source-gen build's isolated bin/obj redirection causes the net8.0 leg of a multi-targeting ProjectReference to resolve transitive assemblies from the net10.0 output — will silently affect any other acceptance asset that has a similar multi-targeting ProjectReference. Opting this asset out is the right short-term fix to unblock main, but it may be worth filing a tracking issue to investigate whether the source-gen build infrastructure should be hardened to pass -p:TargetFrameworks=... or a comparable property to pin transitive reference resolution to the correct TFM subfolder. If such an issue already exists, linking it from the comment would help future readers.


Verdict: This is a correct, minimal, well-documented fix that follows the established pattern. Ready to merge.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jun 29, 2026
…t globs
Root cause of the broken main build: the acceptance source-gen build harness
let a multi-project asset's leftover reflection outputs pollute the later
source-gen build.
TestAssetFixtureBase builds each asset twice in the same directory: first the
reflection build (default bin/Release), then the source-gen build (bin/obj
redirected to a *SourceGen sub-folder). For a multi-project asset (e.g.
AssemblyFixtureProviderAcceptance, whose test project references ProviderLibrary),
the reflection build leaves ProviderLibrary/bin/Release/<tfm>/*.dll on disk. When
the source-gen build then evaluates the host project, the SDK's default item globs
pulled those stray DLLs in as None items, and RAR treated a wrong-TFM copy (a
net10.0 MSTest.TestFramework.Extensions.dll, assembly version 9.0.0.0) as a
candidate for the net8.0 leg -> MSB3277, which MSBuildTreatWarningsAsErrors
promotes to an error, failing the fixture's ClassInitialize.
The injected props already re-excluded bin/obj, but only at the asset root
(bin/**;obj/**), which does not match the nested ProviderLibrary/bin/**. Broaden
the re-exclusion to **/obj/**;**/bin/** so a referenced project's leftover
reflection outputs stay out of the source-gen build's globs.
The reflection build (#1) is unaffected because at its evaluation time the
referenced project's bin is still empty; only the later source-gen build (#2)
runs in a directory polluted by #1.
This is the proper harness fix; the AssemblyFixtureProvider opt-out is no longer
needed, so the asset is restored to source-gen coverage. Verified by running the
AssemblyFixtureProvider acceptance tests (reflection + source-gen builds) green
across net462/net8.0/net10.0.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@EvangelinkAmaury Levé (Evangelink) changed the title Fix broken main: opt AssemblyFixtureProvider acceptance asset out of source-gen buildFix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset buildsJun 29, 2026
@Evangelink

Copy link
Copy Markdown
MemberAuthor

🧪 Test quality grade — PR #9504

No new or modified test methods were identified in the changed regions
of this PR. Nothing to grade.

The only changed file (test/Utilities/Microsoft.Testing.TestInfrastructure/AcceptanceSourceGen.cs) is infrastructure/utility code that generates MSBuild props content — it contains no test methods.

Re-run with /grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 93.1 AIC · ⌖ 13.1 AIC · ⊞ 45.7K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) merged commit 4ea34a2 into mainJun 29, 2026
39 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu branch June 29, 2026 16:17
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset builds - #9504

Merged
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu
Jun 29, 2026
Merged

Fix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset builds#9504
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jun 29, 2026

Copy link
Copy Markdown
Member

Problem

main has been red across many consecutive official builds (e.g. build 3010746).

The failing test is MSTest.Acceptance.IntegrationTests.AssemblyFixtureProviderTests.AssemblyFixtureProvider_FromReferencedLibrary_RunsAssemblyInitializeAndCleanup. It fails in its ClassInitialize because the source-gen build variant of the AssemblyFixtureProviderAcceptance asset fails to compile with MSB3277.

Root cause (a harness bug, not a product bug)

TestAssetFixtureBase.InitializeAsync builds each acceptance asset twice in the same directory: first the reflection build (default bin/Release), then the source-gen build (with bin/obj redirected to a *SourceGen sub-folder).

For a multi-project asset — AssemblyFixtureProviderAcceptance's test project references ProviderLibrary — the first (reflection) build leaves ProviderLibrary/bin/Release/<tfm>/*.dll on disk. When the later source-gen build then evaluates the host project, the SDK's default item globs pull those stray DLLs in as None items (confirmed in the binlog: AddItem None -> ProviderLibrary\bin\Release\net10.0\MSTest.TestFramework.Extensions.dll). RAR then treats the wrong-TFM copy (a net10.0 MSTest.TestFramework.Extensions.dll, assembly version 9.0.0.0) as a candidate for the net8.0 leg:

error MSB3277: Found conflicts between different versions of "System.Runtime" ... 8.0.0.0 vs 9.0.0.0
...ProviderLibrary\bin\Release\net10.0\MSTest.TestFramework.Extensions.dll

MSBuildTreatWarningsAsErrors=true (passed only to the source-gen build) promotes this to an error, so ClassInitialize throws.

The injected props already re-excluded bin/obj, but only at the asset root (bin/**;obj/**), which does not match the nested ProviderLibrary/bin/**. The reflection build (#1) is unaffected because at its evaluation time the referenced project's bin is still empty — only the later source-gen build (#2) runs in a directory polluted by #1.

Fix

Broaden the injected DefaultItemExcludes in AcceptanceSourceGen to also exclude nested bin/obj (**/obj/**;**/bin/**), so a referenced project's leftover reflection outputs stay out of the source-gen build's globs. This is the proper harness fix and keeps the existing redirect-based design.

With the harness corrected, the AssemblyFixtureProvider opt-out is unnecessary, so the asset is restored to source-gen coverage.

Verification

  • Reproduced the original MSB3277 from the CI binlog (binlog-mcp).
  • Ran the AssemblyFixtureProvider acceptance tests (which drive both the reflection and source-gen builds via the fixture) — all 3 pass across net462/net8.0/net10.0.

The AssemblyFixtureProviderAcceptance asset has a multi-targeting
ProjectReference (ProviderLibrary). In the source-gen build variant, RAR
for the net8.0 leg resolves ProviderLibrary's transitive
MSTest.TestFramework.Extensions.dll from the net10.0 reflection output
instead of the net8.0 one, producing MSB3277 System.* version conflicts
(8.0.0.0 vs 9.0.0.0). The source-gen build promotes these to errors via
MSBuildTreatWarningsAsErrors, failing the fixture's ClassInitialize and
breaking the main build.
The test only exercises the reflection build, so opt the asset out of the
source-gen build (matching FrameworkOnlyTests).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 29, 2026 13:46

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a persistent main build break by preventing an unused acceptance-test asset build variant (source-generation) from compiling, avoiding MSBuild reference-resolution conflicts caused by a multi-targeting ProjectReference in the asset.

Changes:

  • Opt the AssemblyFixtureProviderAcceptance asset out of source-generation builds by overriding SourceGenMetadataModes to an empty set.
  • Add an inline comment documenting the RAR/MSB3277 conflict scenario and why reflection-only is sufficient for this test.
Show a summary per file
FileDescription
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.csOverrides the asset fixture’s SourceGenMetadataModes to skip source-gen builds that are not exercised by the test and currently fail due to assembly version conflicts.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Low

@Evangelink

This comment has been minimized.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

Expert Review — PR #9504

Scope: Single file change in test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.cs. The change adds a SourceGenMetadataModes => [] override and explanatory comment to TestAssetFixture, opting the AssemblyFixtureProviderAcceptance asset out of the source-gen build.


Pattern Verification

The escape hatch used here (protected override IReadOnlyList<MetadataMode> SourceGenMetadataModes => []) is the documented, established opt-out mechanism. It is applied identically in FrameworkOnlyTests.TestAssetFixture (which also carries an explanatory comment stating the reason). The TestAssetFixtureBase XML doc on SourceGenMetadataModes explicitly names this as the opt-out path for assets that "genuinely cannot build under source generation."

Confirmed that TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.TestProjectName, tfm) is called without a metadataMode argument — so the reflection build is the only one exercised by the test. Opting out of building the source-gen variant does not remove any test execution coverage.


22-Dimension Summary

#DimensionVerdictNotes
1Algorithmic Correctness✅ LGTMFix matches root cause: source-gen build fails, test never uses source-gen variant; opting out is correct, not a symptom patch.
2Threading & ConcurrencyN/ANo new concurrent code.
3Security & IPC Contract SafetyN/ANo security boundary touched.
4Public API & Binary CompatibilityN/ANo public API changes.
5Performance & AllocationsN/ATest fixture code; not a hot path.
6Cross-TFM CompatibilityN/AThe fix avoids the cross-TFM RAR issue by not building the failing variant. The => [] collection expression is valid on all targeted TFMs.
7Resource & IDisposable ManagementN/ANo new disposable resources.
8Defensive Coding at BoundariesN/ANo boundary logic changed.
9Localization & ResourcesN/ANo user-facing strings.
10Test Isolation✅ LGTMSingle test method in class — no shared mutable asset across methods, so [DoNotParallelize] is not required.
11Assertion QualityN/ANo assertion changes.
12Flakiness Patterns✅ LGTMThis PR fixes a reliability failure; no new flakiness introduced.
13Test Completeness & Coverage✅ LGTMThe source-gen variant was never exercised by any test method. No coverage is lost. See observation below regarding a potential follow-up.
14Data-Driven Test CoverageN/ANo data rows changed.
15Code Structure & Simplification✅ LGTMMinimal, clean override consistent with FrameworkOnlyTests.
16Naming & Conventions✅ LGTMConsistent with existing pattern.
17Documentation Accuracy✅ LGTMComment accurately explains the RAR resolution issue, the exact error (MSB3277), the promoter (MSBuildTreatWarningsAsErrors), and the justification. Matches the FrameworkOnlyTests comment style.
18Analyzer & Code Fix QualityN/ANo src/Analyzers/ changes.
19IPC Wire CompatibilityN/ANo serialization changes.
20Build Infrastructure & DependenciesN/ANo eng/ or package reference changes.
21Scope & PR Discipline✅ LGTMSingle-concern change; PR description is thorough with root-cause analysis and verification steps.
22PowerShell Scripting HygieneN/ANo .ps1 changes.

22/22 dimensions clean.


💡 Optional Follow-up Observation (non-blocking)

The underlying MSBuild/RAR issue — where the source-gen build's isolated bin/obj redirection causes the net8.0 leg of a multi-targeting ProjectReference to resolve transitive assemblies from the net10.0 output — will silently affect any other acceptance asset that has a similar multi-targeting ProjectReference. Opting this asset out is the right short-term fix to unblock main, but it may be worth filing a tracking issue to investigate whether the source-gen build infrastructure should be hardened to pass -p:TargetFrameworks=... or a comparable property to pin transitive reference resolution to the correct TFM subfolder. If such an issue already exists, linking it from the comment would help future readers.


Verdict: This is a correct, minimal, well-documented fix that follows the established pattern. Ready to merge.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jun 29, 2026
…t globs
Root cause of the broken main build: the acceptance source-gen build harness
let a multi-project asset's leftover reflection outputs pollute the later
source-gen build.
TestAssetFixtureBase builds each asset twice in the same directory: first the
reflection build (default bin/Release), then the source-gen build (bin/obj
redirected to a *SourceGen sub-folder). For a multi-project asset (e.g.
AssemblyFixtureProviderAcceptance, whose test project references ProviderLibrary),
the reflection build leaves ProviderLibrary/bin/Release/<tfm>/*.dll on disk. When
the source-gen build then evaluates the host project, the SDK's default item globs
pulled those stray DLLs in as None items, and RAR treated a wrong-TFM copy (a
net10.0 MSTest.TestFramework.Extensions.dll, assembly version 9.0.0.0) as a
candidate for the net8.0 leg -> MSB3277, which MSBuildTreatWarningsAsErrors
promotes to an error, failing the fixture's ClassInitialize.
The injected props already re-excluded bin/obj, but only at the asset root
(bin/**;obj/**), which does not match the nested ProviderLibrary/bin/**. Broaden
the re-exclusion to **/obj/**;**/bin/** so a referenced project's leftover
reflection outputs stay out of the source-gen build's globs.
The reflection build (#1) is unaffected because at its evaluation time the
referenced project's bin is still empty; only the later source-gen build (#2)
runs in a directory polluted by #1.
This is the proper harness fix; the AssemblyFixtureProvider opt-out is no longer
needed, so the asset is restored to source-gen coverage. Verified by running the
AssemblyFixtureProvider acceptance tests (reflection + source-gen builds) green
across net462/net8.0/net10.0.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@EvangelinkAmaury Levé (Evangelink) changed the title Fix broken main: opt AssemblyFixtureProvider acceptance asset out of source-gen buildFix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset buildsJun 29, 2026
@Evangelink

Copy link
Copy Markdown
MemberAuthor

🧪 Test quality grade — PR #9504

No new or modified test methods were identified in the changed regions
of this PR. Nothing to grade.

The only changed file (test/Utilities/Microsoft.Testing.TestInfrastructure/AcceptanceSourceGen.cs) is infrastructure/utility code that generates MSBuild props content — it contains no test methods.

Re-run with /grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 93.1 AIC · ⌖ 13.1 AIC · ⊞ 45.7K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) merged commit 4ea34a2 into mainJun 29, 2026
39 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu branch June 29, 2026 16:17
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset builds - #9504

Merged
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu
Jun 29, 2026
Merged

Fix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset builds#9504
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jun 29, 2026

Copy link
Copy Markdown
Member

Problem

main has been red across many consecutive official builds (e.g. build 3010746).

The failing test is MSTest.Acceptance.IntegrationTests.AssemblyFixtureProviderTests.AssemblyFixtureProvider_FromReferencedLibrary_RunsAssemblyInitializeAndCleanup. It fails in its ClassInitialize because the source-gen build variant of the AssemblyFixtureProviderAcceptance asset fails to compile with MSB3277.

Root cause (a harness bug, not a product bug)

TestAssetFixtureBase.InitializeAsync builds each acceptance asset twice in the same directory: first the reflection build (default bin/Release), then the source-gen build (with bin/obj redirected to a *SourceGen sub-folder).

For a multi-project asset — AssemblyFixtureProviderAcceptance's test project references ProviderLibrary — the first (reflection) build leaves ProviderLibrary/bin/Release/<tfm>/*.dll on disk. When the later source-gen build then evaluates the host project, the SDK's default item globs pull those stray DLLs in as None items (confirmed in the binlog: AddItem None -> ProviderLibrary\bin\Release\net10.0\MSTest.TestFramework.Extensions.dll). RAR then treats the wrong-TFM copy (a net10.0 MSTest.TestFramework.Extensions.dll, assembly version 9.0.0.0) as a candidate for the net8.0 leg:

error MSB3277: Found conflicts between different versions of "System.Runtime" ... 8.0.0.0 vs 9.0.0.0
...ProviderLibrary\bin\Release\net10.0\MSTest.TestFramework.Extensions.dll

MSBuildTreatWarningsAsErrors=true (passed only to the source-gen build) promotes this to an error, so ClassInitialize throws.

The injected props already re-excluded bin/obj, but only at the asset root (bin/**;obj/**), which does not match the nested ProviderLibrary/bin/**. The reflection build (#1) is unaffected because at its evaluation time the referenced project's bin is still empty — only the later source-gen build (#2) runs in a directory polluted by #1.

Fix

Broaden the injected DefaultItemExcludes in AcceptanceSourceGen to also exclude nested bin/obj (**/obj/**;**/bin/**), so a referenced project's leftover reflection outputs stay out of the source-gen build's globs. This is the proper harness fix and keeps the existing redirect-based design.

With the harness corrected, the AssemblyFixtureProvider opt-out is unnecessary, so the asset is restored to source-gen coverage.

Verification

  • Reproduced the original MSB3277 from the CI binlog (binlog-mcp).
  • Ran the AssemblyFixtureProvider acceptance tests (which drive both the reflection and source-gen builds via the fixture) — all 3 pass across net462/net8.0/net10.0.

The AssemblyFixtureProviderAcceptance asset has a multi-targeting
ProjectReference (ProviderLibrary). In the source-gen build variant, RAR
for the net8.0 leg resolves ProviderLibrary's transitive
MSTest.TestFramework.Extensions.dll from the net10.0 reflection output
instead of the net8.0 one, producing MSB3277 System.* version conflicts
(8.0.0.0 vs 9.0.0.0). The source-gen build promotes these to errors via
MSBuildTreatWarningsAsErrors, failing the fixture's ClassInitialize and
breaking the main build.
The test only exercises the reflection build, so opt the asset out of the
source-gen build (matching FrameworkOnlyTests).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 29, 2026 13:46

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a persistent main build break by preventing an unused acceptance-test asset build variant (source-generation) from compiling, avoiding MSBuild reference-resolution conflicts caused by a multi-targeting ProjectReference in the asset.

Changes:

  • Opt the AssemblyFixtureProviderAcceptance asset out of source-generation builds by overriding SourceGenMetadataModes to an empty set.
  • Add an inline comment documenting the RAR/MSB3277 conflict scenario and why reflection-only is sufficient for this test.
Show a summary per file
FileDescription
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.csOverrides the asset fixture’s SourceGenMetadataModes to skip source-gen builds that are not exercised by the test and currently fail due to assembly version conflicts.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Low

@Evangelink

This comment has been minimized.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

Expert Review — PR #9504

Scope: Single file change in test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.cs. The change adds a SourceGenMetadataModes => [] override and explanatory comment to TestAssetFixture, opting the AssemblyFixtureProviderAcceptance asset out of the source-gen build.


Pattern Verification

The escape hatch used here (protected override IReadOnlyList<MetadataMode> SourceGenMetadataModes => []) is the documented, established opt-out mechanism. It is applied identically in FrameworkOnlyTests.TestAssetFixture (which also carries an explanatory comment stating the reason). The TestAssetFixtureBase XML doc on SourceGenMetadataModes explicitly names this as the opt-out path for assets that "genuinely cannot build under source generation."

Confirmed that TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.TestProjectName, tfm) is called without a metadataMode argument — so the reflection build is the only one exercised by the test. Opting out of building the source-gen variant does not remove any test execution coverage.


22-Dimension Summary

#DimensionVerdictNotes
1Algorithmic Correctness✅ LGTMFix matches root cause: source-gen build fails, test never uses source-gen variant; opting out is correct, not a symptom patch.
2Threading & ConcurrencyN/ANo new concurrent code.
3Security & IPC Contract SafetyN/ANo security boundary touched.
4Public API & Binary CompatibilityN/ANo public API changes.
5Performance & AllocationsN/ATest fixture code; not a hot path.
6Cross-TFM CompatibilityN/AThe fix avoids the cross-TFM RAR issue by not building the failing variant. The => [] collection expression is valid on all targeted TFMs.
7Resource & IDisposable ManagementN/ANo new disposable resources.
8Defensive Coding at BoundariesN/ANo boundary logic changed.
9Localization & ResourcesN/ANo user-facing strings.
10Test Isolation✅ LGTMSingle test method in class — no shared mutable asset across methods, so [DoNotParallelize] is not required.
11Assertion QualityN/ANo assertion changes.
12Flakiness Patterns✅ LGTMThis PR fixes a reliability failure; no new flakiness introduced.
13Test Completeness & Coverage✅ LGTMThe source-gen variant was never exercised by any test method. No coverage is lost. See observation below regarding a potential follow-up.
14Data-Driven Test CoverageN/ANo data rows changed.
15Code Structure & Simplification✅ LGTMMinimal, clean override consistent with FrameworkOnlyTests.
16Naming & Conventions✅ LGTMConsistent with existing pattern.
17Documentation Accuracy✅ LGTMComment accurately explains the RAR resolution issue, the exact error (MSB3277), the promoter (MSBuildTreatWarningsAsErrors), and the justification. Matches the FrameworkOnlyTests comment style.
18Analyzer & Code Fix QualityN/ANo src/Analyzers/ changes.
19IPC Wire CompatibilityN/ANo serialization changes.
20Build Infrastructure & DependenciesN/ANo eng/ or package reference changes.
21Scope & PR Discipline✅ LGTMSingle-concern change; PR description is thorough with root-cause analysis and verification steps.
22PowerShell Scripting HygieneN/ANo .ps1 changes.

22/22 dimensions clean.


💡 Optional Follow-up Observation (non-blocking)

The underlying MSBuild/RAR issue — where the source-gen build's isolated bin/obj redirection causes the net8.0 leg of a multi-targeting ProjectReference to resolve transitive assemblies from the net10.0 output — will silently affect any other acceptance asset that has a similar multi-targeting ProjectReference. Opting this asset out is the right short-term fix to unblock main, but it may be worth filing a tracking issue to investigate whether the source-gen build infrastructure should be hardened to pass -p:TargetFrameworks=... or a comparable property to pin transitive reference resolution to the correct TFM subfolder. If such an issue already exists, linking it from the comment would help future readers.


Verdict: This is a correct, minimal, well-documented fix that follows the established pattern. Ready to merge.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jun 29, 2026
…t globs
Root cause of the broken main build: the acceptance source-gen build harness
let a multi-project asset's leftover reflection outputs pollute the later
source-gen build.
TestAssetFixtureBase builds each asset twice in the same directory: first the
reflection build (default bin/Release), then the source-gen build (bin/obj
redirected to a *SourceGen sub-folder). For a multi-project asset (e.g.
AssemblyFixtureProviderAcceptance, whose test project references ProviderLibrary),
the reflection build leaves ProviderLibrary/bin/Release/<tfm>/*.dll on disk. When
the source-gen build then evaluates the host project, the SDK's default item globs
pulled those stray DLLs in as None items, and RAR treated a wrong-TFM copy (a
net10.0 MSTest.TestFramework.Extensions.dll, assembly version 9.0.0.0) as a
candidate for the net8.0 leg -> MSB3277, which MSBuildTreatWarningsAsErrors
promotes to an error, failing the fixture's ClassInitialize.
The injected props already re-excluded bin/obj, but only at the asset root
(bin/**;obj/**), which does not match the nested ProviderLibrary/bin/**. Broaden
the re-exclusion to **/obj/**;**/bin/** so a referenced project's leftover
reflection outputs stay out of the source-gen build's globs.
The reflection build (#1) is unaffected because at its evaluation time the
referenced project's bin is still empty; only the later source-gen build (#2)
runs in a directory polluted by #1.
This is the proper harness fix; the AssemblyFixtureProvider opt-out is no longer
needed, so the asset is restored to source-gen coverage. Verified by running the
AssemblyFixtureProvider acceptance tests (reflection + source-gen builds) green
across net462/net8.0/net10.0.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@EvangelinkAmaury Levé (Evangelink) changed the title Fix broken main: opt AssemblyFixtureProvider acceptance asset out of source-gen buildFix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset buildsJun 29, 2026
@Evangelink

Copy link
Copy Markdown
MemberAuthor

🧪 Test quality grade — PR #9504

No new or modified test methods were identified in the changed regions
of this PR. Nothing to grade.

The only changed file (test/Utilities/Microsoft.Testing.TestInfrastructure/AcceptanceSourceGen.cs) is infrastructure/utility code that generates MSBuild props content — it contains no test methods.

Re-run with /grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 93.1 AIC · ⌖ 13.1 AIC · ⊞ 45.7K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) merged commit 4ea34a2 into mainJun 29, 2026
39 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu branch June 29, 2026 16:17
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Fix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset builds - #9504

Merged
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu
Jun 29, 2026
Merged

Fix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset builds#9504
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jun 29, 2026

Copy link
Copy Markdown
Member

Problem

main has been red across many consecutive official builds (e.g. build 3010746).

The failing test is MSTest.Acceptance.IntegrationTests.AssemblyFixtureProviderTests.AssemblyFixtureProvider_FromReferencedLibrary_RunsAssemblyInitializeAndCleanup. It fails in its ClassInitialize because the source-gen build variant of the AssemblyFixtureProviderAcceptance asset fails to compile with MSB3277.

Root cause (a harness bug, not a product bug)

TestAssetFixtureBase.InitializeAsync builds each acceptance asset twice in the same directory: first the reflection build (default bin/Release), then the source-gen build (with bin/obj redirected to a *SourceGen sub-folder).

For a multi-project asset — AssemblyFixtureProviderAcceptance's test project references ProviderLibrary — the first (reflection) build leaves ProviderLibrary/bin/Release/<tfm>/*.dll on disk. When the later source-gen build then evaluates the host project, the SDK's default item globs pull those stray DLLs in as None items (confirmed in the binlog: AddItem None -> ProviderLibrary\bin\Release\net10.0\MSTest.TestFramework.Extensions.dll). RAR then treats the wrong-TFM copy (a net10.0 MSTest.TestFramework.Extensions.dll, assembly version 9.0.0.0) as a candidate for the net8.0 leg:

error MSB3277: Found conflicts between different versions of "System.Runtime" ... 8.0.0.0 vs 9.0.0.0
...ProviderLibrary\bin\Release\net10.0\MSTest.TestFramework.Extensions.dll

MSBuildTreatWarningsAsErrors=true (passed only to the source-gen build) promotes this to an error, so ClassInitialize throws.

The injected props already re-excluded bin/obj, but only at the asset root (bin/**;obj/**), which does not match the nested ProviderLibrary/bin/**. The reflection build (#1) is unaffected because at its evaluation time the referenced project's bin is still empty — only the later source-gen build (#2) runs in a directory polluted by #1.

Fix

Broaden the injected DefaultItemExcludes in AcceptanceSourceGen to also exclude nested bin/obj (**/obj/**;**/bin/**), so a referenced project's leftover reflection outputs stay out of the source-gen build's globs. This is the proper harness fix and keeps the existing redirect-based design.

With the harness corrected, the AssemblyFixtureProvider opt-out is unnecessary, so the asset is restored to source-gen coverage.

Verification

  • Reproduced the original MSB3277 from the CI binlog (binlog-mcp).
  • Ran the AssemblyFixtureProvider acceptance tests (which drive both the reflection and source-gen builds via the fixture) — all 3 pass across net462/net8.0/net10.0.

The AssemblyFixtureProviderAcceptance asset has a multi-targeting
ProjectReference (ProviderLibrary). In the source-gen build variant, RAR
for the net8.0 leg resolves ProviderLibrary's transitive
MSTest.TestFramework.Extensions.dll from the net10.0 reflection output
instead of the net8.0 one, producing MSB3277 System.* version conflicts
(8.0.0.0 vs 9.0.0.0). The source-gen build promotes these to errors via
MSBuildTreatWarningsAsErrors, failing the fixture's ClassInitialize and
breaking the main build.
The test only exercises the reflection build, so opt the asset out of the
source-gen build (matching FrameworkOnlyTests).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 29, 2026 13:46

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a persistent main build break by preventing an unused acceptance-test asset build variant (source-generation) from compiling, avoiding MSBuild reference-resolution conflicts caused by a multi-targeting ProjectReference in the asset.

Changes:

  • Opt the AssemblyFixtureProviderAcceptance asset out of source-generation builds by overriding SourceGenMetadataModes to an empty set.
  • Add an inline comment documenting the RAR/MSB3277 conflict scenario and why reflection-only is sufficient for this test.
Show a summary per file
FileDescription
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.csOverrides the asset fixture’s SourceGenMetadataModes to skip source-gen builds that are not exercised by the test and currently fail due to assembly version conflicts.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Low

@Evangelink

This comment has been minimized.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

Expert Review — PR #9504

Scope: Single file change in test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.cs. The change adds a SourceGenMetadataModes => [] override and explanatory comment to TestAssetFixture, opting the AssemblyFixtureProviderAcceptance asset out of the source-gen build.


Pattern Verification

The escape hatch used here (protected override IReadOnlyList<MetadataMode> SourceGenMetadataModes => []) is the documented, established opt-out mechanism. It is applied identically in FrameworkOnlyTests.TestAssetFixture (which also carries an explanatory comment stating the reason). The TestAssetFixtureBase XML doc on SourceGenMetadataModes explicitly names this as the opt-out path for assets that "genuinely cannot build under source generation."

Confirmed that TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.TestProjectName, tfm) is called without a metadataMode argument — so the reflection build is the only one exercised by the test. Opting out of building the source-gen variant does not remove any test execution coverage.


22-Dimension Summary

#DimensionVerdictNotes
1Algorithmic Correctness✅ LGTMFix matches root cause: source-gen build fails, test never uses source-gen variant; opting out is correct, not a symptom patch.
2Threading & ConcurrencyN/ANo new concurrent code.
3Security & IPC Contract SafetyN/ANo security boundary touched.
4Public API & Binary CompatibilityN/ANo public API changes.
5Performance & AllocationsN/ATest fixture code; not a hot path.
6Cross-TFM CompatibilityN/AThe fix avoids the cross-TFM RAR issue by not building the failing variant. The => [] collection expression is valid on all targeted TFMs.
7Resource & IDisposable ManagementN/ANo new disposable resources.
8Defensive Coding at BoundariesN/ANo boundary logic changed.
9Localization & ResourcesN/ANo user-facing strings.
10Test Isolation✅ LGTMSingle test method in class — no shared mutable asset across methods, so [DoNotParallelize] is not required.
11Assertion QualityN/ANo assertion changes.
12Flakiness Patterns✅ LGTMThis PR fixes a reliability failure; no new flakiness introduced.
13Test Completeness & Coverage✅ LGTMThe source-gen variant was never exercised by any test method. No coverage is lost. See observation below regarding a potential follow-up.
14Data-Driven Test CoverageN/ANo data rows changed.
15Code Structure & Simplification✅ LGTMMinimal, clean override consistent with FrameworkOnlyTests.
16Naming & Conventions✅ LGTMConsistent with existing pattern.
17Documentation Accuracy✅ LGTMComment accurately explains the RAR resolution issue, the exact error (MSB3277), the promoter (MSBuildTreatWarningsAsErrors), and the justification. Matches the FrameworkOnlyTests comment style.
18Analyzer & Code Fix QualityN/ANo src/Analyzers/ changes.
19IPC Wire CompatibilityN/ANo serialization changes.
20Build Infrastructure & DependenciesN/ANo eng/ or package reference changes.
21Scope & PR Discipline✅ LGTMSingle-concern change; PR description is thorough with root-cause analysis and verification steps.
22PowerShell Scripting HygieneN/ANo .ps1 changes.

22/22 dimensions clean.


💡 Optional Follow-up Observation (non-blocking)

The underlying MSBuild/RAR issue — where the source-gen build's isolated bin/obj redirection causes the net8.0 leg of a multi-targeting ProjectReference to resolve transitive assemblies from the net10.0 output — will silently affect any other acceptance asset that has a similar multi-targeting ProjectReference. Opting this asset out is the right short-term fix to unblock main, but it may be worth filing a tracking issue to investigate whether the source-gen build infrastructure should be hardened to pass -p:TargetFrameworks=... or a comparable property to pin transitive reference resolution to the correct TFM subfolder. If such an issue already exists, linking it from the comment would help future readers.


Verdict: This is a correct, minimal, well-documented fix that follows the established pattern. Ready to merge.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jun 29, 2026
…t globs
Root cause of the broken main build: the acceptance source-gen build harness
let a multi-project asset's leftover reflection outputs pollute the later
source-gen build.
TestAssetFixtureBase builds each asset twice in the same directory: first the
reflection build (default bin/Release), then the source-gen build (bin/obj
redirected to a *SourceGen sub-folder). For a multi-project asset (e.g.
AssemblyFixtureProviderAcceptance, whose test project references ProviderLibrary),
the reflection build leaves ProviderLibrary/bin/Release/<tfm>/*.dll on disk. When
the source-gen build then evaluates the host project, the SDK's default item globs
pulled those stray DLLs in as None items, and RAR treated a wrong-TFM copy (a
net10.0 MSTest.TestFramework.Extensions.dll, assembly version 9.0.0.0) as a
candidate for the net8.0 leg -> MSB3277, which MSBuildTreatWarningsAsErrors
promotes to an error, failing the fixture's ClassInitialize.
The injected props already re-excluded bin/obj, but only at the asset root
(bin/**;obj/**), which does not match the nested ProviderLibrary/bin/**. Broaden
the re-exclusion to **/obj/**;**/bin/** so a referenced project's leftover
reflection outputs stay out of the source-gen build's globs.
The reflection build (#1) is unaffected because at its evaluation time the
referenced project's bin is still empty; only the later source-gen build (#2)
runs in a directory polluted by #1.
This is the proper harness fix; the AssemblyFixtureProvider opt-out is no longer
needed, so the asset is restored to source-gen coverage. Verified by running the
AssemblyFixtureProvider acceptance tests (reflection + source-gen builds) green
across net462/net8.0/net10.0.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@EvangelinkAmaury Levé (Evangelink) changed the title Fix broken main: opt AssemblyFixtureProvider acceptance asset out of source-gen buildFix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset buildsJun 29, 2026
@Evangelink

Copy link
Copy Markdown
MemberAuthor

🧪 Test quality grade — PR #9504

No new or modified test methods were identified in the changed regions
of this PR. Nothing to grade.

The only changed file (test/Utilities/Microsoft.Testing.TestInfrastructure/AcceptanceSourceGen.cs) is infrastructure/utility code that generates MSBuild props content — it contains no test methods.

Re-run with /grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 93.1 AIC · ⌖ 13.1 AIC · ⊞ 45.7K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) merged commit 4ea34a2 into mainJun 29, 2026
39 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu branch June 29, 2026 16:17
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset builds - #9504

Merged
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu
Jun 29, 2026
Merged

Fix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset builds#9504
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jun 29, 2026

Copy link
Copy Markdown
Member

Problem

main has been red across many consecutive official builds (e.g. build 3010746).

The failing test is MSTest.Acceptance.IntegrationTests.AssemblyFixtureProviderTests.AssemblyFixtureProvider_FromReferencedLibrary_RunsAssemblyInitializeAndCleanup. It fails in its ClassInitialize because the source-gen build variant of the AssemblyFixtureProviderAcceptance asset fails to compile with MSB3277.

Root cause (a harness bug, not a product bug)

TestAssetFixtureBase.InitializeAsync builds each acceptance asset twice in the same directory: first the reflection build (default bin/Release), then the source-gen build (with bin/obj redirected to a *SourceGen sub-folder).

For a multi-project asset — AssemblyFixtureProviderAcceptance's test project references ProviderLibrary — the first (reflection) build leaves ProviderLibrary/bin/Release/<tfm>/*.dll on disk. When the later source-gen build then evaluates the host project, the SDK's default item globs pull those stray DLLs in as None items (confirmed in the binlog: AddItem None -> ProviderLibrary\bin\Release\net10.0\MSTest.TestFramework.Extensions.dll). RAR then treats the wrong-TFM copy (a net10.0 MSTest.TestFramework.Extensions.dll, assembly version 9.0.0.0) as a candidate for the net8.0 leg:

error MSB3277: Found conflicts between different versions of "System.Runtime" ... 8.0.0.0 vs 9.0.0.0
...ProviderLibrary\bin\Release\net10.0\MSTest.TestFramework.Extensions.dll

MSBuildTreatWarningsAsErrors=true (passed only to the source-gen build) promotes this to an error, so ClassInitialize throws.

The injected props already re-excluded bin/obj, but only at the asset root (bin/**;obj/**), which does not match the nested ProviderLibrary/bin/**. The reflection build (#1) is unaffected because at its evaluation time the referenced project's bin is still empty — only the later source-gen build (#2) runs in a directory polluted by #1.

Fix

Broaden the injected DefaultItemExcludes in AcceptanceSourceGen to also exclude nested bin/obj (**/obj/**;**/bin/**), so a referenced project's leftover reflection outputs stay out of the source-gen build's globs. This is the proper harness fix and keeps the existing redirect-based design.

With the harness corrected, the AssemblyFixtureProvider opt-out is unnecessary, so the asset is restored to source-gen coverage.

Verification

  • Reproduced the original MSB3277 from the CI binlog (binlog-mcp).
  • Ran the AssemblyFixtureProvider acceptance tests (which drive both the reflection and source-gen builds via the fixture) — all 3 pass across net462/net8.0/net10.0.

The AssemblyFixtureProviderAcceptance asset has a multi-targeting
ProjectReference (ProviderLibrary). In the source-gen build variant, RAR
for the net8.0 leg resolves ProviderLibrary's transitive
MSTest.TestFramework.Extensions.dll from the net10.0 reflection output
instead of the net8.0 one, producing MSB3277 System.* version conflicts
(8.0.0.0 vs 9.0.0.0). The source-gen build promotes these to errors via
MSBuildTreatWarningsAsErrors, failing the fixture's ClassInitialize and
breaking the main build.
The test only exercises the reflection build, so opt the asset out of the
source-gen build (matching FrameworkOnlyTests).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 29, 2026 13:46

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a persistent main build break by preventing an unused acceptance-test asset build variant (source-generation) from compiling, avoiding MSBuild reference-resolution conflicts caused by a multi-targeting ProjectReference in the asset.

Changes:

  • Opt the AssemblyFixtureProviderAcceptance asset out of source-generation builds by overriding SourceGenMetadataModes to an empty set.
  • Add an inline comment documenting the RAR/MSB3277 conflict scenario and why reflection-only is sufficient for this test.
Show a summary per file
FileDescription
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.csOverrides the asset fixture’s SourceGenMetadataModes to skip source-gen builds that are not exercised by the test and currently fail due to assembly version conflicts.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Low

@Evangelink

This comment has been minimized.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

Expert Review — PR #9504

Scope: Single file change in test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.cs. The change adds a SourceGenMetadataModes => [] override and explanatory comment to TestAssetFixture, opting the AssemblyFixtureProviderAcceptance asset out of the source-gen build.


Pattern Verification

The escape hatch used here (protected override IReadOnlyList<MetadataMode> SourceGenMetadataModes => []) is the documented, established opt-out mechanism. It is applied identically in FrameworkOnlyTests.TestAssetFixture (which also carries an explanatory comment stating the reason). The TestAssetFixtureBase XML doc on SourceGenMetadataModes explicitly names this as the opt-out path for assets that "genuinely cannot build under source generation."

Confirmed that TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.TestProjectName, tfm) is called without a metadataMode argument — so the reflection build is the only one exercised by the test. Opting out of building the source-gen variant does not remove any test execution coverage.


22-Dimension Summary

#DimensionVerdictNotes
1Algorithmic Correctness✅ LGTMFix matches root cause: source-gen build fails, test never uses source-gen variant; opting out is correct, not a symptom patch.
2Threading & ConcurrencyN/ANo new concurrent code.
3Security & IPC Contract SafetyN/ANo security boundary touched.
4Public API & Binary CompatibilityN/ANo public API changes.
5Performance & AllocationsN/ATest fixture code; not a hot path.
6Cross-TFM CompatibilityN/AThe fix avoids the cross-TFM RAR issue by not building the failing variant. The => [] collection expression is valid on all targeted TFMs.
7Resource & IDisposable ManagementN/ANo new disposable resources.
8Defensive Coding at BoundariesN/ANo boundary logic changed.
9Localization & ResourcesN/ANo user-facing strings.
10Test Isolation✅ LGTMSingle test method in class — no shared mutable asset across methods, so [DoNotParallelize] is not required.
11Assertion QualityN/ANo assertion changes.
12Flakiness Patterns✅ LGTMThis PR fixes a reliability failure; no new flakiness introduced.
13Test Completeness & Coverage✅ LGTMThe source-gen variant was never exercised by any test method. No coverage is lost. See observation below regarding a potential follow-up.
14Data-Driven Test CoverageN/ANo data rows changed.
15Code Structure & Simplification✅ LGTMMinimal, clean override consistent with FrameworkOnlyTests.
16Naming & Conventions✅ LGTMConsistent with existing pattern.
17Documentation Accuracy✅ LGTMComment accurately explains the RAR resolution issue, the exact error (MSB3277), the promoter (MSBuildTreatWarningsAsErrors), and the justification. Matches the FrameworkOnlyTests comment style.
18Analyzer & Code Fix QualityN/ANo src/Analyzers/ changes.
19IPC Wire CompatibilityN/ANo serialization changes.
20Build Infrastructure & DependenciesN/ANo eng/ or package reference changes.
21Scope & PR Discipline✅ LGTMSingle-concern change; PR description is thorough with root-cause analysis and verification steps.
22PowerShell Scripting HygieneN/ANo .ps1 changes.

22/22 dimensions clean.


💡 Optional Follow-up Observation (non-blocking)

The underlying MSBuild/RAR issue — where the source-gen build's isolated bin/obj redirection causes the net8.0 leg of a multi-targeting ProjectReference to resolve transitive assemblies from the net10.0 output — will silently affect any other acceptance asset that has a similar multi-targeting ProjectReference. Opting this asset out is the right short-term fix to unblock main, but it may be worth filing a tracking issue to investigate whether the source-gen build infrastructure should be hardened to pass -p:TargetFrameworks=... or a comparable property to pin transitive reference resolution to the correct TFM subfolder. If such an issue already exists, linking it from the comment would help future readers.


Verdict: This is a correct, minimal, well-documented fix that follows the established pattern. Ready to merge.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jun 29, 2026
…t globs
Root cause of the broken main build: the acceptance source-gen build harness
let a multi-project asset's leftover reflection outputs pollute the later
source-gen build.
TestAssetFixtureBase builds each asset twice in the same directory: first the
reflection build (default bin/Release), then the source-gen build (bin/obj
redirected to a *SourceGen sub-folder). For a multi-project asset (e.g.
AssemblyFixtureProviderAcceptance, whose test project references ProviderLibrary),
the reflection build leaves ProviderLibrary/bin/Release/<tfm>/*.dll on disk. When
the source-gen build then evaluates the host project, the SDK's default item globs
pulled those stray DLLs in as None items, and RAR treated a wrong-TFM copy (a
net10.0 MSTest.TestFramework.Extensions.dll, assembly version 9.0.0.0) as a
candidate for the net8.0 leg -> MSB3277, which MSBuildTreatWarningsAsErrors
promotes to an error, failing the fixture's ClassInitialize.
The injected props already re-excluded bin/obj, but only at the asset root
(bin/**;obj/**), which does not match the nested ProviderLibrary/bin/**. Broaden
the re-exclusion to **/obj/**;**/bin/** so a referenced project's leftover
reflection outputs stay out of the source-gen build's globs.
The reflection build (#1) is unaffected because at its evaluation time the
referenced project's bin is still empty; only the later source-gen build (#2)
runs in a directory polluted by #1.
This is the proper harness fix; the AssemblyFixtureProvider opt-out is no longer
needed, so the asset is restored to source-gen coverage. Verified by running the
AssemblyFixtureProvider acceptance tests (reflection + source-gen builds) green
across net462/net8.0/net10.0.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@EvangelinkAmaury Levé (Evangelink) changed the title Fix broken main: opt AssemblyFixtureProvider acceptance asset out of source-gen buildFix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset buildsJun 29, 2026
@Evangelink

Copy link
Copy Markdown
MemberAuthor

🧪 Test quality grade — PR #9504

No new or modified test methods were identified in the changed regions
of this PR. Nothing to grade.

The only changed file (test/Utilities/Microsoft.Testing.TestInfrastructure/AcceptanceSourceGen.cs) is infrastructure/utility code that generates MSBuild props content — it contains no test methods.

Re-run with /grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 93.1 AIC · ⌖ 13.1 AIC · ⊞ 45.7K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) merged commit 4ea34a2 into mainJun 29, 2026
39 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu branch June 29, 2026 16:17
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset builds - #9504

Merged
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu
Jun 29, 2026
Merged

Fix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset builds#9504
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jun 29, 2026

Copy link
Copy Markdown
Member

Problem

main has been red across many consecutive official builds (e.g. build 3010746).

The failing test is MSTest.Acceptance.IntegrationTests.AssemblyFixtureProviderTests.AssemblyFixtureProvider_FromReferencedLibrary_RunsAssemblyInitializeAndCleanup. It fails in its ClassInitialize because the source-gen build variant of the AssemblyFixtureProviderAcceptance asset fails to compile with MSB3277.

Root cause (a harness bug, not a product bug)

TestAssetFixtureBase.InitializeAsync builds each acceptance asset twice in the same directory: first the reflection build (default bin/Release), then the source-gen build (with bin/obj redirected to a *SourceGen sub-folder).

For a multi-project asset — AssemblyFixtureProviderAcceptance's test project references ProviderLibrary — the first (reflection) build leaves ProviderLibrary/bin/Release/<tfm>/*.dll on disk. When the later source-gen build then evaluates the host project, the SDK's default item globs pull those stray DLLs in as None items (confirmed in the binlog: AddItem None -> ProviderLibrary\bin\Release\net10.0\MSTest.TestFramework.Extensions.dll). RAR then treats the wrong-TFM copy (a net10.0 MSTest.TestFramework.Extensions.dll, assembly version 9.0.0.0) as a candidate for the net8.0 leg:

error MSB3277: Found conflicts between different versions of "System.Runtime" ... 8.0.0.0 vs 9.0.0.0
...ProviderLibrary\bin\Release\net10.0\MSTest.TestFramework.Extensions.dll

MSBuildTreatWarningsAsErrors=true (passed only to the source-gen build) promotes this to an error, so ClassInitialize throws.

The injected props already re-excluded bin/obj, but only at the asset root (bin/**;obj/**), which does not match the nested ProviderLibrary/bin/**. The reflection build (#1) is unaffected because at its evaluation time the referenced project's bin is still empty — only the later source-gen build (#2) runs in a directory polluted by #1.

Fix

Broaden the injected DefaultItemExcludes in AcceptanceSourceGen to also exclude nested bin/obj (**/obj/**;**/bin/**), so a referenced project's leftover reflection outputs stay out of the source-gen build's globs. This is the proper harness fix and keeps the existing redirect-based design.

With the harness corrected, the AssemblyFixtureProvider opt-out is unnecessary, so the asset is restored to source-gen coverage.

Verification

  • Reproduced the original MSB3277 from the CI binlog (binlog-mcp).
  • Ran the AssemblyFixtureProvider acceptance tests (which drive both the reflection and source-gen builds via the fixture) — all 3 pass across net462/net8.0/net10.0.

The AssemblyFixtureProviderAcceptance asset has a multi-targeting
ProjectReference (ProviderLibrary). In the source-gen build variant, RAR
for the net8.0 leg resolves ProviderLibrary's transitive
MSTest.TestFramework.Extensions.dll from the net10.0 reflection output
instead of the net8.0 one, producing MSB3277 System.* version conflicts
(8.0.0.0 vs 9.0.0.0). The source-gen build promotes these to errors via
MSBuildTreatWarningsAsErrors, failing the fixture's ClassInitialize and
breaking the main build.
The test only exercises the reflection build, so opt the asset out of the
source-gen build (matching FrameworkOnlyTests).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 29, 2026 13:46

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a persistent main build break by preventing an unused acceptance-test asset build variant (source-generation) from compiling, avoiding MSBuild reference-resolution conflicts caused by a multi-targeting ProjectReference in the asset.

Changes:

  • Opt the AssemblyFixtureProviderAcceptance asset out of source-generation builds by overriding SourceGenMetadataModes to an empty set.
  • Add an inline comment documenting the RAR/MSB3277 conflict scenario and why reflection-only is sufficient for this test.
Show a summary per file
FileDescription
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.csOverrides the asset fixture’s SourceGenMetadataModes to skip source-gen builds that are not exercised by the test and currently fail due to assembly version conflicts.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Low

@Evangelink

This comment has been minimized.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

Expert Review — PR #9504

Scope: Single file change in test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.cs. The change adds a SourceGenMetadataModes => [] override and explanatory comment to TestAssetFixture, opting the AssemblyFixtureProviderAcceptance asset out of the source-gen build.


Pattern Verification

The escape hatch used here (protected override IReadOnlyList<MetadataMode> SourceGenMetadataModes => []) is the documented, established opt-out mechanism. It is applied identically in FrameworkOnlyTests.TestAssetFixture (which also carries an explanatory comment stating the reason). The TestAssetFixtureBase XML doc on SourceGenMetadataModes explicitly names this as the opt-out path for assets that "genuinely cannot build under source generation."

Confirmed that TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.TestProjectName, tfm) is called without a metadataMode argument — so the reflection build is the only one exercised by the test. Opting out of building the source-gen variant does not remove any test execution coverage.


22-Dimension Summary

#DimensionVerdictNotes
1Algorithmic Correctness✅ LGTMFix matches root cause: source-gen build fails, test never uses source-gen variant; opting out is correct, not a symptom patch.
2Threading & ConcurrencyN/ANo new concurrent code.
3Security & IPC Contract SafetyN/ANo security boundary touched.
4Public API & Binary CompatibilityN/ANo public API changes.
5Performance & AllocationsN/ATest fixture code; not a hot path.
6Cross-TFM CompatibilityN/AThe fix avoids the cross-TFM RAR issue by not building the failing variant. The => [] collection expression is valid on all targeted TFMs.
7Resource & IDisposable ManagementN/ANo new disposable resources.
8Defensive Coding at BoundariesN/ANo boundary logic changed.
9Localization & ResourcesN/ANo user-facing strings.
10Test Isolation✅ LGTMSingle test method in class — no shared mutable asset across methods, so [DoNotParallelize] is not required.
11Assertion QualityN/ANo assertion changes.
12Flakiness Patterns✅ LGTMThis PR fixes a reliability failure; no new flakiness introduced.
13Test Completeness & Coverage✅ LGTMThe source-gen variant was never exercised by any test method. No coverage is lost. See observation below regarding a potential follow-up.
14Data-Driven Test CoverageN/ANo data rows changed.
15Code Structure & Simplification✅ LGTMMinimal, clean override consistent with FrameworkOnlyTests.
16Naming & Conventions✅ LGTMConsistent with existing pattern.
17Documentation Accuracy✅ LGTMComment accurately explains the RAR resolution issue, the exact error (MSB3277), the promoter (MSBuildTreatWarningsAsErrors), and the justification. Matches the FrameworkOnlyTests comment style.
18Analyzer & Code Fix QualityN/ANo src/Analyzers/ changes.
19IPC Wire CompatibilityN/ANo serialization changes.
20Build Infrastructure & DependenciesN/ANo eng/ or package reference changes.
21Scope & PR Discipline✅ LGTMSingle-concern change; PR description is thorough with root-cause analysis and verification steps.
22PowerShell Scripting HygieneN/ANo .ps1 changes.

22/22 dimensions clean.


💡 Optional Follow-up Observation (non-blocking)

The underlying MSBuild/RAR issue — where the source-gen build's isolated bin/obj redirection causes the net8.0 leg of a multi-targeting ProjectReference to resolve transitive assemblies from the net10.0 output — will silently affect any other acceptance asset that has a similar multi-targeting ProjectReference. Opting this asset out is the right short-term fix to unblock main, but it may be worth filing a tracking issue to investigate whether the source-gen build infrastructure should be hardened to pass -p:TargetFrameworks=... or a comparable property to pin transitive reference resolution to the correct TFM subfolder. If such an issue already exists, linking it from the comment would help future readers.


Verdict: This is a correct, minimal, well-documented fix that follows the established pattern. Ready to merge.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jun 29, 2026
…t globs
Root cause of the broken main build: the acceptance source-gen build harness
let a multi-project asset's leftover reflection outputs pollute the later
source-gen build.
TestAssetFixtureBase builds each asset twice in the same directory: first the
reflection build (default bin/Release), then the source-gen build (bin/obj
redirected to a *SourceGen sub-folder). For a multi-project asset (e.g.
AssemblyFixtureProviderAcceptance, whose test project references ProviderLibrary),
the reflection build leaves ProviderLibrary/bin/Release/<tfm>/*.dll on disk. When
the source-gen build then evaluates the host project, the SDK's default item globs
pulled those stray DLLs in as None items, and RAR treated a wrong-TFM copy (a
net10.0 MSTest.TestFramework.Extensions.dll, assembly version 9.0.0.0) as a
candidate for the net8.0 leg -> MSB3277, which MSBuildTreatWarningsAsErrors
promotes to an error, failing the fixture's ClassInitialize.
The injected props already re-excluded bin/obj, but only at the asset root
(bin/**;obj/**), which does not match the nested ProviderLibrary/bin/**. Broaden
the re-exclusion to **/obj/**;**/bin/** so a referenced project's leftover
reflection outputs stay out of the source-gen build's globs.
The reflection build (#1) is unaffected because at its evaluation time the
referenced project's bin is still empty; only the later source-gen build (#2)
runs in a directory polluted by #1.
This is the proper harness fix; the AssemblyFixtureProvider opt-out is no longer
needed, so the asset is restored to source-gen coverage. Verified by running the
AssemblyFixtureProvider acceptance tests (reflection + source-gen builds) green
across net462/net8.0/net10.0.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@EvangelinkAmaury Levé (Evangelink) changed the title Fix broken main: opt AssemblyFixtureProvider acceptance asset out of source-gen buildFix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset buildsJun 29, 2026
@Evangelink

Copy link
Copy Markdown
MemberAuthor

🧪 Test quality grade — PR #9504

No new or modified test methods were identified in the changed regions
of this PR. Nothing to grade.

The only changed file (test/Utilities/Microsoft.Testing.TestInfrastructure/AcceptanceSourceGen.cs) is infrastructure/utility code that generates MSBuild props content — it contains no test methods.

Re-run with /grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 93.1 AIC · ⌖ 13.1 AIC · ⊞ 45.7K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) merged commit 4ea34a2 into mainJun 29, 2026
39 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu branch June 29, 2026 16:17
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Fix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset builds - #9504

Merged
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu
Jun 29, 2026
Merged

Fix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset builds#9504
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu

Conversation

@Evangelink

@EvangelinkAmaury Levé (Evangelink) commented Jun 29, 2026

Copy link
Copy Markdown
Member

Problem

main has been red across many consecutive official builds (e.g. build 3010746).

The failing test is MSTest.Acceptance.IntegrationTests.AssemblyFixtureProviderTests.AssemblyFixtureProvider_FromReferencedLibrary_RunsAssemblyInitializeAndCleanup. It fails in its ClassInitialize because the source-gen build variant of the AssemblyFixtureProviderAcceptance asset fails to compile with MSB3277.

Root cause (a harness bug, not a product bug)

TestAssetFixtureBase.InitializeAsync builds each acceptance asset twice in the same directory: first the reflection build (default bin/Release), then the source-gen build (with bin/obj redirected to a *SourceGen sub-folder).

For a multi-project asset — AssemblyFixtureProviderAcceptance's test project references ProviderLibrary — the first (reflection) build leaves ProviderLibrary/bin/Release/<tfm>/*.dll on disk. When the later source-gen build then evaluates the host project, the SDK's default item globs pull those stray DLLs in as None items (confirmed in the binlog: AddItem None -> ProviderLibrary\bin\Release\net10.0\MSTest.TestFramework.Extensions.dll). RAR then treats the wrong-TFM copy (a net10.0 MSTest.TestFramework.Extensions.dll, assembly version 9.0.0.0) as a candidate for the net8.0 leg:

error MSB3277: Found conflicts between different versions of "System.Runtime" ... 8.0.0.0 vs 9.0.0.0
...ProviderLibrary\bin\Release\net10.0\MSTest.TestFramework.Extensions.dll

MSBuildTreatWarningsAsErrors=true (passed only to the source-gen build) promotes this to an error, so ClassInitialize throws.

The injected props already re-excluded bin/obj, but only at the asset root (bin/**;obj/**), which does not match the nested ProviderLibrary/bin/**. The reflection build (#1) is unaffected because at its evaluation time the referenced project's bin is still empty — only the later source-gen build (#2) runs in a directory polluted by #1.

Fix

Broaden the injected DefaultItemExcludes in AcceptanceSourceGen to also exclude nested bin/obj (**/obj/**;**/bin/**), so a referenced project's leftover reflection outputs stay out of the source-gen build's globs. This is the proper harness fix and keeps the existing redirect-based design.

With the harness corrected, the AssemblyFixtureProvider opt-out is unnecessary, so the asset is restored to source-gen coverage.

Verification

  • Reproduced the original MSB3277 from the CI binlog (binlog-mcp).
  • Ran the AssemblyFixtureProvider acceptance tests (which drive both the reflection and source-gen builds via the fixture) — all 3 pass across net462/net8.0/net10.0.

The AssemblyFixtureProviderAcceptance asset has a multi-targeting
ProjectReference (ProviderLibrary). In the source-gen build variant, RAR
for the net8.0 leg resolves ProviderLibrary's transitive
MSTest.TestFramework.Extensions.dll from the net10.0 reflection output
instead of the net8.0 one, producing MSB3277 System.* version conflicts
(8.0.0.0 vs 9.0.0.0). The source-gen build promotes these to errors via
MSBuildTreatWarningsAsErrors, failing the fixture's ClassInitialize and
breaking the main build.
The test only exercises the reflection build, so opt the asset out of the
source-gen build (matching FrameworkOnlyTests).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 29, 2026 13:46

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a persistent main build break by preventing an unused acceptance-test asset build variant (source-generation) from compiling, avoiding MSBuild reference-resolution conflicts caused by a multi-targeting ProjectReference in the asset.

Changes:

  • Opt the AssemblyFixtureProviderAcceptance asset out of source-generation builds by overriding SourceGenMetadataModes to an empty set.
  • Add an inline comment documenting the RAR/MSB3277 conflict scenario and why reflection-only is sufficient for this test.
Show a summary per file
FileDescription
test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.csOverrides the asset fixture’s SourceGenMetadataModes to skip source-gen builds that are not exercised by the test and currently fail due to assembly version conflicts.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Low

@Evangelink

This comment has been minimized.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

Expert Review — PR #9504

Scope: Single file change in test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.cs. The change adds a SourceGenMetadataModes => [] override and explanatory comment to TestAssetFixture, opting the AssemblyFixtureProviderAcceptance asset out of the source-gen build.


Pattern Verification

The escape hatch used here (protected override IReadOnlyList<MetadataMode> SourceGenMetadataModes => []) is the documented, established opt-out mechanism. It is applied identically in FrameworkOnlyTests.TestAssetFixture (which also carries an explanatory comment stating the reason). The TestAssetFixtureBase XML doc on SourceGenMetadataModes explicitly names this as the opt-out path for assets that "genuinely cannot build under source generation."

Confirmed that TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.TestProjectName, tfm) is called without a metadataMode argument — so the reflection build is the only one exercised by the test. Opting out of building the source-gen variant does not remove any test execution coverage.


22-Dimension Summary

#DimensionVerdictNotes
1Algorithmic Correctness✅ LGTMFix matches root cause: source-gen build fails, test never uses source-gen variant; opting out is correct, not a symptom patch.
2Threading & ConcurrencyN/ANo new concurrent code.
3Security & IPC Contract SafetyN/ANo security boundary touched.
4Public API & Binary CompatibilityN/ANo public API changes.
5Performance & AllocationsN/ATest fixture code; not a hot path.
6Cross-TFM CompatibilityN/AThe fix avoids the cross-TFM RAR issue by not building the failing variant. The => [] collection expression is valid on all targeted TFMs.
7Resource & IDisposable ManagementN/ANo new disposable resources.
8Defensive Coding at BoundariesN/ANo boundary logic changed.
9Localization & ResourcesN/ANo user-facing strings.
10Test Isolation✅ LGTMSingle test method in class — no shared mutable asset across methods, so [DoNotParallelize] is not required.
11Assertion QualityN/ANo assertion changes.
12Flakiness Patterns✅ LGTMThis PR fixes a reliability failure; no new flakiness introduced.
13Test Completeness & Coverage✅ LGTMThe source-gen variant was never exercised by any test method. No coverage is lost. See observation below regarding a potential follow-up.
14Data-Driven Test CoverageN/ANo data rows changed.
15Code Structure & Simplification✅ LGTMMinimal, clean override consistent with FrameworkOnlyTests.
16Naming & Conventions✅ LGTMConsistent with existing pattern.
17Documentation Accuracy✅ LGTMComment accurately explains the RAR resolution issue, the exact error (MSB3277), the promoter (MSBuildTreatWarningsAsErrors), and the justification. Matches the FrameworkOnlyTests comment style.
18Analyzer & Code Fix QualityN/ANo src/Analyzers/ changes.
19IPC Wire CompatibilityN/ANo serialization changes.
20Build Infrastructure & DependenciesN/ANo eng/ or package reference changes.
21Scope & PR Discipline✅ LGTMSingle-concern change; PR description is thorough with root-cause analysis and verification steps.
22PowerShell Scripting HygieneN/ANo .ps1 changes.

22/22 dimensions clean.


💡 Optional Follow-up Observation (non-blocking)

The underlying MSBuild/RAR issue — where the source-gen build's isolated bin/obj redirection causes the net8.0 leg of a multi-targeting ProjectReference to resolve transitive assemblies from the net10.0 output — will silently affect any other acceptance asset that has a similar multi-targeting ProjectReference. Opting this asset out is the right short-term fix to unblock main, but it may be worth filing a tracking issue to investigate whether the source-gen build infrastructure should be hardened to pass -p:TargetFrameworks=... or a comparable property to pin transitive reference resolution to the correct TFM subfolder. If such an issue already exists, linking it from the comment would help future readers.


Verdict: This is a correct, minimal, well-documented fix that follows the established pattern. Ready to merge.

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jun 29, 2026
…t globs
Root cause of the broken main build: the acceptance source-gen build harness
let a multi-project asset's leftover reflection outputs pollute the later
source-gen build.
TestAssetFixtureBase builds each asset twice in the same directory: first the
reflection build (default bin/Release), then the source-gen build (bin/obj
redirected to a *SourceGen sub-folder). For a multi-project asset (e.g.
AssemblyFixtureProviderAcceptance, whose test project references ProviderLibrary),
the reflection build leaves ProviderLibrary/bin/Release/<tfm>/*.dll on disk. When
the source-gen build then evaluates the host project, the SDK's default item globs
pulled those stray DLLs in as None items, and RAR treated a wrong-TFM copy (a
net10.0 MSTest.TestFramework.Extensions.dll, assembly version 9.0.0.0) as a
candidate for the net8.0 leg -> MSB3277, which MSBuildTreatWarningsAsErrors
promotes to an error, failing the fixture's ClassInitialize.
The injected props already re-excluded bin/obj, but only at the asset root
(bin/**;obj/**), which does not match the nested ProviderLibrary/bin/**. Broaden
the re-exclusion to **/obj/**;**/bin/** so a referenced project's leftover
reflection outputs stay out of the source-gen build's globs.
The reflection build (#1) is unaffected because at its evaluation time the
referenced project's bin is still empty; only the later source-gen build (#2)
runs in a directory polluted by #1.
This is the proper harness fix; the AssemblyFixtureProvider opt-out is no longer
needed, so the asset is restored to source-gen coverage. Verified by running the
AssemblyFixtureProvider acceptance tests (reflection + source-gen builds) green
across net462/net8.0/net10.0.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@EvangelinkAmaury Levé (Evangelink) changed the title Fix broken main: opt AssemblyFixtureProvider acceptance asset out of source-gen buildFix broken main: acceptance source-gen harness leaks nested bin/obj into multi-project asset buildsJun 29, 2026
@Evangelink

Copy link
Copy Markdown
MemberAuthor

🧪 Test quality grade — PR #9504

No new or modified test methods were identified in the changed regions
of this PR. Nothing to grade.

The only changed file (test/Utilities/Microsoft.Testing.TestInfrastructure/AcceptanceSourceGen.cs) is infrastructure/utility code that generates MSBuild props content — it contains no test methods.

Re-run with /grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 93.1 AIC · ⌖ 13.1 AIC · ⊞ 45.7K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) merged commit 4ea34a2 into mainJun 29, 2026
39 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/fix-assemblyfixtureprovider-sourcegen-bu branch June 29, 2026 16:17
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Evangelink@0101