Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/Changelog-Platform.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)

## [Unreleased]

### Fixed

* Clarify TreeNodeFilter OR-pattern diagnostics for unsupported parenthesized full-path expressions by @Evangelink in [#7415](https://github.com/microsoft/testfx/pull/7415)

## <a name="2.2.3" />[2.2.3] - 2026-05-14

See full log [of v4.2.2...v4.2.3](https://github.com/microsoft/testfx/compare/v4.2.2...v4.2.3)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ internal TreeNodeFilter(string filter)
/// OP = '&amp;' | '|'
/// NODE_VALUE = TOKEN | TOKEN '[' FILTER_EXPR ']'
/// TOKEN = string
///
/// OR expressions are supported for a single path segment, for example:
/// <c>/A/B/C/(Test1|Test2)</c>.
///
/// OR expressions over full paths are not supported, for example:
/// <c>(/A/B/C/Test1)|(/A/B/C/Test2)</c>.
Comment on lines +62 to +66
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the missing changelog entry in docs/Changelog-Platform.md in bc8c8cf so the PR description matches the branch again. Re-ran .\build.cmd successfully with 0 warnings and 0 errors.

/// </code>
/// </remarks>
/// <exception cref="InvalidOperationException">
Expand Down Expand Up @@ -105,14 +111,14 @@ private static List<FilterExpression> 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;
Expand Down Expand Up @@ -155,7 +161,7 @@ private static List<FilterExpression> ParseFilter(string filter)
break;
}

ProcessStackOperator(topStackOperator, expressionStack, operatorStack);
ProcessStackOperator(topStackOperator, expressionStack, operatorStack, filter);
}

isOperatorAllowed = true;
Expand Down Expand Up @@ -187,7 +193,7 @@ private static List<FilterExpression> ParseFilter(string filter)
break;
}

ProcessStackOperator(topStackOperator, expressionStack, operatorStack);
ProcessStackOperator(topStackOperator, expressionStack, operatorStack, filter);
}

// We should end up with an expression and a property.
Expand Down Expand Up @@ -252,7 +258,7 @@ private static List<FilterExpression> 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();
Expand All @@ -273,12 +279,13 @@ private static List<FilterExpression> ParseFilter(string filter)
static void ProcessHigherPrecedenceOperators(
Stack<FilterExpression> expressionStack,
Stack<OperatorKind> 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;
}

Expand Down Expand Up @@ -311,7 +318,7 @@ private static void ValidateExpression(FilterExpression expr, bool isMatchAllAll
}
}

private static void ProcessStackOperator(OperatorKind op, Stack<FilterExpression> expr, Stack<OperatorKind> ops)
private static void ProcessStackOperator(OperatorKind op, Stack<FilterExpression> expr, Stack<OperatorKind> ops, string filter)
{
switch (op)
{
Expand All @@ -333,14 +340,14 @@ private static void ProcessStackOperator(OperatorKind op, Stack<FilterExpression
subexprs.Add(expr.Pop());
}

FilterOperator filter = op switch
FilterOperator filterOperator = op switch
{
OperatorKind.And => 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:
Expand Down Expand Up @@ -372,7 +379,8 @@ private static void ProcessStackOperator(OperatorKind op, Stack<FilterExpression
// Note: Handling of other operations in valid scenarios should be handled by the caller.
// Reaching this code for instance means that we're trying to process / operator
// in the middle of a ( expression ).
throw new InvalidOperationException(PlatformResources.TreeNodeFilterUnexpectedSlashOperatorErrorMessage);
throw new InvalidOperationException(
string.Format(CultureInfo.InvariantCulture, PlatformResources.TreeNodeFilterUnexpectedSlashOperatorInPathSegmentErrorMessage, filter));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,10 @@
<data name="TreeNodeFilterUnexpectedSlashOperatorErrorMessage" xml:space="preserve">
<value>Filter contains an unexpected '/' operator inside a parenthesized expression</value>
</data>
<data name="TreeNodeFilterUnexpectedSlashOperatorInPathSegmentErrorMessage" xml:space="preserve">
<value>Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</value>
<comment>{0} is the filter expression string. Diagnostic thrown by the TreeNodeFilter parser when a '/' is encountered inside a parenthesized alternative such as '/A/(B/C)'.</comment>
</data>
<data name="TEstExecutionFilterFactoryFactoryAlreadySetErrorMessage" xml:space="preserve">
<value>An 'ITestExecutionFilterFactory' factory is already set</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,11 @@ Platné hodnoty jsou All, Failed, None. Výchozí hodnota je All.</target>
<target state="translated">Filtr uvnitř výrazu v závorkách obsahuje neočekávaný operátor /.</target>
<note />
</trans-unit>
<trans-unit id="TreeNodeFilterUnexpectedSlashOperatorInPathSegmentErrorMessage">
<source>Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</source>
<target state="new">Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</target>
<note>{0} is the filter expression string. Diagnostic thrown by the TreeNodeFilter parser when a '/' is encountered inside a parenthesized alternative such as '/A/(B/C)'.</note>
</trans-unit>
<trans-unit id="UnexpectedCallTelemetryIsDisabledErrorMessage">
<source>Unexpected telemetry call, the telemetry is disabled.</source>
<target state="translated">Neočekávané volání telemetrie, telemetrie je zakázaná.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,11 @@ Gültige Werte sind „Alle“, „Fehlgeschlagen“ und „Keine“. Der Standa
<target state="translated">Der Filter enthält einen unerwarteten "/"-Operator in einem in Klammern gesetzten Ausdruck</target>
<note />
</trans-unit>
<trans-unit id="TreeNodeFilterUnexpectedSlashOperatorInPathSegmentErrorMessage">
<source>Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</source>
<target state="new">Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</target>
<note>{0} is the filter expression string. Diagnostic thrown by the TreeNodeFilter parser when a '/' is encountered inside a parenthesized alternative such as '/A/(B/C)'.</note>
</trans-unit>
<trans-unit id="UnexpectedCallTelemetryIsDisabledErrorMessage">
<source>Unexpected telemetry call, the telemetry is disabled.</source>
<target state="translated">Unerwarteter Telemetrieaufruf. Die Telemetrie ist deaktiviert.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,11 @@ Los valores válidos son "All", "Failed", "None". El valor predeterminado es "Al
<target state="translated">El filtro contiene un operador "/" inesperado dentro de una expresión entre paréntesis</target>
<note />
</trans-unit>
<trans-unit id="TreeNodeFilterUnexpectedSlashOperatorInPathSegmentErrorMessage">
<source>Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</source>
<target state="new">Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</target>
<note>{0} is the filter expression string. Diagnostic thrown by the TreeNodeFilter parser when a '/' is encountered inside a parenthesized alternative such as '/A/(B/C)'.</note>
</trans-unit>
<trans-unit id="UnexpectedCallTelemetryIsDisabledErrorMessage">
<source>Unexpected telemetry call, the telemetry is disabled.</source>
<target state="translated">Llamada de telemetría inesperada, la telemetría está deshabilitada.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,11 @@ Les valeurs valides sont « All », « Failed » et « None ». La valeur
<target state="translated">Désolé, le filtre contient un opérateur '/' inattendu dans une expression entre parenthèses</target>
<note />
</trans-unit>
<trans-unit id="TreeNodeFilterUnexpectedSlashOperatorInPathSegmentErrorMessage">
<source>Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</source>
<target state="new">Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</target>
<note>{0} is the filter expression string. Diagnostic thrown by the TreeNodeFilter parser when a '/' is encountered inside a parenthesized alternative such as '/A/(B/C)'.</note>
</trans-unit>
<trans-unit id="UnexpectedCallTelemetryIsDisabledErrorMessage">
<source>Unexpected telemetry call, the telemetry is disabled.</source>
<target state="translated">Appel de télémétrie inattendu, la télémétrie est désactivée.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,11 @@ I valori validi sono 'All', 'Failed', 'None'. L'impostazione predefinita è 'All
<target state="translated">Il filtro contiene un operatore '/' imprevisto all'interno di un'espressione tra parentesi</target>
<note />
</trans-unit>
<trans-unit id="TreeNodeFilterUnexpectedSlashOperatorInPathSegmentErrorMessage">
<source>Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</source>
<target state="new">Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</target>
<note>{0} is the filter expression string. Diagnostic thrown by the TreeNodeFilter parser when a '/' is encountered inside a parenthesized alternative such as '/A/(B/C)'.</note>
</trans-unit>
<trans-unit id="UnexpectedCallTelemetryIsDisabledErrorMessage">
<source>Unexpected telemetry call, the telemetry is disabled.</source>
<target state="translated">Chiamata di telemetria imprevista. La telemetria è disabilitata.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,11 @@ Valid values are 'All', 'Failed', 'None'. Default is 'All'.</source>
<target state="translated">かっこで囲まれた式内に、予期しない '/' 演算子がフィルターに含まれています</target>
<note />
</trans-unit>
<trans-unit id="TreeNodeFilterUnexpectedSlashOperatorInPathSegmentErrorMessage">
<source>Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</source>
<target state="new">Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</target>
<note>{0} is the filter expression string. Diagnostic thrown by the TreeNodeFilter parser when a '/' is encountered inside a parenthesized alternative such as '/A/(B/C)'.</note>
</trans-unit>
<trans-unit id="UnexpectedCallTelemetryIsDisabledErrorMessage">
<source>Unexpected telemetry call, the telemetry is disabled.</source>
<target state="translated">予期しないテレメトリ呼び出しです。テレメトリは無効になっています。</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,11 @@ Valid values are 'All', 'Failed', 'None'. Default is 'All'.</source>
<target state="translated">필터는 괄호가 있는 식 내에 예기치 않은 '/' 연산자를 포함함</target>
<note />
</trans-unit>
<trans-unit id="TreeNodeFilterUnexpectedSlashOperatorInPathSegmentErrorMessage">
<source>Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</source>
<target state="new">Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</target>
<note>{0} is the filter expression string. Diagnostic thrown by the TreeNodeFilter parser when a '/' is encountered inside a parenthesized alternative such as '/A/(B/C)'.</note>
</trans-unit>
<trans-unit id="UnexpectedCallTelemetryIsDisabledErrorMessage">
<source>Unexpected telemetry call, the telemetry is disabled.</source>
<target state="translated">예기치 않은 원격 분석 호출로 인해 원격 분석을 사용할 수 없습니다.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,11 @@ Prawidłowe wartości to „All”, „Failed”, „None”. Wartość domyśln
<target state="translated">Filtr zawiera nieoczekiwany operator „/” wewnątrz wyrażenia w nawiasach</target>
<note />
</trans-unit>
<trans-unit id="TreeNodeFilterUnexpectedSlashOperatorInPathSegmentErrorMessage">
<source>Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</source>
<target state="new">Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</target>
<note>{0} is the filter expression string. Diagnostic thrown by the TreeNodeFilter parser when a '/' is encountered inside a parenthesized alternative such as '/A/(B/C)'.</note>
</trans-unit>
<trans-unit id="UnexpectedCallTelemetryIsDisabledErrorMessage">
<source>Unexpected telemetry call, the telemetry is disabled.</source>
<target state="translated">Nieoczekiwane wywołanie telemetrii, a telemetria jest wyłączona.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,11 @@ Os valores válidos são 'All', 'Failed', 'None'. O padrão é 'All'.</target>
<target state="translated">O filtro contém um operador “/” inesperado dentro de uma expressão entre parênteses</target>
<note />
</trans-unit>
<trans-unit id="TreeNodeFilterUnexpectedSlashOperatorInPathSegmentErrorMessage">
<source>Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</source>
<target state="new">Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</target>
<note>{0} is the filter expression string. Diagnostic thrown by the TreeNodeFilter parser when a '/' is encountered inside a parenthesized alternative such as '/A/(B/C)'.</note>
</trans-unit>
<trans-unit id="UnexpectedCallTelemetryIsDisabledErrorMessage">
<source>Unexpected telemetry call, the telemetry is disabled.</source>
<target state="translated">Chamada de telemetria inesperada, a telemetria está desabilitada.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,11 @@ Valid values are 'All', 'Failed', 'None'. Default is 'All'.</source>
<target state="translated">Фильтр содержит непредвиденный оператор "/" внутри выражения в круглых скобах</target>
<note />
</trans-unit>
<trans-unit id="TreeNodeFilterUnexpectedSlashOperatorInPathSegmentErrorMessage">
<source>Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</source>
<target state="new">Filter contains an unexpected '/' operator inside a parenthesized expression. Filter: '{0}'. To combine alternatives for one path segment, use syntax like '/A/B/C/(X|Y)'.</target>
<note>{0} is the filter expression string. Diagnostic thrown by the TreeNodeFilter parser when a '/' is encountered inside a parenthesized alternative such as '/A/(B/C)'.</note>
</trans-unit>
<trans-unit id="UnexpectedCallTelemetryIsDisabledErrorMessage">
<source>Unexpected telemetry call, the telemetry is disabled.</source>
<target state="translated">Непредвиденный вызов телеметрии. Телеметрия отключена.</target>
Expand Down
Loading