From 4cd9674a450c2a7187ecdecb2c20318c317404fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Sun, 22 Feb 2026 13:49:45 +0100 Subject: [PATCH 1/9] Improve Native AOT acceptance tests to catch trim warnings (#7153) - Add AssertOutputDoesNotContain("warning") to NativeAotTests_WillRunWithExitCodeZero - Add TrimmerSingleWarn=false to surface individual IL warnings instead of collapsed IL2104 - Add new NativeAotPublish_ShouldNotProduceTrimWarnings test with TrimmerRootAssembly to force deep trim analysis of Microsoft.Testing.Platform across all AOT-supported TFMs - Update SdkTests NativeAot_Smoke_Test to run on all platforms (except macOS), add TrimmerSingleWarn=false, and use TargetFrameworks.NetCurrent instead of hardcoded TFM --- .../NativeAotTests.cs | 67 +++++++++++++++++++ .../SdkTests.cs | 8 +-- 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs index 51063b1c8b..f9216a41fe 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs @@ -20,6 +20,8 @@ public class NativeAotTests : AcceptanceTestBase true preview true + + false @@ -79,6 +81,33 @@ public void TestMethod3(int a, int b) new object[] { 1, 2 } }; } +"""; + + // Source code for a minimal project that deeply validates trim/AOT compatibility of + // Microsoft.Testing.Platform by using TrimmerRootAssembly to force the trimmer to analyze + // all code paths in the assembly, not just those reachable from the test entry point. + // See https://learn.microsoft.com/dotnet/core/deploying/trimming/prepare-libraries-for-trimming + private const string TrimAnalysisSourceCode = """ +#file TrimAnalysisTest.csproj + + + $TargetFramework$ + Exe + true + + false + + + + + + + + + + +#file Program.cs +System.Console.WriteLine("This project validates trim/AOT compatibility via dotnet publish."); """; [TestMethod] @@ -106,6 +135,7 @@ await DotnetCli.RunAsync( retryCount: 0, cancellationToken: TestContext.CancellationToken); compilationResult.AssertOutputContains("Generating native code"); + compilationResult.AssertOutputDoesNotContain("warning"); var testHost = TestHost.LocateFrom(generator.TargetAssetPath, "NativeAotTests", TargetFrameworks.NetCurrent, RID, Verb.publish); @@ -114,5 +144,42 @@ await DotnetCli.RunAsync( result.AssertExitCodeIs(0); } + [TestMethod] + [DynamicData(nameof(NativeAotTfmsForDynamicData))] + // The hosted AzDO agents for Mac OS don't have the required tooling for us to test Native AOT. + [OSCondition(ConditionMode.Exclude, OperatingSystems.OSX)] + public async Task NativeAotPublish_ShouldNotProduceTrimWarnings(string tfm) + { + // See https://github.com/microsoft/testfx/issues/7153 + // This test forces deep trim analysis of Microsoft.Testing.Platform using TrimmerRootAssembly + // to catch trim warnings that would not be caught by only testing reachable code paths. + using TestAsset generator = await TestAsset.GenerateAssetAsync( + $"TrimAnalysisTest_{tfm}", + TrimAnalysisSourceCode + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$TargetFramework$", tfm), + addPublicFeeds: true); + + await DotnetCli.RunAsync( + $"restore {generator.TargetAssetPath} -r {RID}", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + retryCount: 0, + cancellationToken: TestContext.CancellationToken); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync( + $"publish {generator.TargetAssetPath} -r {RID} -f {tfm}", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + retryCount: 0, + cancellationToken: TestContext.CancellationToken); + compilationResult.AssertOutputContains("Generating native code"); + compilationResult.AssertOutputDoesNotContain("warning"); + } + + // Native AOT is supported on net8.0+. We test each supported TFM to catch + // framework-version-specific trim issues (e.g. the net8.0-specific IL2104 in #7153). + public static IEnumerable NativeAotTfmsForDynamicData => + TargetFrameworks.Net + .Where(tfm => tfm is not ("net6.0" or "net7.0")) + .Select(tfm => new object[] { tfm }); + public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs index 2b694ed400..b86d008f09 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs @@ -291,25 +291,25 @@ public async Task Invalid_TestingProfile_Name_Should_Fail(string multiTfm, Build } [TestMethod] - [OSCondition(OperatingSystems.Windows)] - public async Task NativeAot_Smoke_Test_Windows() + [OSCondition(ConditionMode.Exclude, OperatingSystems.OSX)] + public async Task NativeAot_Smoke_Test() { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( AssetName, SingleTestSourceCode .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) - // temporarily set test to be on net10.0 as older TFMs are broken until https://github.com/dotnet/runtime/pull/115951 is serviced. .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetCurrent) .PatchCodeWithReplace("$ExtraProperties$", """ true false + + false """), addPublicFeeds: true); DotnetMuxerResult compilationResult = await DotnetCli.RunAsync( $"publish -r {RID} -f {TargetFrameworks.NetCurrent} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, - // We prefer to use the outer retry mechanism as we need some extra checks retryCount: 0, cancellationToken: TestContext.CancellationToken); compilationResult.AssertOutputContains("Generating native code"); From 9c9b79a3e226c1c8789e921228275c4d36ba871b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Sun, 22 Feb 2026 14:02:48 +0100 Subject: [PATCH 2/9] Remove redundant TFM filter, use TargetFrameworks.NetForDynamicData directly --- .../MSTest.Acceptance.IntegrationTests/NativeAotTests.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs index f9216a41fe..ba1ef38fd4 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs @@ -145,7 +145,7 @@ await DotnetCli.RunAsync( } [TestMethod] - [DynamicData(nameof(NativeAotTfmsForDynamicData))] + [DynamicData(nameof(TargetFrameworks.NetForDynamicData), typeof(TargetFrameworks))] // The hosted AzDO agents for Mac OS don't have the required tooling for us to test Native AOT. [OSCondition(ConditionMode.Exclude, OperatingSystems.OSX)] public async Task NativeAotPublish_ShouldNotProduceTrimWarnings(string tfm) @@ -174,12 +174,5 @@ await DotnetCli.RunAsync( compilationResult.AssertOutputDoesNotContain("warning"); } - // Native AOT is supported on net8.0+. We test each supported TFM to catch - // framework-version-specific trim issues (e.g. the net8.0-specific IL2104 in #7153). - public static IEnumerable NativeAotTfmsForDynamicData => - TargetFrameworks.Net - .Where(tfm => tfm is not ("net6.0" or "net7.0")) - .Select(tfm => new object[] { tfm }); - public TestContext TestContext { get; set; } } From 3d5adf84feeffd537ac110ab3449b11f24786344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Sun, 22 Feb 2026 15:47:41 +0100 Subject: [PATCH 3/9] Update code --- .../MSTest.Acceptance.IntegrationTests/NativeAotTests.cs | 4 ++-- .../MSTest.Acceptance.IntegrationTests/SdkTests.cs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs index ba1ef38fd4..46e17a185b 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs @@ -20,6 +20,7 @@ public class NativeAotTests : AcceptanceTestBase true preview true + true false @@ -94,6 +95,7 @@ public void TestMethod3(int a, int b) $TargetFramework$ Exe true + true false @@ -135,7 +137,6 @@ await DotnetCli.RunAsync( retryCount: 0, cancellationToken: TestContext.CancellationToken); compilationResult.AssertOutputContains("Generating native code"); - compilationResult.AssertOutputDoesNotContain("warning"); var testHost = TestHost.LocateFrom(generator.TargetAssetPath, "NativeAotTests", TargetFrameworks.NetCurrent, RID, Verb.publish); @@ -171,7 +172,6 @@ await DotnetCli.RunAsync( retryCount: 0, cancellationToken: TestContext.CancellationToken); compilationResult.AssertOutputContains("Generating native code"); - compilationResult.AssertOutputDoesNotContain("warning"); } public TestContext TestContext { get; set; } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs index b86d008f09..4f4a8e5ba9 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs @@ -301,6 +301,7 @@ public async Task NativeAot_Smoke_Test() .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetCurrent) .PatchCodeWithReplace("$ExtraProperties$", """ true + true false false From 88a4a7188ac6bf14731151c418151d0afa0452e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Sun, 22 Feb 2026 20:52:00 +0100 Subject: [PATCH 4/9] Remove property --- .../MSTest.Acceptance.IntegrationTests/NativeAotTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs index 46e17a185b..aa9cb9b671 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs @@ -95,7 +95,6 @@ public void TestMethod3(int a, int b) $TargetFramework$ Exe true - true false From b66b5b74521e65a2f3d6b7548c15b325b7687866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Mon, 23 Feb 2026 10:47:22 +0100 Subject: [PATCH 5/9] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Amaury Levé --- .../MSTest.Acceptance.IntegrationTests/NativeAotTests.cs | 1 - .../MSTest.Acceptance.IntegrationTests/SdkTests.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs index aa9cb9b671..6e618d9b17 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs @@ -20,7 +20,6 @@ public class NativeAotTests : AcceptanceTestBase true preview true - true false diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs index 4f4a8e5ba9..b86d008f09 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs @@ -301,7 +301,6 @@ public async Task NativeAot_Smoke_Test() .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetCurrent) .PatchCodeWithReplace("$ExtraProperties$", """ true - true false false From b89848e63cb179113b969a67460b4c39ef074461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Mon, 23 Feb 2026 15:22:30 +0100 Subject: [PATCH 6/9] Split tests --- .../NativeAotTests.cs | 59 +--------- .../TrimTests.cs | 72 ++++++++++++ .../NativeAotTests.cs | 106 ++++++++++++++++++ .../TrimTests.cs | 63 +++++++++++ 4 files changed, 244 insertions(+), 56 deletions(-) create mode 100644 test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TrimTests.cs create mode 100644 test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NativeAotTests.cs create mode 100644 test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrimTests.cs diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs index 6e618d9b17..63d1ffc121 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs @@ -9,6 +9,9 @@ namespace MSTest.Acceptance.IntegrationTests; [TestClass] public class NativeAotTests : AcceptanceTestBase { + // Source code for a project that validates MSTest supporting Native AOT. + // Because MSTest is built on top of Microsoft.Testing.Platform, this also exercises + // additional MTP code paths beyond what the MTP-only NativeAOT test covers. private const string SourceCode = """ #file NativeAotTests.csproj @@ -81,33 +84,6 @@ public void TestMethod3(int a, int b) new object[] { 1, 2 } }; } -"""; - - // Source code for a minimal project that deeply validates trim/AOT compatibility of - // Microsoft.Testing.Platform by using TrimmerRootAssembly to force the trimmer to analyze - // all code paths in the assembly, not just those reachable from the test entry point. - // See https://learn.microsoft.com/dotnet/core/deploying/trimming/prepare-libraries-for-trimming - private const string TrimAnalysisSourceCode = """ -#file TrimAnalysisTest.csproj - - - $TargetFramework$ - Exe - true - - false - - - - - - - - - - -#file Program.cs -System.Console.WriteLine("This project validates trim/AOT compatibility via dotnet publish."); """; [TestMethod] @@ -143,34 +119,5 @@ await DotnetCli.RunAsync( result.AssertExitCodeIs(0); } - [TestMethod] - [DynamicData(nameof(TargetFrameworks.NetForDynamicData), typeof(TargetFrameworks))] - // The hosted AzDO agents for Mac OS don't have the required tooling for us to test Native AOT. - [OSCondition(ConditionMode.Exclude, OperatingSystems.OSX)] - public async Task NativeAotPublish_ShouldNotProduceTrimWarnings(string tfm) - { - // See https://github.com/microsoft/testfx/issues/7153 - // This test forces deep trim analysis of Microsoft.Testing.Platform using TrimmerRootAssembly - // to catch trim warnings that would not be caught by only testing reachable code paths. - using TestAsset generator = await TestAsset.GenerateAssetAsync( - $"TrimAnalysisTest_{tfm}", - TrimAnalysisSourceCode - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$TargetFramework$", tfm), - addPublicFeeds: true); - - await DotnetCli.RunAsync( - $"restore {generator.TargetAssetPath} -r {RID}", - AcceptanceFixture.NuGetGlobalPackagesFolder.Path, - retryCount: 0, - cancellationToken: TestContext.CancellationToken); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync( - $"publish {generator.TargetAssetPath} -r {RID} -f {tfm}", - AcceptanceFixture.NuGetGlobalPackagesFolder.Path, - retryCount: 0, - cancellationToken: TestContext.CancellationToken); - compilationResult.AssertOutputContains("Generating native code"); - } - public TestContext TestContext { get; set; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TrimTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TrimTests.cs new file mode 100644 index 0000000000..3c7c22b4e4 --- /dev/null +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TrimTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +namespace MSTest.Acceptance.IntegrationTests; + +[TestClass] +public class TrimTests : AcceptanceTestBase +{ + // Source code for a minimal project that deeply validates trim/AOT compatibility of + // MSTest assemblies by using TrimmerRootAssembly to force the trimmer to analyze + // all code paths in each assembly, not just those reachable from the test entry point. + // See https://learn.microsoft.com/dotnet/core/deploying/trimming/prepare-libraries-for-trimming + private const string TrimAnalysisSourceCode = """ +#file TrimAnalysisTest.csproj + + + $TargetFramework$ + Exe + true + + false + + + + + + + + + + + + + + + +#file Program.cs +System.Console.WriteLine("This project validates trim/AOT compatibility via dotnet publish."); +"""; + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.NetForDynamicData), typeof(TargetFrameworks))] + public async Task Publish_ShouldNotProduceTrimWarnings(string tfm) + { + // See https://github.com/microsoft/testfx/issues/7153 + // This test forces deep trim analysis of MSTest assemblies using TrimmerRootAssembly + // to catch trim warnings that would not be caught by only testing reachable code paths. + using TestAsset generator = await TestAsset.GenerateAssetAsync( + $"TrimAnalysisTest_{tfm}", + TrimAnalysisSourceCode + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) + .PatchCodeWithReplace("$MSTestEngineVersion$", MSTestEngineVersion) + .PatchCodeWithReplace("$TargetFramework$", tfm), + addPublicFeeds: true); + + await DotnetCli.RunAsync( + $"restore {generator.TargetAssetPath} -r {RID}", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + retryCount: 0, + cancellationToken: TestContext.CancellationToken); + await DotnetCli.RunAsync( + $"publish {generator.TargetAssetPath} -r {RID} -f {tfm}", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + retryCount: 0, + cancellationToken: TestContext.CancellationToken); + } + + public TestContext TestContext { get; set; } +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NativeAotTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NativeAotTests.cs new file mode 100644 index 0000000000..c0f3acefa1 --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NativeAotTests.cs @@ -0,0 +1,106 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestClass] +public class NativeAotTests : AcceptanceTestBase +{ + // Source code for a minimal NativeAOT test project using a locally defined test framework + // (not MSTest) to validate that Microsoft.Testing.Platform itself supports Native AOT. + private const string SourceCode = """ +#file NativeAotTests.csproj + + + $TargetFramework$ + enable + enable + Exe + true + preview + true + + false + + + + + + +#file Program.cs +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); +builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestFramework()); +using ITestApplication app = await builder.BuildAsync(); +return await app.RunAsync(); + +internal class DummyTestFramework : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestFramework); + + public string Version => "1.0.0"; + + public string DisplayName => nameof(DummyTestFramework); + + public string Description => nameof(DummyTestFramework); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { typeof(TestNodeUpdateMessage) }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test1", + DisplayName = "Test1", + Properties = new PropertyBag(PassedTestNodeStateProperty.CachedInstance), + })); + + context.Complete(); + } +} +"""; + + [TestMethod] + // The hosted AzDO agents for Mac OS don't have the required tooling for us to test Native AOT. + [OSCondition(ConditionMode.Exclude, OperatingSystems.OSX)] + public async Task NativeAotTests_WillRunWithExitCodeZero() + { + using TestAsset generator = await TestAsset.GenerateAssetAsync( + "NativeAotTests", + SourceCode + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetCurrent), + addPublicFeeds: true); + + await DotnetCli.RunAsync( + $"restore {generator.TargetAssetPath} -r {RID}", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + retryCount: 0, + cancellationToken: TestContext.CancellationToken); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync( + $"publish {generator.TargetAssetPath} -r {RID}", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + retryCount: 0, + cancellationToken: TestContext.CancellationToken); + compilationResult.AssertOutputContains("Generating native code"); + + var testHost = TestInfrastructure.TestHost.LocateFrom(generator.TargetAssetPath, "NativeAotTests", TargetFrameworks.NetCurrent, RID, Verb.publish); + + TestHostResult result = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); + result.AssertExitCodeIs(0); + } + + public TestContext TestContext { get; set; } +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrimTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrimTests.cs new file mode 100644 index 0000000000..d936e81f5e --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrimTests.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestClass] +public class TrimTests : AcceptanceTestBase +{ + // Source code for a minimal project that deeply validates trim/AOT compatibility of + // Microsoft.Testing.Platform by using TrimmerRootAssembly to force the trimmer to analyze + // all code paths in the assembly, not just those reachable from the test entry point. + // See https://learn.microsoft.com/dotnet/core/deploying/trimming/prepare-libraries-for-trimming + private const string TrimAnalysisSourceCode = """ +#file TrimAnalysisTest.csproj + + + $TargetFramework$ + Exe + true + + false + + + + + + + + + + +#file Program.cs +System.Console.WriteLine("This project validates trim/AOT compatibility via dotnet publish."); +"""; + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.NetForDynamicData), typeof(TargetFrameworks))] + public async Task Publish_ShouldNotProduceTrimWarnings(string tfm) + { + // See https://github.com/microsoft/testfx/issues/7153 + // This test forces deep trim analysis of Microsoft.Testing.Platform using TrimmerRootAssembly + // to catch trim warnings that would not be caught by only testing reachable code paths. + using TestAsset generator = await TestAsset.GenerateAssetAsync( + $"TrimAnalysisTest_{tfm}", + TrimAnalysisSourceCode + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$TargetFramework$", tfm), + addPublicFeeds: true); + + await DotnetCli.RunAsync( + $"restore {generator.TargetAssetPath} -r {RID}", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + retryCount: 0, + cancellationToken: TestContext.CancellationToken); + await DotnetCli.RunAsync( + $"publish {generator.TargetAssetPath} -r {RID} -f {tfm}", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + retryCount: 0, + cancellationToken: TestContext.CancellationToken); + } + + public TestContext TestContext { get; set; } +} From 7dd1d957a6873ede9948370b6af7e24adb134585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Tue, 24 Feb 2026 09:26:29 +0100 Subject: [PATCH 7/9] Address issues --- .../MSTest.Acceptance.IntegrationTests/TrimTests.cs | 6 +++--- .../NativeAotTests.cs | 2 +- .../TrimTests.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TrimTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TrimTests.cs index 3c7c22b4e4..508a2d06f5 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TrimTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TrimTests.cs @@ -28,11 +28,11 @@ public class TrimTests : AcceptanceTestBase - + - - diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NativeAotTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NativeAotTests.cs index c0f3acefa1..fe2c8929ea 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NativeAotTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NativeAotTests.cs @@ -78,7 +78,7 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) public async Task NativeAotTests_WillRunWithExitCodeZero() { using TestAsset generator = await TestAsset.GenerateAssetAsync( - "NativeAotTests", + "MTPNativeAotTests", SourceCode .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetCurrent), diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrimTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrimTests.cs index d936e81f5e..8924c399e3 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrimTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrimTests.cs @@ -41,7 +41,7 @@ public async Task Publish_ShouldNotProduceTrimWarnings(string tfm) // This test forces deep trim analysis of Microsoft.Testing.Platform using TrimmerRootAssembly // to catch trim warnings that would not be caught by only testing reachable code paths. using TestAsset generator = await TestAsset.GenerateAssetAsync( - $"TrimAnalysisTest_{tfm}", + $"MTPTrimAnalysisTest_{tfm}", TrimAnalysisSourceCode .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) .PatchCodeWithReplace("$TargetFramework$", tfm), From b603161dc37f055fb6dccd52bd55b14f5ce9418c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Tue, 24 Feb 2026 09:35:41 +0100 Subject: [PATCH 8/9] Proper fix --- .../MSTest.Acceptance.IntegrationTests/NativeAotTests.cs | 8 ++++---- .../MSTest.Acceptance.IntegrationTests/TrimTests.cs | 4 ++-- .../NativeAotTests.cs | 2 +- .../TrimTests.cs | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs index 63d1ffc121..922e310b5e 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs @@ -13,7 +13,7 @@ public class NativeAotTests : AcceptanceTestBase // Because MSTest is built on top of Microsoft.Testing.Platform, this also exercises // additional MTP code paths beyond what the MTP-only NativeAOT test covers. private const string SourceCode = """ -#file NativeAotTests.csproj +#file MSTestNativeAotTests.csproj $TargetFramework$ @@ -46,7 +46,7 @@ public class NativeAotTests : AcceptanceTestBase using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Extensions.TestFramework; -using NativeAotTests; +using MSTestNativeAotTests; ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); @@ -92,7 +92,7 @@ public void TestMethod3(int a, int b) public async Task NativeAotTests_WillRunWithExitCodeZero() { using TestAsset generator = await TestAsset.GenerateAssetAsync( - "NativeAotTests", + "MSTestNativeAotTests", SourceCode .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetCurrent) @@ -112,7 +112,7 @@ await DotnetCli.RunAsync( cancellationToken: TestContext.CancellationToken); compilationResult.AssertOutputContains("Generating native code"); - var testHost = TestHost.LocateFrom(generator.TargetAssetPath, "NativeAotTests", TargetFrameworks.NetCurrent, RID, Verb.publish); + var testHost = TestHost.LocateFrom(generator.TargetAssetPath, "MSTestNativeAotTests", TargetFrameworks.NetCurrent, RID, Verb.publish); TestHostResult result = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); result.AssertOutputContains($"MSTest.Engine v{MSTestEngineVersion}"); diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TrimTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TrimTests.cs index 508a2d06f5..0226b15693 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TrimTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TrimTests.cs @@ -13,7 +13,7 @@ public class TrimTests : AcceptanceTestBase // all code paths in each assembly, not just those reachable from the test entry point. // See https://learn.microsoft.com/dotnet/core/deploying/trimming/prepare-libraries-for-trimming private const string TrimAnalysisSourceCode = """ -#file TrimAnalysisTest.csproj +#file MSTestTrimAnalysisTest.csproj $TargetFramework$ @@ -48,7 +48,7 @@ public async Task Publish_ShouldNotProduceTrimWarnings(string tfm) // This test forces deep trim analysis of MSTest assemblies using TrimmerRootAssembly // to catch trim warnings that would not be caught by only testing reachable code paths. using TestAsset generator = await TestAsset.GenerateAssetAsync( - $"TrimAnalysisTest_{tfm}", + $"MSTestTrimAnalysisTest_{tfm}", TrimAnalysisSourceCode .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NativeAotTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NativeAotTests.cs index fe2c8929ea..c0f3acefa1 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NativeAotTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NativeAotTests.cs @@ -78,7 +78,7 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) public async Task NativeAotTests_WillRunWithExitCodeZero() { using TestAsset generator = await TestAsset.GenerateAssetAsync( - "MTPNativeAotTests", + "NativeAotTests", SourceCode .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetCurrent), diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrimTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrimTests.cs index 8924c399e3..d936e81f5e 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrimTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrimTests.cs @@ -41,7 +41,7 @@ public async Task Publish_ShouldNotProduceTrimWarnings(string tfm) // This test forces deep trim analysis of Microsoft.Testing.Platform using TrimmerRootAssembly // to catch trim warnings that would not be caught by only testing reachable code paths. using TestAsset generator = await TestAsset.GenerateAssetAsync( - $"MTPTrimAnalysisTest_{tfm}", + $"TrimAnalysisTest_{tfm}", TrimAnalysisSourceCode .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) .PatchCodeWithReplace("$TargetFramework$", tfm), From c140cb8a5e24f60ef702ee643ce4b276785ed147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Fri, 27 Feb 2026 09:42:24 +0100 Subject: [PATCH 9/9] Fix tfm --- .../NativeAotTests.cs | 11 ++++++----- .../NativeAotTests.cs | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs index 922e310b5e..711840bcd4 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs @@ -89,13 +89,14 @@ public void TestMethod3(int a, int b) [TestMethod] // The hosted AzDO agents for Mac OS don't have the required tooling for us to test Native AOT. [OSCondition(ConditionMode.Exclude, OperatingSystems.OSX)] - public async Task NativeAotTests_WillRunWithExitCodeZero() + [DynamicData(nameof(TargetFrameworks.NetForDynamicData), typeof(TargetFrameworks))] + public async Task NativeAotTests_WillRunWithExitCodeZero(string tfm) { using TestAsset generator = await TestAsset.GenerateAssetAsync( - "MSTestNativeAotTests", + $"MSTestNativeAotTests_{tfm}", SourceCode .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetCurrent) + .PatchCodeWithReplace("$TargetFramework$", tfm) .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) .PatchCodeWithReplace("$MSTestEngineVersion$", MSTestEngineVersion), addPublicFeeds: true); @@ -106,13 +107,13 @@ await DotnetCli.RunAsync( retryCount: 0, cancellationToken: TestContext.CancellationToken); DotnetMuxerResult compilationResult = await DotnetCli.RunAsync( - $"publish {generator.TargetAssetPath} -r {RID}", + $"publish {generator.TargetAssetPath} -r {RID} -f {tfm}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, retryCount: 0, cancellationToken: TestContext.CancellationToken); compilationResult.AssertOutputContains("Generating native code"); - var testHost = TestHost.LocateFrom(generator.TargetAssetPath, "MSTestNativeAotTests", TargetFrameworks.NetCurrent, RID, Verb.publish); + var testHost = TestHost.LocateFrom(generator.TargetAssetPath, "MSTestNativeAotTests", tfm, RID, Verb.publish); TestHostResult result = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); result.AssertOutputContains($"MSTest.Engine v{MSTestEngineVersion}"); diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NativeAotTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NativeAotTests.cs index c0f3acefa1..3cd9100b23 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NativeAotTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NativeAotTests.cs @@ -75,13 +75,14 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) [TestMethod] // The hosted AzDO agents for Mac OS don't have the required tooling for us to test Native AOT. [OSCondition(ConditionMode.Exclude, OperatingSystems.OSX)] - public async Task NativeAotTests_WillRunWithExitCodeZero() + [DynamicData(nameof(TargetFrameworks.NetForDynamicData), typeof(TargetFrameworks))] + public async Task NativeAotTests_WillRunWithExitCodeZero(string tfm) { using TestAsset generator = await TestAsset.GenerateAssetAsync( - "NativeAotTests", + $"NativeAotTests_{tfm}", SourceCode .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetCurrent), + .PatchCodeWithReplace("$TargetFramework$", tfm), addPublicFeeds: true); await DotnetCli.RunAsync( @@ -90,13 +91,13 @@ await DotnetCli.RunAsync( retryCount: 0, cancellationToken: TestContext.CancellationToken); DotnetMuxerResult compilationResult = await DotnetCli.RunAsync( - $"publish {generator.TargetAssetPath} -r {RID}", + $"publish {generator.TargetAssetPath} -r {RID} -f {tfm}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, retryCount: 0, cancellationToken: TestContext.CancellationToken); compilationResult.AssertOutputContains("Generating native code"); - var testHost = TestInfrastructure.TestHost.LocateFrom(generator.TargetAssetPath, "NativeAotTests", TargetFrameworks.NetCurrent, RID, Verb.publish); + var testHost = TestInfrastructure.TestHost.LocateFrom(generator.TargetAssetPath, "NativeAotTests", tfm, RID, Verb.publish); TestHostResult result = await testHost.ExecuteAsync(cancellationToken: TestContext.CancellationToken); result.AssertExitCodeIs(0);