// Copyright (c) 2017-2024, University of Cincinnati, developed by Henry Schreiner // under NSF AWARD 1414736 and by the respective contributors. // All rights reserved. // // SPDX-License-Identifier: BSD-3-Clause #include "app_helper.hpp" #include #include #include using vs_t = std::vector; TEST_CASE_METHOD(TApp, "BasicOptionGroup", "[optiongroup]") { auto *ogroup = app.add_option_group("clusters"); int res = 0; ogroup->add_option("--test1", res); ogroup->add_option("--test2", res); ogroup->add_option("--test3", res); args = {"--test1", "5"}; run(); CHECK(5 == res); CHECK(1u == app.count_all()); } TEST_CASE_METHOD(TApp, "OptionGroupInvalidNames", "[optiongroup]") { CHECK_THROWS_AS(app.add_option_group("clusters\ncluster2", "description"), CLI::IncorrectConstruction); std::string groupName("group1"); groupName += '\0'; groupName.append("group2"); CHECK_THROWS_AS(app.add_option_group(groupName), CLI::IncorrectConstruction); } TEST_CASE_METHOD(TApp, "BasicOptionGroupExact", "[optiongroup]") { auto *ogroup = app.add_option_group("clusters"); int res{0}; ogroup->add_option("--test1", res); ogroup->add_option("--test2", res); ogroup->add_option("--test3", res); int val2{0}; app.add_option("--option", val2); ogroup->require_option(1); args = {"--test1", "5"}; run(); CHECK(5 == res); args = {"--test1", "5", "--test2", "4"}; CHECK_THROWS_AS(run(), CLI::RequiredError); args = {"--option", "9"}; CHECK_THROWS_AS(run(), CLI::RequiredError); std::string help = ogroup->help(); auto exactloc = help.find("[Exactly 1"); CHECK(std::string::npos != exactloc); } TEST_CASE_METHOD(TApp, "BasicOptionGroupExactTooMany", "[optiongroup]") { auto *ogroup = app.add_option_group("clusters"); int res{0}; ogroup->add_option("--test1", res); ogroup->add_option("--test2", res); ogroup->add_option("--test3", res); int val2{0}; app.add_option("--option", val2); ogroup->require_option(10); args = {"--test1", "5"}; CHECK_THROWS_AS(run(), CLI::InvalidError); } TEST_CASE_METHOD(TApp, "BasicOptionGroupMinMax", "[optiongroup]") { auto *ogroup = app.add_option_group("clusters"); int res{0}; ogroup->add_option("--test1", res); ogroup->add_option("--test2", res); ogroup->add_option("--test3", res); int val2{0}; app.add_option("--option", val2); ogroup->require_option(1, 1); args = {"--test1", "5"}; run(); CHECK(5 == res); args = {"--test1", "5", "--test2", "4"}; CHECK_THROWS_AS(run(), CLI::RequiredError); args = {"--option", "9"}; CHECK_THROWS_AS(run(), CLI::RequiredError); std::string help = ogroup->help(); auto exactloc = help.find("[Exactly 1"); CHECK(std::string::npos != exactloc); } TEST_CASE_METHOD(TApp, "BasicOptionGroupMinMaxDifferent", "[optiongroup]") { auto *ogroup = app.add_option_group("clusters"); int res{0}; ogroup->add_option("--test1", res); ogroup->add_option("--test2", res); ogroup->add_option("--test3", res); int val2{0}; app.add_option("--option", val2); ogroup->require_option(1, 2); args = {"--test1", "5"}; run(); CHECK(5 == res); args = {"--test1", "5", "--test2", "4"}; CHECK_NOTHROW(run()); CHECK(2u == app.count_all()); args = {"--option", "9"}; CHECK_THROWS_AS(run(), CLI::RequiredError); args = {"--test1", "5", "--test2", "4", "--test3=5"}; CHECK_THROWS_AS(run(), CLI::RequiredError); std::string help = ogroup->help(); auto exactloc = help.find("[Between 1 and 2"); CHECK(std::string::npos != exactloc); } TEST_CASE_METHOD(TApp, "BasicOptionGroupMinMaxDifferentReversed", "[optiongroup]") { auto *ogroup = app.add_option_group("clusters"); int res{0}; ogroup->add_option("--test1", res); ogroup->add_option("--test2", res); ogroup->add_option("--test3", res); int val2{0}; app.add_option("--option", val2); ogroup->require_option(2, 1); CHECK(2u == ogroup->get_require_option_min()); CHECK(1u == ogroup->get_require_option_max()); args = {"--test1", "5"}; CHECK_THROWS_AS(run(), CLI::InvalidError); ogroup->require_option(1, 2); CHECK_NOTHROW(run()); CHECK(5 == res); CHECK(1u == ogroup->get_require_option_min()); CHECK(2u == ogroup->get_require_option_max()); args = {"--test1", "5", "--test2", "4"}; CHECK_NOTHROW(run()); args = {"--option", "9"}; CHECK_THROWS_AS(run(), CLI::RequiredError); args = {"--test1", "5", "--test2", "4", "--test3=5"}; CHECK_THROWS_AS(run(), CLI::RequiredError); std::string help = ogroup->help(); auto exactloc = help.find("[Between 1 and 2"); CHECK(std::string::npos != exactloc); } TEST_CASE_METHOD(TApp, "BasicOptionGroupMax", "[optiongroup]") { auto *ogroup = app.add_option_group("clusters"); int res{0}; ogroup->add_option("--test1", res); ogroup->add_option("--test2", res); ogroup->add_option("--test3", res); int val2 = 0; app.add_option("--option", val2); ogroup->require_option(-2); args = {"--test1", "5"}; run(); CHECK(5 == res); args = {"--option", "9"}; CHECK_NOTHROW(run()); args = {"--test1", "5", "--test2", "4", "--test3=5"}; CHECK_THROWS_AS(run(), CLI::RequiredError); std::string help = ogroup->help(); auto exactloc = help.find("[At most 2"); CHECK(std::string::npos != exactloc); } TEST_CASE_METHOD(TApp, "BasicOptionGroupMax1", "[optiongroup]") { auto *ogroup = app.add_option_group("clusters"); int res{0}; ogroup->add_option("--test1", res); ogroup->add_option("--test2", res); ogroup->add_option("--test3", res); int val2{0}; app.add_option("--option", val2); ogroup->require_option(-1); args = {"--test1", "5"}; run(); CHECK(5 == res); args = {"--option", "9"}; CHECK_NOTHROW(run()); args = {"--test1", "5", "--test2", "4"}; CHECK_THROWS_AS(run(), CLI::RequiredError); std::string help = ogroup->help(); auto exactloc = help.find("[At most 1"); CHECK(std::string::npos != exactloc); } TEST_CASE_METHOD(TApp, "BasicOptionGroupMin", "[optiongroup]") { auto *ogroup = app.add_option_group("clusters"); int res{0}; ogroup->add_option("--test1", res); ogroup->add_option("--test2", res); ogroup->add_option("--test3", res); int val2{0}; app.add_option("--option", val2); ogroup->require_option(); args = {"--option", "9"}; CHECK_THROWS_AS(run(), CLI::RequiredError); args = {"--test1", "5", "--test2", "4", "--test3=5"}; CHECK_NOTHROW(run()); std::string help = ogroup->help(); auto exactloc = help.find("[At least 1"); CHECK(std::string::npos != exactloc); } TEST_CASE_METHOD(TApp, "integratedOptionGroup", "[optiongroup]") { auto *ogroup = app.add_option_group("+clusters"); int res{0}; ogroup->add_option("--test1", res); ogroup->add_option("--test2", res); ogroup->add_option("--test3", res); int val2{0}; app.add_option("--option", val2); ogroup->require_option(); args = {"--option", "9"}; CHECK_THROWS_AS(run(), CLI::RequiredError); args = {"--test1", "5", "--test2", "4", "--test3=5"}; CHECK_NOTHROW(run()); auto options = app.get_options(); CHECK(options.size() == 5); const CLI::App *capp = &app; auto coptions = capp->get_options(); CHECK(coptions.size() == 5); std::string help = app.help(); auto exactloc = help.find("clusters"); CHECK(std::string::npos == exactloc); } TEST_CASE_METHOD(TApp, "BasicOptionGroupExact2", "[optiongroup]") { auto *ogroup = app.add_option_group("clusters"); int res{0}; ogroup->add_option("--test1", res); ogroup->add_option("--test2", res); ogroup->add_option("--test3", res); int val2{0}; app.add_option("--option", val2); ogroup->require_option(2); args = {"--option", "9"}; CHECK_THROWS_AS(run(), CLI::RequiredError); args = {"--test1", "5", "--test2", "4", "--test3=5"}; CHECK_THROWS_AS(run(), CLI::RequiredError); args = {"--test1", "5", "--test3=5"}; CHECK_NOTHROW(run()); std::string help = ogroup->help(); auto exactloc = help.find("[Exactly 2"); CHECK(std::string::npos != exactloc); } TEST_CASE_METHOD(TApp, "BasicOptionGroupMin2", "[optiongroup]") { auto *ogroup = app.add_option_group("clusters"); int res{0}; ogroup->add_option("--test1", res); ogroup->add_option("--test2", res); ogroup->add_option("--test3", res); int val2{0}; app.add_option("--option", val2); ogroup->require_option(2, 0); args = {"--option", "9"}; CHECK_THROWS_AS(run(), CLI::RequiredError); args = {"--test1", "5", "--test2", "4", "--test3=5"}; CHECK_NOTHROW(run()); std::string help = ogroup->help(); auto exactloc = help.find("[At least 2"); CHECK(std::string::npos != exactloc); } TEST_CASE_METHOD(TApp, "BasicOptionGroupMinMoved", "[optiongroup]") { int res{0}; auto *opt1 = app.add_option("--test1", res); auto *opt2 = app.add_option("--test2", res); auto *opt3 = app.add_option("--test3", res); int val2{0}; app.add_option("--option", val2); auto *ogroup = app.add_option_group("clusters"); ogroup->require_option(); ogroup->add_option(opt1); ogroup->add_option(opt2); ogroup->add_option(opt3); args = {"--option", "9"}; CHECK_THROWS_AS(run(), CLI::RequiredError); args = {"--test1", "5", "--test2", "4", "--test3=5"}; CHECK_NOTHROW(run()); std::string help = app.help(); auto exactloc = help.find("[At least 1"); auto oloc = help.find("--test1"); CHECK(std::string::npos != exactloc); CHECK(std::string::npos != oloc); CHECK(oloc > exactloc); } TEST_CASE_METHOD(TApp, "BasicOptionGroupMinMovedAsGroup", "[optiongroup]") { int res{0}; auto *opt1 = app.add_option("--test1", res); auto *opt2 = app.add_option("--test2", res); auto *opt3 = app.add_option("--test3", res); int val2{0}; app.add_option("--option", val2); auto *ogroup = app.add_option_group("clusters"); ogroup->require_option(); ogroup->add_options(opt1, opt2, opt3); CHECK_THROWS_AS(ogroup->add_options(opt1), CLI::OptionNotFound); args = {"--option", "9"}; CHECK_THROWS_AS(run(), CLI::RequiredError); args = {"--test1", "5", "--test2", "4", "--test3=5"}; CHECK_NOTHROW(run()); std::string help = app.help(); auto exactloc = help.find("[At least 1"); auto oloc = help.find("--test1"); CHECK(std::string::npos != exactloc); CHECK(std::string::npos != oloc); CHECK(oloc > exactloc); } TEST_CASE_METHOD(TApp, "BasicOptionGroupAddFailures", "[optiongroup]") { int res{0}; auto *opt1 = app.add_option("--test1", res); app.set_config("--config"); int val2{0}; app.add_option("--option", val2); auto *ogroup = app.add_option_group("clusters"); CHECK_THROWS_AS(ogroup->add_options(app.get_config_ptr()), CLI::OptionAlreadyAdded); CHECK_THROWS_AS(ogroup->add_options(app.get_help_ptr()), CLI::OptionAlreadyAdded); auto *sub = app.add_subcommand("sub", "subcommand"); auto *opt2 = sub->add_option("--option2", val2); CHECK_THROWS_AS(ogroup->add_option(opt2), CLI::OptionNotFound); CHECK_THROWS_AS(ogroup->add_options(nullptr), CLI::OptionNotFound); ogroup->add_option(opt1); auto *opt3 = app.add_option("--test1", res); CHECK_THROWS_AS(ogroup->add_option(opt3), CLI::OptionAlreadyAdded); } TEST_CASE_METHOD(TApp, "BasicOptionGroupScrewedUpMove", "[optiongroup]") { int res{0}; auto *opt1 = app.add_option("--test1", res); auto *opt2 = app.add_option("--test2", res); int val2{0}; app.add_option("--option", val2); auto *ogroup = app.add_option_group("clusters"); ogroup->require_option(); auto *ogroup2 = ogroup->add_option_group("clusters2"); CHECK_THROWS_AS(ogroup2->add_options(opt1, opt2), CLI::OptionNotFound); CLI::Option_group EmptyGroup("description", "new group", nullptr); CHECK_THROWS_AS(EmptyGroup.add_option(opt2), CLI::OptionNotFound); CHECK_THROWS_AS(app._move_option(opt2, ogroup2), CLI::OptionNotFound); } TEST_CASE_METHOD(TApp, "InvalidOptions", "[optiongroup]") { auto *ogroup = app.add_option_group("clusters"); CLI::Option *opt = nullptr; CHECK_THROWS_AS(ogroup->excludes(opt), CLI::OptionNotFound); CLI::App *app_p = nullptr; CHECK_THROWS_AS(ogroup->excludes(app_p), CLI::OptionNotFound); CHECK_THROWS_AS(ogroup->excludes(ogroup), CLI::OptionNotFound); CHECK_THROWS_AS(ogroup->add_option(opt), CLI::OptionNotFound); } TEST_CASE_METHOD(TApp, "OptionGroupInheritedOptionDefaults", "[optiongroup]") { app.option_defaults()->ignore_case(); auto *ogroup = app.add_option_group("clusters"); int res{0}; ogroup->add_option("--test1", res); args = {"--Test1", "5"}; run(); CHECK(5 == res); CHECK(1u == app.count_all()); } struct ManyGroups : public TApp { CLI::Option_group *main{nullptr}; CLI::Option_group *g1{nullptr}; CLI::Option_group *g2{nullptr}; CLI::Option_group *g3{nullptr}; std::string name1{}; std::string name2{}; std::string name3{}; std::string val1{}; std::string val2{}; std::string val3{}; ManyGroups(const ManyGroups &) = delete; ManyGroups &operator=(const ManyGroups &) = delete; ManyGroups() { main = app.add_option_group("main", "the main outer group"); g1 = main->add_option_group("g1", "group1 description"); g2 = main->add_option_group("g2", "group2 description"); g3 = main->add_option_group("g3", "group3 description"); g1->add_option("--name1", name1)->required(); g1->add_option("--val1", val1); g2->add_option("--name2", name2)->required(); g2->add_option("--val2", val2); g3->add_option("--name3", name3)->required(); g3->add_option("--val3", val3); } void remove_required() { // NOLINT(readability-make-member-function-const) g1->get_option("--name1")->required(false); g2->get_option("--name2")->required(false); g3->get_option("--name3")->required(false); g1->required(false); g2->required(false); g3->required(false); } }; TEST_CASE_METHOD(ManyGroups, "SingleGroup", "[optiongroup]") { // only 1 group can be used main->require_option(1); args = {"--name1", "test"}; run(); CHECK("test" == name1); args = {"--name2", "test", "--val2", "tval"}; run(); CHECK("tval" == val2); args = {"--name1", "test", "--val2", "tval"}; CHECK_THROWS_AS(run(), CLI::RequiredError); } TEST_CASE_METHOD(ManyGroups, "getGroup", "[optiongroup]") { auto *mn = app.get_option_group("main"); CHECK(mn == main); CHECK_THROWS_AS(app.get_option_group("notfound"), CLI::OptionNotFound); } TEST_CASE_METHOD(ManyGroups, "ExcludesGroup", "[optiongroup]") { // only 1 group can be used g1->excludes(g2); g1->excludes(g3); args = {"--name1", "test"}; run(); CHECK("test" == name1); args = {"--name1", "test", "--name2", "test2"}; CHECK_THROWS_AS(run(), CLI::ExcludesError); CHECK(g1->remove_excludes(g2)); CHECK_NOTHROW(run()); CHECK(!g1->remove_excludes(g1)); CHECK(!g1->remove_excludes(g2)); } TEST_CASE_METHOD(ManyGroups, "NeedsGroup", "[optiongroup]") { remove_required(); // all groups needed if g1 is used g1->needs(g2); g1->needs(g3); args = {"--name1", "test"}; CHECK_THROWS_AS(run(), CLI::RequiresError); // other groups should run fine args = {"--name2", "test2"}; run(); // all three groups should be fine args = {"--name1", "test", "--name2", "test2", "--name3", "test3"}; CHECK_NOTHROW(run()); } // test adding an option group with existing subcommands to an app TEST_CASE_METHOD(TApp, "ExistingSubcommandMatch", "[optiongroup]") { auto sshared = std::make_shared("documenting the subcommand", "sub1g", nullptr); auto *s1 = sshared->add_subcommand("sub1"); auto *o1 = sshared->add_option_group("opt1"); o1->add_subcommand("sub3")->alias("sub4"); app.add_subcommand("sub1"); try { app.add_subcommand(sshared); // this should throw the next line should never be reached CHECK(!true); } catch(const CLI::OptionAlreadyAdded &oaa) { CHECK_THAT(oaa.what(), Contains("sub1")); } sshared->remove_subcommand(s1); app.add_subcommand("sub3"); // now check that the subsubcommand overlaps try { app.add_subcommand(sshared); // this should throw the next line should never be reached CHECK(!true); } catch(const CLI::OptionAlreadyAdded &oaa) { CHECK_THAT(oaa.what(), Contains("sub3")); } } TEST_CASE_METHOD(ManyGroups, "SingleGroupError", "[optiongroup]") { // only 1 group can be used main->require_option(1); args = {"--name1", "test", "--name2", "test3"}; CHECK_THROWS_AS(run(), CLI::RequiredError); } TEST_CASE_METHOD(ManyGroups, "AtMostOneGroup", "[optiongroup]") { // only 1 group can be used main->require_option(0, 1); args = {"--name1", "test", "--name2", "test3"}; CHECK_THROWS_AS(run(), CLI::RequiredError); args = {}; CHECK_NOTHROW(run()); } TEST_CASE_METHOD(ManyGroups, "AtLeastTwoGroups", "[optiongroup]") { // only 1 group can be used main->require_option(2, 0); args = {"--name1", "test", "--name2", "test3"}; run(); args = {"--name1", "test"}; CHECK_THROWS_AS(run(), CLI::RequiredError); } TEST_CASE_METHOD(ManyGroups, "BetweenOneAndTwoGroups", "[optiongroup]") { // only 1 group can be used main->require_option(1, 2); args = {"--name1", "test", "--name2", "test3"}; run(); args = {"--name1", "test"}; run(); args = {}; CHECK_THROWS_AS(run(), CLI::RequiredError); args = {"--name1", "test", "--name2", "test3", "--name3=test3"}; CHECK_THROWS_AS(run(), CLI::RequiredError); } TEST_CASE_METHOD(ManyGroups, "RequiredFirst", "[optiongroup]") { // only 1 group can be used remove_required(); g1->required(); CHECK(g1->get_required()); CHECK(!g2->get_required()); args = {"--name1", "test", "--name2", "test3"}; run(); args = {"--name2", "test"}; try { run(); } catch(const CLI::RequiredError &re) { CHECK_THAT(re.what(), Contains("g1")); } args = {"--name1", "test", "--name2", "test3", "--name3=test3"}; CHECK_NOTHROW(run()); } TEST_CASE_METHOD(ManyGroups, "DisableFirst", "[optiongroup]") { // only 1 group can be used if remove_required not used remove_required(); g1->disabled(); CHECK(g1->get_disabled()); CHECK(!g2->get_disabled()); args = {"--name2", "test"}; run(); args = {"--name1", "test", "--name2", "test3"}; CHECK_THROWS_AS(run(), CLI::ExtrasError); g1->disabled(false); args = {"--name1", "test", "--name2", "test3", "--name3=test3"}; CHECK_NOTHROW(run()); } TEST_CASE_METHOD(ManyGroups, "SameSubcommand", "[optiongroup]") { // only 1 group can be used if remove_required not used remove_required(); auto *sub1 = g1->add_subcommand("sub1")->disabled(); auto *sub2 = g2->add_subcommand("sub1")->disabled(); auto *sub3 = g3->add_subcommand("sub1"); // so when the subcommands are disabled they can have the same name sub1->disabled(false); sub2->disabled(false); // if they are re-enabled they are not checked for overlap on enabling so they can have the same name args = {"sub1", "sub1", "sub1"}; run(); CHECK(*sub1); CHECK(*sub2); CHECK(*sub3); auto subs = app.get_subcommands(); CHECK(3u == subs.size()); CHECK(sub1 == subs[0]); CHECK(sub2 == subs[1]); CHECK(sub3 == subs[2]); args = {"sub1", "sub1", "sub1", "sub1"}; // for the 4th and future ones they will route to the first one run(); CHECK(2u == sub1->count()); CHECK(1u == sub2->count()); CHECK(1u == sub3->count()); // subs should remain the same since the duplicate would not be registered there subs = app.get_subcommands(); CHECK(3u == subs.size()); CHECK(sub1 == subs[0]); CHECK(sub2 == subs[1]); CHECK(sub3 == subs[2]); } TEST_CASE_METHOD(ManyGroups, "CallbackOrder", "[optiongroup]") { // only 1 group can be used if remove_required not used remove_required(); std::vector callback_order; g1->callback([&callback_order]() { callback_order.push_back(1); }); g2->callback([&callback_order]() { callback_order.push_back(2); }); main->callback([&callback_order]() { callback_order.push_back(3); }); args = {"--name2", "test"}; run(); CHECK(std::vector({2, 3}) == callback_order); callback_order.clear(); args = {"--name1", "t2", "--name2", "test"}; g2->immediate_callback(); run(); CHECK(std::vector({2, 1, 3}) == callback_order); callback_order.clear(); args = {"--name2", "test", "--name1", "t2"}; g2->immediate_callback(false); run(); CHECK(std::vector({1, 2, 3}) == callback_order); } // Test the fallthrough for extra arguments TEST_CASE_METHOD(ManyGroups, "ExtrasFallDown", "[optiongroup]") { // only 1 group can be used if remove_required not used remove_required(); args = {"--test1", "--flag", "extra"}; CHECK_THROWS_AS(run(), CLI::ExtrasError); main->allow_extras(); CHECK_NOTHROW(run()); CHECK(3u == app.remaining_size(true)); CHECK(3u == main->remaining_size()); std::vector extras{"--test1", "--flag", "extra"}; CHECK(extras == app.remaining(true)); CHECK(extras == main->remaining()); } // Test the option Inheritance TEST_CASE_METHOD(ManyGroups, "Inheritance", "[optiongroup]") { remove_required(); g1->ignore_case(); g1->ignore_underscore(); auto *t2 = g1->add_subcommand("t2"); args = {"T2", "t_2"}; CHECK(t2->get_ignore_underscore()); CHECK(t2->get_ignore_case()); run(); CHECK(2u == t2->count()); } TEST_CASE_METHOD(ManyGroups, "Moving", "[optiongroup]") { remove_required(); auto *mg = app.add_option_group("maing"); mg->add_subcommand(g1); mg->add_subcommand(g2); CHECK(mg == g1->get_parent()); CHECK(mg == g2->get_parent()); CHECK(main == g3->get_parent()); } struct ManyGroupsPreTrigger : public ManyGroups { std::size_t triggerMain{0u}, trigger1{87u}, trigger2{34u}, trigger3{27u}; ManyGroupsPreTrigger() { remove_required(); app.preparse_callback([this](std::size_t count) { triggerMain = count; }); g1->preparse_callback([this](std::size_t count) { trigger1 = count; }); g2->preparse_callback([this](std::size_t count) { trigger2 = count; }); g3->preparse_callback([this](std::size_t count) { trigger3 = count; }); } }; TEST_CASE_METHOD(ManyGroupsPreTrigger, "PreTriggerTestsOptions", "[optiongroup]") { args = {"--name1", "test", "--name2", "test3"}; run(); CHECK(4u == triggerMain); CHECK(2u == trigger1); CHECK(0u == trigger2); CHECK(27u == trigger3); args = {"--name1", "test"}; trigger2 = 34u; run(); CHECK(2u == triggerMain); CHECK(0u == trigger1); CHECK(34u == trigger2); args = {}; run(); CHECK(0u == triggerMain); args = {"--name1", "test", "--val1", "45", "--name2", "test3", "--name3=test3", "--val2=37"}; run(); CHECK(8u == triggerMain); CHECK(6u == trigger1); CHECK(2u == trigger2); CHECK(1u == trigger3); } TEST_CASE_METHOD(ManyGroupsPreTrigger, "PreTriggerTestsPositionals", "[optiongroup]") { // only 1 group can be used g1->add_option("pos1"); g2->add_option("pos2"); g3->add_option("pos3"); args = {"pos1"}; run(); CHECK(1u == triggerMain); CHECK(0u == trigger1); CHECK(34u == trigger2); CHECK(27u == trigger3); args = {"pos1", "pos2"}; run(); CHECK(2u == triggerMain); CHECK(1u == trigger1); CHECK(0u == trigger2); args = {"pos1", "pos2", "pos3"}; run(); CHECK(3u == triggerMain); CHECK(2u == trigger1); CHECK(1u == trigger2); CHECK(0u == trigger3); } TEST_CASE_METHOD(ManyGroupsPreTrigger, "PreTriggerTestsSubcommand", "[optiongroup]") { auto *sub1 = g1->add_subcommand("sub1")->fallthrough(); g2->add_subcommand("sub2")->fallthrough(); g3->add_subcommand("sub3")->fallthrough(); std::size_t subtrigger = 0; sub1->preparse_callback([&subtrigger](std::size_t count) { subtrigger = count; }); args = {"sub1"}; run(); CHECK(1u == triggerMain); CHECK(0u == trigger1); CHECK(34u == trigger2); CHECK(27u == trigger3); args = {"sub1", "sub2"}; run(); CHECK(2u == triggerMain); CHECK(1u == subtrigger); CHECK(1u == trigger1); CHECK(0u == trigger2); args = {"sub2", "sub3", "--name1=test", "sub1"}; run(); CHECK(4u == triggerMain); CHECK(1u == trigger1); CHECK(3u == trigger2); CHECK(1u == trigger3); // go until the sub1 command is given }