From fefca5c63fd5b15ce4380e71182495cfb652f19f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Mon, 29 Jun 2026 15:44:29 +0200 Subject: [PATCH 1/2] Opt AssemblyFixtureProvider acceptance asset out of source-gen build 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> --- .../AssemblyFixtureProviderTests.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.cs index a29ef46f4a..c00a218918 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.cs @@ -32,6 +32,15 @@ public async Task AssemblyFixtureProvider_FromReferencedLibrary_RunsAssemblyInit public sealed class TestAssetFixture() : TestAssetFixtureBase() { + // This asset has a multi-targeting ProjectReference (ProviderLibrary). The default + // SourceGeneration build redirects bin/obj to an isolated sub-folder, but RAR for the net8.0 + // leg ends up resolving ProviderLibrary's transitive MSTest.TestFramework.Extensions.dll from + // the net10.0 reflection output (bin/Release/net10.0) instead of the net8.0 one. That produces + // MSB3277 System.* version conflicts (8.0.0.0 vs 9.0.0.0) which the source-gen build promotes to + // errors via MSBuildTreatWarningsAsErrors. The test only exercises the reflection build, so opt + // out of the source-gen build; it stays reflection-only. + protected override IReadOnlyList SourceGenMetadataModes => []; + public const string AssetName = "AssemblyFixtureProviderAcceptance"; public const string TestProjectName = "AssemblyFixtureProvider.Tests"; public const string LibraryProjectName = "ProviderLibrary"; From 29aa20f195a8e7378b02ce44bd9f68659b0104c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Mon, 29 Jun 2026 16:38:06 +0200 Subject: [PATCH 2/2] Fix acceptance source-gen harness: exclude nested bin/obj from default 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//*.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> --- .../AssemblyFixtureProviderTests.cs | 9 --------- .../AcceptanceSourceGen.cs | 11 ++++++++++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.cs index c00a218918..a29ef46f4a 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyFixtureProviderTests.cs @@ -32,15 +32,6 @@ public async Task AssemblyFixtureProvider_FromReferencedLibrary_RunsAssemblyInit public sealed class TestAssetFixture() : TestAssetFixtureBase() { - // This asset has a multi-targeting ProjectReference (ProviderLibrary). The default - // SourceGeneration build redirects bin/obj to an isolated sub-folder, but RAR for the net8.0 - // leg ends up resolving ProviderLibrary's transitive MSTest.TestFramework.Extensions.dll from - // the net10.0 reflection output (bin/Release/net10.0) instead of the net8.0 one. That produces - // MSB3277 System.* version conflicts (8.0.0.0 vs 9.0.0.0) which the source-gen build promotes to - // errors via MSBuildTreatWarningsAsErrors. The test only exercises the reflection build, so opt - // out of the source-gen build; it stays reflection-only. - protected override IReadOnlyList SourceGenMetadataModes => []; - public const string AssetName = "AssemblyFixtureProviderAcceptance"; public const string TestProjectName = "AssemblyFixtureProvider.Tests"; public const string LibraryProjectName = "ProviderLibrary"; diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/AcceptanceSourceGen.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/AcceptanceSourceGen.cs index 0b1a4a54ce..a63758c2bb 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/AcceptanceSourceGen.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/AcceptanceSourceGen.cs @@ -122,8 +122,17 @@ private static string BuildPropsContent(string packageId, string versionProperty MicrosoftTestingPlatformEntryPoint, SelfRegisteredExtensions) be double-compiled into this build. Re-exclude them here. This runs in CustomBeforeMicrosoftCommonProps (before the SDK seeds DefaultItemExcludes), so the SDK appends its own excludes after ours. + + The nested '**/obj/**;**/bin/**' globs matter for multi-project assets. The reflection build (which + always runs first, into the default bin/Release) leaves a referenced project's outputs on disk, e.g. + 'SomeRef/bin/Release//*.dll'. When this (later) source-gen build evaluates the host project, the + SDK's default item globs would otherwise pull those stray DLLs in as None/Content items, and RAR then + treats a wrong-TFM copy (for example 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. The root-level 'bin/**;obj/**' does not match the nested 'SomeRef/bin/**', so exclude nested + bin/obj as well to keep a referenced project's leftover reflection outputs out of this build's globs. --> - $(DefaultItemExcludes);obj/**;bin/** + $(DefaultItemExcludes);obj/**;bin/**;**/obj/**;**/bin/**