From dc4c4d8f63d6e7e7baab5ba1861ce2e565b3755d Mon Sep 17 00:00:00 2001 From: Amaury Leveque Date: Fri, 3 Jul 2026 19:14:27 +0200 Subject: [PATCH] Move test-case filter parsing to adapter boundary (Phase 6d-1) Move TestMethodFilter (and its nested TestElementFilter) out of MSTestAdapter.PlatformServices up into MSTest.TestAdapter, and inject the neutral ITestElementFilter into the engine and discoverer via a new ITestElementFilterProvider abstraction. - New neutral ITestElementFilterProvider (PlatformServices.Interface): the boundary builds it (TestElementFilterProvider, closing over the VSTest IRunContext/IDiscoveryContext) and passes it into TestExecutionManager.RunTestsAsync/ExecuteTestsAsync and UnitTestDiscoverer.DiscoverTests. - The engine/discoverer invoke the provider at the EXACT points they previously built the filter (per source), so filter parse-error reporting keeps the same timing and per-source semantics; TestElementFilter.Matches still does element.ToTestCase() (byte-for-byte; #9568 deferred). - This removes ITestCaseFilterExpression / GetTestCaseFilter / MatchTestCase / the VSTest TestProperty filter set from PlatformServices code. IRunContext/IDiscoveryContext remain only for deployment + settings extraction (removed in a follow-up). No behavior change: filtered set/order and the discovery/execution filterHasError bail-out are identical; TestCaseFilteringTests (out-of-proc filter regression net) stays green. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../TestMethodFilter.cs | 17 +++++ .../VSTestAdapter/MSTestDiscoverer.cs | 2 +- .../VSTestAdapter/MSTestExecutor.cs | 4 +- .../Discovery/UnitTestDiscoverer.cs | 21 +++---- .../TestExecutionManager.Parallelization.cs | 7 ++- .../Execution/TestExecutionManager.cs | 18 +++--- .../Interfaces/ITestElementFilterProvider.cs | 31 ++++++++++ .../Utilities/CLITestBase.discovery.cs | 6 +- .../Discovery/UnitTestDiscovererTests.cs | 25 ++++---- .../Execution/TestExecutionManagerTests.cs | 62 +++++++++---------- 10 files changed, 123 insertions(+), 70 deletions(-) rename src/Adapter/{MSTestAdapter.PlatformServices => MSTest.TestAdapter}/TestMethodFilter.cs (90%) create mode 100644 src/Adapter/MSTestAdapter.PlatformServices/Interfaces/ITestElementFilterProvider.cs diff --git a/src/Adapter/MSTestAdapter.PlatformServices/TestMethodFilter.cs b/src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs similarity index 90% rename from src/Adapter/MSTestAdapter.PlatformServices/TestMethodFilter.cs rename to src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs index b10d7d9130..a2d0f20162 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/TestMethodFilter.cs +++ b/src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs @@ -182,3 +182,20 @@ public bool Matches(UnitTestElement testElement) } } } + +/// +/// Adapter-boundary that closes over the VSTest discovery/run context +/// and produces the neutral via . Created at the +/// boundary and passed into the platform services engine/discoverer so the filter (and any parse-error report) +/// is built at the exact point the engine needs it, preserving the original per-source timing. +/// +internal sealed class TestElementFilterProvider : ITestElementFilterProvider +{ + private readonly TestMethodFilter _testMethodFilter = new(); + private readonly IDiscoveryContext? _context; + + public TestElementFilterProvider(IDiscoveryContext? context) => _context = context; + + public ITestElementFilter? GetTestElementFilter(IAdapterMessageLogger logger, out bool filterHasError) + => _testMethodFilter.GetTestElementFilter(_context, logger, out filterHasError); +} diff --git a/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestDiscoverer.cs b/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestDiscoverer.cs index b57182075e..8bb3548db8 100644 --- a/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestDiscoverer.cs +++ b/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestDiscoverer.cs @@ -87,7 +87,7 @@ internal async Task DiscoverTestsAsync(IEnumerable sources, IDiscoveryCo IAdapterMessageLogger adapterLogger = logger.ToAdapterMessageLogger(); if (MSTestDiscovererHelpers.InitializeDiscovery(sources, discoveryContext, adapterLogger, configuration, _testSourceHandler)) { - new UnitTestDiscoverer(_testSourceHandler).DiscoverTests(sources, adapterLogger, discoverySink.ToUnitTestElementSink(), discoveryContext, isMTP); + new UnitTestDiscoverer(_testSourceHandler).DiscoverTests(sources, adapterLogger, discoverySink.ToUnitTestElementSink(), discoveryContext, new TestElementFilterProvider(discoveryContext), isMTP); } } finally diff --git a/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestExecutor.cs b/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestExecutor.cs index e8ce1faca9..9f718aefd6 100644 --- a/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestExecutor.cs +++ b/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestExecutor.cs @@ -156,7 +156,7 @@ internal async Task RunTestsAsync(IEnumerable? tests, IRunContext? run // execution engine reports results through this neutral recorder and never constructs VSTest results. ITestResultRecorder testResultRecorder = frameworkHandle.ToTestResultRecorder(Environment.MachineName, MSTestSettings.CurrentSettings); - await RunTestsFromRightContextAsync(frameworkHandle, async testRunToken => await TestExecutionManager.RunTestsAsync(testElements, runContext, frameworkHandle, testResultRecorder, testRunToken).ConfigureAwait(false)).ConfigureAwait(false); + await RunTestsFromRightContextAsync(frameworkHandle, async testRunToken => await TestExecutionManager.RunTestsAsync(testElements, runContext, frameworkHandle, testResultRecorder, new TestElementFilterProvider(runContext), testRunToken).ConfigureAwait(false)).ConfigureAwait(false); } finally { @@ -214,7 +214,7 @@ internal async Task RunTestsAsync(IEnumerable? sources, IRunContext? run // execution engine reports results through this neutral recorder and never constructs VSTest results. ITestResultRecorder testResultRecorder = frameworkHandle.ToTestResultRecorder(Environment.MachineName, MSTestSettings.CurrentSettings); - await RunTestsFromRightContextAsync(frameworkHandle, async testRunToken => await TestExecutionManager.RunTestsAsync(sources, runContext, frameworkHandle, testResultRecorder, testSourceHandler, isMTP, testRunToken).ConfigureAwait(false)).ConfigureAwait(false); + await RunTestsFromRightContextAsync(frameworkHandle, async testRunToken => await TestExecutionManager.RunTestsAsync(sources, runContext, frameworkHandle, testResultRecorder, new TestElementFilterProvider(runContext), testSourceHandler, isMTP, testRunToken).ConfigureAwait(false)).ConfigureAwait(false); } finally { diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Discovery/UnitTestDiscoverer.cs b/src/Adapter/MSTestAdapter.PlatformServices/Discovery/UnitTestDiscoverer.cs index caa53d2aad..11c94f1180 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Discovery/UnitTestDiscoverer.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Discovery/UnitTestDiscoverer.cs @@ -12,13 +12,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; [SuppressMessage("Performance", "CA1852: Seal internal types", Justification = "Overrides required for testability")] internal class UnitTestDiscoverer { - private readonly TestMethodFilter _testMethodFilter; - - internal UnitTestDiscoverer(ITestSourceHandler testSourceHandler) - { - _testMethodFilter = new TestMethodFilter(); - _testSource = testSourceHandler; - } + internal UnitTestDiscoverer(ITestSourceHandler testSourceHandler) => _testSource = testSourceHandler; /// /// Discovers the tests available from the provided sources. @@ -27,17 +21,19 @@ internal UnitTestDiscoverer(ITestSourceHandler testSourceHandler) /// The logger. /// The discovery Sink. /// The discovery context. + /// Provider for the test filter, or for no filter. /// Flag set to true when the platform running discovery is MTP. internal void DiscoverTests( IEnumerable sources, IAdapterMessageLogger logger, IUnitTestElementSink discoverySink, IDiscoveryContext discoveryContext, + ITestElementFilterProvider? filterProvider, bool isMTP) { foreach (string source in sources) { - DiscoverTestsInSource(source, logger, discoverySink, discoveryContext, isMTP); + DiscoverTestsInSource(source, logger, discoverySink, discoveryContext, filterProvider, isMTP); } } @@ -48,12 +44,14 @@ internal void DiscoverTests( /// The logger. /// The discovery Sink. /// The discovery context. + /// Provider for the test filter, or for no filter. /// Flag set to true when the platform running discovery is MTP. internal virtual void DiscoverTestsInSource( string source, IAdapterMessageLogger logger, IUnitTestElementSink discoverySink, IDiscoveryContext? discoveryContext, + ITestElementFilterProvider? filterProvider, bool isMTP) { ICollection? testElements = AssemblyEnumeratorWrapper.GetTests(source, discoveryContext?.RunSettings?.SettingsXml, _testSource, isMTP, out List warnings); @@ -103,15 +101,16 @@ internal virtual void DiscoverTestsInSource( source); } - SendTestCases(testElements, discoverySink, discoveryContext, logger); + SendTestCases(testElements, discoverySink, filterProvider, logger); } private readonly ITestSourceHandler _testSource; - internal void SendTestCases(IEnumerable testElements, IUnitTestElementSink discoverySink, IDiscoveryContext? discoveryContext, IAdapterMessageLogger logger) + internal static void SendTestCases(IEnumerable testElements, IUnitTestElementSink discoverySink, ITestElementFilterProvider? filterProvider, IAdapterMessageLogger logger) { // Get filter and skip discovery in case filter expression has parsing error. - ITestElementFilter? filter = _testMethodFilter.GetTestElementFilter(discoveryContext, logger, out bool filterHasError); + bool filterHasError = false; + ITestElementFilter? filter = filterProvider?.GetTestElementFilter(logger, out filterHasError); if (filterHasError) { return; diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.Parallelization.cs b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.Parallelization.cs index 06f1cb5f5f..f9c6925112 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.Parallelization.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.Parallelization.cs @@ -21,10 +21,12 @@ internal partial class TestExecutionManager /// The run context. /// Handle to record test start/end/results. /// Recorder used to report test results back to the host. + /// Provider for the test filter, or for no filter. /// Indicates if deployment is done. - internal virtual async Task ExecuteTestsAsync(IEnumerable tests, IRunContext? runContext, IFrameworkHandle frameworkHandle, ITestResultRecorder testResultRecorder, bool isDeploymentDone) + internal virtual async Task ExecuteTestsAsync(IEnumerable tests, IRunContext? runContext, IFrameworkHandle frameworkHandle, ITestResultRecorder testResultRecorder, ITestElementFilterProvider? filterProvider, bool isDeploymentDone) { _testResultRecorder = testResultRecorder; + _testElementFilterProvider = filterProvider; InitializeRandomTestOrder(frameworkHandle.ToAdapterMessageLogger()); @@ -74,7 +76,8 @@ private async Task ExecuteTestsInSourceAsync(IEnumerable tests, } // Default test set is filtered tests based on user provided filter criteria - ITestElementFilter? filter = _testMethodFilter.GetTestElementFilter(runContext, adapterMessageLogger, out bool filterHasError); + bool filterHasError = false; + ITestElementFilter? filter = _testElementFilterProvider?.GetTestElementFilter(adapterMessageLogger, out filterHasError); if (filterHasError) { // Bail out without processing everything else below. diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.cs b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.cs index f0722e7cb5..e5ea313a5d 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.cs @@ -53,7 +53,6 @@ public TestExecutionManager() internal TestExecutionManager(Func, Task>? taskFactory = null) { - _testMethodFilter = new TestMethodFilter(); _sessionParameters = new Dictionary(); _taskFactory = taskFactory ?? DefaultFactoryAsync; } @@ -74,9 +73,11 @@ private static Task DefaultFactoryAsync(Func taskGetter) } /// - /// Gets or sets method filter for filtering tests. + /// Provider for the test filter, supplied at the adapter boundary and set once per run in + /// so the filter is built at the same point (and with the same per-source + /// timing) as before. /// - private readonly TestMethodFilter _testMethodFilter; + private ITestElementFilterProvider? _testElementFilterProvider; #if !WINDOWS_UWP && !WIN_UI /// @@ -92,8 +93,9 @@ private static Task DefaultFactoryAsync(Func taskGetter) /// Context to use when executing the tests. /// Handle to the framework to record results and to do framework operations. /// Recorder used to report test results back to the host. + /// Provider for the test filter, or for no filter. /// Test run cancellation token. - internal async Task RunTestsAsync(IEnumerable tests, IRunContext? runContext, IFrameworkHandle frameworkHandle, ITestResultRecorder testResultRecorder, TestRunCancellationToken runCancellationToken) + internal async Task RunTestsAsync(IEnumerable tests, IRunContext? runContext, IFrameworkHandle frameworkHandle, ITestResultRecorder testResultRecorder, ITestElementFilterProvider? filterProvider, TestRunCancellationToken runCancellationToken) { DebugEx.Assert(tests != null, "tests"); DebugEx.Assert(runContext != null, "runContext"); @@ -113,7 +115,7 @@ internal async Task RunTestsAsync(IEnumerable tests, IRunContex CacheSessionParameters(runContext?.RunSettings?.SettingsXml, frameworkHandle.ToAdapterMessageLogger()); // Execute the tests - await ExecuteTestsAsync(tests, runContext, frameworkHandle, testResultRecorder, isDeploymentDone).ConfigureAwait(false); + await ExecuteTestsAsync(tests, runContext, frameworkHandle, testResultRecorder, filterProvider, isDeploymentDone).ConfigureAwait(false); #if !WINDOWS_UWP && !WIN_UI if (!_hasAnyTestFailed) @@ -123,7 +125,7 @@ internal async Task RunTestsAsync(IEnumerable tests, IRunContex #endif } - internal async Task RunTestsAsync(IEnumerable sources, IRunContext? runContext, IFrameworkHandle frameworkHandle, ITestResultRecorder testResultRecorder, ITestSourceHandler testSourceHandler, bool isMTP, TestRunCancellationToken cancellationToken) + internal async Task RunTestsAsync(IEnumerable sources, IRunContext? runContext, IFrameworkHandle frameworkHandle, ITestResultRecorder testResultRecorder, ITestElementFilterProvider? filterProvider, ITestSourceHandler testSourceHandler, bool isMTP, TestRunCancellationToken cancellationToken) { _testRunCancellationToken = cancellationToken; PlatformServiceProvider.Instance.TestRunCancellationToken = _testRunCancellationToken; @@ -140,7 +142,7 @@ internal async Task RunTestsAsync(IEnumerable sources, IRunContext? runC _testRunCancellationToken?.ThrowIfCancellationRequested(); // discover the tests - GetUnitTestDiscoverer(testSourceHandler).DiscoverTestsInSource(source, logger, discoverySink, runContext, isMTP); + GetUnitTestDiscoverer(testSourceHandler).DiscoverTestsInSource(source, logger, discoverySink, runContext, filterProvider, isMTP); tests.AddRange(discoverySink.TestElements); // Clear discoverSinksTests so that it just stores test for one source at one point of time @@ -157,7 +159,7 @@ internal async Task RunTestsAsync(IEnumerable sources, IRunContext? runC CacheSessionParameters(runContext?.RunSettings?.SettingsXml, frameworkHandle.ToAdapterMessageLogger()); // Run tests. - await ExecuteTestsAsync(tests, runContext, frameworkHandle, testResultRecorder, isDeploymentDone).ConfigureAwait(false); + await ExecuteTestsAsync(tests, runContext, frameworkHandle, testResultRecorder, filterProvider, isDeploymentDone).ConfigureAwait(false); #if !WINDOWS_UWP && !WIN_UI if (!_hasAnyTestFailed) diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/ITestElementFilterProvider.cs b/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/ITestElementFilterProvider.cs new file mode 100644 index 0000000000..202b395383 --- /dev/null +++ b/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/ITestElementFilterProvider.cs @@ -0,0 +1,31 @@ +// 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.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; + +/// +/// Platform-agnostic factory that produces the in effect for the current +/// run or discovery. +/// +/// +/// This abstraction lets the platform services discovery and execution pipelines obtain the active filter +/// without taking a dependency on a specific test platform's filter object model (for example the VSTest +/// ITestCaseFilterExpression parsed from an IRunContext / IDiscoveryContext). The concrete +/// provider is created at the adapter boundary, closing over the host filter context; the engine invokes it at +/// the exact point it needs the filter so parse-error reporting keeps the same timing and per-source semantics. +/// +internal interface ITestElementFilterProvider +{ + /// + /// Builds the for the current run/discovery. + /// + /// Logger used to report a filter parsing error. + /// + /// Set to when the filter is unsupported or failed to parse; in that case the caller + /// should report no tests for the affected source. + /// + /// + /// The filter to apply, or when no filter applies (every element is included). + /// + ITestElementFilter? GetTestElementFilter(IAdapterMessageLogger logger, out bool filterHasError); +} diff --git a/test/IntegrationTests/MSTest.IntegrationTests/Utilities/CLITestBase.discovery.cs b/test/IntegrationTests/MSTest.IntegrationTests/Utilities/CLITestBase.discovery.cs index 4ac356c178..72d364219b 100644 --- a/test/IntegrationTests/MSTest.IntegrationTests/Utilities/CLITestBase.discovery.cs +++ b/test/IntegrationTests/MSTest.IntegrationTests/Utilities/CLITestBase.discovery.cs @@ -30,7 +30,7 @@ internal static ImmutableArray DiscoverTests(string assemblyPath, stri string runSettingsXml = GetRunSettingsXml(string.Empty); var context = new InternalDiscoveryContext(runSettingsXml, testCaseFilter); - unitTestDiscoverer.DiscoverTestsInSource(assemblyPath, logger.ToAdapterMessageLogger(), sink.ToUnitTestElementSink(), context, false); + unitTestDiscoverer.DiscoverTestsInSource(assemblyPath, logger.ToAdapterMessageLogger(), sink.ToUnitTestElementSink(), context, new TestElementFilterProvider(context), false); return sink.DiscoveredTests; } @@ -41,7 +41,7 @@ internal static async Task> RunTestsAsync(IEnumerable var frameworkHandle = new InternalFrameworkHandle(); ITestResultRecorder testResultRecorder = frameworkHandle.ToTestResultRecorder(Environment.MachineName, MSTestSettings.CurrentSettings); - await testExecutionManager.ExecuteTestsAsync(ToUnitTestElements(testCases), null, frameworkHandle, testResultRecorder, false); + await testExecutionManager.ExecuteTestsAsync(ToUnitTestElements(testCases), null, frameworkHandle, testResultRecorder, filterProvider: null, false); return frameworkHandle.GetFlattenedTestResults(); } @@ -54,7 +54,7 @@ internal static async Task> RunTestsAsync(IEnumerable var runContext = new InternalRunContext(runSettingsXml, testCaseFilter); ITestResultRecorder testResultRecorder = frameworkHandle.ToTestResultRecorder(Environment.MachineName, MSTestSettings.CurrentSettings); - await testExecutionManager.ExecuteTestsAsync(ToUnitTestElements(testCases), runContext, frameworkHandle, testResultRecorder, false); + await testExecutionManager.ExecuteTestsAsync(ToUnitTestElements(testCases), runContext, frameworkHandle, testResultRecorder, new TestElementFilterProvider(runContext), false); return frameworkHandle.GetFlattenedTestResults(); } diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/UnitTestDiscovererTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/UnitTestDiscovererTests.cs index b15605e885..0febd7b5ef 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/UnitTestDiscovererTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/UnitTestDiscovererTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using AwesomeAssertions; @@ -80,7 +80,7 @@ public void DiscoverTestsShouldThrowOnFileNotFound() .Returns(false); } - Action act = () => _unitTestDiscoverer.DiscoverTests(sources, _mockMessageLogger.Object.ToAdapterMessageLogger(), _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), _mockDiscoveryContext.Object, false); + Action act = () => _unitTestDiscoverer.DiscoverTests(sources, _mockMessageLogger.Object.ToAdapterMessageLogger(), _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), _mockDiscoveryContext.Object, new TestElementFilterProvider(_mockDiscoveryContext.Object), false); act.Should().Throw() .WithMessage(string.Format(CultureInfo.CurrentCulture, Resource.TestAssembly_FileDoesNotExist, sources[0])); } @@ -93,7 +93,7 @@ public void DiscoverTestsInSourceShouldThrowOnFileNotFound() _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.DoesFileExist(Source)) .Returns(false); - Action act = () => _unitTestDiscoverer.DiscoverTestsInSource(Source, _mockMessageLogger.Object.ToAdapterMessageLogger(), _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), _mockDiscoveryContext.Object, false); + Action act = () => _unitTestDiscoverer.DiscoverTestsInSource(Source, _mockMessageLogger.Object.ToAdapterMessageLogger(), _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), _mockDiscoveryContext.Object, new TestElementFilterProvider(_mockDiscoveryContext.Object), false); act.Should().Throw() .WithMessage(string.Format(CultureInfo.CurrentCulture, Resource.TestAssembly_FileDoesNotExist, Source)); } @@ -125,7 +125,7 @@ public void DiscoverTestsInSourceShouldSendBackTestCasesDiscovered() MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object.ToAdapterMessageLogger(), null); // Act - _unitTestDiscoverer.DiscoverTestsInSource(Source, _mockMessageLogger.Object.ToAdapterMessageLogger(), _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), _mockDiscoveryContext.Object, false); + _unitTestDiscoverer.DiscoverTestsInSource(Source, _mockMessageLogger.Object.ToAdapterMessageLogger(), _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), _mockDiscoveryContext.Object, new TestElementFilterProvider(_mockDiscoveryContext.Object), false); // Assert. _mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.IsAny()), Times.AtLeastOnce); @@ -160,7 +160,7 @@ public void DiscoverTestsInSourceShouldThrowWhenTreatDiscoveryWarningsAsErrorsIs MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object.ToAdapterMessageLogger(), null); // Act - Action action = () => _unitTestDiscoverer.DiscoverTestsInSource(Source, _mockMessageLogger.Object.ToAdapterMessageLogger(), _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), _mockDiscoveryContext.Object, false); + Action action = () => _unitTestDiscoverer.DiscoverTestsInSource(Source, _mockMessageLogger.Object.ToAdapterMessageLogger(), _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), _mockDiscoveryContext.Object, new TestElementFilterProvider(_mockDiscoveryContext.Object), false); // Assert action.Should().Throw() @@ -199,7 +199,7 @@ public void DiscoverTestsInSourceShouldNotThrowWhenTreatDiscoveryWarningsAsError // Act & Assert // Should not throw an exception - _unitTestDiscoverer.DiscoverTestsInSource(Source, _mockMessageLogger.Object.ToAdapterMessageLogger(), _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), _mockDiscoveryContext.Object, false); + _unitTestDiscoverer.DiscoverTestsInSource(Source, _mockMessageLogger.Object.ToAdapterMessageLogger(), _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), _mockDiscoveryContext.Object, new TestElementFilterProvider(_mockDiscoveryContext.Object), false); // Verify warning message was sent to logger (not error) _mockMessageLogger.Verify(lm => lm.SendMessage(TestMessageLevel.Warning, It.IsAny()), Times.AtLeastOnce); @@ -209,7 +209,7 @@ public void DiscoverTestsInSourceShouldNotThrowWhenTreatDiscoveryWarningsAsError public void SendTestCasesShouldNotSendAnyTestCasesIfThereAreNoTestElements() { // There is a null check for testElements in the code flow before this function call. So not adding a unit test for that. - _unitTestDiscoverer.SendTestCases(new List { }, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), _mockDiscoveryContext.Object, _mockMessageLogger.Object.ToAdapterMessageLogger()); + UnitTestDiscoverer.SendTestCases(new List { }, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), new TestElementFilterProvider(_mockDiscoveryContext.Object), _mockMessageLogger.Object.ToAdapterMessageLogger()); // Assert. _mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.IsAny()), Times.Never); @@ -221,7 +221,7 @@ public void SendTestCasesShouldSendAllTestCaseData() var test2 = new UnitTestElement(CreateTestMethod("M2", "C", "A", displayName: null)); var testElements = new List { test1, test2 }; - _unitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), _mockDiscoveryContext.Object, _mockMessageLogger.Object.ToAdapterMessageLogger()); + UnitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), new TestElementFilterProvider(_mockDiscoveryContext.Object), _mockMessageLogger.Object.ToAdapterMessageLogger()); // Assert. _mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M1")), Times.Once); @@ -240,7 +240,7 @@ public void SendTestCasesShouldSendFilteredTestCasesIfValidFilterExpression() var testElements = new List { test1, test2 }; // Action - _unitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), discoveryContext, _mockMessageLogger.Object.ToAdapterMessageLogger()); + UnitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), new TestElementFilterProvider(discoveryContext), _mockMessageLogger.Object.ToAdapterMessageLogger()); // Assert. _mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M1")), Times.Once); @@ -259,7 +259,7 @@ public void SendTestCasesShouldSendAllTestCasesIfNullFilterExpression() var testElements = new List { test1, test2 }; // Action - _unitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), discoveryContext, _mockMessageLogger.Object.ToAdapterMessageLogger()); + UnitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), new TestElementFilterProvider(discoveryContext), _mockMessageLogger.Object.ToAdapterMessageLogger()); // Assert. _mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M1")), Times.Once); @@ -278,7 +278,7 @@ public void SendTestCasesShouldSendAllTestCasesIfGetTestCaseFilterNotPresent() var testElements = new List { test1, test2 }; // Action - _unitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), discoveryContext, _mockMessageLogger.Object.ToAdapterMessageLogger()); + UnitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), new TestElementFilterProvider(discoveryContext), _mockMessageLogger.Object.ToAdapterMessageLogger()); // Assert. _mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M1")), Times.Once); @@ -297,7 +297,7 @@ public void SendTestCasesShouldNotSendAnyTestCasesIfFilterError() var testElements = new List { test1, test2 }; // Action - _unitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), discoveryContext, _mockMessageLogger.Object.ToAdapterMessageLogger()); + UnitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), new TestElementFilterProvider(discoveryContext), _mockMessageLogger.Object.ToAdapterMessageLogger()); // Assert. _mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M1")), Times.Never); @@ -328,6 +328,7 @@ internal override void DiscoverTestsInSource( IAdapterMessageLogger logger, IUnitTestElementSink discoverySink, IDiscoveryContext? discoveryContext, + ITestElementFilterProvider? filterProvider, bool isMTP) { var testElement1 = new UnitTestElement(new TestMethod("A", "C", source, displayName: null)); diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestExecutionManagerTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestExecutionManagerTests.cs index 57341c0d04..b7a9d9fe3d 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestExecutionManagerTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestExecutionManagerTests.cs @@ -100,7 +100,7 @@ public async Task RunTestsForTestWithFilterErrorShouldSendZeroResults() // Causing the FilterExpressionError _runContext = new TestableRunContextTestExecutionTests(() => throw new TestPlatformFormatException()); - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, _cancellationToken); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), _cancellationToken); // No Results _frameworkHandle.TestCaseStartList.Count.Should().Be(0); @@ -116,7 +116,7 @@ public async Task RunTestsForTestWithFilterShouldSendResultsForFilteredTests() _runContext = new TestableRunContextTestExecutionTests(() => new TestableTestCaseFilterExpression(p => p.DisplayName == "PassingTest")); - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, _cancellationToken); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), _cancellationToken); // FailingTest should be skipped because it does not match the filter criteria. List expectedTestCaseStartList = ["PassingTest"]; @@ -144,7 +144,7 @@ public async Task RunTestsForIgnoredTestShouldSendResultsMarkingIgnoredTestsAsSk TestCase testCase = GetTestCase(typeof(DummyTestClass), "IgnoredTest"); TestCase[] tests = [testCase]; - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, _cancellationToken); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), _cancellationToken); _frameworkHandle.TestCaseStartList[0].Should().Be("IgnoredTest"); _frameworkHandle.TestCaseEndList[0].Should().Be("IgnoredTest:Skipped"); @@ -157,7 +157,7 @@ public async Task RunTestsForASingleTestShouldSendSingleResult() TestCase[] tests = [testCase]; - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); List expectedTestCaseStartList = ["PassingTest"]; List expectedTestCaseEndList = ["PassingTest:Passed"]; @@ -174,7 +174,7 @@ public async Task RunTestsForMultipleTestShouldSendMultipleResults() TestCase failingTestCase = GetTestCase(typeof(DummyTestClass), "FailingTest"); TestCase[] tests = [testCase, failingTestCase]; - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, _cancellationToken); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), _cancellationToken); List expectedTestCaseStartList = ["PassingTest", "FailingTest"]; List expectedTestCaseEndList = ["PassingTest:Passed", "FailingTest:Failed"]; @@ -194,7 +194,7 @@ public async Task RunTestsForCancellationTokenCanceledSetToTrueShouldSendZeroRes // Cancel the test run _cancellationToken.Cancel(); - Func func = () => _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, _cancellationToken); + Func func = () => _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), _cancellationToken); await func.Should().ThrowAsync(); // No Results @@ -218,7 +218,7 @@ await _testExecutionManager.RunTestsAsync( ToUnitTestElements(tests), _runContext, _frameworkHandle, - TestResultRecorder, new TestRunCancellationToken()); + TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); _callers[0].Should().Be("Deploy", "Deploy should be called before execution."); _callers[1].Should().Be("LoadAssembly", "Deploy should be called before execution."); @@ -238,7 +238,7 @@ public async Task RunTestsForTestShouldCleanupAfterExecution() td => td.Cleanup()).Callback(() => SetCaller("Cleanup")); #endif - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); _callers[0].Should().Be("LoadAssembly", "Cleanup should be called after execution."); @@ -255,7 +255,7 @@ public async Task RunTestsForTestShouldNotCleanupOnTestFailure() TestCase[] tests = [testCase, failingTestCase]; TestablePlatformServiceProvider testablePlatformService = SetupTestablePlatformService(); - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); testablePlatformService.MockTestDeployment.Verify(td => td.Cleanup(), Times.Never); } @@ -274,7 +274,7 @@ public async Task RunTestsForTestShouldLoadSourceFromDeploymentDirectoryIfDeploy testablePlatformService.MockTestDeployment.Setup(td => td.GetDeploymentDirectory()) .Returns(@"C:\temp"); - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); testablePlatformService.MockFileOperations.Verify( fo => fo.LoadAssembly(It.Is(s => s.StartsWith("C:\\temp"))), @@ -300,7 +300,7 @@ public async Task RunTestsForTestShouldPassInTestRunParametersInformationAsPrope """); - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); DummyTestClass.TestContextProperties!.Contains( new KeyValuePair("webAppUrl", "http://localhost")).Should().BeTrue(); @@ -322,7 +322,7 @@ public async Task RunTestsForTestShouldPassInTcmPropertiesAsPropertiesToTheTest( """); - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); VerifyTcmProperties(DummyTestClass.TestContextProperties, testCase); } @@ -335,7 +335,7 @@ public async Task RunTestsForTestShouldPassInDeploymentInformationAsPropertiesTo // Setup mocks. TestablePlatformServiceProvider testablePlatformService = SetupTestablePlatformService(); - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); testablePlatformService.MockSettingsProvider.Verify(sp => sp.GetProperties(It.IsAny()), Times.Once); } @@ -359,7 +359,7 @@ public async Task RunTestsShouldClearSessionParametersAcrossRuns() """); // Trigger First Run - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); // Update runsettings to have different values for similar keys _runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns( @@ -376,7 +376,7 @@ public async Task RunTestsShouldClearSessionParametersAcrossRuns() """); // Trigger another Run - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); "http://updatedLocalHost".Equals(DummyTestClass.TestContextProperties!["webAppUrl"]).Should().BeTrue(); } @@ -390,7 +390,7 @@ private async Task RunTestsForSourceShouldRunTestsInASource() { var sources = new List { Assembly.GetExecutingAssembly().Location }; - await _testExecutionManager.RunTestsAsync(sources, _runContext, _frameworkHandle, TestResultRecorder, _mockTestSourceHandler.Object, false, _cancellationToken); + await _testExecutionManager.RunTestsAsync(sources, _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), _mockTestSourceHandler.Object, false, _cancellationToken); _frameworkHandle.TestCaseStartList.Contains("PassingTest").Should().BeTrue(); _frameworkHandle.TestCaseEndList.Contains("PassingTest:Passed").Should().BeTrue(); @@ -412,7 +412,7 @@ private async Task RunTestsForSourceShouldPassInTestRunParametersInformationAsPr """); - await _testExecutionManager.RunTestsAsync(sources, _runContext, _frameworkHandle, TestResultRecorder, _mockTestSourceHandler.Object, false, _cancellationToken); + await _testExecutionManager.RunTestsAsync(sources, _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), _mockTestSourceHandler.Object, false, _cancellationToken); DummyTestClass.TestContextProperties!.Contains( new KeyValuePair("webAppUrl", "http://localhost")).Should().BeTrue(); @@ -423,7 +423,7 @@ private async Task RunTestsForSourceShouldPassInDeploymentInformationAsPropertie { var sources = new List { Assembly.GetExecutingAssembly().Location }; - await _testExecutionManager.RunTestsAsync(sources, _runContext, _frameworkHandle, TestResultRecorder, _mockTestSourceHandler.Object, false, _cancellationToken); + await _testExecutionManager.RunTestsAsync(sources, _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), _mockTestSourceHandler.Object, false, _cancellationToken); DummyTestClass.TestContextProperties.Should().NotBeNull(); } @@ -437,7 +437,7 @@ public async Task RunTestsForMultipleSourcesShouldRunEachTestJustOnce() ExecuteTestsWrapper = (tests, runContext, frameworkHandle, testResultRecorder, isDeploymentDone) => testsCount += tests.Count(), }; - await testableTestExecutionManager.RunTestsAsync(sources, _runContext, _frameworkHandle, TestResultRecorder, _mockTestSourceHandler.Object, false, _cancellationToken); + await testableTestExecutionManager.RunTestsAsync(sources, _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), _mockTestSourceHandler.Object, false, _cancellationToken); testsCount.Should().Be(4); } @@ -470,7 +470,7 @@ public async Task RunTestsForTestShouldRunTestsInParallelWhenEnabledInRunsetting try { MSTestSettings.PopulateSettings(_runContext, _mockMessageLogger.Object.ToAdapterMessageLogger(), null); - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); DummyTestClassForParallelize.ThreadIds.Count.Should().Be(1); DummyTestClassForParallelize2.ThreadIds.Count.Should().Be(1); @@ -507,7 +507,7 @@ public async Task RunTestsForTestShouldRunTestsByMethodLevelWhenSpecified() try { MSTestSettings.PopulateSettings(_runContext, _mockMessageLogger.Object.ToAdapterMessageLogger(), null); - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); _enqueuedParallelTestsCount.Should().Be(2); @@ -544,7 +544,7 @@ public async Task RunTestsForTestShouldRunTestsWithSpecifiedNumberOfWorkers() try { MSTestSettings.PopulateSettings(_runContext, _mockMessageLogger.Object.ToAdapterMessageLogger(), null); - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); DummyTestClassForParallelize.ThreadIds.Count.Should().Be(1); DummyTestClassForParallelize2.ThreadIds.Count.Should().Be(1); @@ -607,7 +607,7 @@ public async Task RunTestsForTestShouldNotRunTestsInParallelWhenDisabledFromRuns testablePlatformService.MockReflectionOperations.Setup(fo => fo.GetRuntimeMethods(It.IsAny())) .Returns((Type t) => originalReflectionOperation.GetRuntimeMethods(t)); - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); DummyTestClassForParallelize.ThreadIds.Count.Should().Be(1); } @@ -666,7 +666,7 @@ public async Task RunTestsForTestShouldNotRunTestsInParallelWhenDisabledFromSour testablePlatformService.MockReflectionOperations.Setup(fo => fo.GetRuntimeMethods(It.IsAny())) .Returns((Type t) => originalReflectionOperation.GetRuntimeMethods(t)); - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); DummyTestClassForParallelize.ThreadIds.Count.Should().Be(1); } @@ -705,7 +705,7 @@ public async Task RunTestsForTestShouldRunNonParallelizableTestsSeparately() try { MSTestSettings.PopulateSettings(_runContext, _mockMessageLogger.Object.ToAdapterMessageLogger(), null); - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); _enqueuedParallelTestsCount.Should().Be(2); DummyTestClassWithDoNotParallelizeMethods.ParallelizableTestsThreadIds.Count.Should().BeOneOf(1, 2); @@ -771,7 +771,7 @@ public async Task RunTestsForTestShouldPreferParallelSettingsFromRunSettingsOver testablePlatformService.MockReflectionOperations.Setup(fo => fo.GetRuntimeMethods(It.IsAny())) .Returns((Type t) => originalReflectionOperation.GetRuntimeMethods(t)); - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); _enqueuedParallelTestsCount.Should().Be(2); @@ -811,7 +811,7 @@ private async Task RunTestsForTestShouldRunTestsInTheParentDomainsApartmentState try { MSTestSettings.PopulateSettings(_runContext, _mockMessageLogger.Object.ToAdapterMessageLogger(), null); - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); DummyTestClassWithDoNotParallelizeMethods.ThreadApartmentStates.Count.Should().Be(1); DummyTestClassWithDoNotParallelizeMethods.ThreadApartmentStates.ToArray()[0].Should().Be(Thread.CurrentThread.GetApartmentState()); @@ -854,11 +854,11 @@ static TestCase[] BuildTests() => var firstHandle = new TestableFrameworkHandle(); var firstManager = new TestExecutionManager(task => task()); - await firstManager.RunTestsAsync(ToUnitTestElements(BuildTests()), _runContext, firstHandle, firstHandle.ToTestResultRecorder(EnvironmentWrapper.Instance.MachineName, MSTestSettings.CurrentSettings), new TestRunCancellationToken()); + await firstManager.RunTestsAsync(ToUnitTestElements(BuildTests()), _runContext, firstHandle, firstHandle.ToTestResultRecorder(EnvironmentWrapper.Instance.MachineName, MSTestSettings.CurrentSettings), new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); var secondHandle = new TestableFrameworkHandle(); var secondManager = new TestExecutionManager(task => task()); - await secondManager.RunTestsAsync(ToUnitTestElements(BuildTests()), _runContext, secondHandle, secondHandle.ToTestResultRecorder(EnvironmentWrapper.Instance.MachineName, MSTestSettings.CurrentSettings), new TestRunCancellationToken()); + await secondManager.RunTestsAsync(ToUnitTestElements(BuildTests()), _runContext, secondHandle, secondHandle.ToTestResultRecorder(EnvironmentWrapper.Instance.MachineName, MSTestSettings.CurrentSettings), new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); // Same seed must produce the same order across separate runs. firstHandle.TestCaseStartList.Should().Equal(secondHandle.TestCaseStartList); @@ -895,7 +895,7 @@ public async Task RunTestsWithoutRandomizeTestOrderShouldPreserveInputOrder() MSTestSettings.PopulateSettings(_runContext, _mockMessageLogger.Object.ToAdapterMessageLogger(), null); - await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestRunCancellationToken()); + await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), _runContext, _frameworkHandle, TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken()); _frameworkHandle.TestCaseStartList.Should().Equal("TestA", "TestB", "TestC"); } @@ -1273,7 +1273,7 @@ internal class TestableTestExecutionManager : TestExecutionManager { internal Action, IRunContext?, IFrameworkHandle, ITestResultRecorder, bool> ExecuteTestsWrapper { get; set; } = null!; - internal override Task ExecuteTestsAsync(IEnumerable tests, IRunContext? runContext, IFrameworkHandle frameworkHandle, ITestResultRecorder testResultRecorder, bool isDeploymentDone) + internal override Task ExecuteTestsAsync(IEnumerable tests, IRunContext? runContext, IFrameworkHandle frameworkHandle, ITestResultRecorder testResultRecorder, ITestElementFilterProvider? filterProvider, bool isDeploymentDone) { ExecuteTestsWrapper?.Invoke(tests, runContext, frameworkHandle, testResultRecorder, isDeploymentDone); return Task.CompletedTask;