Skip to content

Commit

Permalink
Fix fast filter for property with multiple values
Browse files Browse the repository at this point in the history
  • Loading branch information
genlu committed Oct 4, 2017
1 parent e1057b9 commit b62c6ed
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 19 deletions.
73 changes: 54 additions & 19 deletions src/Microsoft.TestPlatform.Common/Filtering/FastFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Microsoft.VisualStudio.TestPlatform.Common.Filtering
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
Expand Down Expand Up @@ -55,33 +56,67 @@ internal bool Evaluate(Func<string, Object> propertyValueProvider)
{
ValidateArg.NotNull(propertyValueProvider, "propertyValueProvider");

if (!(propertyValueProvider(this.FilterPropertyName) is string value))
if (!TryGetPropertyValue(this.FilterPropertyName, propertyValueProvider, out var singleValue, out var multiValues))
{
return false;
// No value corresponding to given name, treat it as unmatched.
return IsFilteredOutWhenMatched;
}

if (PropertyValueRegex != null)
bool matched;
if (singleValue != null)
{
if (PropertyValueRegexReplacement == null)
{
var match = PropertyValueRegex.Match(value);
if (match.Success)
{
value = match.Value;
}
else
{
return false;
}
}
else
var value = PropertyValueRegex == null ? singleValue : ApplyRegex(singleValue);
matched = value != null && this.FilterPropertyValues.Contains(value);
}
else
{
matched = (PropertyValueRegex == null ? multiValues : multiValues.Select(value => ApplyRegex(value)))
.Any(result => result != null && this.FilterPropertyValues.Contains(result));
}

return IsFilteredOutWhenMatched ? !matched : matched;
}

/// <summary>
/// Apply regex matching or replacement to given value.
/// </summary>
/// <returns>For matching, returns the result of matching, null if no match found. For replacement, returns the result of replacement.</returns>
private string ApplyRegex(string value)
{
Debug.Assert(PropertyValueRegex != null);

string result = null;
if (PropertyValueRegexReplacement == null)
{
var match = PropertyValueRegex.Match(value);
if (match.Success)
{
value = PropertyValueRegex.Replace(value, PropertyValueRegexReplacement);
result = match.Value;
}
}
else
{
result = PropertyValueRegex.Replace(value, PropertyValueRegexReplacement);
}
return result;
}

var matched = this.FilterPropertyValues.Contains(value);
return IsFilteredOutWhenMatched ? !matched : matched;
/// <summary>
/// Returns property value for Property using propertValueProvider.
/// </summary>
private static bool TryGetPropertyValue(string name, Func<string, Object> propertyValueProvider, out string singleValue, out string[] multiValues)
{
var propertyValue = propertyValueProvider(name);
if (null != propertyValue)
{
singleValue = propertyValue as string;
multiValues = singleValue == null ? propertyValue as string[] : null;
return true;
}

singleValue = null;
multiValues = null;
return false;
}

internal static Builder CreateBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,29 @@ public void FastFilterWithMultipleEqualsClauseAndRegex()
Assert.IsFalse(fastFilter.Evaluate((s) => "Test4 ()"));
}

[TestMethod]
public void FastFilterWithMultipleEqualsClauseForMultiplePropertyValues()
{
var filterExpressionWrapper = new FilterExpressionWrapper("Category=UnitTest|Category=PerfTest", null);
var fastFilter = filterExpressionWrapper.fastFilter;

var expectedFilterValues = new HashSet<string>() { "UnitTest", "PerfTest"};

Assert.IsTrue(fastFilter != null);
Assert.AreEqual("Category", fastFilter.FilterPropertyName);
Assert.IsFalse(fastFilter.IsFilteredOutWhenMatched);
Assert.IsTrue(expectedFilterValues.SetEquals(fastFilter.FilterPropertyValues));

filterExpressionWrapper.ValidForProperties(new List<string>() { "Category" }, null);

Assert.IsTrue(fastFilter.Evaluate((s) => new[] { "UnitTest" }));
Assert.IsTrue(fastFilter.Evaluate((s) => new[] { "PerfTest" }));
Assert.IsTrue(fastFilter.Evaluate((s) => new[] { "UnitTest", "PerfTest" }));
Assert.IsTrue(fastFilter.Evaluate((s) => new[] { "UnitTest", "IntegrationTest" }));
Assert.IsFalse(fastFilter.Evaluate((s) => new[] { "IntegrationTest" }));
Assert.IsFalse(fastFilter.Evaluate((s) => null));
}

[TestMethod]
public void FastFilterWithMultipleEqualsClauseAndRegexReplacement()
{
Expand Down Expand Up @@ -243,6 +266,29 @@ public void FastFilterWithMultipleNotEqualsClauseAndRegex()
Assert.IsTrue(fastFilter.Evaluate((s) => "Test4 (123)"));
}

[TestMethod]
public void FastFilterWithMultipleNotEqualsClauseForMultiplePropertyValues()
{
var filterExpressionWrapper = new FilterExpressionWrapper("Category!=UnitTest&Category!=PerfTest", null);
var fastFilter = filterExpressionWrapper.fastFilter;

var expectedFilterValues = new HashSet<string>() { "UnitTest", "PerfTest" };

Assert.IsTrue(fastFilter != null);
Assert.AreEqual("Category", fastFilter.FilterPropertyName);
Assert.IsTrue(fastFilter.IsFilteredOutWhenMatched);
Assert.IsTrue(expectedFilterValues.SetEquals(fastFilter.FilterPropertyValues));

filterExpressionWrapper.ValidForProperties(new List<string>() { "Category" }, null);

Assert.IsFalse(fastFilter.Evaluate((s) => new[] { "UnitTest" }));
Assert.IsFalse(fastFilter.Evaluate((s) => new[] { "PerfTest" }));
Assert.IsFalse(fastFilter.Evaluate((s) => new[] { "UnitTest", "PerfTest" }));
Assert.IsFalse(fastFilter.Evaluate((s) => new[] { "UnitTest", "IntegrationTest" }));
Assert.IsTrue(fastFilter.Evaluate((s) => new[] { "IntegrationTest" }));
Assert.IsTrue(fastFilter.Evaluate((s) => null));
}

[TestMethod]
public void FastFilterWithWithRegexParseErrorShouldNotCreateFastFilter()
{
Expand Down

0 comments on commit b62c6ed

Please sign in to comment.