Skip to content

The filter "tests" truncates the provided value at the first closing parenthesis. #1680

@east1k

Description

@east1k

Hello everyone!

The filter "tests" truncates the provided value at the first closing parenthesis, which can lead to the following:

  • It breaks further use of the filter.
  • It returns more results than expected.
  • It returns fewer results than expected.

The bug appeared when we upgraded nunit.consolerunner.netcore from version 3.18.3 to version 3.20.0.
I suspect that the bug itself was introduced in version 3.19.0 as a result of this commit: 1af35ed.

The bug reproduces in the following cases:

  • If a regex with parentheses is used, the parser may truncate the regex without an explicit error.

    Example

    If I run the following command:

    nunit "test.dll" -where "test =~ '(namespace1|namespace2)\\.test1'" --explore
    

    The parser will truncate to (namespace1|namespace2), and I will get all tests from these namespaces instead of namespace1.test1 and namespace2.test1.

  • If a regex without parentheses is used but with test arguments, the parser may truncate the regex without an explicit error.

    Example

    If I run the following command:

    nunit "test.dll" -where "test =~ 'namespace\\.test1\\(1\\)|namespace\\.test2\\(2\\)'" --explore
    

    The parser will truncate to namespace\\.test1\\(1\\), and I will get only one test instead of two.

  • If a regex with parentheses is used, it may break the regex with an error.

    Example

    If I run the following command:

    nunit "test.dll" -where "test =~ '(namespace\\.test1\\(1\\)|namespace\\.test2\\(2\\))'" --explore
    

    The parser will truncate to (namespace\\.test1\\(1\\), resulting in an invalid regex that will fail when creating Regex:

    Unit.Engine.NUnitEngineException : An exception occurred in the driver while exploring tests.
      ----> System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
      ----> System.Text.RegularExpressions.RegexParseException : Invalid pattern '(namespace\.test1\(1\)' at offset 22. Not enough )'s.
    
    --NUnitEngineException
    An exception occurred in the driver while exploring tests.
       at NUnit.Engine.Runners.DirectTestRunner.Explore(TestFilter filter) in D:\a\nunit-console\nunit-console\src\NUnitEngine\nunit.engine.core\Runners\DirectTestRunner.cs:line 81
       at NUnit.Engine.Runners.MasterTestRunner.Explore(TestFilter filter) in D:\a\nunit-console\nunit-console\src\NUnitEngine\nunit.engine\Runners\MasterTestRunner.cs:line 216
       at NUnit.ConsoleRunner.ConsoleRunner.ExploreTests(TestPackage package, TestFilter filter) in D:\a\nunit-console\nunit-console\src\NUnitConsole\nunit3-console\ConsoleRunner.cs:line 171
       at NUnit.ConsoleRunner.ConsoleRunner.Execute() in D:\a\nunit-console\nunit-console\src\NUnitConsole\nunit3-console\ConsoleRunner.cs:line 153
       at NUnit.ConsoleRunner.Program.Main(String[] args) in D:\a\nunit-console\nunit-console\src\NUnitConsole\nunit3-console\Program.cs:line 117
    --
    TargetInvocationException
    Exception has been thrown by the target of an invocation.
       at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
       at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
       at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
       at NUnit.Engine.Drivers.NUnitNetCore31Driver.ExecuteMethod(MethodInfo method, Object[] args) in D:\a\nunit-console\nunit-console\src\NUnitEngine\nunit.engine.core\Drivers\NUnitNetCore31Driver.cs:line 207
       at NUnit.Engine.Drivers.NUnitNetCore31Driver.ExecuteMethod(String methodName, Object[] args) in D:\a\nunit-console\nunit-console\src\NUnitEngine\nunit.engine.core\Drivers\NUnitNetCore31Driver.cs:line 189
       at NUnit.Engine.Drivers.NUnitNetCore31Driver.Explore(String filter) in D:\a\nunit-console\nunit-console\src\NUnitEngine\nunit.engine.core\Drivers\NUnitNetCore31Driver.cs:line 164
       at NUnit.Engine.Runners.DirectTestRunner.Explore(TestFilter filter) in D:\a\nunit-console\nunit-console\src\NUnitEngine\nunit.engine.core\Runners\DirectTestRunner.cs:line 77
    --
    RegexParseException
    Invalid pattern '(namespace\.test1\(1\)' at offset 22. Not enough )'s.
       at System.Text.RegularExpressions.RegexParser.ScanRegex()
       at System.Text.RegularExpressions.RegexParser.Parse(String pattern, RegexOptions options, CultureInfo culture)
       at System.Text.RegularExpressions.Regex.Init(String pattern, RegexOptions options, TimeSpan matchTimeout, CultureInfo& culture)
       at System.Text.RegularExpressions.Regex..ctor(String pattern, CultureInfo culture)
       at System.Text.RegularExpressions.Regex..ctor(String pattern)
       at NUnit.Framework.Internal.Filters.ValueMatchFilter.Match(String input)
       at NUnit.Framework.Internal.Filters.FullNameFilter.Match(ITest test)
       at NUnit.Framework.Internal.TestFilter.Pass(ITest test, Boolean negated)
       at NUnit.Framework.Internal.TestFilter.Pass(ITest test)
       at NUnit.Framework.Internal.TestSuite..ctor(TestSuite suite, ITestFilter filter)
       at NUnit.Framework.Internal.TestAssembly..ctor(TestAssembly assembly, ITestFilter filter)
       at NUnit.Framework.Api.NUnitTestAssemblyRunner.ExploreTests(ITestFilter filter)
       at NUnit.Framework.Api.FrameworkController.ExploreTests(String filter)
       at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
       at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
    
    
  • If a TestFixture with constructor arguments is used, the parser will truncate the value to TestFixture.

    Example

    If I run the following command:

    nunit "test.dll" -where "test == 'namespace.class(1).test1(1)'" --explore
    

    The parser will truncate to namespace.class(1), and I will get all tests from the class instead of a specific test.

Test cases for TestSelectionParserTests.cs to reproduce the issue:

new TestCaseData("test == namespace.class(1).test1(1)", "<test>namespace.class(1).test1(1)</test>"),
new TestCaseData("test == \"namespace.class(1).test1(1)\"", "<test>namespace.class(1).test1(1)</test>"),
new TestCaseData("test == 'namespace.class(1).test1(1)'", "<test>namespace.class(1).test1(1)</test>"),
new TestCaseData("test =~ \"(namespace\\.test1\\(1\\)|namespace\\.test2\\(2\\))\"", "<test re='1'>(namespace.test1(1)|namespace.test2(2))</test>"),
new TestCaseData("test =~ '(namespace\\.test1\\(1\\)|namespace\\.test2\\(2\\))'", "<test re='1'>(namespace.test1(1)|namespace.test2(2))</test>"),
new TestCaseData("test =~ /(namespace\\.test1\\(1\\)|namespace\\.test2\\(2\\))/", "<test re='1'>(namespace.test1(1)|namespace.test2(2))</test>"),
new TestCaseData("test =~ \"(namespace1|namespace2)\\.test1\"", "<test re='1'>(namespace1|namespace2).test1</test>"),
new TestCaseData("test =~ '(namespace1|namespace2)\\.test1'", "<test re='1'>(namespace1|namespace2).test1</test>"),
new TestCaseData("test =~ /(namespace1|namespace2)\\.test1/", "<test re='1'>(namespace1|namespace2).test1</test>"),

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions