From 1a9c93e8ed992b3258f7041533dbbddd0dbf46ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Sat, 14 Feb 2026 22:39:12 +0100 Subject: [PATCH] Fix TreeNodeFilter OR-pattern diagnostics --- .../Requests/TreeNodeFilter/TreeNodeFilter.cs | 31 ++++++++++++------- .../Requests/TreeNodeFilterTests.cs | 28 +++++++++++++++++ 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs index 3bc210353c..1b8f468f16 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs @@ -50,6 +50,12 @@ internal TreeNodeFilter(string filter) /// OP = '&' | '|' /// NODE_VALUE = TOKEN | TOKEN '[' FILTER_EXPR ']' /// TOKEN = string + /// + /// OR expressions are supported for a single path segment, for example: + /// /A/B/C/(Test1|Test2). + /// + /// OR expressions over full paths are not supported, for example: + /// (/A/B/C/Test1)|(/A/B/C/Test2). /// /// /// @@ -97,14 +103,14 @@ private static List ParseFilter(string filter) _ => throw ApplicationStateGuard.Unreachable(), }; - ProcessHigherPrecedenceOperators(expressionStack, operatorStack, currentOp); + ProcessHigherPrecedenceOperators(expressionStack, operatorStack, currentOp, filter); isOperatorAllowed = false; isPropAllowed = false; break; case "/": - ProcessHigherPrecedenceOperators(expressionStack, operatorStack, OperatorKind.Separator); + ProcessHigherPrecedenceOperators(expressionStack, operatorStack, OperatorKind.Separator, filter); isOperatorAllowed = false; isPropAllowed = false; @@ -147,7 +153,7 @@ private static List ParseFilter(string filter) break; } - ProcessStackOperator(topStackOperator, expressionStack, operatorStack); + ProcessStackOperator(topStackOperator, expressionStack, operatorStack, filter); } isOperatorAllowed = true; @@ -179,7 +185,7 @@ private static List ParseFilter(string filter) break; } - ProcessStackOperator(topStackOperator, expressionStack, operatorStack); + ProcessStackOperator(topStackOperator, expressionStack, operatorStack, filter); } // We should end up with an expression and a property. @@ -244,7 +250,7 @@ private static List ParseFilter(string filter) while (operatorStack.Count > 0 && operatorStack.Peek() != OperatorKind.Separator) { topStackOperator = operatorStack.Pop(); - ProcessStackOperator(topStackOperator, expressionStack, operatorStack); + ProcessStackOperator(topStackOperator, expressionStack, operatorStack, filter); } var parsedFilter = expressionStack.Reverse().ToList(); @@ -265,12 +271,13 @@ private static List ParseFilter(string filter) static void ProcessHigherPrecedenceOperators( Stack expressionStack, Stack operatorStack, - OperatorKind currentOp) + OperatorKind currentOp, + string currentFilter) { while (operatorStack.Count != 0 && operatorStack.Peek() > currentOp) { OperatorKind topStackOperator = operatorStack.Pop(); - ProcessStackOperator(topStackOperator, expressionStack, operatorStack); + ProcessStackOperator(topStackOperator, expressionStack, operatorStack, currentFilter); break; } @@ -303,7 +310,7 @@ private static void ValidateExpression(FilterExpression expr, bool isMatchAllAll } } - private static void ProcessStackOperator(OperatorKind op, Stack expr, Stack ops) + private static void ProcessStackOperator(OperatorKind op, Stack expr, Stack ops, string filter) { switch (op) { @@ -325,14 +332,14 @@ private static void ProcessStackOperator(OperatorKind op, Stack FilterOperator.And, OperatorKind.Or => FilterOperator.Or, _ => throw ApplicationStateGuard.Unreachable(), }; - expr.Push(new OperatorExpression(filter, subexprs)); + expr.Push(new OperatorExpression(filterOperator, subexprs)); break; case OperatorKind.FilterEquals: @@ -364,7 +371,9 @@ private static void ProcessStackOperator(OperatorKind op, Stack( + () => _ = new TreeNodeFilter("(/*/*/*/MyTest1)|(/*/*/*/MyTest2)")); + + Assert.IsTrue(exception.Message.Contains("/A/B/C/(X|Y)", StringComparison.Ordinal)); + } + [TestMethod] public void AndExpression() {