From 58e78c3b8f7ab16772ca3d2cf1038738de705497 Mon Sep 17 00:00:00 2001 From: Chad Condon Date: Fri, 24 Apr 2026 19:21:22 -0700 Subject: [PATCH 1/7] refactor CommandLineArguments: return args consumed instead of mutating index parse_prefix_arg and parse_argument now return int (-1 = unrecognised, >=0 = extra args consumed) instead of bool with an int& index threaded through the call chain. parse() advances its loop variable by the return value. Helpers (add_group_filter, set_repeat_count, etc.) now receive a pre-sliced view (argc remaining, argv pointing at the current flag) so they work with argv[0]/argv[1] directly and carry no knowledge of their position in the global argument array. parse_prefix_arg is the single site that computes ac_ - index / av_ + index. --- include/mu/tiny/test/CommandLineArguments.hpp | 38 ++- src/test/CommandLineArguments.cpp | 221 +++++++++--------- 2 files changed, 124 insertions(+), 135 deletions(-) diff --git a/include/mu/tiny/test/CommandLineArguments.hpp b/include/mu/tiny/test/CommandLineArguments.hpp index 113c6c3aa..2f4d34ebe 100644 --- a/include/mu/tiny/test/CommandLineArguments.hpp +++ b/include/mu/tiny/test/CommandLineArguments.hpp @@ -116,41 +116,31 @@ class MUTINY_EXPORT CommandLineArguments unsigned int shuffle_seed_{ 0 }; Filter* group_filters_{ nullptr }; Filter* name_filters_{ nullptr }; - void set_repeat_count(int argc, const char* const* argv, int& index); - bool set_shuffle(int argc, const char* const* argv, int& index); - void add_group_filter(int argc, const char* const* argv, int& index); - bool add_group_dot_name_filter( + int set_repeat_count(int argc, const char* const* argv); + int set_shuffle(int argc, const char* const* argv); + int add_group_filter(int argc, const char* const* argv); + int add_group_dot_name_filter( int argc, const char* const* argv, - int& index, const String& parameter_name, bool strict, bool exclude ); - void add_strict_group_filter(int argc, const char* const* argv, int& index); - void add_exclude_group_filter(int argc, const char* const* argv, int& index); - void add_exclude_strict_group_filter( + int add_strict_group_filter(int argc, const char* const* argv); + int add_exclude_group_filter(int argc, const char* const* argv); + int add_exclude_strict_group_filter(int argc, const char* const* argv); + int add_name_filter(int argc, const char* const* argv); + int add_strict_name_filter(int argc, const char* const* argv); + int add_exclude_name_filter(int argc, const char* const* argv); + int add_exclude_strict_name_filter(int argc, const char* const* argv); + int add_test_to_run_based_on_verbose_output( int argc, const char* const* argv, - int& index - ); - void add_name_filter(int argc, const char* const* argv, int& index); - void add_strict_name_filter(int argc, const char* const* argv, int& index); - void add_exclude_name_filter(int argc, const char* const* argv, int& index); - void add_exclude_strict_name_filter( - int argc, - const char* const* argv, - int& index - ); - void add_test_to_run_based_on_verbose_output( - int argc, - const char* const* argv, - int& index, const char* parameter_name ); bool parse_simple_flag(const String& argument); - bool parse_prefix_arg(const String& argument, Plugin* plugin, int& index); - bool parse_argument(const String& argument, Plugin* plugin, int& index); + int parse_prefix_arg(const String& argument, Plugin* plugin, int index); + int parse_argument(const String& argument, Plugin* plugin, int index); }; } // namespace test diff --git a/src/test/CommandLineArguments.cpp b/src/test/CommandLineArguments.cpp index d1cdfa625..b898dcd70 100644 --- a/src/test/CommandLineArguments.cpp +++ b/src/test/CommandLineArguments.cpp @@ -12,22 +12,31 @@ namespace tiny { namespace test { namespace { -String get_parameter_field( +class ParsedField +{ +public: + String value; + int extra; +}; + +ParsedField get_parameter_field( int argc, const char* const* argv, - int& i, const String& parameter_name ) { size_t parameter_length = parameter_name.size(); - String parameter(argv[i]); + String parameter(argv[0]); if (parameter.size() > parameter_length) { - return argv[i] + parameter_length; + ParsedField result = { String(argv[0] + parameter_length), 0 }; + return result; } - if (i + 1 < argc) { - return argv[++i]; + if (argc > 1) { + ParsedField result = { String(argv[1]), 1 }; + return result; } - return ""; + ParsedField result = { String(""), 0 }; + return result; } String sub_string_from_till( @@ -128,85 +137,78 @@ bool CommandLineArguments::parse_simple_flag(const String& argument) return false; } -bool CommandLineArguments::parse_prefix_arg( +int CommandLineArguments::parse_prefix_arg( const String& argument, Plugin* plugin, - int& index + int index ) { + int remaining = ac_ - index; + const char* const* args = av_ + index; if (string_starts_with(argument, "-r")) { - set_repeat_count(ac_, av_, index); - return true; + return set_repeat_count(remaining, args); } if (string_starts_with(argument, "-g")) { - add_group_filter(ac_, av_, index); - return true; + return add_group_filter(remaining, args); } if (string_starts_with(argument, "-t")) { - return add_group_dot_name_filter(ac_, av_, index, "-t", false, false); + return add_group_dot_name_filter(remaining, args, "-t", false, false); } if (string_starts_with(argument, "-st")) { - return add_group_dot_name_filter(ac_, av_, index, "-st", true, false); + return add_group_dot_name_filter(remaining, args, "-st", true, false); } if (string_starts_with(argument, "-xt")) { - return add_group_dot_name_filter(ac_, av_, index, "-xt", false, true); + return add_group_dot_name_filter(remaining, args, "-xt", false, true); } if (string_starts_with(argument, "-xst")) { - return add_group_dot_name_filter(ac_, av_, index, "-xst", true, true); + return add_group_dot_name_filter(remaining, args, "-xst", true, true); } if (string_starts_with(argument, "-sg")) { - add_strict_group_filter(ac_, av_, index); - return true; + return add_strict_group_filter(remaining, args); } if (string_starts_with(argument, "-xg")) { - add_exclude_group_filter(ac_, av_, index); - return true; + return add_exclude_group_filter(remaining, args); } if (string_starts_with(argument, "-xsg")) { - add_exclude_strict_group_filter(ac_, av_, index); - return true; + return add_exclude_strict_group_filter(remaining, args); } if (string_starts_with(argument, "-n")) { - add_name_filter(ac_, av_, index); - return true; + return add_name_filter(remaining, args); } if (string_starts_with(argument, "-sn")) { - add_strict_name_filter(ac_, av_, index); - return true; + return add_strict_name_filter(remaining, args); } if (string_starts_with(argument, "-xn")) { - add_exclude_name_filter(ac_, av_, index); - return true; + return add_exclude_name_filter(remaining, args); } if (string_starts_with(argument, "-xsn")) { - add_exclude_strict_name_filter(ac_, av_, index); - return true; + return add_exclude_strict_name_filter(remaining, args); } if (string_starts_with(argument, "-s")) { - return set_shuffle(ac_, av_, index); + return set_shuffle(remaining, args); } if (string_starts_with(argument, "TEST(")) { - add_test_to_run_based_on_verbose_output(ac_, av_, index, "TEST("); - return true; + return add_test_to_run_based_on_verbose_output(remaining, args, "TEST("); } if (string_starts_with(argument, "SKIPPED_TEST(")) { - add_test_to_run_based_on_verbose_output(ac_, av_, index, "SKIPPED_TEST("); - return true; + return add_test_to_run_based_on_verbose_output( + remaining, args, "SKIPPED_TEST(" + ); } if (string_starts_with(argument, "-p")) { - return plugin->parse_all_arguments(ac_, av_, index); + return plugin->parse_all_arguments(ac_, av_, index) ? 0 : -1; } - return false; + return -1; } -bool CommandLineArguments::parse_argument( +int CommandLineArguments::parse_argument( const String& argument, Plugin* plugin, - int& index + int index ) { if (parse_simple_flag(argument)) { - return true; + return 0; } return parse_prefix_arg(argument, plugin, index); } @@ -214,9 +216,11 @@ bool CommandLineArguments::parse_argument( bool CommandLineArguments::parse(Plugin* plugin) { for (int i = 1; i < ac_; i++) { - if (!parse_argument(av_[i], plugin, i)) { + int extra = parse_argument(av_[i], plugin, i); + if (extra < 0) { return false; } + i += extra; } return true; } @@ -388,34 +392,28 @@ const Filter* CommandLineArguments::get_name_filters() const return name_filters_; } -void CommandLineArguments::set_repeat_count( - int argc, - const char* const* argv, - int& i -) +int CommandLineArguments::set_repeat_count(int argc, const char* const* argv) { repeat_ = 0; + int extra = 0; - String repeat_parameter(argv[i]); + String repeat_parameter(argv[0]); if (repeat_parameter.size() > 2) { - repeat_ = static_cast(strtoul(argv[i] + 2)); - } else if (i + 1 < argc) { - repeat_ = static_cast(strtoul(argv[i + 1])); + repeat_ = static_cast(strtoul(argv[0] + 2)); + } else if (argc > 1) { + repeat_ = static_cast(strtoul(argv[1])); if (repeat_ != 0) { - i++; + extra = 1; } } if (0 == repeat_) { repeat_ = 2; } + return extra; } -bool CommandLineArguments::set_shuffle( - int argc, - const char* const* argv, - int& i -) +int CommandLineArguments::set_shuffle(int argc, const char* const* argv) { shuffling_ = true; shuffle_seed_ = static_cast(get_time_in_millis()); @@ -423,45 +421,43 @@ bool CommandLineArguments::set_shuffle( shuffle_seed_++; } - String shuffle_parameter = argv[i]; + int extra = 0; + String shuffle_parameter = argv[0]; if (shuffle_parameter.size() > 2) { shuffling_pre_seeded_ = true; - shuffle_seed_ = static_cast(strtoul(argv[i] + 2)); - } else if (i + 1 < argc) { - auto parsed_parameter = static_cast(strtoul(argv[i + 1])); + shuffle_seed_ = static_cast(strtoul(argv[0] + 2)); + } else if (argc > 1) { + auto parsed_parameter = static_cast(strtoul(argv[1])); if (parsed_parameter != 0) { shuffling_pre_seeded_ = true; shuffle_seed_ = parsed_parameter; - i++; + extra = 1; } } - return (shuffle_seed_ != 0); + return (shuffle_seed_ != 0) ? extra : -1; } -void CommandLineArguments::add_group_filter( - int argc, - const char* const* argv, - int& i -) +int CommandLineArguments::add_group_filter(int argc, const char* const* argv) { - auto* group_filter = new Filter(get_parameter_field(argc, argv, i, "-g")); + ParsedField field = get_parameter_field(argc, argv, "-g"); + auto* group_filter = new Filter(field.value); group_filters_ = group_filter->add(group_filters_); + return field.extra; } -bool CommandLineArguments::add_group_dot_name_filter( +int CommandLineArguments::add_group_dot_name_filter( int argc, const char* const* argv, - int& i, const String& parameter_name, bool strict, bool exclude ) { - String group_dot_name = get_parameter_field(argc, argv, i, parameter_name); - StringCollection collection(group_dot_name, '.'); + ParsedField field = get_parameter_field(argc, argv, parameter_name); + StringCollection collection(field.value, '.'); if (collection.size() != 2) { - return false; + return -1; } auto* group_filter = @@ -477,105 +473,108 @@ bool CommandLineArguments::add_group_dot_name_filter( } group_filters_ = group_filter->add(group_filters_); name_filters_ = name_filter->add(name_filters_); - return true; + return field.extra; } -void CommandLineArguments::add_strict_group_filter( +int CommandLineArguments::add_strict_group_filter( int argc, - const char* const* argv, - int& i + const char* const* argv ) { - auto* group_filter = new Filter(get_parameter_field(argc, argv, i, "-sg")); + ParsedField field = get_parameter_field(argc, argv, "-sg"); + auto* group_filter = new Filter(field.value); group_filter->strict_matching(); group_filters_ = group_filter->add(group_filters_); + return field.extra; } -void CommandLineArguments::add_exclude_group_filter( +int CommandLineArguments::add_exclude_group_filter( int argc, - const char* const* argv, - int& i + const char* const* argv ) { - auto* group_filter = new Filter(get_parameter_field(argc, argv, i, "-xg")); + ParsedField field = get_parameter_field(argc, argv, "-xg"); + auto* group_filter = new Filter(field.value); group_filter->invert_matching(); group_filters_ = group_filter->add(group_filters_); + return field.extra; } -void CommandLineArguments::add_exclude_strict_group_filter( +int CommandLineArguments::add_exclude_strict_group_filter( int argc, - const char* const* argv, - int& i + const char* const* argv ) { - auto* group_filter = new Filter(get_parameter_field(argc, argv, i, "-xsg")); + ParsedField field = get_parameter_field(argc, argv, "-xsg"); + auto* group_filter = new Filter(field.value); group_filter->strict_matching(); group_filter->invert_matching(); group_filters_ = group_filter->add(group_filters_); + return field.extra; } -void CommandLineArguments::add_name_filter( - int argc, - const char* const* argv, - int& i -) +int CommandLineArguments::add_name_filter(int argc, const char* const* argv) { - auto* name_filter = new Filter(get_parameter_field(argc, argv, i, "-n")); + ParsedField field = get_parameter_field(argc, argv, "-n"); + auto* name_filter = new Filter(field.value); name_filters_ = name_filter->add(name_filters_); + return field.extra; } -void CommandLineArguments::add_strict_name_filter( +int CommandLineArguments::add_strict_name_filter( int argc, - const char* const* argv, - int& index + const char* const* argv ) { - auto* name_filter = new Filter(get_parameter_field(argc, argv, index, "-sn")); + ParsedField field = get_parameter_field(argc, argv, "-sn"); + auto* name_filter = new Filter(field.value); name_filter->strict_matching(); name_filters_ = name_filter->add(name_filters_); + return field.extra; } -void CommandLineArguments::add_exclude_name_filter( +int CommandLineArguments::add_exclude_name_filter( int argc, - const char* const* argv, - int& index + const char* const* argv ) { - auto* name_filter = new Filter(get_parameter_field(argc, argv, index, "-xn")); + ParsedField field = get_parameter_field(argc, argv, "-xn"); + auto* name_filter = new Filter(field.value); name_filter->invert_matching(); name_filters_ = name_filter->add(name_filters_); + return field.extra; } -void CommandLineArguments::add_exclude_strict_name_filter( +int CommandLineArguments::add_exclude_strict_name_filter( int argc, - const char* const* argv, - int& index + const char* const* argv ) { - auto* name_filter = - new Filter(get_parameter_field(argc, argv, index, "-xsn")); + ParsedField field = get_parameter_field(argc, argv, "-xsn"); + auto* name_filter = new Filter(field.value); name_filter->invert_matching(); name_filter->strict_matching(); name_filters_ = name_filter->add(name_filters_); + return field.extra; } -void CommandLineArguments::add_test_to_run_based_on_verbose_output( +int CommandLineArguments::add_test_to_run_based_on_verbose_output( int argc, const char* const* argv, - int& index, const char* parameter_name ) { - String wholename = get_parameter_field(argc, argv, index, parameter_name); - String testname = sub_string_from_till(wholename, ',', ')'); + ParsedField field = get_parameter_field(argc, argv, parameter_name); + String testname = sub_string_from_till(field.value, ',', ')'); testname = testname.substr(2); auto* namefilter = new Filter(testname); auto* groupfilter = - new Filter(sub_string_from_till(wholename, wholename[0], ',')); + new Filter(sub_string_from_till(field.value, field.value[0], ',')); namefilter->strict_matching(); groupfilter->strict_matching(); group_filters_ = groupfilter->add(group_filters_); name_filters_ = namefilter->add(name_filters_); + return field.extra; } } // namespace test From cf0ba57f2a5d055f8a8d417ec392b605fa1d17e2 Mon Sep 17 00:00:00 2001 From: Chad Condon Date: Fri, 24 Apr 2026 19:31:53 -0700 Subject: [PATCH 2/7] refactor Plugin::parse_arguments to take a pre-sliced (argc, argv) view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parse_arguments() and parse_all_arguments() no longer take a global index. Callers pass (remaining_argc, argv + current_pos) so that argv[0] is always the argument under inspection — consistent with the same convention now used throughout CommandLineArguments. parse_prefix_arg() and parse_argument() follow suit: they now accept (int argc, const char* const* argv, Plugin*) and argv[0] is the current flag, eliminating the last index/argument-string parameters from the internal parsing chain. JUnitOutputPlugin no longer derives a package name from the binary path (argv[0] in the old global array). Plain -pjunit leaves package_name_ empty; -pjunit= still sets it explicitly. --- examples/tests/TeamCityOutputPlugin.cpp | 5 +- examples/tests/TeamCityOutputPlugin.hpp | 2 +- include/mu/tiny/test/CommandLineArguments.hpp | 4 +- include/mu/tiny/test/JUnitOutputPlugin.hpp | 7 +-- include/mu/tiny/test/Plugin.hpp | 32 +++++------ include/mu/tiny/test/TapOutputPlugin.hpp | 7 +-- src/test/CommandLineArguments.cpp | 57 +++++++++---------- src/test/JUnitOutputPlugin.cpp | 27 +-------- src/test/Plugin.cpp | 13 ++--- src/test/TapOutputPlugin.cpp | 8 +-- tests/src/CommandLineArguments.test.cpp | 6 +- tests/src/CommandLineTestRunner.test.cpp | 8 +-- tests/src/CompositeOutput.test.cpp | 6 +- tests/src/JUnitOutput.test.cpp | 14 ++--- tests/src/TapOutput.test.cpp | 12 ++-- tests/src/TestPlugin.test.cpp | 10 ++-- 16 files changed, 83 insertions(+), 135 deletions(-) diff --git a/examples/tests/TeamCityOutputPlugin.cpp b/examples/tests/TeamCityOutputPlugin.cpp index 179932aff..985dea169 100644 --- a/examples/tests/TeamCityOutputPlugin.cpp +++ b/examples/tests/TeamCityOutputPlugin.cpp @@ -112,11 +112,10 @@ TeamCityOutputPlugin::TeamCityOutputPlugin() bool TeamCityOutputPlugin::parse_arguments( int /*argc*/, - const char* const* argv, - int index + const char* const* argv ) { - mu::tiny::String arg = argv[index]; + mu::tiny::String arg = argv[0]; if (arg.size() > 2 && mu::tiny::String(arg.c_str() + 2) == "teamcity") { active_ = true; return true; diff --git a/examples/tests/TeamCityOutputPlugin.hpp b/examples/tests/TeamCityOutputPlugin.hpp index 2d8d495cb..8021fc384 100644 --- a/examples/tests/TeamCityOutputPlugin.hpp +++ b/examples/tests/TeamCityOutputPlugin.hpp @@ -28,7 +28,7 @@ class TeamCityOutputPlugin : public mu::tiny::test::Plugin public: TeamCityOutputPlugin(); - bool parse_arguments(int argc, const char* const* argv, int index) override; + bool parse_arguments(int argc, const char* const* argv) override; mu::tiny::String get_help() const override; mu::tiny::test::Output* create_output() override; diff --git a/include/mu/tiny/test/CommandLineArguments.hpp b/include/mu/tiny/test/CommandLineArguments.hpp index 2f4d34ebe..252b2c5e6 100644 --- a/include/mu/tiny/test/CommandLineArguments.hpp +++ b/include/mu/tiny/test/CommandLineArguments.hpp @@ -139,8 +139,8 @@ class MUTINY_EXPORT CommandLineArguments const char* parameter_name ); bool parse_simple_flag(const String& argument); - int parse_prefix_arg(const String& argument, Plugin* plugin, int index); - int parse_argument(const String& argument, Plugin* plugin, int index); + int parse_prefix_arg(int argc, const char* const* argv, Plugin* plugin); + int parse_argument(int argc, const char* const* argv, Plugin* plugin); }; } // namespace test diff --git a/include/mu/tiny/test/JUnitOutputPlugin.hpp b/include/mu/tiny/test/JUnitOutputPlugin.hpp index 37e014ac4..56f3519e4 100644 --- a/include/mu/tiny/test/JUnitOutputPlugin.hpp +++ b/include/mu/tiny/test/JUnitOutputPlugin.hpp @@ -35,12 +35,11 @@ class MUTINY_EXPORT JUnitOutputPlugin : public Plugin /** * @brief Handle the `-pjunit[=]` command-line argument. * - * @param argc Argument count. - * @param argv Argument vector. - * @param index Current argument index. + * @param argc Remaining argument count (@p argv[0] through end). + * @param argv Pointer to the current argument. * @return true if the argument was consumed. */ - bool parse_arguments(int argc, const char* const* argv, int index) override; + bool parse_arguments(int argc, const char* const* argv) override; /** * @brief Return help text for the `-pjunit` option. diff --git a/include/mu/tiny/test/Plugin.hpp b/include/mu/tiny/test/Plugin.hpp index 4d278a868..363a74589 100644 --- a/include/mu/tiny/test/Plugin.hpp +++ b/include/mu/tiny/test/Plugin.hpp @@ -67,15 +67,15 @@ class MUTINY_EXPORT Plugin /** * @brief Handle a custom command-line argument. * - * Called for each argument position @p index. Return true to indicate the - * argument was consumed (the runner will skip to the next one). + * Called with a pre-sliced view where @p argv[0] is the argument to inspect + * and @p argc is the number of remaining arguments (including @p argv[0]). + * Return true to indicate the argument was consumed. * - * @param argc Total argument count. - * @param argv Argument vector. - * @param index Current argument index to inspect. - * @return true if the argument at @p index was consumed. + * @param argc Remaining argument count (@p argv[0] through end). + * @param argv Pointer to the current argument. + * @return true if @p argv[0] was consumed. */ - virtual bool parse_arguments(int argc, const char* const* argv, int index); + virtual bool parse_arguments(int argc, const char* const* argv); /** * @brief Return a help string for this plugin's command-line arguments. @@ -110,24 +110,18 @@ class MUTINY_EXPORT Plugin /** * @brief Invoke parse_arguments() on each plugin in the chain. - * @param argc Argument count. - * @param argv Argument vector (const). - * @param index Current argument index. + * @param argc Remaining argument count (@p argv[0] through end). + * @param argv Pointer to the current argument. * @return true if any plugin consumed the argument. */ - virtual bool parse_all_arguments( - int argc, - const char* const* argv, - int index - ); + virtual bool parse_all_arguments(int argc, const char* const* argv); /** * @brief Overload accepting a non-const argv. - * @param argc Argument count. - * @param argv Argument vector. - * @param index Current argument index. + * @param argc Remaining argument count (@p argv[0] through end). + * @param argv Pointer to the current argument. * @return true if any plugin consumed the argument. */ - virtual bool parse_all_arguments(int argc, char** argv, int index); + virtual bool parse_all_arguments(int argc, char** argv); /** @return Concatenated help strings from all plugins in the chain. */ virtual String get_all_help() const; diff --git a/include/mu/tiny/test/TapOutputPlugin.hpp b/include/mu/tiny/test/TapOutputPlugin.hpp index ca960d4db..b1d59e057 100644 --- a/include/mu/tiny/test/TapOutputPlugin.hpp +++ b/include/mu/tiny/test/TapOutputPlugin.hpp @@ -35,12 +35,11 @@ class MUTINY_EXPORT TapOutputPlugin : public Plugin /** * @brief Handle the @c -ptap command-line argument. * - * @param argc Argument count. - * @param argv Argument vector. - * @param index Current argument index. + * @param argc Remaining argument count (@p argv[0] through end). + * @param argv Pointer to the current argument. * @return true if the argument was consumed. */ - bool parse_arguments(int argc, const char* const* argv, int index) override; + bool parse_arguments(int argc, const char* const* argv) override; /** * @brief Return help text for the @c -ptap option. diff --git a/src/test/CommandLineArguments.cpp b/src/test/CommandLineArguments.cpp index b898dcd70..b9cafbf55 100644 --- a/src/test/CommandLineArguments.cpp +++ b/src/test/CommandLineArguments.cpp @@ -138,85 +138,82 @@ bool CommandLineArguments::parse_simple_flag(const String& argument) } int CommandLineArguments::parse_prefix_arg( - const String& argument, - Plugin* plugin, - int index + int argc, + const char* const* argv, + Plugin* plugin ) { - int remaining = ac_ - index; - const char* const* args = av_ + index; + String argument(argv[0]); if (string_starts_with(argument, "-r")) { - return set_repeat_count(remaining, args); + return set_repeat_count(argc, argv); } if (string_starts_with(argument, "-g")) { - return add_group_filter(remaining, args); + return add_group_filter(argc, argv); } if (string_starts_with(argument, "-t")) { - return add_group_dot_name_filter(remaining, args, "-t", false, false); + return add_group_dot_name_filter(argc, argv, "-t", false, false); } if (string_starts_with(argument, "-st")) { - return add_group_dot_name_filter(remaining, args, "-st", true, false); + return add_group_dot_name_filter(argc, argv, "-st", true, false); } if (string_starts_with(argument, "-xt")) { - return add_group_dot_name_filter(remaining, args, "-xt", false, true); + return add_group_dot_name_filter(argc, argv, "-xt", false, true); } if (string_starts_with(argument, "-xst")) { - return add_group_dot_name_filter(remaining, args, "-xst", true, true); + return add_group_dot_name_filter(argc, argv, "-xst", true, true); } if (string_starts_with(argument, "-sg")) { - return add_strict_group_filter(remaining, args); + return add_strict_group_filter(argc, argv); } if (string_starts_with(argument, "-xg")) { - return add_exclude_group_filter(remaining, args); + return add_exclude_group_filter(argc, argv); } if (string_starts_with(argument, "-xsg")) { - return add_exclude_strict_group_filter(remaining, args); + return add_exclude_strict_group_filter(argc, argv); } if (string_starts_with(argument, "-n")) { - return add_name_filter(remaining, args); + return add_name_filter(argc, argv); } if (string_starts_with(argument, "-sn")) { - return add_strict_name_filter(remaining, args); + return add_strict_name_filter(argc, argv); } if (string_starts_with(argument, "-xn")) { - return add_exclude_name_filter(remaining, args); + return add_exclude_name_filter(argc, argv); } if (string_starts_with(argument, "-xsn")) { - return add_exclude_strict_name_filter(remaining, args); + return add_exclude_strict_name_filter(argc, argv); } if (string_starts_with(argument, "-s")) { - return set_shuffle(remaining, args); + return set_shuffle(argc, argv); } if (string_starts_with(argument, "TEST(")) { - return add_test_to_run_based_on_verbose_output(remaining, args, "TEST("); + return add_test_to_run_based_on_verbose_output(argc, argv, "TEST("); } if (string_starts_with(argument, "SKIPPED_TEST(")) { - return add_test_to_run_based_on_verbose_output( - remaining, args, "SKIPPED_TEST(" - ); + return add_test_to_run_based_on_verbose_output(argc, argv, "SKIPPED_TEST("); } if (string_starts_with(argument, "-p")) { - return plugin->parse_all_arguments(ac_, av_, index) ? 0 : -1; + return plugin->parse_all_arguments(argc, argv) ? 0 : -1; } return -1; } int CommandLineArguments::parse_argument( - const String& argument, - Plugin* plugin, - int index + int argc, + const char* const* argv, + Plugin* plugin ) { - if (parse_simple_flag(argument)) { + if (parse_simple_flag(String(argv[0]))) { return 0; } - return parse_prefix_arg(argument, plugin, index); + return parse_prefix_arg(argc, argv, plugin); } bool CommandLineArguments::parse(Plugin* plugin) { for (int i = 1; i < ac_; i++) { - int extra = parse_argument(av_[i], plugin, i); + int extra = parse_argument(ac_ - i, av_ + i, plugin); if (extra < 0) { return false; } diff --git a/src/test/JUnitOutputPlugin.cpp b/src/test/JUnitOutputPlugin.cpp index f311250fb..ab740bea1 100644 --- a/src/test/JUnitOutputPlugin.cpp +++ b/src/test/JUnitOutputPlugin.cpp @@ -8,36 +8,15 @@ namespace mu { namespace tiny { namespace test { -namespace { - -String basename_from_path(const char* path) -{ - if ((path == nullptr) || (*path == 0)) { - return ""; - } - const char* base = path; - for (const char* p = path; *p != 0; ++p) { - if (*p == '/' || *p == '\\') { - base = p + 1; - } - } - return base; -} - -} // namespace JUnitOutputPlugin::JUnitOutputPlugin() : Plugin("JUnitOutputPlugin") { } -bool JUnitOutputPlugin::parse_arguments( - int /*argc*/, - const char* const* argv, - int index -) +bool JUnitOutputPlugin::parse_arguments(int /*argc*/, const char* const* argv) { - String arg = argv[index]; + String arg = argv[0]; constexpr char flag[] = "-pjunit"; constexpr size_t flag_len = sizeof(flag) / sizeof(flag[0]); @@ -49,8 +28,6 @@ bool JUnitOutputPlugin::parse_arguments( return false; } package_name_ = arg.substr(flag_len); - } else { - package_name_ = basename_from_path(argv[0]); } active_ = true; return true; diff --git a/src/test/Plugin.cpp b/src/test/Plugin.cpp index 6f7517f14..2038eeb54 100644 --- a/src/test/Plugin.cpp +++ b/src/test/Plugin.cpp @@ -47,24 +47,23 @@ void Plugin::run_all_post_test_action(Shell& test, Result& result) } } -bool Plugin:: - parse_arguments(int /*argc*/, const char* const* /*argv*/, int /*index*/) +bool Plugin::parse_arguments(int /*argc*/, const char* const* /*argv*/) { return false; } -bool Plugin::parse_all_arguments(int argc, char** argv, int index) +bool Plugin::parse_all_arguments(int argc, char** argv) { - return parse_all_arguments(argc, const_cast(argv), index); + return parse_all_arguments(argc, const_cast(argv)); } -bool Plugin::parse_all_arguments(int argc, const char* const* argv, int index) +bool Plugin::parse_all_arguments(int argc, const char* const* argv) { - if (parse_arguments(argc, argv, index)) { + if (parse_arguments(argc, argv)) { return true; } if (next_ != nullptr) { - return next_->parse_all_arguments(argc, argv, index); + return next_->parse_all_arguments(argc, argv); } return false; } diff --git a/src/test/TapOutputPlugin.cpp b/src/test/TapOutputPlugin.cpp index ec9f065ad..4685356df 100644 --- a/src/test/TapOutputPlugin.cpp +++ b/src/test/TapOutputPlugin.cpp @@ -11,13 +11,9 @@ TapOutputPlugin::TapOutputPlugin() { } -bool TapOutputPlugin::parse_arguments( - int /*argc*/, - const char* const* argv, - int index -) +bool TapOutputPlugin::parse_arguments(int /*argc*/, const char* const* argv) { - String arg = argv[index]; + String arg = argv[0]; if (arg != "-ptap") { return false; } diff --git a/tests/src/CommandLineArguments.test.cpp b/tests/src/CommandLineArguments.test.cpp index 151a872e8..f0f1b8850 100644 --- a/tests/src/CommandLineArguments.test.cpp +++ b/tests/src/CommandLineArguments.test.cpp @@ -14,11 +14,7 @@ class OptionsPlugin : public mu::tiny::test::Plugin { } ~OptionsPlugin() override = default; - bool parse_arguments( - int /*argc*/, - const char* const* /*argv*/, - int /*index*/ - ) override + bool parse_arguments(int /*argc*/, const char* const* /*argv*/) override { return true; } diff --git a/tests/src/CommandLineTestRunner.test.cpp b/tests/src/CommandLineTestRunner.test.cpp index 8ae86b33a..c28bb4e8f 100644 --- a/tests/src/CommandLineTestRunner.test.cpp +++ b/tests/src/CommandLineTestRunner.test.cpp @@ -21,11 +21,7 @@ class DummyPluginWhichCountsThePlugins : public mu::tiny::test::Plugin { } - bool parse_arguments( - int /*argc*/, - const char* const* /*argv*/, - int /*index*/ - ) override + bool parse_arguments(int /*argc*/, const char* const* /*argv*/) override { /* Remove ourselves from the count */ amount_of_plugins = registry_->count_plugins() - 1; @@ -460,7 +456,7 @@ TEST(CommandLineRunner, realJunitOutputShouldBeCreatedAndWorkProperly) fake_output.restore_originals(); STRCMP_CONTAINS( - "parse_arguments(2, argv, 1); + const char* argv[] = { junit_arg }; + plugin->parse_arguments(1, argv); output = plugin->create_output(); result = new mu::tiny::test::Result(*output); test_case_runner = new JUnitTestOutputTestRunner(*result); @@ -1179,16 +1179,16 @@ TEST(JUnitOutput, TestCaseBlockForSkippedTestEscapesXmlInMessage) ); } -TEST(JUnitOutput, parseArguments_derivesBasenameFromPathInArgv0) +TEST(JUnitOutput, parseArguments_pjunitWithoutName_succeeds) { mu::tiny::test::JUnitOutputPlugin p; - const char* argv[] = { "path/to/binary", "-pjunit" }; - CHECK(p.parse_arguments(2, argv, 1)); + const char* argv[] = { "-pjunit" }; + CHECK(p.parse_arguments(1, argv)); } TEST(JUnitOutput, parseArguments_pjunitWithInvalidSuffix_returnsFalse) { mu::tiny::test::JUnitOutputPlugin p; - const char* argv[] = { "", "-pjunitX" }; - CHECK(!p.parse_arguments(2, argv, 1)); + const char* argv[] = { "-pjunitX" }; + CHECK(!p.parse_arguments(1, argv)); } diff --git a/tests/src/TapOutput.test.cpp b/tests/src/TapOutput.test.cpp index d496cdf1c..b01aa0d26 100644 --- a/tests/src/TapOutput.test.cpp +++ b/tests/src/TapOutput.test.cpp @@ -210,8 +210,8 @@ TEST_GROUP(TapOutput) MUTINY_PTR_SET(mu::tiny::test::Output::fputs_, mock_tap_fputs); plugin = new mu::tiny::test::TapOutputPlugin(); - const char* argv[] = { "", "-ptap" }; - plugin->parse_arguments(2, argv, 1); + const char* argv[] = { "-ptap" }; + plugin->parse_arguments(1, argv); output = plugin->create_output(); result = new mu::tiny::test::Result(*output); test_case_runner = new TapTestOutputTestRunner(*result); @@ -382,8 +382,8 @@ TEST(TapOutput, secondRunResetsResults) TEST(TapOutput, parseArguments_ptapIsRecognised) { mu::tiny::test::TapOutputPlugin p; - const char* argv[] = { "", "-ptap" }; - CHECK(p.parse_arguments(2, argv, 1)); + const char* argv[] = { "-ptap" }; + CHECK(p.parse_arguments(1, argv)); auto* created = p.create_output(); CHECK(created != nullptr); delete created; @@ -392,8 +392,8 @@ TEST(TapOutput, parseArguments_ptapIsRecognised) TEST(TapOutput, parseArguments_otherArgReturnsFalse) { mu::tiny::test::TapOutputPlugin p; - const char* argv[] = { "", "-pjunit" }; - CHECK(!p.parse_arguments(2, argv, 1)); + const char* argv[] = { "-pjunit" }; + CHECK(!p.parse_arguments(1, argv)); CHECK(p.create_output() == nullptr); } diff --git a/tests/src/TestPlugin.test.cpp b/tests/src/TestPlugin.test.cpp index e2b5e17e7..f66b960f7 100644 --- a/tests/src/TestPlugin.test.cpp +++ b/tests/src/TestPlugin.test.cpp @@ -53,13 +53,13 @@ class DummyPluginWhichAcceptsParameters : public DummyPlugin { } - bool parse_arguments(int argc, const char* const* argv, int index) override + bool parse_arguments(int argc, const char* const* argv) override { - mu::tiny::String argument(argv[index]); + mu::tiny::String argument(argv[0]); if (argument == "-paccept") { return true; } - return Plugin::parse_arguments(argc, argv, index); + return Plugin::parse_arguments(argc, argv); } }; @@ -166,7 +166,7 @@ TEST(Plugin, ParseArgumentsForUnknownArgumentsFails) const char* cmd_line[] = { "nonsense", "andmorenonsense" }; CHECK( registry->get_first_plugin()->parse_all_arguments( - 2, const_cast(cmd_line), 0 + 2, const_cast(cmd_line) ) == false ); /* cover non-const wrapper, too */ } @@ -176,6 +176,6 @@ TEST(Plugin, ParseArgumentsContinuesAndSucceedsWhenAPluginCanParse) registry->install_plugin(second_plugin); const char* cmd_line[] = { "-paccept", "andmorenonsense" }; CHECK(registry->get_first_plugin()->parse_all_arguments( - 2, const_cast(cmd_line), 0 + 2, const_cast(cmd_line) )); /* cover non-const wrapper, too */ } From 452e2657bca86d2b4ca825080b788be430ce9ae9 Mon Sep 17 00:00:00 2001 From: Chad Condon Date: Fri, 24 Apr 2026 19:42:17 -0700 Subject: [PATCH 3/7] restore binary basename as JUnitOutputPlugin default package name JUnitOutputPlugin now takes an optional default_package_name in its constructor, used when -pjunit is given without an explicit = suffix. CommandLineRunner extracts the basename from argv[0] at construction time and passes it to the plugin, restoring the original behaviour where the executable name prefixes JUnit classnames. --- include/mu/tiny/test/CommandLineRunner.hpp | 1 + include/mu/tiny/test/JUnitOutputPlugin.hpp | 12 ++++++++++-- src/test/CommandLineRunner.cpp | 19 ++++++++++++++++++- src/test/JUnitOutputPlugin.cpp | 6 ++++-- tests/src/CommandLineTestRunner.test.cpp | 2 +- tests/src/JUnitOutput.test.cpp | 7 +++++-- 6 files changed, 39 insertions(+), 8 deletions(-) diff --git a/include/mu/tiny/test/CommandLineRunner.hpp b/include/mu/tiny/test/CommandLineRunner.hpp index 1afb6575b..e4cac8f92 100644 --- a/include/mu/tiny/test/CommandLineRunner.hpp +++ b/include/mu/tiny/test/CommandLineRunner.hpp @@ -123,6 +123,7 @@ class MUTINY_EXPORT CommandLineRunner private: CommandLineArguments* arguments_{ nullptr }; Registry* registry_; + String program_name_; bool parse_arguments(Plugin* plugin); int run_all_tests(); diff --git a/include/mu/tiny/test/JUnitOutputPlugin.hpp b/include/mu/tiny/test/JUnitOutputPlugin.hpp index 56f3519e4..20e922503 100644 --- a/include/mu/tiny/test/JUnitOutputPlugin.hpp +++ b/include/mu/tiny/test/JUnitOutputPlugin.hpp @@ -29,8 +29,15 @@ class Output; class MUTINY_EXPORT JUnitOutputPlugin : public Plugin { public: - /** @brief Construct and name the plugin. */ - JUnitOutputPlugin(); + /** + * @brief Construct the plugin with an optional default package name. + * + * @param default_package_name Used as the JUnit package name when + * @c -pjunit is given without an explicit @c = suffix. + * Pass the executable basename to reproduce the standard behaviour. + * Defaults to empty, which leaves the package name unset. + */ + explicit JUnitOutputPlugin(const String& default_package_name = ""); /** * @brief Handle the `-pjunit[=]` command-line argument. @@ -55,6 +62,7 @@ class MUTINY_EXPORT JUnitOutputPlugin : public Plugin private: bool active_{ false }; + String default_package_name_; String package_name_; }; diff --git a/src/test/CommandLineRunner.cpp b/src/test/CommandLineRunner.cpp index 2a7992fbe..8d14d2996 100644 --- a/src/test/CommandLineRunner.cpp +++ b/src/test/CommandLineRunner.cpp @@ -14,6 +14,22 @@ namespace mu { namespace tiny { namespace test { +namespace { +String basename_from_path(const char* path) +{ + if ((path == nullptr) || (*path == 0)) { + return ""; + } + const char* base = path; + for (const char* p = path; *p != 0; ++p) { + if (*p == '/' || *p == '\\') { + base = p + 1; + } + } + return base; +} +} // namespace + int CommandLineRunner::run_all_tests(int argc, char** argv) { return run_all_tests(argc, reinterpret_cast(argv)); @@ -36,6 +52,7 @@ CommandLineRunner::CommandLineRunner( Registry* registry ) : registry_(registry) + , program_name_(argc > 0 ? basename_from_path(argv[0]) : "") { arguments_ = new CommandLineArguments(argc, argv); } @@ -53,7 +70,7 @@ int CommandLineRunner::run_all_tests_main() Plugin* const user_plugins = registry_->get_first_plugin(); SetPointerPlugin set_pointer_plugin; - JUnitOutputPlugin junit_plugin; + JUnitOutputPlugin junit_plugin(program_name_); TapOutputPlugin tap_plugin; registry_->install_plugin(&set_pointer_plugin); registry_->install_plugin(&junit_plugin); diff --git a/src/test/JUnitOutputPlugin.cpp b/src/test/JUnitOutputPlugin.cpp index ab740bea1..c8478f9f7 100644 --- a/src/test/JUnitOutputPlugin.cpp +++ b/src/test/JUnitOutputPlugin.cpp @@ -8,9 +8,9 @@ namespace mu { namespace tiny { namespace test { - -JUnitOutputPlugin::JUnitOutputPlugin() +JUnitOutputPlugin::JUnitOutputPlugin(const String& default_package_name) : Plugin("JUnitOutputPlugin") + , default_package_name_(default_package_name) { } @@ -28,6 +28,8 @@ bool JUnitOutputPlugin::parse_arguments(int /*argc*/, const char* const* argv) return false; } package_name_ = arg.substr(flag_len); + } else { + package_name_ = default_package_name_; } active_ = true; return true; diff --git a/tests/src/CommandLineTestRunner.test.cpp b/tests/src/CommandLineTestRunner.test.cpp index c28bb4e8f..9f1449fb0 100644 --- a/tests/src/CommandLineTestRunner.test.cpp +++ b/tests/src/CommandLineTestRunner.test.cpp @@ -456,7 +456,7 @@ TEST(CommandLineRunner, realJunitOutputShouldBeCreatedAndWorkProperly) fake_output.restore_originals(); STRCMP_CONTAINS( - " Date: Fri, 24 Apr 2026 19:47:42 -0700 Subject: [PATCH 4/7] fix doxygen: escape angle brackets in JUnitOutputPlugin doc comment --- include/mu/tiny/test/JUnitOutputPlugin.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mu/tiny/test/JUnitOutputPlugin.hpp b/include/mu/tiny/test/JUnitOutputPlugin.hpp index 20e922503..22558b56d 100644 --- a/include/mu/tiny/test/JUnitOutputPlugin.hpp +++ b/include/mu/tiny/test/JUnitOutputPlugin.hpp @@ -33,7 +33,7 @@ class MUTINY_EXPORT JUnitOutputPlugin : public Plugin * @brief Construct the plugin with an optional default package name. * * @param default_package_name Used as the JUnit package name when - * @c -pjunit is given without an explicit @c = suffix. + * @c -pjunit is given without an explicit @c =name suffix. * Pass the executable basename to reproduce the standard behaviour. * Defaults to empty, which leaves the package name unset. */ From faaa042c4d8bec797261b18deaf092dd47a0475b Mon Sep 17 00:00:00 2001 From: Chad Condon Date: Fri, 24 Apr 2026 19:51:42 -0700 Subject: [PATCH 5/7] add tests for JUnitOutputPlugin default_package_name behaviour - defaultPackageName_usedWhenNoPjunitSuffix: verifies that the constructor default is reflected in the XML classname and filename when -pjunit is given with no = suffix - defaultPackageName_overriddenByExplicitSuffix: verifies that an explicit -pjunit=name overrides a non-empty constructor default - emptyDefaultPackageName_producesNoPrefix: verifies that an empty default (the no-arg constructor) produces no package prefix in the classname and writes to mutiny.xml --- tests/src/JUnitOutput.test.cpp | 53 +++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/tests/src/JUnitOutput.test.cpp b/tests/src/JUnitOutput.test.cpp index 41209f046..ed3380bb4 100644 --- a/tests/src/JUnitOutput.test.cpp +++ b/tests/src/JUnitOutput.test.cpp @@ -389,13 +389,16 @@ TEST_GROUP(JUnitOutput) // Recreate the JUnit output with the given -pjunit argument. // Use "-pjunit" (no package) for "mutiny.xml"; "-pjunit=name" for "name.xml". - void init(const char* junit_arg = "-pjunit") + void init( + const char* junit_arg = "-pjunit", + const char* default_package_name = "" + ) { delete test_case_runner; delete result; delete output; delete plugin; - plugin = new mu::tiny::test::JUnitOutputPlugin(); + plugin = new mu::tiny::test::JUnitOutputPlugin(default_package_name); const char* argv[] = { junit_arg }; plugin->parse_arguments(1, argv); output = plugin->create_output(); @@ -838,6 +841,45 @@ TEST(JUnitOutput, TestCaseBlockWithAPackageName) STRCMP_EQUAL("\n", output_file->line(5)); } +TEST(JUnitOutput, defaultPackageName_usedWhenNoPjunitSuffix) +{ + init("-pjunit", "mypackage"); + test_case_runner->start().with_group("groupname").with_test("testname").end(); + + output_file = file_system.file("mypackage.xml"); + STRCMP_EQUAL( + "\n", + output_file->line(4) + ); +} + +TEST(JUnitOutput, defaultPackageName_overriddenByExplicitSuffix) +{ + init("-pjunit=explicit", "mydefault"); + test_case_runner->start().with_group("groupname").with_test("testname").end(); + + output_file = file_system.file("explicit.xml"); + STRCMP_EQUAL( + "\n", + output_file->line(4) + ); +} + +TEST(JUnitOutput, emptyDefaultPackageName_producesNoPrefix) +{ + init("-pjunit"); + test_case_runner->start().with_group("groupname").with_test("testname").end(); + + output_file = file_system.file("mutiny.xml"); + STRCMP_EQUAL( + "\n", + output_file->line(4) + ); +} + TEST(JUnitOutput, TestCaseBlockForSkippedTest) { init("-pjunit=packagename"); @@ -1179,14 +1221,11 @@ TEST(JUnitOutput, TestCaseBlockForSkippedTestEscapesXmlInMessage) ); } -TEST(JUnitOutput, parseArguments_pjunitWithoutName_usesDefault) +TEST(JUnitOutput, parseArguments_pjunitWithoutName_succeeds) { - mu::tiny::test::JUnitOutputPlugin p("mypackage"); + mu::tiny::test::JUnitOutputPlugin p; const char* argv[] = { "-pjunit" }; CHECK(p.parse_arguments(1, argv)); - auto* created = p.create_output(); - CHECK(created != nullptr); - delete created; } TEST(JUnitOutput, parseArguments_pjunitWithInvalidSuffix_returnsFalse) From 5206cc84ee8dc28fd3656b82d4f890e78736cf1e Mon Sep 17 00:00:00 2001 From: Chad Condon Date: Fri, 24 Apr 2026 19:57:31 -0700 Subject: [PATCH 6/7] docs: update plugins.rst and junit-output.rst for new parse_arguments API - plugins.rst: remove int index from the parse_arguments signature in the code block; note that argv[0] is the argument to inspect - junit-output.rst: update the manual installation example to show passing a default package name to the JUnitOutputPlugin constructor; document the three-tier fallback for the output filename --- docs/junit-output.rst | 16 ++++++++++++++-- docs/plugins.rst | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/docs/junit-output.rst b/docs/junit-output.rst index d74a87bf3..9540a1a18 100644 --- a/docs/junit-output.rst +++ b/docs/junit-output.rst @@ -42,11 +42,19 @@ Install the plugin via :cpp:class:`Registry ` before c int main(int argc, char** argv) { - mu::tiny::test::JUnitOutputPlugin junit; + // Pass the executable basename so that bare -pjunit writes + // .xml rather than mutiny.xml. + mu::tiny::test::JUnitOutputPlugin junit("my_suite"); mu::tiny::test::Registry::get_current_registry()->install_plugin(&junit); return mu::tiny::test::CommandLineRunner::run_all_tests(argc, argv); } +The string passed to the constructor is used as the package name when +``-pjunit`` is given without an explicit ``=`` suffix. It can be +overridden at runtime with ``-pjunit=``. Passing an empty string +(the default) makes bare ``-pjunit`` write to ``mutiny.xml`` with no +package prefix in classnames. + With the plugin installed, ``-pjunit`` on the command line enables the XML output. Without the flag, no XML is written and the console output is unchanged. @@ -60,7 +68,11 @@ When running the executable directly, a single file is produced for the entire run. The file is named ``.xml``, where ```` comes from: - the ``-pjunit=`` argument if given, or -- the executable basename (e.g. ``my_tests``) if only ``-pjunit`` is used. +- the default package name supplied to the :cpp:class:`JUnitOutputPlugin + ` constructor (the executable basename + when going through :cpp:func:`CommandLineRunner::run_all_tests + `), or +- ``mutiny`` if no default was provided. When using :cmake:variable:`MUTINY_JUNIT_REPORT` with :cmake:command:`mutiny_discover_tests`, CTest runs each test group as a separate process and gives each one a unique diff --git a/docs/plugins.rst b/docs/plugins.rst index b6ad6d172..c7abb7ff2 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -21,7 +21,7 @@ Subclass :cpp:class:`mu::tiny::test::Plugin` to intercept the test lifecycle. virtual void pre_test_action(Shell&, Result&) {} virtual void post_test_action(Shell&, Result&) {} - virtual bool parse_arguments(int argc, const char* const* argv, int index) + virtual bool parse_arguments(int argc, const char* const* argv) { return false; } virtual String get_help() const { return ""; } @@ -44,7 +44,7 @@ Override the methods you need: - Restore pointers, verify mock expectations * - :cpp:func:`parse_arguments() ` - During argument parsing - - Activate plugin via a :option:`-p\` argument + - Inspect ``argv[0]``; activate plugin via a :option:`-p\` argument * - :cpp:func:`get_help() ` - When :option:`-h` is printed - Show plugin's flag description From de968ee267ed1a29d769fad1d48b3d767473037d Mon Sep 17 00:00:00 2001 From: Chad Condon Date: Fri, 24 Apr 2026 20:15:32 -0700 Subject: [PATCH 7/7] add tests for uncovered edge cases in arg parsing and program name extraction Cover get_parameter_field's empty-fallback path (flag with no inline value and no following token) and basename_from_path's null/empty guard. --- tests/src/CommandLineArguments.test.cpp | 8 ++++++++ tests/src/CommandLineTestRunner.test.cpp | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/tests/src/CommandLineArguments.test.cpp b/tests/src/CommandLineArguments.test.cpp index f0f1b8850..59f3e3b7b 100644 --- a/tests/src/CommandLineArguments.test.cpp +++ b/tests/src/CommandLineArguments.test.cpp @@ -179,6 +179,14 @@ TEST(CommandLineArguments, setGroupFilter) CHECK_EQUAL(mu::tiny::test::Filter("group"), *args->get_group_filters()); } +TEST(CommandLineArguments, setGroupFilterWithNoValueUsesEmptyFilter) +{ + int argc = 2; + const char* argv[] = { "tests.exe", "-g" }; + CHECK(new_argument_parser(argc, argv)); + CHECK_EQUAL(mu::tiny::test::Filter(""), *args->get_group_filters()); +} + TEST(CommandLineArguments, setCompleteGroupDotNameFilterInvalidArgument) { int argc = 3; diff --git a/tests/src/CommandLineTestRunner.test.cpp b/tests/src/CommandLineTestRunner.test.cpp index 9f1449fb0..8fc43d2aa 100644 --- a/tests/src/CommandLineTestRunner.test.cpp +++ b/tests/src/CommandLineTestRunner.test.cpp @@ -462,6 +462,27 @@ TEST(CommandLineRunner, realJunitOutputShouldBeCreatedAndWorkProperly) STRCMP_CONTAINS("TEST(group1, test1)", fake_output.console.c_str()); } +TEST(CommandLineRunner, junitPackageNameEmptyWhenArgvZeroIsEmpty) +{ + const char* argv[] = { + "", + "-pjunit", + }; + + FakeOutput fake_output; /* MUTINY_PTR_SET() is not reentrant */ + + mu::tiny::test::CommandLineRunner command_line_test_runner( + 2, argv, ®istry + ); + command_line_test_runner.run_all_tests_main(); + + fake_output.restore_originals(); + + STRCMP_CONTAINS( + "