tests: Add some test coverage for ArgsManager::AddCommand
What changed, and why it matters
This commit only adds new unit tests for an existing argument-parsing helper in Bitcoin Core. It does not change any production code, so it cannot introduce a security vulnerability or fix one. It simply verifies that command-specific options are accepted or rejected as expected.
No action required; this is a test-only change. Reviewers may optionally verify the new tests exercise the intended command-option validation paths.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff adds BOOST_AUTO_TEST_CASE blocks to src/test/argsman_tests.cpp covering ArgsManager::AddCommand, ParseParameters, GetCommand, CheckCommandOptions, ClearArgs, and GetHelpMessage behavior. No source code outside the test suite is modified. There are no functional, consensus, networking, or wallet changes.
Changed components
src/test/argsman_tests.cppInspect captured patch +76 / −0
diff --git a/src/test/argsman_tests.cpp b/src/test/argsman_tests.cpp
index c8802d36..da0d6840 100644
--- a/src/test/argsman_tests.cpp
+++ b/src/test/argsman_tests.cpp
@@ -634,6 +634,82 @@ BOOST_AUTO_TEST_CASE(util_GetArg)
BOOST_CHECK_EQUAL(testArgs.GetArg("pritest4", "default"), "b");
}
+BOOST_AUTO_TEST_CASE(util_AddCommand)
+{
+ enum TestFail { SUCCESS,
+ PARSE_FAIL,
+ PARSE_ERROR,
+ NO_COMMAND,
+ COMMAND_OPTS_BAD_DETAILS,
+ COMMAND_OPTS };
+
+ auto testfn = [&](const auto& argv) -> TestFail {
+ TestArgsManager test_args;
+ test_args.AddArg("-opt1=<file name>", "Opt 1", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::COMMAND_OPTIONS);
+ test_args.AddArg("-opt2=<file name>", "Opt 2", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::COMMAND_OPTIONS);
+ test_args.AddArg("-opt3=<file name>", "Opt 3", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
+
+ test_args.AddCommand("cmd1", "No specific options");
+ test_args.AddCommand("cmd2", "Opt 1", {"-opt1"});
+ test_args.AddCommand("cmd3", "Opt 1 or 2", {"-opt1", "-opt2"});
+
+ std::string error;
+ if (!test_args.ParseParameters(argv.size(), argv.data(), error)) return PARSE_FAIL;
+ if (!error.empty()) return PARSE_ERROR;
+ const auto command = test_args.GetCommand();
+ if (!command) return NO_COMMAND;
+ std::vector<std::string> details;
+ if (!test_args.CheckCommandOptions(command->command, &details)) {
+ if (details.empty()) return COMMAND_OPTS_BAD_DETAILS;
+ return COMMAND_OPTS;
+ } else if (!details.empty()) {
+ return COMMAND_OPTS_BAD_DETAILS;
+ }
+ return SUCCESS;
+ };
+
+ BOOST_CHECK_EQUAL(COMMAND_OPTS, testfn(std::array{"x", "-opt1=foo", "cmd1"}));
+ BOOST_CHECK_EQUAL(SUCCESS, testfn(std::array{"x", "cmd1", "-opt1=foo"})); // things after the command are "args" and left unparsed, not options
+
+ BOOST_CHECK_EQUAL(SUCCESS, testfn(std::array{"x", "-opt1=foo", "cmd2"}));
+ BOOST_CHECK_EQUAL(SUCCESS, testfn(std::array{"x", "-opt1=foo", "cmd3"}));
+ BOOST_CHECK_EQUAL(COMMAND_OPTS, testfn(std::array{"x", "-opt2=foo", "cmd1"}));
+ BOOST_CHECK_EQUAL(COMMAND_OPTS, testfn(std::array{"x", "-opt2=foo", "cmd2"}));
+ BOOST_CHECK_EQUAL(SUCCESS, testfn(std::array{"x", "-opt2=foo", "cmd3"}));
+ BOOST_CHECK_EQUAL(SUCCESS, testfn(std::array{"x", "-opt3=foo", "cmd1"}));
+ BOOST_CHECK_EQUAL(SUCCESS, testfn(std::array{"x", "-opt3=foo", "cmd2"}));
+ BOOST_CHECK_EQUAL(SUCCESS, testfn(std::array{"x", "-opt3=foo", "cmd3"}));
+
+ BOOST_CHECK_EQUAL(COMMAND_OPTS, testfn(std::array{"x", "-opt1=foo", "-opt3=bar", "-opt2=baz", "cmd1"}));
+ BOOST_CHECK_EQUAL(COMMAND_OPTS, testfn(std::array{"x", "-opt1=foo", "-opt3=bar", "-opt2=baz", "cmd2"}));
+ BOOST_CHECK_EQUAL(SUCCESS, testfn(std::array{"x", "-opt1=foo", "-opt3=bar", "-opt2=baz", "cmd3"}));
+
+ BOOST_CHECK_EQUAL(PARSE_FAIL, testfn(std::array{"x", "cmd4"}));
+ BOOST_CHECK_EQUAL(NO_COMMAND, testfn(std::array{"x", "-opt3=foo"}));
+ BOOST_CHECK_EQUAL(PARSE_FAIL, testfn(std::array{"x", "-opt4=foo"}));
+}
+
+BOOST_AUTO_TEST_CASE(util_AddCommand_clearargs_replaces_command_options)
+{
+ const auto add_command{[&](TestArgsManager& test_args, const std::string& option) {
+ test_args.AddArg(option, "option", ArgsManager::ALLOW_ANY, OptionsCategory::COMMAND_OPTIONS);
+ test_args.AddCommand("cmd", "cmd", {option});
+ }};
+
+ TestArgsManager test_args;
+ add_command(test_args, "-opt1");
+ test_args.ClearArgs();
+ add_command(test_args, "-opt2");
+
+ const auto help{test_args.GetHelpMessage()};
+ BOOST_CHECK(help.find("-opt2") != std::string::npos);
+
+ test_args.ForceSetArg("-opt2", "1");
+ std::vector<std::string> details;
+ BOOST_CHECK(test_args.CheckCommandOptions("cmd", &details));
+ BOOST_CHECK(details.empty());
+}
+
BOOST_AUTO_TEST_CASE(util_GetChainTypeString)
{
TestArgsManager test_args;
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.