diff --git a/src/Adapter/MSTest.CoreAdapter/Discovery/UnitTestDiscoverer.cs b/src/Adapter/MSTest.CoreAdapter/Discovery/UnitTestDiscoverer.cs index fb1c62ec18..1291077ddb 100644 --- a/src/Adapter/MSTest.CoreAdapter/Discovery/UnitTestDiscoverer.cs +++ b/src/Adapter/MSTest.CoreAdapter/Discovery/UnitTestDiscoverer.cs @@ -7,6 +7,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; @@ -17,24 +18,30 @@ internal class UnitTestDiscoverer internal UnitTestDiscoverer() { this.assemblyEnumeratorWrapper = new AssemblyEnumeratorWrapper(); + this.TestMethodFilter = new TestMethodFilter(); } + /// + /// Gets or sets method filter for filtering tests + /// + private TestMethodFilter TestMethodFilter { get; set; } + /// /// Discovers the tests available from the provided sources. /// /// The sources. /// The logger. /// The discovery Sink. - /// The run settings. + /// The discovery context. internal void DiscoverTests( IEnumerable sources, IMessageLogger logger, ITestCaseDiscoverySink discoverySink, - IRunSettings runSettings) + IDiscoveryContext discoveryContext) { foreach (var source in sources) { - this.DiscoverTestsInSource(source, logger, discoverySink, runSettings); + this.DiscoverTestsInSource(source, logger, discoverySink, discoveryContext); } } @@ -44,16 +51,16 @@ internal void DiscoverTests( /// The source. /// The logger. /// The discovery Sink. - /// The run settings. + /// The discovery context. internal virtual void DiscoverTestsInSource( string source, IMessageLogger logger, ITestCaseDiscoverySink discoverySink, - IRunSettings runSettings) + IDiscoveryContext discoveryContext) { ICollection warnings; - var testElements = this.assemblyEnumeratorWrapper.GetTests(source, runSettings, out warnings); + var testElements = this.assemblyEnumeratorWrapper.GetTests(source, discoveryContext?.RunSettings, out warnings); // log the warnings foreach (var warning in warnings) @@ -76,10 +83,10 @@ internal virtual void DiscoverTestsInSource( testElements.Count, source); - this.SendTestCases(source, testElements, discoverySink); + this.SendTestCases(source, testElements, discoverySink, discoveryContext, logger); } - internal void SendTestCases(string source, IEnumerable testElements, ITestCaseDiscoverySink discoverySink) + internal void SendTestCases(string source, IEnumerable testElements, ITestCaseDiscoverySink discoverySink, IDiscoveryContext discoveryContext, IMessageLogger logger) { var shouldCollectSourceInformation = MSTestSettings.RunConfigurationSettings.CollectSourceInformation; @@ -91,11 +98,25 @@ internal void SendTestCases(string source, IEnumerable testElem navigationSessions.Add(source, PlatformServiceProvider.Instance.FileOperations.CreateNavigationSession(source)); } + // Get filter expression and skip discovery in case filter expression has parsing error. + bool filterHasError = false; + ITestCaseFilterExpression filterExpression = this.TestMethodFilter.GetFilterExpression(discoveryContext, logger, out filterHasError); + if (filterHasError) + { + return; + } + foreach (var testElement in testElements) { - object testNavigationSession; var testCase = testElement.ToTestCase(); + // Filter tests based on test case filters + if (filterExpression != null && filterExpression.MatchTestCase(testCase, (p) => this.TestMethodFilter.PropertyValueProvider(testCase, p)) == false) + { + continue; + } + + object testNavigationSession; if (shouldCollectSourceInformation) { string testSource = testElement.TestMethod.DeclaringAssemblyName ?? source; diff --git a/src/Adapter/MSTest.CoreAdapter/Execution/TestExecutionManager.cs b/src/Adapter/MSTest.CoreAdapter/Execution/TestExecutionManager.cs index c3b297f8c9..74db14ba75 100644 --- a/src/Adapter/MSTest.CoreAdapter/Execution/TestExecutionManager.cs +++ b/src/Adapter/MSTest.CoreAdapter/Execution/TestExecutionManager.cs @@ -98,7 +98,7 @@ public void RunTests(IEnumerable sources, IRunContext runContext, IFrame var logger = (IMessageLogger)frameworkHandle; // discover the tests - this.GetUnitTestDiscoverer().DiscoverTestsInSource(source, logger, discoverySink, runContext?.RunSettings); + this.GetUnitTestDiscoverer().DiscoverTestsInSource(source, logger, discoverySink, runContext); tests.AddRange(discoverySink.Tests); // Clear discoverSinksTests so that it just stores test for one source at one point of time diff --git a/src/Adapter/MSTest.CoreAdapter/MSTest.CoreAdapter.csproj b/src/Adapter/MSTest.CoreAdapter/MSTest.CoreAdapter.csproj index 03689015d1..addb976c88 100644 --- a/src/Adapter/MSTest.CoreAdapter/MSTest.CoreAdapter.csproj +++ b/src/Adapter/MSTest.CoreAdapter/MSTest.CoreAdapter.csproj @@ -50,7 +50,7 @@ - + diff --git a/src/Adapter/MSTest.CoreAdapter/MSTestDiscoverer.cs b/src/Adapter/MSTest.CoreAdapter/MSTestDiscoverer.cs index 04c3e90772..f72940448c 100644 --- a/src/Adapter/MSTest.CoreAdapter/MSTestDiscoverer.cs +++ b/src/Adapter/MSTest.CoreAdapter/MSTestDiscoverer.cs @@ -54,7 +54,7 @@ public void DiscoverTests( return; } - new UnitTestDiscoverer().DiscoverTests(sources, logger, discoverySink, discoveryContext?.RunSettings); + new UnitTestDiscoverer().DiscoverTests(sources, logger, discoverySink, discoveryContext); } /// diff --git a/src/Adapter/MSTest.CoreAdapter/Execution/TestMethodFilter.cs b/src/Adapter/MSTest.CoreAdapter/TestMethodFilter.cs similarity index 66% rename from src/Adapter/MSTest.CoreAdapter/Execution/TestMethodFilter.cs rename to src/Adapter/MSTest.CoreAdapter/TestMethodFilter.cs index 062af5af36..118f645648 100644 --- a/src/Adapter/MSTest.CoreAdapter/Execution/TestMethodFilter.cs +++ b/src/Adapter/MSTest.CoreAdapter/TestMethodFilter.cs @@ -1,16 +1,16 @@ // 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.MSTest.TestAdapter.Execution +namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter { using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; + using System.Reflection; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; - using Constants = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Constants; internal class TestMethodFilter { @@ -34,24 +34,24 @@ internal TestMethodFilter() /// /// Returns ITestCaseFilterExpression for TestProperties supported by adapter. /// - /// The current context of the run. - /// Handler to report test messages/start/end and results. + /// The current context of the run. + /// Handler to report test messages/start/end and results. /// Indicates that the filter is unsupported/has an error. /// A filter expression. - internal ITestCaseFilterExpression GetFilterExpression(IRunContext runContext, IMessageLogger testExecutionRecorder, out bool filterHasError) + internal ITestCaseFilterExpression GetFilterExpression(IDiscoveryContext context, IMessageLogger logger, out bool filterHasError) { filterHasError = false; ITestCaseFilterExpression filter = null; - if (runContext != null) + if (context != null) { try { - filter = runContext.GetTestCaseFilter(this.supportedProperties.Keys, this.PropertyProvider); + filter = (context is IRunContext) ? this.GetTestCaseFilterFromRunContext(context as IRunContext) : this.GetTestCaseFilterFromDiscoveryContext(context); } catch (TestPlatformFormatException ex) { filterHasError = true; - testExecutionRecorder.SendMessage(TestMessageLevel.Error, ex.Message); + logger.SendMessage(TestMessageLevel.Error, ex.Message); } } @@ -95,5 +95,34 @@ internal object PropertyValueProvider(TestCase currentTest, string propertyName) return null; } + + /// + /// Gets filter expression from run context. + /// + /// Run context + /// Filter expression. + private ITestCaseFilterExpression GetTestCaseFilterFromRunContext(IRunContext context) + { + return context.GetTestCaseFilter(this.supportedProperties.Keys, this.PropertyProvider); + } + + /// + /// Gets filter expression from discovery context. + /// + /// Discovery context + /// Filter expression. + private ITestCaseFilterExpression GetTestCaseFilterFromDiscoveryContext(IDiscoveryContext context) + { + try + { + // GetTestCaseFilter is present in DiscoveryContext but not in IDiscoveryContext interface. + MethodInfo methodGetTestCaseFilter = context.GetType().GetRuntimeMethod("GetTestCaseFilter", new[] { typeof(IEnumerable), typeof(Func) }); + return (ITestCaseFilterExpression)methodGetTestCaseFilter?.Invoke(context, new object[] { this.supportedProperties.Keys, (Func)this.PropertyProvider }); + } + catch (TargetInvocationException ex) + { + throw ex.InnerException; + } + } } } \ No newline at end of file diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Discovery/UnitTestDiscovererTests.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Discovery/UnitTestDiscovererTests.cs index 39fd8358c5..eae996e215 100644 --- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Discovery/UnitTestDiscovererTests.cs +++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Discovery/UnitTestDiscovererTests.cs @@ -34,6 +34,7 @@ public class UnitTestDiscovererTests private Mock mockMessageLogger; private Mock mockTestCaseDiscoverySink; private Mock mockRunSettings; + private Mock mockDiscoveryContext; private UnitTestElement test; private List testElements; @@ -47,6 +48,9 @@ public void TestInit() this.mockTestCaseDiscoverySink = new Mock(); this.mockRunSettings = new Mock(); + this.mockDiscoveryContext = new Mock(); + this.mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(this.mockRunSettings.Object); + this.test = new UnitTestElement(new TestMethod("M", "C", "A", false)); this.testElements = new List { this.test }; PlatformServiceProvider.Instance = this.testablePlatformServiceProvider; @@ -75,7 +79,7 @@ public void DiscoverTestsShouldDiscoverForAllSources() .Returns(false); } - this.unitTestDiscoverer.DiscoverTests(sources, this.mockMessageLogger.Object, this.mockTestCaseDiscoverySink.Object, this.mockRunSettings.Object); + this.unitTestDiscoverer.DiscoverTests(sources, this.mockMessageLogger.Object, this.mockTestCaseDiscoverySink.Object, this.mockDiscoveryContext.Object); // Assert. this.mockMessageLogger.Verify(lm => lm.SendMessage(TestMessageLevel.Warning, It.IsAny()), Times.Exactly(2)); @@ -90,7 +94,7 @@ public void DiscoverTestsInSourceShouldSendBackAllWarnings() this.testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.DoesFileExist(Source)) .Returns(false); - this.unitTestDiscoverer.DiscoverTestsInSource(Source, this.mockMessageLogger.Object, this.mockTestCaseDiscoverySink.Object, this.mockRunSettings.Object); + this.unitTestDiscoverer.DiscoverTestsInSource(Source, this.mockMessageLogger.Object, this.mockTestCaseDiscoverySink.Object, this.mockDiscoveryContext.Object); // Assert. this.mockMessageLogger.Verify(lm => lm.SendMessage(TestMessageLevel.Warning, It.IsAny()), Times.Once); @@ -114,7 +118,7 @@ public void DiscoverTestsInSourceShouldSendBackTestCasesDiscovered() ih => ih.CreateInstanceForType(It.IsAny(), It.IsAny())) .Returns(new AssemblyEnumerator()); - this.unitTestDiscoverer.DiscoverTestsInSource(Source, this.mockMessageLogger.Object, this.mockTestCaseDiscoverySink.Object, this.mockRunSettings.Object); + this.unitTestDiscoverer.DiscoverTestsInSource(Source, this.mockMessageLogger.Object, this.mockTestCaseDiscoverySink.Object, this.mockDiscoveryContext.Object); // Assert. this.mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.IsAny()), Times.AtLeastOnce); @@ -128,7 +132,7 @@ public void SendTestCasesShouldNotSendAnyTestCasesIfThereAreNoTestElements() .Returns((object)null); // There is a null check for testElements in the code flow before this function call. So not adding a unit test for that. - this.unitTestDiscoverer.SendTestCases(Source, new List { }, this.mockTestCaseDiscoverySink.Object); + this.unitTestDiscoverer.SendTestCases(Source, new List { }, this.mockTestCaseDiscoverySink.Object, this.mockDiscoveryContext.Object, this.mockMessageLogger.Object); // Assert. this.mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.IsAny()), Times.Never); @@ -145,7 +149,7 @@ public void SendTestCasesShouldSendAllTestCaseData() var test2 = new UnitTestElement(new TestMethod("M2", "C", "A", false)); var testElements = new List { test1, test2 }; - this.unitTestDiscoverer.SendTestCases(Source, testElements, this.mockTestCaseDiscoverySink.Object); + this.unitTestDiscoverer.SendTestCases(Source, testElements, this.mockTestCaseDiscoverySink.Object, this.mockDiscoveryContext.Object, this.mockMessageLogger.Object); // Assert. this.mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M1")), Times.Once); @@ -171,12 +175,9 @@ public void SendTestCasesShouldSendTestCasesWithoutNavigationDataWhenCollectSour this.SetupNavigation(Source, this.test, this.test.TestMethod.FullClassName, this.test.TestMethod.Name); this.mockRunSettings.Setup(rs => rs.SettingsXml).Returns(settingsXml); - Mock mockDiscoveryContext = new Mock(); - mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(this.mockRunSettings.Object); - // Act - MSTestSettings.PopulateSettings(mockDiscoveryContext.Object); - this.unitTestDiscoverer.SendTestCases(Source, this.testElements, this.mockTestCaseDiscoverySink.Object); + MSTestSettings.PopulateSettings(this.mockDiscoveryContext.Object); + this.unitTestDiscoverer.SendTestCases(Source, this.testElements, this.mockTestCaseDiscoverySink.Object, this.mockDiscoveryContext.Object, this.mockMessageLogger.Object); // Assert this.mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.LineNumber == -1)), Times.Once); @@ -193,7 +194,7 @@ public void SendTestCasesShouldSendTestCasesWithNavigationData() this.SetupNavigation(Source, this.test, this.test.TestMethod.FullClassName, this.test.TestMethod.Name); // Act - this.unitTestDiscoverer.SendTestCases(Source, this.testElements, this.mockTestCaseDiscoverySink.Object); + this.unitTestDiscoverer.SendTestCases(Source, this.testElements, this.mockTestCaseDiscoverySink.Object, this.mockDiscoveryContext.Object, this.mockMessageLogger.Object); // Assert this.VerifyNavigationDataIsPresent(); @@ -211,7 +212,7 @@ public void SendTestCasesShouldSendTestCasesWithNavigationDataWhenDeclaredClassF this.SetupNavigation(Source, this.test, this.test.TestMethod.DeclaringClassFullName, this.test.TestMethod.Name); // Act - this.unitTestDiscoverer.SendTestCases(Source, this.testElements, this.mockTestCaseDiscoverySink.Object); + this.unitTestDiscoverer.SendTestCases(Source, this.testElements, this.mockTestCaseDiscoverySink.Object, this.mockDiscoveryContext.Object, this.mockMessageLogger.Object); // Assert this.VerifyNavigationDataIsPresent(); @@ -234,7 +235,7 @@ public void SendTestCasesShouldUseNaigationSessionForDeclaredAssemblyName() this.SetupNavigation(Source, this.test, this.test.TestMethod.DeclaringClassFullName, this.test.TestMethod.Name); // Act - this.unitTestDiscoverer.SendTestCases(Source, this.testElements, this.mockTestCaseDiscoverySink.Object); + this.unitTestDiscoverer.SendTestCases(Source, this.testElements, this.mockTestCaseDiscoverySink.Object, this.mockDiscoveryContext.Object, this.mockMessageLogger.Object); // Assert this.testablePlatformServiceProvider.MockFileOperations.Verify(fo => fo.CreateNavigationSession("DummyAssembly2.dll"), Times.Once); @@ -252,12 +253,92 @@ public void SendTestCasesShouldSendTestCasesWithNavigationDataForAsyncMethods() this.SetupNavigation(Source, this.test, this.test.AsyncTypeName, "MoveNext"); // Act - this.unitTestDiscoverer.SendTestCases(Source, this.testElements, this.mockTestCaseDiscoverySink.Object); + this.unitTestDiscoverer.SendTestCases(Source, this.testElements, this.mockTestCaseDiscoverySink.Object, this.mockDiscoveryContext.Object, this.mockMessageLogger.Object); // Assert this.VerifyNavigationDataIsPresent(); } + /// + /// Send test cases should send filtered test cases only. + /// + [TestMethodV1] + public void SendTestCasesShouldSendFilteredTestCasesIfValidFilterExpression() + { + TestableDiscoveryContextWithGetTestCaseFilter discoveryContext = new TestableDiscoveryContextWithGetTestCaseFilter(() => new TestableTestCaseFilterExpression((p) => (p.DisplayName == "M1"))); + + var test1 = new UnitTestElement(new TestMethod("M1", "C", "A", false)); + var test2 = new UnitTestElement(new TestMethod("M2", "C", "A", false)); + var testElements = new List { test1, test2 }; + + // Action + this.unitTestDiscoverer.SendTestCases(Source, testElements, this.mockTestCaseDiscoverySink.Object, discoveryContext, this.mockMessageLogger.Object); + + // Assert. + this.mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M1")), Times.Once); + this.mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M2")), Times.Never); + } + + /// + /// Send test cases should send all test cases if filter expression is null. + /// + [TestMethodV1] + public void SendTestCasesShouldSendAllTestCasesIfNullFilterExpression() + { + TestableDiscoveryContextWithGetTestCaseFilter discoveryContext = new TestableDiscoveryContextWithGetTestCaseFilter(() => null); + + var test1 = new UnitTestElement(new TestMethod("M1", "C", "A", false)); + var test2 = new UnitTestElement(new TestMethod("M2", "C", "A", false)); + var testElements = new List { test1, test2 }; + + // Action + this.unitTestDiscoverer.SendTestCases(Source, testElements, this.mockTestCaseDiscoverySink.Object, discoveryContext, this.mockMessageLogger.Object); + + // Assert. + this.mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M1")), Times.Once); + this.mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M2")), Times.Once); + } + + /// + /// Send test cases should send all test cases if GetTestCaseFilter method is not present in DiscoveryContext. + /// + [TestMethodV1] + public void SendTestCasesShouldSendAllTestCasesIfGetTestCaseFilterNotPresent() + { + TestableDiscoveryContextWithoutGetTestCaseFilter discoveryContext = new TestableDiscoveryContextWithoutGetTestCaseFilter(); + + var test1 = new UnitTestElement(new TestMethod("M1", "C", "A", false)); + var test2 = new UnitTestElement(new TestMethod("M2", "C", "A", false)); + var testElements = new List { test1, test2 }; + + // Action + this.unitTestDiscoverer.SendTestCases(Source, testElements, this.mockTestCaseDiscoverySink.Object, discoveryContext, this.mockMessageLogger.Object); + + // Assert. + this.mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M1")), Times.Once); + this.mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M2")), Times.Once); + } + + /// + /// Send test cases should not send any test cases if filter parsing error. + /// + [TestMethodV1] + public void SendTestCasesShouldNotSendAnyTestCasesIfFilterError() + { + TestableDiscoveryContextWithGetTestCaseFilter discoveryContext = new TestableDiscoveryContextWithGetTestCaseFilter(() => { throw new TestPlatformFormatException("DummyException"); }); + + var test1 = new UnitTestElement(new TestMethod("M1", "C", "A", false)); + var test2 = new UnitTestElement(new TestMethod("M2", "C", "A", false)); + var testElements = new List { test1, test2 }; + + // Action + this.unitTestDiscoverer.SendTestCases(Source, testElements, this.mockTestCaseDiscoverySink.Object, discoveryContext, this.mockMessageLogger.Object); + + // Assert. + this.mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M1")), Times.Never); + this.mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M2")), Times.Never); + } + private void SetupNavigation(string source, UnitTestElement test, string className, string methodName) { var testNavigationData = new DummyNavigationData("DummyFileName.cs", 1, 10); @@ -316,7 +397,7 @@ internal override void DiscoverTestsInSource( string source, IMessageLogger logger, ITestCaseDiscoverySink discoverySink, - IRunSettings runSettings) + IDiscoveryContext discoveryContext) { var testCase1 = new TestCase("A", new System.Uri("executor://testExecutor"), source); var testCase2 = new TestCase("B", new System.Uri("executor://testExecutor"), source); @@ -324,4 +405,45 @@ internal override void DiscoverTestsInSource( discoverySink.SendTestCase(testCase2); } } + + internal class TestableDiscoveryContextWithGetTestCaseFilter : IDiscoveryContext + { + private readonly Func getFilter; + + public TestableDiscoveryContextWithGetTestCaseFilter(Func getFilter) + { + this.getFilter = getFilter; + } + + public IRunSettings RunSettings { get; } + + public ITestCaseFilterExpression GetTestCaseFilter( + IEnumerable supportedProperties, + Func propertyProvider) + { + return this.getFilter(); + } + } + + internal class TestableDiscoveryContextWithoutGetTestCaseFilter : IDiscoveryContext + { + public IRunSettings RunSettings { get; } + } + + internal class TestableTestCaseFilterExpression : ITestCaseFilterExpression + { + private readonly Func matchTest; + + public TestableTestCaseFilterExpression(Func matchTestCase) + { + this.matchTest = matchTestCase; + } + + public string TestCaseFilterValue { get; } + + public bool MatchTestCase(TestCase testCase, Func propertyValueProvider) + { + return this.matchTest(testCase); + } + } } \ No newline at end of file diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/TestMethodFilterTests.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/TestMethodFilterTests.cs index d4fcf4bcc8..a53d5ddc45 100644 --- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/TestMethodFilterTests.cs +++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/TestMethodFilterTests.cs @@ -11,7 +11,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution using System.Collections.Generic; using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; + using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; @@ -132,6 +132,37 @@ public void GetFilterExpressionForValidRunContextReturnsValidTestCaseFilterExpre Assert.AreEqual(false, filterHasError); } + /// + /// GetFilterExpression should return valid test case filter expression if DiscoveryContext has GetTestCaseFilter. + /// + [TestMethod] + public void GetFilterExpressionForDiscoveryContextWithGetTestCaseFilterReturnsValidTestCaseFilterExpression() + { + TestableTestExecutionRecorder recorder = new TestableTestExecutionRecorder(); + var dummyFilterExpression = new TestableTestCaseFilterExpression(); + TestableDiscoveryContextWithGetTestCaseFilter discoveryContext = new TestableDiscoveryContextWithGetTestCaseFilter(() => dummyFilterExpression); + bool filterHasError; + var filterExpression = this.TestMethodFilter.GetFilterExpression(discoveryContext, recorder, out filterHasError); + + Assert.AreEqual(dummyFilterExpression, filterExpression); + Assert.AreEqual(false, filterHasError); + } + + /// + /// GetFilterExpression shoould return null test case filter expression in case DiscoveryContext doesnt have GetTestCaseFilter. + /// + [TestMethod] + public void GetFilterExpressionForDiscoveryContextWithoutGetTestCaseFilterReturnsNullTestCaseFilterExpression() + { + TestableTestExecutionRecorder recorder = new TestableTestExecutionRecorder(); + TestableDiscoveryContextWithoutGetTestCaseFilter discoveryContext = new TestableDiscoveryContextWithoutGetTestCaseFilter(); + bool filterHasError; + var filterExpression = this.TestMethodFilter.GetFilterExpression(discoveryContext, recorder, out filterHasError); + + Assert.AreEqual(null, filterExpression); + Assert.AreEqual(false, filterHasError); + } + [TestMethod] public void GetFilterExpressionForRunContextGetTestCaseFilterThrowingExceptionReturnsNullWithFilterHasErrorTrue() { @@ -146,6 +177,23 @@ public void GetFilterExpressionForRunContextGetTestCaseFilterThrowingExceptionRe Assert.AreEqual(TestMessageLevel.Error, recorder.TestMessageLevel); } + /// + /// GetFilterExpression should return null filter expression and filterHasError as true in case GetTestCaseFilter throws exception. + /// + [TestMethod] + public void GetFilterExpressionForDiscoveryContextWithGetTestCaseFilterThrowingExceptionReturnsNullWithFilterHasErrorTrue() + { + TestableTestExecutionRecorder recorder = new TestableTestExecutionRecorder(); + TestableDiscoveryContextWithGetTestCaseFilter discoveryContext = new TestableDiscoveryContextWithGetTestCaseFilter(() => { throw new TestPlatformFormatException("DummyException"); }); + bool filterHasError; + var filterExpression = this.TestMethodFilter.GetFilterExpression(discoveryContext, recorder, out filterHasError); + + Assert.AreEqual(null, filterExpression); + Assert.AreEqual(true, filterHasError); + Assert.AreEqual("DummyException", recorder.Message); + Assert.AreEqual(TestMessageLevel.Error, recorder.TestMessageLevel); + } + [UTF.TestClass] internal class DummyTestClassWithTestMethods { @@ -201,6 +249,30 @@ public ITestCaseFilterExpression GetTestCaseFilter( } } + private class TestableDiscoveryContextWithGetTestCaseFilter : IDiscoveryContext + { + private readonly Func getFilter; + + public TestableDiscoveryContextWithGetTestCaseFilter(Func getFilter) + { + this.getFilter = getFilter; + } + + public IRunSettings RunSettings { get; } + + public ITestCaseFilterExpression GetTestCaseFilter( + IEnumerable supportedProperties, + Func propertyProvider) + { + return this.getFilter(); + } + } + + private class TestableDiscoveryContextWithoutGetTestCaseFilter : IDiscoveryContext + { + public IRunSettings RunSettings { get; } + } + private class TestableTestCaseFilterExpression : ITestCaseFilterExpression { public string TestCaseFilterValue { get; }