diff --git a/scripts/test.ps1 b/scripts/test.ps1 index 338e5e39eb..ed2bea934b 100644 --- a/scripts/test.ps1 +++ b/scripts/test.ps1 @@ -82,8 +82,11 @@ Get-ChildItem "Env:\dotnet_*" & "$env:DOTNET_ROOT\dotnet.exe" --info "`n`n---- x86 dotnet" -& "${env:DOTNET_ROOT(x86)}\dotnet.exe" --info - +# avoid erroring out because we don't have the sdk for x86 that global.json requires +try { + & "${env:DOTNET_ROOT(x86)}\dotnet.exe" --info 2> $null +} catch {} + # Dotnet build doesn't support --packages yet. See https://github.com/dotnet/cli/issues/2712 $env:NUGET_PACKAGES = $env:TP_PACKAGES_DIR diff --git a/src/Microsoft.TestPlatform.ObjectModel/RunSettings/RunConfiguration.cs b/src/Microsoft.TestPlatform.ObjectModel/RunSettings/RunConfiguration.cs index 24f5ff8959..8b1476124a 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/RunSettings/RunConfiguration.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/RunSettings/RunConfiguration.cs @@ -488,6 +488,10 @@ public bool ResultsDirectorySet public bool CollectSourceInformationSet { get; private set; } = false; /// + /// Default filter to use to filter tests + /// + public string TestCaseFilter { get; private set; } + /// Path to dotnet executable to be used to invoke testhost.dll. Specifying this will skip looking up testhost.exe and will force usage of the testhost.dll. /// public string DotnetHostPath { get; private set; } @@ -576,6 +580,13 @@ public override XmlElement ToXml() root.AppendChild(targetDevice); } + if (!string.IsNullOrEmpty(this.TestCaseFilter)) + { + XmlElement testCaseFilter = doc.CreateElement(nameof(TestCaseFilter)); + testCaseFilter.InnerXml = this.TestCaseFilter; + root.AppendChild(testCaseFilter); + } + if (!string.IsNullOrEmpty(this.DotnetHostPath)) { XmlElement dotnetHostPath = doc.CreateElement(nameof(DotnetHostPath)); @@ -879,6 +890,11 @@ public static RunConfiguration FromXml(XmlReader reader) runConfiguration.TargetDevice = reader.ReadElementContentAsString(); break; + case "TestCaseFilter": + XmlRunSettingsUtilities.ThrowOnHasAttributes(reader); + runConfiguration.TestCaseFilter = reader.ReadElementContentAsString(); + break; + case "DotNetHostPath": XmlRunSettingsUtilities.ThrowOnHasAttributes(reader); runConfiguration.DotnetHostPath = reader.ReadElementContentAsString(); diff --git a/src/vstest.console/Processors/RunSettingsArgumentProcessor.cs b/src/vstest.console/Processors/RunSettingsArgumentProcessor.cs index 92866c7ec8..4b7d903567 100644 --- a/src/vstest.console/Processors/RunSettingsArgumentProcessor.cs +++ b/src/vstest.console/Processors/RunSettingsArgumentProcessor.cs @@ -142,6 +142,12 @@ public void Initialize(string argument) this.commandLineOptions.InIsolation = true; this.runSettingsManager.UpdateRunSettingsNode(InIsolationArgumentExecutor.RunSettingsPath, "true"); } + + var testCaseFilter = this.runSettingsManager.QueryRunSettingsNode("RunConfiguration.TestCaseFilter"); + if (testCaseFilter != null) + { + this.commandLineOptions.TestCaseFilterValue = testCaseFilter; + } } catch (XmlException exception) { diff --git a/src/vstest.console/Processors/TestCaseFilterArgumentProcessor.cs b/src/vstest.console/Processors/TestCaseFilterArgumentProcessor.cs index 3f10e0143d..8eb75e0f05 100644 --- a/src/vstest.console/Processors/TestCaseFilterArgumentProcessor.cs +++ b/src/vstest.console/Processors/TestCaseFilterArgumentProcessor.cs @@ -117,12 +117,23 @@ public TestCaseFilterArgumentExecutor(CommandLineOptions options) /// Argument that was provided with the command. public void Initialize(string argument) { - if (string.IsNullOrWhiteSpace(argument)) + var defaultFilter = this.commandLineOptions.TestCaseFilterValue; + var hasDefaultFilter = !string.IsNullOrWhiteSpace(defaultFilter); + + if (!hasDefaultFilter && string.IsNullOrWhiteSpace(argument)) { throw new CommandLineException(string.Format(CultureInfo.CurrentUICulture, CommandLineResources.TestCaseFilterValueRequired)); } - this.commandLineOptions.TestCaseFilterValue = argument; + if (!hasDefaultFilter) + { + this.commandLineOptions.TestCaseFilterValue = argument; + } + else + { + // Merge default filter an provided filter by AND operator to have both the default filter and custom filter applied. + this.commandLineOptions.TestCaseFilterValue = $"({defaultFilter})&({argument})"; + } } /// diff --git a/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs b/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs index 08699a5f82..778006ebf5 100644 --- a/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs +++ b/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs @@ -150,6 +150,7 @@ public void DiscoverTests(DiscoveryRequestPayload discoveryPayload, ITestDiscove var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(runsettings); var batchSize = runConfiguration.BatchSize; + var testCaseFilterFromRunsettings = runConfiguration.TestCaseFilter; if (requestData.IsTelemetryOptedIn) { @@ -160,10 +161,12 @@ public void DiscoverTests(DiscoveryRequestPayload discoveryPayload, ITestDiscove this.LogCommandsTelemetryPoints(requestData); } + + // create discovery request var criteria = new DiscoveryCriteria(discoveryPayload.Sources, batchSize, this.commandLineOptions.TestStatsEventTimeout, runsettings) { - TestCaseFilter = this.commandLineOptions.TestCaseFilterValue + TestCaseFilter = this.commandLineOptions.TestCaseFilterValue ?? testCaseFilterFromRunsettings }; // Make sure to run the run request inside a lock as the below section is not thread-safe diff --git a/test/vstest.console.UnitTests/Processors/RunSettingsArgumentProcessorTests.cs b/test/vstest.console.UnitTests/Processors/RunSettingsArgumentProcessorTests.cs index bc3d6ef0fa..838f76fc6e 100644 --- a/test/vstest.console.UnitTests/Processors/RunSettingsArgumentProcessorTests.cs +++ b/test/vstest.console.UnitTests/Processors/RunSettingsArgumentProcessorTests.cs @@ -363,6 +363,30 @@ public void InitializeShouldNotSetInIsolataionToTrueIfEnvironmentVariablesNotSpe Assert.IsNull(this.settingsProvider.QueryRunSettingsNode(InIsolationArgumentExecutor.RunSettingsPath)); } + [TestMethod] + public void InitializeShouldUpdateTestCaseFilterIfProvided() + { + // Arrange. + var fileName = "C:\\temp\\r.runsettings"; + var filter = "TestCategory=Included"; + var settingsXml = $"{filter}"; + + var executor = new TestableRunSettingsArgumentExecutor( + CommandLineOptions.Instance, + this.settingsProvider, + settingsXml); + + // Setup mocks. + var mockFileHelper = new Mock(); + mockFileHelper.Setup(fh => fh.Exists(It.IsAny())).Returns(true); + executor.FileHelper = mockFileHelper.Object; + + // Act. + executor.Initialize(fileName); + + // Assert. + Assert.AreEqual(filter, CommandLineOptions.Instance.TestCaseFilterValue); + } #endregion #region Testable Implementations diff --git a/test/vstest.console.UnitTests/Processors/TestCaseFilterArgumentProcessorTests.cs b/test/vstest.console.UnitTests/Processors/TestCaseFilterArgumentProcessorTests.cs index d684c7109c..7bd0c9292d 100644 --- a/test/vstest.console.UnitTests/Processors/TestCaseFilterArgumentProcessorTests.cs +++ b/test/vstest.console.UnitTests/Processors/TestCaseFilterArgumentProcessorTests.cs @@ -64,13 +64,29 @@ public void ExecutorInitializeWithNullOrEmptyTestCaseFilterShouldThrowCommandLin } [TestMethod] - public void ExecutorInitializeWithValidTestCaseFilterShouldAddTestCaseFilterToCommandLineOptions() + public void ExecutorInitializeWithNullOrEmptyTestCaseFilterShouldNotThrowWhenTestFilterWasSpecifiedByPreviousStep() + { + var options = CommandLineOptions.Instance; + options.TestCaseFilterValue = "Test=FilterFromPreviousStep"; + TestCaseFilterArgumentExecutor executor = new TestCaseFilterArgumentExecutor(options); + + executor.Initialize(null); + } + + [TestMethod] + public void ExecutorInitializeWithTestCaseFilterShouldMergeWithTheValueProvidedByPreviousStep() { var options = CommandLineOptions.Instance; + var defaultValue = "Test=FilterFromPreviousStep"; + options.TestCaseFilterValue = defaultValue; + Assert.AreEqual(defaultValue, options.TestCaseFilterValue); TestCaseFilterArgumentExecutor executor = new TestCaseFilterArgumentExecutor(options); - executor.Initialize("Debug"); - Assert.AreEqual("Debug", options.TestCaseFilterValue); + var value = "Test=NewFilter"; + executor.Initialize(value); + + var expectedValue = $"({defaultValue})&({value})"; + Assert.AreEqual(expectedValue, options.TestCaseFilterValue); } [TestMethod]