Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ internal sealed class OperatorExpression(FilterOperator op, IReadOnlyCollection<
{
public FilterOperator Op { get; } = op;

public IReadOnlyCollection<FilterExpression> SubExpressions { get; } = subExpressions;
public IReadOnlyList<FilterExpression> SubExpressions { get; } = [.. subExpressions];
Comment thread
abdelghani-moussaid marked this conversation as resolved.
Outdated
}
Comment thread
abdelghani-moussaid marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,15 @@
{
Filter = filter ?? throw new ArgumentNullException(nameof(filter));
_filters = ParseFilter(filter);
ContainsPropertyFilters = _filters.Any(HasPropertyFilterExpression);
ContainsPropertyFilters = false;
for (int i = 0; i < _filters.Count; i++)
{
if (HasPropertyFilterExpression(_filters[i]))
{
ContainsPropertyFilters = true;
break;
}
}
}

/// <summary>
Expand Down Expand Up @@ -502,8 +510,13 @@

if (currentFragmentIndex >= _filters.Count)
{
Comment thread
abdelghani-moussaid marked this conversation as resolved.
if (_filters.Count == 0)
{
throw new InvalidOperationException();
Comment thread
abdelghani-moussaid marked this conversation as resolved.
Outdated
}

// Note: The regex for ** is .*.*, so we match against such a value expression.
FilterExpression lastFilter = _filters.Last();
FilterExpression lastFilter = _filters[_filters.Count - 1];
if (lastFilter is ValueAndPropertyExpression valueAndPropertyExpression)
{
lastFilter = valueAndPropertyExpression.Value;
Expand Down Expand Up @@ -542,52 +555,132 @@
int endFragmentIndex,
PropertyBag properties)
{
string str = testNodeFullPath[startFragmentIndex..endFragmentIndex];
return MatchFilterPattern(filterExpression, str, properties);
ReadOnlySpan<char> fragment = testNodeFullPath.AsSpan(startFragmentIndex, endFragmentIndex - startFragmentIndex);
return MatchFilterPattern(filterExpression, fragment, properties);
}

private static bool MatchFilterPattern(
FilterExpression filterExpression,
string testNodeFragment,
ReadOnlySpan<char> testNodeFragment,

Check failure on line 564 in src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build Linux Release)

src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs#L564

src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs(564,9): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'ReadOnlySpan<>' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 564 in src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs

View check run for this annotation

Azure Pipelines / microsoft.testfx (Build Linux Debug)

src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs#L564

src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs(564,9): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'ReadOnlySpan<>' could not be found (are you missing a using directive or an assembly reference?)
PropertyBag properties)
Comment thread
abdelghani-moussaid marked this conversation as resolved.
=> filterExpression switch
{
switch (filterExpression)
{
ValueExpression vExpr => vExpr.Regex.IsMatch(testNodeFragment),
OperatorExpression { Op: FilterOperator.Or, SubExpressions: var subexprs }
=> subexprs.Any(expr => MatchFilterPattern(expr, testNodeFragment, properties)),
OperatorExpression { Op: FilterOperator.And, SubExpressions: var subexprs }
=> subexprs.All(expr => MatchFilterPattern(expr, testNodeFragment, properties)),
OperatorExpression { Op: FilterOperator.Not, SubExpressions: var subexprs }
=> !MatchFilterPattern(subexprs.Single(), testNodeFragment, properties),
ValueAndPropertyExpression { Value: var valueExpr, Properties: var propExpr }
=> MatchFilterPattern(valueExpr, testNodeFragment, properties)
&& MatchProperties(propExpr, properties),
NopExpression => true,
_ => throw ApplicationStateGuard.Unreachable(),
};
case ValueExpression vExpr:
#if NETSTANDARD2_0
// Fallback for netstandard2.0 which doesn't support Span in Regex
return vExpr.Regex.IsMatch(testNodeFragment.ToString());
#else
return vExpr.Regex.IsMatch(testNodeFragment);
Comment thread
abdelghani-moussaid marked this conversation as resolved.
#endif

case OperatorExpression { Op: FilterOperator.Or, SubExpressions: var subexprs }:
for (int i = 0; i < subexprs.Count; i++)
{
if (MatchFilterPattern(subexprs[i], testNodeFragment, properties))
{
return true;
}
}

return false;

case OperatorExpression { Op: FilterOperator.And, SubExpressions: var subexprs }:
for (int i = 0; i < subexprs.Count; i++)
{
if (!MatchFilterPattern(subexprs[i], testNodeFragment, properties))
{
return false;
}
}

return true;

case OperatorExpression { Op: FilterOperator.Not, SubExpressions: var subexprs }:
return !MatchFilterPattern(subexprs[0], testNodeFragment, properties);

case ValueAndPropertyExpression { Value: var valueExpr, Properties: var propExpr }:
return MatchFilterPattern(valueExpr, testNodeFragment, properties)
&& MatchProperties(propExpr, properties);

case NopExpression:
return true;

default:
throw ApplicationStateGuard.Unreachable();
}
}

private static bool MatchProperties(
FilterExpression propertyExpr,
PropertyBag properties)
=> propertyExpr switch
{
switch (propertyExpr)
{
PropertyExpression { PropertyName: var propExpr, Value: var valueExpr }
=> properties.AsEnumerable().Any(prop => IsMatchingProperty(prop, propExpr, valueExpr)),
OperatorExpression { Op: FilterOperator.Or, SubExpressions: var subExprs }
=> subExprs.Any(expr => MatchProperties(expr, properties)),
OperatorExpression { Op: FilterOperator.And, SubExpressions: var subExprs }
=> subExprs.All(expr => MatchProperties(expr, properties)),
OperatorExpression { Op: FilterOperator.Not, SubExpressions: var subExprs }
=> !MatchProperties(subExprs.Single(), properties),
_ => throw ApplicationStateGuard.Unreachable(),
};
case PropertyExpression { PropertyName: var propExpr, Value: var valueExpr }:
foreach (IProperty prop in properties)
{
if (IsMatchingProperty(prop, propExpr, valueExpr))
{
Comment thread
abdelghani-moussaid marked this conversation as resolved.
return true;
}
}
Comment thread
abdelghani-moussaid marked this conversation as resolved.

return false;

case OperatorExpression { Op: FilterOperator.Or, SubExpressions: var subExprs }:
for (int i = 0; i < subExprs.Count; i++)
{
if (MatchProperties(subExprs[i], properties))
{
return true;
}
}

return false;

case OperatorExpression { Op: FilterOperator.And, SubExpressions: var subExprs }:
for (int i = 0; i < subExprs.Count; i++)
{
if (!MatchProperties(subExprs[i], properties))
{
return false;
}
}

return true;

case OperatorExpression { Op: FilterOperator.Not, SubExpressions: var subExprs }:
return !MatchProperties(subExprs[0], properties);

default:
throw ApplicationStateGuard.Unreachable();
}
}

private static bool IsMatchingProperty(IProperty prop, ValueExpression propExpr, ValueExpression valueExpr)
=> prop is TestMetadataProperty testMetadataProperty &&
propExpr.Regex.IsMatch(testMetadataProperty.Key) &&
valueExpr.Regex.IsMatch(testMetadataProperty.Value);

private static bool HasPropertyFilterExpression(FilterExpression expression)
=> expression is ValueAndPropertyExpression ||
(expression is OperatorExpression op && op.SubExpressions.Any(HasPropertyFilterExpression));
{
if (expression is ValueAndPropertyExpression)
{
return true;
}

if (expression is OperatorExpression op)
{
for (int i = 0; i < op.SubExpressions.Count; i++)
{
if (HasPropertyFilterExpression(op.SubExpressions[i]))
{
return true;
}
}
}

return false;
}
}
Loading