diff --git a/test/Microsoft.TestPlatform.Filter.Source.UnitTests/FilterExpressionWrapperTests.cs b/test/Microsoft.TestPlatform.Filter.Source.UnitTests/FilterExpressionWrapperTests.cs new file mode 100644 index 0000000000..ecafa76e71 --- /dev/null +++ b/test/Microsoft.TestPlatform.Filter.Source.UnitTests/FilterExpressionWrapperTests.cs @@ -0,0 +1,155 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; + +using Microsoft.VisualStudio.TestPlatform.Common.Filtering; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.TestPlatform.Filter.Source.UnitTests; + +[TestClass] +public class FilterExpressionWrapperTests +{ + [TestMethod] + public void ConstructorShouldSetFilterString() + { + var filterString = "FullyQualifiedName=Test1"; + var wrapper = new FilterExpressionWrapper(filterString); + Assert.AreEqual(filterString, wrapper.FilterString); + } + + [TestMethod] + public void ParseErrorShouldBeNullForValidFilter() + { + var wrapper = new FilterExpressionWrapper("Name=Test1"); + Assert.IsNull(wrapper.ParseError); + } + + [TestMethod] + public void ParseErrorShouldBeSetForInvalidFilter() + { + var wrapper = new FilterExpressionWrapper("(Name=Test1"); + Assert.IsNotNull(wrapper.ParseError); + } + + [TestMethod] + public void EvaluateShouldReturnTrueWhenPropertyMatches() + { + var wrapper = new FilterExpressionWrapper("Name=Test1"); + + bool result = wrapper.Evaluate(prop => prop == "Name" ? "Test1" : null); + + Assert.IsTrue(result); + } + + [TestMethod] + public void EvaluateShouldReturnFalseWhenPropertyDoesNotMatch() + { + var wrapper = new FilterExpressionWrapper("Name=Test1"); + + bool result = wrapper.Evaluate(prop => prop == "Name" ? "Test2" : null); + + Assert.IsFalse(result); + } + + [TestMethod] + public void EvaluateShouldReturnFalseWhenFilterHasParseError() + { + var wrapper = new FilterExpressionWrapper("(Name=Test1"); + + bool result = wrapper.Evaluate(prop => "Test1"); + + Assert.IsFalse(result); + } + + [TestMethod] + public void EvaluateShouldSupportAndOperator() + { + var wrapper = new FilterExpressionWrapper("Name=Test1&Category=Unit"); + + bool result = wrapper.Evaluate(prop => prop switch + { + "Name" => "Test1", + "Category" => "Unit", + _ => null, + }); + + Assert.IsTrue(result); + } + + [TestMethod] + public void EvaluateShouldSupportOrOperator() + { + var wrapper = new FilterExpressionWrapper("Name=Test1|Name=Test2"); + + bool matchFirst = wrapper.Evaluate(prop => prop == "Name" ? "Test1" : null); + bool matchSecond = wrapper.Evaluate(prop => prop == "Name" ? "Test2" : null); + bool matchNeither = wrapper.Evaluate(prop => prop == "Name" ? "Test3" : null); + + Assert.IsTrue(matchFirst); + Assert.IsTrue(matchSecond); + Assert.IsFalse(matchNeither); + } + + [TestMethod] + public void EvaluateShouldSupportNotEqualOperator() + { + var wrapper = new FilterExpressionWrapper("Name!=Test1"); + + bool result = wrapper.Evaluate(prop => prop == "Name" ? "Test2" : null); + + Assert.IsTrue(result); + } + + [TestMethod] + public void EvaluateShouldSupportContainsOperator() + { + var wrapper = new FilterExpressionWrapper("Name~Test"); + + bool result = wrapper.Evaluate(prop => prop == "Name" ? "MyTestMethod" : null); + + Assert.IsTrue(result); + } + + [TestMethod] + public void ValidForPropertiesShouldReturnNullWhenAllPropertiesAreSupported() + { + var wrapper = new FilterExpressionWrapper("Name=Test1"); + var supportedProperties = new List { "Name" }; + + string[]? invalidProperties = wrapper.ValidForProperties(supportedProperties); + + Assert.IsNull(invalidProperties); + } + + [TestMethod] + public void ValidForPropertiesShouldReturnUnsupportedPropertyNames() + { + var wrapper = new FilterExpressionWrapper("UnknownProperty=Value"); + var supportedProperties = new List { "Name", "Category" }; + + string[]? invalidProperties = wrapper.ValidForProperties(supportedProperties); + + Assert.IsNotNull(invalidProperties); + Assert.AreEqual(1, invalidProperties.Length); + Assert.AreEqual("UnknownProperty", invalidProperties[0]); + } + + [TestMethod] + public void FastFilterShouldBeCreatedForSimpleEqualityFilter() + { + var wrapper = new FilterExpressionWrapper("Name=Test1"); + + Assert.IsNotNull(wrapper.FastFilter); + } + + [TestMethod] + public void FastFilterShouldBeNullForComplexFilter() + { + var wrapper = new FilterExpressionWrapper("Name=Test1&Name=Test2"); + + Assert.IsNull(wrapper.FastFilter); + } +} diff --git a/test/Microsoft.TestPlatform.Filter.Source.UnitTests/FilterHelperTests.cs b/test/Microsoft.TestPlatform.Filter.Source.UnitTests/FilterHelperTests.cs new file mode 100644 index 0000000000..bf1aa85ccc --- /dev/null +++ b/test/Microsoft.TestPlatform.Filter.Source.UnitTests/FilterHelperTests.cs @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.TestPlatform.Filter.Source.UnitTests; + +[TestClass] +public class FilterHelperTests +{ + [TestMethod] + public void EscapeShouldReturnOriginalStringWhenNoSpecialCharacters() + { + var input = "TestMethod"; + Assert.AreEqual(input, FilterHelper.Escape(input)); + } + + [TestMethod] + public void EscapeShouldEscapeOpenParenthesis() + { + Assert.AreEqual(@"Test\(Method", FilterHelper.Escape("Test(Method")); + } + + [TestMethod] + public void EscapeShouldEscapeCloseParenthesis() + { + Assert.AreEqual(@"Test\)Method", FilterHelper.Escape("Test)Method")); + } + + [TestMethod] + public void EscapeShouldEscapeAmpersand() + { + Assert.AreEqual(@"Test\&Method", FilterHelper.Escape("Test&Method")); + } + + [TestMethod] + public void EscapeShouldEscapePipe() + { + Assert.AreEqual(@"Test\|Method", FilterHelper.Escape("Test|Method")); + } + + [TestMethod] + public void EscapeShouldEscapeEqualSign() + { + Assert.AreEqual(@"Test\=Method", FilterHelper.Escape("Test=Method")); + } + + [TestMethod] + public void EscapeShouldEscapeExclamationMark() + { + Assert.AreEqual(@"Test\!Method", FilterHelper.Escape("Test!Method")); + } + + [TestMethod] + public void EscapeShouldEscapeTilde() + { + Assert.AreEqual(@"Test\~Method", FilterHelper.Escape("Test~Method")); + } + + [TestMethod] + public void EscapeShouldEscapeBackslash() + { + Assert.AreEqual(@"Test\\Method", FilterHelper.Escape(@"Test\Method")); + } + + [TestMethod] + public void EscapeShouldEscapeMultipleSpecialCharacters() + { + Assert.AreEqual(@"Test\(A\|B\)", FilterHelper.Escape("Test(A|B)")); + } + + [TestMethod] + public void UnescapeShouldReturnOriginalStringWhenNoEscapeCharacters() + { + var input = "TestMethod"; + Assert.AreEqual(input, FilterHelper.Unescape(input)); + } + + [TestMethod] + public void UnescapeShouldUnescapeOpenParenthesis() + { + Assert.AreEqual("Test(Method", FilterHelper.Unescape(@"Test\(Method")); + } + + [TestMethod] + public void UnescapeShouldUnescapeCloseParenthesis() + { + Assert.AreEqual("Test)Method", FilterHelper.Unescape(@"Test\)Method")); + } + + [TestMethod] + public void UnescapeShouldUnescapeBackslash() + { + Assert.AreEqual(@"Test\Method", FilterHelper.Unescape(@"Test\\Method")); + } + + [TestMethod] + public void UnescapeShouldThrowOnInvalidEscapeSequence() + { + Assert.ThrowsExactly(() => FilterHelper.Unescape(@"Test\AMethod")); + } + + [TestMethod] + public void EscapeAndUnescapeRoundtrip() + { + var input = "TestClass(\"param\").Method(1.5)"; + Assert.AreEqual(input, FilterHelper.Unescape(FilterHelper.Escape(input))); + } +} diff --git a/test/Microsoft.TestPlatform.Filter.Source.UnitTests/Microsoft.TestPlatform.Filter.Source.UnitTests.csproj b/test/Microsoft.TestPlatform.Filter.Source.UnitTests/Microsoft.TestPlatform.Filter.Source.UnitTests.csproj new file mode 100644 index 0000000000..734ed2ef62 --- /dev/null +++ b/test/Microsoft.TestPlatform.Filter.Source.UnitTests/Microsoft.TestPlatform.Filter.Source.UnitTests.csproj @@ -0,0 +1,42 @@ + + + + true + true + + true + + + + net9.0;net48 + Exe + Microsoft.TestPlatform.Filter.Source.UnitTests + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Microsoft.TestPlatform.Filter.Source.UnitTests/TestCaseFilterExpressionTests.cs b/test/Microsoft.TestPlatform.Filter.Source.UnitTests/TestCaseFilterExpressionTests.cs new file mode 100644 index 0000000000..00d392cc3c --- /dev/null +++ b/test/Microsoft.TestPlatform.Filter.Source.UnitTests/TestCaseFilterExpressionTests.cs @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; + +using Microsoft.VisualStudio.TestPlatform.Common.Filtering; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.TestPlatform.Filter.Source.UnitTests; + +[TestClass] +public class TestCaseFilterExpressionTests +{ + [TestMethod] + public void ConstructorShouldThrowForNullFilterWrapper() + { + Assert.ThrowsExactly(() => new TestCaseFilterExpression(null!)); + } + + [TestMethod] + public void TestCaseFilterValueShouldMatchFilterString() + { + var filterString = "Name=Test1"; + var wrapper = new FilterExpressionWrapper(filterString); + var expression = new TestCaseFilterExpression(wrapper); + + Assert.AreEqual(filterString, expression.TestCaseFilterValue); + } + + [TestMethod] + public void MatchTestCaseShouldReturnTrueWhenPropertyMatches() + { + var wrapper = new FilterExpressionWrapper("Name=Test1"); + var expression = new TestCaseFilterExpression(wrapper); + + bool result = expression.MatchTestCase(prop => prop == "Name" ? "Test1" : null); + + Assert.IsTrue(result); + } + + [TestMethod] + public void MatchTestCaseShouldReturnFalseWhenPropertyDoesNotMatch() + { + var wrapper = new FilterExpressionWrapper("Name=Test1"); + var expression = new TestCaseFilterExpression(wrapper); + + bool result = expression.MatchTestCase(prop => prop == "Name" ? "Test2" : null); + + Assert.IsFalse(result); + } + + [TestMethod] + public void MatchTestCaseShouldReturnFalseWhenFilterHasParseError() + { + var wrapper = new FilterExpressionWrapper("(Name=Test1"); + var expression = new TestCaseFilterExpression(wrapper); + + bool result = expression.MatchTestCase(prop => "Test1"); + + Assert.IsFalse(result); + } + + [TestMethod] + public void ValidForPropertiesShouldReturnNullWhenAllPropertiesAreSupported() + { + var wrapper = new FilterExpressionWrapper("Name=Test1"); + var expression = new TestCaseFilterExpression(wrapper); + var supportedProperties = new List { "Name" }; + + string[]? invalidProperties = expression.ValidForProperties(supportedProperties); + + Assert.IsNull(invalidProperties); + } + + [TestMethod] + public void ValidForPropertiesShouldReturnUnsupportedPropertyNames() + { + var wrapper = new FilterExpressionWrapper("UnknownProperty=Value"); + var expression = new TestCaseFilterExpression(wrapper); + var supportedProperties = new List { "Name", "Category" }; + + string[]? invalidProperties = expression.ValidForProperties(supportedProperties); + + Assert.IsNotNull(invalidProperties); + Assert.AreEqual(1, invalidProperties.Length); + Assert.AreEqual("UnknownProperty", invalidProperties[0]); + } + + [TestMethod] + public void ValidForPropertiesShouldReturnNullWhenFilterHasParseError() + { + var wrapper = new FilterExpressionWrapper("(Name=Test1"); + var expression = new TestCaseFilterExpression(wrapper); + var supportedProperties = new List { "Name" }; + + // When filter has parse error, ValidForProperties returns null + string[]? invalidProperties = expression.ValidForProperties(supportedProperties); + + Assert.IsNull(invalidProperties); + } + + [TestMethod] + public void MatchTestCaseShouldSupportOrOperator() + { + var wrapper = new FilterExpressionWrapper("Name=Test1|Name=Test2"); + var expression = new TestCaseFilterExpression(wrapper); + + bool matchFirst = expression.MatchTestCase(prop => prop == "Name" ? "Test1" : null); + bool matchSecond = expression.MatchTestCase(prop => prop == "Name" ? "Test2" : null); + bool matchNeither = expression.MatchTestCase(prop => prop == "Name" ? "Test3" : null); + + Assert.IsTrue(matchFirst); + Assert.IsTrue(matchSecond); + Assert.IsFalse(matchNeither); + } + + [TestMethod] + public void MatchTestCaseShouldSupportAndOperator() + { + var wrapper = new FilterExpressionWrapper("Name=Test1&Category=Unit"); + var expression = new TestCaseFilterExpression(wrapper); + + bool result = expression.MatchTestCase(prop => prop switch + { + "Name" => "Test1", + "Category" => "Unit", + _ => null, + }); + + Assert.IsTrue(result); + } +}