From 2a162d5467786cfa37a66edf46fc6dc56c17565b Mon Sep 17 00:00:00 2001 From: Gaurav Jadhav Date: Tue, 29 Sep 2026 19:21:26 +0530 Subject: [PATCH] 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 --- docs/UnityChangeLog.md | 1 + docs/UnityHelperScriptsGuide.md | 3 + src/unity.c | 29 ++++- .../testRunnerGeneratorParameterizedArgs.c | 34 ++++++ test/tests/test_generate_test_runner.rb | 100 ++++++++++++++++++ 5 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 test/testdata/testRunnerGeneratorParameterizedArgs.c diff --git a/docs/UnityChangeLog.md b/docs/UnityChangeLog.md index 6e73e27c1..e0e1a285d 100644 --- a/docs/UnityChangeLog.md +++ b/docs/UnityChangeLog.md @@ -19,6 +19,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 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) diff --git a/docs/UnityHelperScriptsGuide.md b/docs/UnityHelperScriptsGuide.md index 30841cf94..f4564bb47 100644 --- a/docs/UnityHelperScriptsGuide.md +++ b/docs/UnityHelperScriptsGuide.md @@ -231,6 +231,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. diff --git a/src/unity.c b/src/unity.c index 68e833ccd..49280b3cb 100644 --- a/src/unity.c +++ b/src/unity.c @@ -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] == ','))) { diff --git a/test/testdata/testRunnerGeneratorParameterizedArgs.c b/test/testdata/testRunnerGeneratorParameterizedArgs.c new file mode 100644 index 000000000..91fd8d2f7 --- /dev/null +++ b/test/testdata/testRunnerGeneratorParameterizedArgs.c @@ -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(); +} diff --git a/test/tests/test_generate_test_runner.rb b/test/tests/test_generate_test_runner.rb index 3d05020d7..4efbf3bac 100644 --- a/test/tests/test_generate_test_runner.rb +++ b/test/tests/test_generate_test_runner.rb @@ -1119,6 +1119,106 @@ } }, + { :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'],