// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Immutable; using System.Runtime.Loader; using Microsoft.MSTestV2.CLIAutomation; using Microsoft.Testing.Platform.Acceptance.IntegrationTests; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using TestResult = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult; namespace MSTest.Acceptance.IntegrationTests; /// /// Regression guard for filtering over the neutral UnitTestElement. Discovery matches /// element.ToTestCase(), while execution reconstructs a UnitTestElement from each incoming /// before matching. These tests preserve the object-model assertions around the /// TestCase to UnitTestElement round trip for the two identity properties most sensitive to it. /// [TestClass] [DoNotParallelize] public sealed class TestCaseFilteringTests : AcceptanceTestBase { private const string AssetName = "DiscoverInternalsFiltering"; private const string TargetDisplayName = "TopLevelInternalClass_TestMethod1"; private static readonly Lock AssetAssemblyLock = new(); private static Assembly? s_assetAssembly; [TestMethod] public void FilterByFullyQualifiedNameSelectsExactlyTheMatchingTestDuringDiscovery() { TestCase target = GetTargetTestCase(); ImmutableArray filtered = DiscoverTests($"FullyQualifiedName={target.FullyQualifiedName}"); Assert.HasCount(1, filtered); Assert.AreEqual(target.FullyQualifiedName, filtered[0].FullyQualifiedName); Assert.AreEqual(target.Id, filtered[0].Id); } [TestMethod] public void FilterByIdSelectsExactlyTheMatchingTestDuringDiscovery() { TestCase target = GetTargetTestCase(); ImmutableArray filtered = DiscoverTests($"Id={target.Id}"); Assert.HasCount(1, filtered); Assert.AreEqual(target.FullyQualifiedName, filtered[0].FullyQualifiedName); Assert.AreEqual(target.Id, filtered[0].Id); } [TestMethod] public async Task FilterByFullyQualifiedNameSelectsExactlyTheMatchingTestDuringExecution() { ImmutableArray allTests = DiscoverTests(); TestCase target = allTests.First(testCase => testCase.DisplayName == TargetDisplayName); SimulateCrossProcessTestCases(allTests); ImmutableArray results = await CLITestBase.RunTestsAsync( allTests, $"FullyQualifiedName={target.FullyQualifiedName}"); Assert.HasCount(1, results); Assert.AreEqual(target.FullyQualifiedName, results[0].TestCase.FullyQualifiedName); Assert.AreEqual(TestOutcome.Passed, results[0].Outcome); } [TestMethod] public async Task FilterByIdSelectsExactlyTheMatchingTestDuringExecution() { ImmutableArray allTests = DiscoverTests(); Guid targetId = allTests.First(testCase => testCase.DisplayName == TargetDisplayName).Id; SimulateCrossProcessTestCases(allTests); ImmutableArray results = await CLITestBase.RunTestsAsync(allTests, $"Id={targetId}"); Assert.HasCount(1, results); Assert.AreEqual(targetId, results[0].TestCase.Id); Assert.AreEqual(TestOutcome.Passed, results[0].Outcome); } private static TestCase GetTargetTestCase() => DiscoverTests().First(testCase => testCase.DisplayName == TargetDisplayName); private static ImmutableArray DiscoverTests(string? testCaseFilter = null) { EnsureAssetAssemblyIsLoaded(); return CLITestBase.DiscoverTests(AssetFixture.AssemblyPath, testCaseFilter); } private static void EnsureAssetAssemblyIsLoaded() { lock (AssetAssemblyLock) { s_assetAssembly ??= AssemblyLoadContext.Default.LoadFromAssemblyPath(AssetFixture.AssemblyPath); } } // Deserialized VSTest test cases carry no in-process adapter payload. Clearing LocalExtensionData // deliberately forces execution to reconstruct UnitTestElement and regenerate TestCase identity. private static void SimulateCrossProcessTestCases(ImmutableArray testCases) { foreach (TestCase testCase in testCases) { testCase.LocalExtensionData = null; } } public sealed class TestAssetFixture : TestAssetFixtureBase { public string TargetAssetPath => GetAssetPath(AssetName); public string AssemblyPath => Path.Combine(TargetAssetPath, "bin", "Release", TargetFrameworks.NetCurrent, $"{AssetName}.dll"); public override (string ID, string Name, string Code) GetAssetsToGenerate() => (AssetName, AssetName, SourceCode .PatchTargetFrameworks(TargetFrameworks.NetCurrent) .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); private const string SourceCode = """ #file DiscoverInternalsFiltering.csproj true $TargetFrameworks$ #file UnitTest1.cs using Microsoft.VisualStudio.TestTools.UnitTesting; [assembly: DiscoverInternals] namespace DiscoverInternalsFiltering; [TestClass] internal class TopLevelInternalClass { [TestMethod] public void TopLevelInternalClass_TestMethod1() { } [TestClass] internal class NestedInternalClass { [TestMethod] public void NestedInternalClass_TestMethod1() { } } } """; } }