From 34ce88557f5549422a35502dc90a687cee772460 Mon Sep 17 00:00:00 2001 From: Niclas Ahlqvist Date: Mon, 29 Jun 2026 17:30:26 +0200 Subject: [PATCH] fix: enable dotnet test --filter for FullyQualifiedName and DisplayName Two issues prevented test filtering from working: 1. The supportedProperties array and property lookup dictionary were keyed by TestProperty.Id (e.g. "TestCase.FullyQualifiedName") but the vstest filter framework passes the Label (e.g. "FullyQualifiedName"). Added both Id and Label as dictionary keys. 2. TestObject.GetPropertyValue() returns empty string for well-known properties like DisplayName across ObjectModel assembly versions. Replaced the property bag lookup with direct TestCase property access for FullyQualifiedName and DisplayName. Trait-based filters (Tag, Subject, ClassName) were already working. --- .../Execution/SpecificationFilterProvider.cs | 57 +++++++++---------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/src/Machine.Specifications.Runner.VisualStudio/Execution/SpecificationFilterProvider.cs b/src/Machine.Specifications.Runner.VisualStudio/Execution/SpecificationFilterProvider.cs index e076449d..253a0729 100644 --- a/src/Machine.Specifications.Runner.VisualStudio/Execution/SpecificationFilterProvider.cs +++ b/src/Machine.Specifications.Runner.VisualStudio/Execution/SpecificationFilterProvider.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Machine.Specifications.Model; @@ -19,7 +19,9 @@ public class SpecificationFilterProvider : ISpecificationFilterProvider private readonly Dictionary testCaseProperties = new Dictionary(StringComparer.OrdinalIgnoreCase) { [TestCaseProperties.FullyQualifiedName.Id] = TestCaseProperties.FullyQualifiedName, - [TestCaseProperties.DisplayName.Id] = TestCaseProperties.DisplayName + [TestCaseProperties.FullyQualifiedName.Label] = TestCaseProperties.FullyQualifiedName, + [TestCaseProperties.DisplayName.Id] = TestCaseProperties.DisplayName, + [TestCaseProperties.DisplayName.Label] = TestCaseProperties.DisplayName }; private readonly Dictionary traitProperties = new Dictionary(StringComparer.OrdinalIgnoreCase) @@ -52,53 +54,50 @@ public IEnumerable FilteredTests(IEnumerable testCases, IRun return null; }); - handle?.SendMessage(TestMessageLevel.Informational, $"Machine Specifications Visual Studio Test Adapter - Filter property set '{filterExpression?.TestCaseFilterValue}'"); - if (filterExpression == null) { return testCases; } var filteredTests = testCases - .Where(x => filterExpression.MatchTestCase(x, propertyName => GetPropertyValue(propertyName, x))); + .Where(x => filterExpression.MatchTestCase(x, propertyName => ResolvePropertyValue(propertyName, x))); return filteredTests; } - private object GetPropertyValue(string propertyName, TestObject testCase) + private object ResolvePropertyValue(string propertyName, TestCase testCase) { - if (testCaseProperties.TryGetValue(propertyName, out var testProperty)) + // Read well-known properties directly from TestCase — the TestObject property + // bag may not contain them reliably across ObjectModel assembly versions + if (string.Equals(propertyName, "FullyQualifiedName", StringComparison.OrdinalIgnoreCase) || + string.Equals(propertyName, "TestCase.FullyQualifiedName", StringComparison.OrdinalIgnoreCase)) { - if (testCase.Properties.Contains(testProperty)) - { - return testCase.GetPropertyValue(testProperty); - } + return testCase.FullyQualifiedName; } - if (traitProperties.TryGetValue(propertyName, out var traitProperty)) + if (string.Equals(propertyName, "DisplayName", StringComparison.OrdinalIgnoreCase) || + string.Equals(propertyName, "TestCase.DisplayName", StringComparison.OrdinalIgnoreCase)) { - var val = TraitContains(testCase, traitProperty.Id); + return testCase.DisplayName; + } - if (val.Length == 1) - { - return val[0]; - } + // Trait-based properties (Tag, Subject, ClassName) + var traits = testCase.Traits? + .Where(x => string.Equals(x.Name, propertyName, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Value) + .ToArray(); - if (val.Length > 1) - { - return val; - } + if (traits?.Length == 1) + { + return traits[0]; } - return null; - } + if (traits?.Length > 1) + { + return traits; + } - private static string[] TraitContains(TestObject testCase, string traitName) - { - return testCase?.Traits? - .Where(x => x.Name == traitName) - .Select(x => x.Value) - .ToArray(); + return null; } } }