diff --git a/src/xunit.runner.visualstudio/Sinks/VsDiscoverySink.cs b/src/xunit.runner.visualstudio/Sinks/VsDiscoverySink.cs index 3f597142..209fa88b 100644 --- a/src/xunit.runner.visualstudio/Sinks/VsDiscoverySink.cs +++ b/src/xunit.runner.visualstudio/Sinks/VsDiscoverySink.cs @@ -40,6 +40,7 @@ public class VsDiscoverySink : IMessageSinkWithTypes, IVsDiscoverySink, IDisposa readonly string source; readonly List testCaseBatch = new List(); readonly TestPlatformContext testPlatformContext; + readonly TestCaseFilter testCaseFilter; public VsDiscoverySink(string source, ITestFrameworkDiscoverer discoverer, @@ -47,6 +48,7 @@ public VsDiscoverySink(string source, ITestCaseDiscoverySink discoverySink, ITestFrameworkDiscoveryOptions discoveryOptions, TestPlatformContext testPlatformContext, + TestCaseFilter testCaseFilter, Func cancelThunk) { this.source = source; @@ -55,6 +57,7 @@ public VsDiscoverySink(string source, this.discoverySink = discoverySink; this.discoveryOptions = discoveryOptions; this.testPlatformContext = testPlatformContext; + this.testCaseFilter = testCaseFilter; this.cancelThunk = cancelThunk; descriptorProvider = (discoverer as ITestCaseDescriptorProvider) ?? new DefaultTestCaseDescriptorProvider(discoverer); @@ -205,7 +208,7 @@ private void SendExistingTestCases() foreach (var descriptor in descriptors) { var vsTestCase = CreateVsTestCase(source, descriptor, logger, testPlatformContext); - if (vsTestCase != null) + if (vsTestCase != null && testCaseFilter.MatchTestCase(vsTestCase)) { if (discoveryOptions.GetInternalDiagnosticMessagesOrDefault()) logger.LogWithSource(source, "Discovered test case '{0}' (ID = '{1}', VS FQN = '{2}')", descriptor.DisplayName, descriptor.UniqueID, vsTestCase.FullyQualifiedName); diff --git a/src/xunit.runner.visualstudio/Utility/TestCaseFilter.cs b/src/xunit.runner.visualstudio/Utility/TestCaseFilter.cs index c2fe8753..2268f3d9 100644 --- a/src/xunit.runner.visualstudio/Utility/TestCaseFilter.cs +++ b/src/xunit.runner.visualstudio/Utility/TestCaseFilter.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; @@ -14,8 +15,9 @@ public class TestCaseFilter readonly HashSet knownTraits; List supportedPropertyNames; - ITestCaseFilterExpression filterExpression; + readonly ITestCaseFilterExpression filterExpression; readonly bool successfullyGotFilter; + readonly bool isDiscovery; public TestCaseFilter(IRunContext runContext, LoggerHelper logger, string assemblyFileName, HashSet knownTraits) { @@ -25,6 +27,16 @@ public TestCaseFilter(IRunContext runContext, LoggerHelper logger, string assemb successfullyGotFilter = GetTestCaseFilterExpression(runContext, logger, assemblyFileName, out filterExpression); } + public TestCaseFilter(IDiscoveryContext discoveryContext, LoggerHelper logger) + { + // Traits are not known at discovery time because we load them from tests + isDiscovery = true; + knownTraits = new HashSet(); + supportedPropertyNames = GetSupportedPropertyNames(); + + successfullyGotFilter = GetTestCaseFilterExpressionFromDiscoveryContext(discoveryContext, logger, out filterExpression); + } + public bool MatchTestCase(TestCase testCase) { if (!successfullyGotFilter) @@ -44,7 +56,7 @@ public bool MatchTestCase(TestCase testCase) public object PropertyProvider(TestCase testCase, string name) { // Traits filtering - if (knownTraits.Contains(name)) + if (isDiscovery || knownTraits.Contains(name)) { var result = new List(); @@ -83,6 +95,49 @@ bool GetTestCaseFilterExpression(IRunContext runContext, LoggerHelper logger, st } } + bool GetTestCaseFilterExpressionFromDiscoveryContext(IDiscoveryContext discoveryContext, LoggerHelper logger, out ITestCaseFilterExpression filter) + { + filter = null; + + if (discoveryContext is IRunContext runContext) + { + try + { + filter = runContext.GetTestCaseFilter(supportedPropertyNames, null); + return true; + } + catch (TestPlatformException e) + { + logger.LogWarning("Exception filtering tests: {0}", e.Message); + return false; + } + } + else + { + try + { +#if WINDOWS_UAP + // on UWP .Net Native Tool Chain we are not able to run methods via invoke, act like no filter was specified for UWP +#else + // GetTestCaseFilter is present on DiscoveryContext but not in IDiscoveryContext interface + var method = discoveryContext.GetType().GetRuntimeMethod("GetTestCaseFilter", new[] { typeof(IEnumerable), typeof(Func) }); + filter = (ITestCaseFilterExpression)method?.Invoke(discoveryContext, new object[] { supportedPropertyNames, null }); +#endif + return true; + } + catch (TargetInvocationException e) + { + if (e?.InnerException is TestPlatformException ex) + { + logger.LogWarning("Exception filtering tests: {0}", ex.InnerException?.Message); + return false; + } + + throw e.InnerException; + } + } + } + List GetSupportedPropertyNames() { // Returns the set of well-known property names usually used with the Test Plugins (Used Test Traits + DisplayName + FullyQualifiedName) diff --git a/src/xunit.runner.visualstudio/VsTestRunner.cs b/src/xunit.runner.visualstudio/VsTestRunner.cs index fb8b6450..e366bc09 100644 --- a/src/xunit.runner.visualstudio/VsTestRunner.cs +++ b/src/xunit.runner.visualstudio/VsTestRunner.cs @@ -101,9 +101,10 @@ void ITestDiscoverer.DiscoverTests(IEnumerable sources, IDiscoveryContex RequireXunitTestProperty = true }; + var testCaseFilter = new TestCaseFilter(discoveryContext, loggerHelper); DiscoverTests( sources, loggerHelper, testPlatformContext, runSettings, - (source, discoverer, discoveryOptions) => new VsDiscoverySink(source, discoverer, loggerHelper, discoverySink, discoveryOptions, testPlatformContext, () => cancelled) + (source, discoverer, discoveryOptions) => new VsDiscoverySink(source, discoverer, loggerHelper, discoverySink, discoveryOptions, testPlatformContext, testCaseFilter, () => cancelled) ); }