From 302cedd8ae061500d96830d3c68cc92cf1e56f2f Mon Sep 17 00:00:00 2001 From: Henrik Gran Date: Thu, 18 Oct 2018 16:38:37 +0200 Subject: [PATCH 1/3] Add NotContains condition --- .../Filtering/Condition.cs | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.TestPlatform.Common/Filtering/Condition.cs b/src/Microsoft.TestPlatform.Common/Filtering/Condition.cs index 4b0a52604f..150dd20830 100644 --- a/src/Microsoft.TestPlatform.Common/Filtering/Condition.cs +++ b/src/Microsoft.TestPlatform.Common/Filtering/Condition.cs @@ -18,6 +18,7 @@ internal enum Operation Equal, NotEqual, Contains, + NotContains } /// @@ -148,6 +149,24 @@ internal bool Evaluate(Func propertyValueProvider) } } break; + + case Operation.NotContains: + // all values in multi-valued property should not contain 'this.Value' for NotContains to evaluate true. + result = true; + + if (null != multiValue) + { + foreach (string propertyValue in multiValue) + { + Debug.Assert(null != propertyValue, "PropertyValue can not be null."); + result = result && propertyValue.IndexOf(Value, StringComparison.OrdinalIgnoreCase) == -1; + if (!result) + { + break; + } + } + } + break; } return result; } @@ -251,6 +270,9 @@ private static Operation GetOperator(string operationString) case "~": return Operation.Contains; + + case "!~": + return Operation.NotContains; } throw new FormatException(string.Format(CultureInfo.CurrentCulture, CommonResources.TestCaseFilterFormatException, string.Format(CultureInfo.CurrentCulture, CommonResources.InvalidOperator, operationString))); } @@ -327,7 +349,7 @@ IEnumerable TokenizeFilterConditionStringWorker(string s) yield return tokenBuilder.ToString(); tokenBuilder.Clear(); } - // Determine if this is a "!=" or just a single "!". + // Determine if this is a "!=" or just a single "!" or "!~". var next = i + 1; if (next < s.Length && s[next] == '=') { @@ -335,6 +357,12 @@ IEnumerable TokenizeFilterConditionStringWorker(string s) current = '='; yield return "!="; } + else if (next < s.Length && s[next] == '~') + { + i = next; + current = '~'; + yield return "!~"; + } else { yield return "!"; From dace53d86e76056a2f2a8830252cee4c6e04dff9 Mon Sep 17 00:00:00 2001 From: Henrik Gran Date: Thu, 18 Oct 2018 16:40:13 +0200 Subject: [PATCH 2/3] Add tests for NotContains condition --- .../Filtering/ConditionTests.cs | 24 +++++++++++++++++++ .../Filtering/FastFilterTests.cs | 9 +++++++ 2 files changed, 33 insertions(+) diff --git a/test/Microsoft.TestPlatform.Common.UnitTests/Filtering/ConditionTests.cs b/test/Microsoft.TestPlatform.Common.UnitTests/Filtering/ConditionTests.cs index 74d8583e31..a77a2c1abd 100644 --- a/test/Microsoft.TestPlatform.Common.UnitTests/Filtering/ConditionTests.cs +++ b/test/Microsoft.TestPlatform.Common.UnitTests/Filtering/ConditionTests.cs @@ -96,6 +96,17 @@ public void ParseShouldHandleEscapedTilde() Assert.AreEqual(@"TestClass1(""~"").TestMethod(1.5)", condition.Value); } + [TestMethod] + public void ParseShouldHandleEscapedNotTilde() + { + var conditionString = @"FullyQualifiedName!~TestClass1\(""\!\~""\).TestMethod\(1.5\)"; + + Condition condition = Condition.Parse(conditionString); + Assert.AreEqual("FullyQualifiedName", condition.Name); + Assert.AreEqual(Operation.NotContains, condition.Operation); + Assert.AreEqual(@"TestClass1(""!~"").TestMethod(1.5)", condition.Value); + } + [TestMethod] public void ParseStringWithSingleUnescapedBangThrowsFormatException1() { @@ -185,6 +196,19 @@ public void TokenizeConditionShouldHandleEscapedTilde() Assert.AreEqual(@"TestMethod\(""\~""\)", tokens[2]); } + [TestMethod] + public void TokenizeConditionShouldHandleEscapedNotTilde() + { + var conditionString = @"FullyQualifiedName!~TestMethod\(""\!\~""\)"; + + var tokens = Condition.TokenizeFilterConditionString(conditionString).ToArray(); + + Assert.AreEqual(3, tokens.Length); + Assert.AreEqual("FullyQualifiedName", tokens[0]); + Assert.AreEqual("!~", tokens[1]); + Assert.AreEqual(@"TestMethod\(""\!\~""\)", tokens[2]); + } + [TestMethod] public void TokenizeConditionShouldHandleSingleUnescapedBang() { diff --git a/test/Microsoft.TestPlatform.Common.UnitTests/Filtering/FastFilterTests.cs b/test/Microsoft.TestPlatform.Common.UnitTests/Filtering/FastFilterTests.cs index 01e7f26ac7..f5e1f7b382 100644 --- a/test/Microsoft.TestPlatform.Common.UnitTests/Filtering/FastFilterTests.cs +++ b/test/Microsoft.TestPlatform.Common.UnitTests/Filtering/FastFilterTests.cs @@ -41,6 +41,15 @@ public void ContainsOperationShouldNotCreateFastFilter() Assert.IsTrue(fastFilter == null); } + [TestMethod] + public void NotContainsOperationShouldNotCreateFastFilter() + { + var filterExpressionWrapper = new FilterExpressionWrapper("Name!~TestClass1"); + var fastFilter = filterExpressionWrapper.fastFilter; + + Assert.IsTrue(fastFilter == null); + } + [TestMethod] public void AndOperatorAndEqualsOperationShouldNotCreateFastFilter() { From 0ddba7872fb12152fbe66940ee7ee4b2e8f115c1 Mon Sep 17 00:00:00 2001 From: Henrik Gran Date: Thu, 18 Oct 2018 16:42:46 +0200 Subject: [PATCH 3/3] Update comment to follow code strucutre --- src/Microsoft.TestPlatform.Common/Filtering/Condition.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.TestPlatform.Common/Filtering/Condition.cs b/src/Microsoft.TestPlatform.Common/Filtering/Condition.cs index 150dd20830..a6688709f9 100644 --- a/src/Microsoft.TestPlatform.Common/Filtering/Condition.cs +++ b/src/Microsoft.TestPlatform.Common/Filtering/Condition.cs @@ -349,7 +349,7 @@ IEnumerable TokenizeFilterConditionStringWorker(string s) yield return tokenBuilder.ToString(); tokenBuilder.Clear(); } - // Determine if this is a "!=" or just a single "!" or "!~". + // Determine if this is a "!=" or "!~" or just a single "!". var next = i + 1; if (next < s.Length && s[next] == '=') {