Keep commas in parentheses in test name filters

The -f, -n and -x filters split their argument at every comma, so a
parameterized test name such as test_first(0,0) was treated as the two
alternatives "test_first(0" and "0)". This ran unrelated tests with -f
and no tests at all with -n.

A comma nested inside parentheses is now part of the name. Plain
comma-separated lists behave as before.

Fixes #779

Signed-off-by: Gaurav Jadhav <[email protected]>
This commit is contained in:
Gaurav Jadhav
2026-10-31 11:26:43 +05:30
parent e2bbcd1e80
commit 8042f6c463
5 changed files with 166 additions and 1 deletions
+1
View File
@@ -21,6 +21,7 @@ Significant Bugfixes:
- Default `UNITY_INCLUDE_EXEC_TIME` macros compile as ISO C99, are statement-safe, and accept `-Wsign-conversion` (#838)
- `unity_test_summary.rb` honors its own default result directory and root path again. @youdie006
- Test runner generator keeps `TEST_CASE` arguments that contain braces, such as compound literals (#752)
- Test name filters (`-f`, `-n`, `-x`) no longer split at a comma inside parentheses, so parameterized tests like `test_first(0,0)` can be selected (#779) @jadhavgaurav
### Unity 2.7.0 (July 2026)
+3
View File
@@ -240,6 +240,9 @@ These are the available options:
| `-v` | increase Verbosity |
| `-x NAME` | eXclude tests whose name includes NAME |
Several names can be given as a comma-separated list. A comma inside parentheses is
part of the name, so `-f "test_first(0,0)"` selects one parameterized test case.
##### `:setup_name`
Override the default test `setUp` function name.
+28 -1
View File
@@ -2706,6 +2706,7 @@ static int IsStringInBiggerString(const char* longstring, const char* shortstrin
const char* lptr = longstring;
const char* sptr = shortstring;
const char* lnext = lptr;
int depth = 0; /* parenthesis nesting inside the match string; a comma only ends a match at depth 0 */
if (*sptr == '*')
{
@@ -2719,9 +2720,24 @@ static int IsStringInBiggerString(const char* longstring, const char* shortstrin
/* If they current bytes match, go on to the next bytes */
while (*lptr && *sptr && (*lptr == *sptr))
{
if (*sptr == '(')
{
depth++;
}
else if ((*sptr == ')') && (depth > 0))
{
depth--;
}
lptr++;
sptr++;
/* a comma inside parentheses belongs to the name, e.g. test_foo(1,2) */
if ((*sptr == ',') && (depth > 0))
{
continue;
}
switch (*sptr)
{
case '*': /* we encountered a wild-card */
@@ -2750,6 +2766,7 @@ static int IsStringInBiggerString(const char* longstring, const char* shortstrin
/* Otherwise we start in the long pointer 1 character further and try again */
lptr = lnext;
sptr = shortstring;
depth = 0;
}
return 0;
@@ -2762,6 +2779,7 @@ static int UnityStringArgumentMatches(const char* str)
const char* ptr1;
const char* ptr2;
const char* ptrf;
int depth; /* parenthesis nesting; commas inside parentheses do not separate filters */
/* Go through the options and get the substrings for matching one at a time */
ptr1 = str;
@@ -2775,14 +2793,23 @@ static int UnityStringArgumentMatches(const char* str)
/* look for the start of the next partial */
ptr2 = ptr1;
ptrf = 0;
depth = (ptr1[0] == '(') ? 1 : 0;
do
{
ptr2++;
if (ptr2[0] == '(')
{
depth++;
}
else if ((ptr2[0] == ')') && (depth > 0))
{
depth--;
}
if ((ptr2[0] == ':') && (ptr2[1] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ','))
{
ptrf = &ptr2[1];
}
} while ((ptr2[0] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ','));
} while ((ptr2[0] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && ((ptr2[0] != ',') || (depth > 0)));
while ((ptr2[0] != 0) && ((ptr2[0] == ':') || (ptr2[0] == '\'') || (ptr2[0] == '"') || (ptr2[0] == ',')))
{
+34
View File
@@ -0,0 +1,34 @@
/* =========================================================================
Unity - A Test Framework for C
ThrowTheSwitch.org
Copyright (c) 2007-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
SPDX-License-Identifier: MIT
========================================================================= */
/* This Test File Is Used To Verify Name Filters Applied To Parameterized Tests With More Than One Argument */
#include "unity.h"
void setUp(void) {}
void tearDown(void) {}
TEST_CASE(0,0)
TEST_CASE(1,1)
TEST_CASE(10,10)
void paratest_First(int a, int b)
{
TEST_ASSERT_EQUAL_INT(a, b);
}
TEST_CASE(0,0)
TEST_CASE(2,2)
TEST_CASE(20,20)
void paratest_Second(int a, int b)
{
TEST_ASSERT_EQUAL_INT(a, b);
}
void paratest_Plain(void)
{
TEST_PASS();
}
+100
View File
@@ -1134,6 +1134,106 @@ RUNNER_TESTS = [
}
},
{ :name => 'ArgsIncludeParameterizedWithComma',
:testfile => 'testdata/testRunnerGeneratorParameterizedArgs.c',
:testdefines => ['TEST', 'UNITY_USE_COMMAND_LINE_ARGS'],
:options => {
:test_prefix => "paratest",
:use_param_tests => true,
:cmdline_args => true,
},
:cmdline_args => "-f 'paratest_First(0,0)'",
:features => [ :parameterized ],
:expected => {
:to_pass => [
'paratest_First\(0,0\)',
],
:to_fail => [ ],
:to_ignore => [ ],
}
},
{ :name => 'ArgsPreciseMatchParameterizedWithComma',
:testfile => 'testdata/testRunnerGeneratorParameterizedArgs.c',
:testdefines => ['TEST', 'UNITY_USE_COMMAND_LINE_ARGS'],
:options => {
:test_prefix => "paratest",
:use_param_tests => true,
:cmdline_args => true,
},
:cmdline_args => "-n 'paratest_First(0,0)'",
:features => [ :parameterized ],
:expected => {
:to_pass => [
'paratest_First\(0,0\)',
],
:to_fail => [ ],
:to_ignore => [ ],
}
},
{ :name => 'ArgsIncludeParameterizedWithCommaNoMatch',
:testfile => 'testdata/testRunnerGeneratorParameterizedArgs.c',
:testdefines => ['TEST', 'UNITY_USE_COMMAND_LINE_ARGS'],
:options => {
:test_prefix => "paratest",
:use_param_tests => true,
:cmdline_args => true,
},
:cmdline_args => "-f 'paratest_First(0,100)'",
:features => [ :parameterized ],
:expected => {
:to_pass => [ ],
:to_fail => [ ],
:to_ignore => [ ],
}
},
{ :name => 'ArgsIncludeParameterizedWithCommaAlternatives',
:testfile => 'testdata/testRunnerGeneratorParameterizedArgs.c',
:testdefines => ['TEST', 'UNITY_USE_COMMAND_LINE_ARGS'],
:options => {
:test_prefix => "paratest",
:use_param_tests => true,
:cmdline_args => true,
},
:cmdline_args => "-f 'paratest_First(1,1),paratest_Second(2,2),paratest_Plain'",
:features => [ :parameterized ],
:expected => {
:to_pass => [
'paratest_First\(1,1\)',
'paratest_Second\(2,2\)',
'paratest_Plain',
],
:to_fail => [ ],
:to_ignore => [ ],
}
},
{ :name => 'ArgsExcludeParameterizedWithComma',
:testfile => 'testdata/testRunnerGeneratorParameterizedArgs.c',
:testdefines => ['TEST', 'UNITY_USE_COMMAND_LINE_ARGS'],
:options => {
:test_prefix => "paratest",
:use_param_tests => true,
:cmdline_args => true,
},
:cmdline_args => "-x 'paratest_First(0,0)'",
:features => [ :parameterized ],
:expected => {
:to_pass => [
'paratest_First\(1,1\)',
'paratest_First\(10,10\)',
'paratest_Second\(0,0\)',
'paratest_Second\(2,2\)',
'paratest_Second\(20,20\)',
'paratest_Plain',
],
:to_fail => [ ],
:to_ignore => [ ],
}
},
{ :name => 'ArgsList',
:testfile => 'testdata/testRunnerGenerator.c',
:testdefines => ['TEST', 'UNITY_USE_COMMAND_LINE_ARGS'],