#ifdef CLI11_SINGLE_FILE #include "CLI11.hpp" #else #include "CLI/CLI.hpp" #endif #include "gtest/gtest.h" #include "gmock/gmock.h" #include using ::testing::HasSubstr; using ::testing::Not; TEST(THelp, Basic) { CLI::App app{"My prog"}; std::string help = app.help(); EXPECT_THAT(help, HasSubstr("My prog")); EXPECT_THAT(help, HasSubstr("-h,--help")); EXPECT_THAT(help, HasSubstr("Options:")); EXPECT_THAT(help, HasSubstr("Usage:")); } TEST(THelp, Footer) { CLI::App app{"My prog"}; app.set_footer("Report bugs to bugs@example.com"); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("My prog")); EXPECT_THAT(help, HasSubstr("-h,--help")); EXPECT_THAT(help, HasSubstr("Options:")); EXPECT_THAT(help, HasSubstr("Usage:")); EXPECT_THAT(help, HasSubstr("Report bugs to bugs@example.com")); } TEST(THelp, OptionalPositional) { CLI::App app{"My prog", "program"}; std::string x; app.add_option("something", x, "My option here"); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("My prog")); EXPECT_THAT(help, HasSubstr("-h,--help")); EXPECT_THAT(help, HasSubstr("Options:")); EXPECT_THAT(help, HasSubstr("Positionals:")); EXPECT_THAT(help, HasSubstr("something TEXT")); EXPECT_THAT(help, HasSubstr("My option here")); EXPECT_THAT(help, HasSubstr("Usage: program [OPTIONS] [something]")); } TEST(THelp, Hidden) { CLI::App app{"My prog"}; std::string x; app.add_option("something", x, "My option here")->group(""); std::string y; app.add_option("--another", y)->group(""); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("My prog")); EXPECT_THAT(help, HasSubstr("-h,--help")); EXPECT_THAT(help, HasSubstr("Options:")); EXPECT_THAT(help, HasSubstr("[something]")); EXPECT_THAT(help, Not(HasSubstr("something "))); EXPECT_THAT(help, Not(HasSubstr("another"))); } TEST(THelp, OptionalPositionalAndOptions) { CLI::App app{"My prog", "AnotherProgram"}; app.add_flag("-q,--quick"); std::string x; app.add_option("something", x, "My option here"); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("My prog")); EXPECT_THAT(help, HasSubstr("-h,--help")); EXPECT_THAT(help, HasSubstr("Options:")); EXPECT_THAT(help, HasSubstr("Usage: AnotherProgram [OPTIONS] [something]")); } TEST(THelp, RequiredPositionalAndOptions) { CLI::App app{"My prog"}; app.add_flag("-q,--quick"); std::string x; app.add_option("something", x, "My option here")->required(); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("My prog")); EXPECT_THAT(help, HasSubstr("-h,--help")); EXPECT_THAT(help, HasSubstr("Options:")); EXPECT_THAT(help, HasSubstr("Positionals:")); EXPECT_THAT(help, HasSubstr("Usage: [OPTIONS] something")); } TEST(THelp, MultiOpts) { CLI::App app{"My prog"}; std::vector x, y; app.add_option("-q,--quick", x, "Disc")->expected(2); app.add_option("-v,--vals", y, "Other"); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("My prog")); EXPECT_THAT(help, Not(HasSubstr("Positionals:"))); EXPECT_THAT(help, HasSubstr("Usage: [OPTIONS]")); EXPECT_THAT(help, HasSubstr("INT x 2")); EXPECT_THAT(help, HasSubstr("INT ...")); } TEST(THelp, VectorOpts) { CLI::App app{"My prog"}; std::vector x = {1, 2}; app.add_option("-q,--quick", x, "", true); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("INT=[1,2] ...")); } TEST(THelp, MultiPosOpts) { CLI::App app{"My prog"}; app.set_name("program"); std::vector x, y; app.add_option("quick", x, "Disc")->expected(2); app.add_option("vals", y, "Other"); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("My prog")); EXPECT_THAT(help, HasSubstr("Positionals:")); EXPECT_THAT(help, HasSubstr("Usage: program [OPTIONS]")); EXPECT_THAT(help, HasSubstr("INT x 2")); EXPECT_THAT(help, HasSubstr("INT ...")); EXPECT_THAT(help, HasSubstr("[quick(2x)]")); EXPECT_THAT(help, HasSubstr("[vals...]")); } TEST(THelp, EnvName) { CLI::App app{"My prog"}; std::string input; app.add_option("--something", input)->envname("SOME_ENV"); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("SOME_ENV")); } TEST(THelp, Needs) { CLI::App app{"My prog"}; CLI::Option *op1 = app.add_flag("--op1"); app.add_flag("--op2")->needs(op1); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("Needs: --op1")); } TEST(THelp, NeedsPositional) { CLI::App app{"My prog"}; int x, y; CLI::Option *op1 = app.add_option("op1", x, "one"); app.add_option("op2", y, "two")->needs(op1); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("Positionals:")); EXPECT_THAT(help, HasSubstr("Needs: op1")); } TEST(THelp, Excludes) { CLI::App app{"My prog"}; CLI::Option *op1 = app.add_flag("--op1"); app.add_flag("--op2")->excludes(op1); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("Excludes: --op1")); } TEST(THelp, ExcludesPositional) { CLI::App app{"My prog"}; int x, y; CLI::Option *op1 = app.add_option("op1", x); app.add_option("op2", y)->excludes(op1); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("Positionals:")); EXPECT_THAT(help, HasSubstr("Excludes: op1")); } TEST(THelp, ExcludesSymmetric) { CLI::App app{"My prog"}; CLI::Option *op1 = app.add_flag("--op1"); app.add_flag("--op2")->excludes(op1); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("Excludes: --op2")); } TEST(THelp, ManualSetters) { CLI::App app{"My prog"}; int x = 1; CLI::Option *op1 = app.add_option("--op", x); op1->set_default_str("12"); op1->set_type_name("BIGGLES"); EXPECT_EQ(x, 1); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("=12")); EXPECT_THAT(help, HasSubstr("BIGGLES")); op1->set_default_val("14"); EXPECT_EQ(x, 14); help = app.help(); EXPECT_THAT(help, HasSubstr("=14")); } TEST(THelp, Subcom) { CLI::App app{"My prog"}; auto sub1 = app.add_subcommand("sub1"); app.add_subcommand("sub2"); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("Usage: [OPTIONS] [SUBCOMMAND]")); app.require_subcommand(); help = app.help(); EXPECT_THAT(help, HasSubstr("Usage: [OPTIONS] SUBCOMMAND")); help = sub1->help(); EXPECT_THAT(help, HasSubstr("Usage: sub1")); char x[] = "./myprogram"; char y[] = "sub2"; std::vector args = {x, y}; app.parse((int)args.size(), args.data()); help = app.help(); EXPECT_THAT(help, HasSubstr("Usage: ./myprogram sub2")); } TEST(THelp, MasterName) { CLI::App app{"My prog", "MyRealName"}; char x[] = "./myprogram"; std::vector args = {x}; app.parse((int)args.size(), args.data()); EXPECT_THAT(app.help(), HasSubstr("Usage: MyRealName")); } TEST(THelp, IntDefaults) { CLI::App app{"My prog"}; int one{1}, two{2}; app.add_option("--one", one, "Help for one", true); app.add_set("--set", two, {2, 3, 4}, "Help for set", true); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("--one")); EXPECT_THAT(help, HasSubstr("--set")); EXPECT_THAT(help, HasSubstr("1")); EXPECT_THAT(help, HasSubstr("=2")); EXPECT_THAT(help, HasSubstr("2,3,4")); } TEST(THelp, SetLower) { CLI::App app{"My prog"}; std::string def{"One"}; app.add_set_ignore_case("--set", def, {"oNe", "twO", "THREE"}, "Help for set", true); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("--set")); EXPECT_THAT(help, HasSubstr("=One")); EXPECT_THAT(help, HasSubstr("oNe")); EXPECT_THAT(help, HasSubstr("twO")); EXPECT_THAT(help, HasSubstr("THREE")); } TEST(THelp, OnlyOneHelp) { CLI::App app{"My prog"}; // It is not supported to have more than one help flag, last one wins app.set_help_flag("--help", "No short name allowed"); app.set_help_flag("--yelp", "Alias for help"); std::vector input{"--help"}; EXPECT_THROW(app.parse(input), CLI::ExtrasError); } TEST(THelp, OnlyOneAllHelp) { CLI::App app{"My prog"}; // It is not supported to have more than one help flag, last one wins app.set_help_all_flag("--help-all", "No short name allowed"); app.set_help_all_flag("--yelp", "Alias for help"); std::vector input{"--help-all"}; EXPECT_THROW(app.parse(input), CLI::ExtrasError); app.reset(); std::vector input2{"--yelp"}; EXPECT_THROW(app.parse(input2), CLI::CallForAllHelp); // Remove the flag app.set_help_all_flag(); app.reset(); std::vector input3{"--yelp"}; EXPECT_THROW(app.parse(input3), CLI::ExtrasError); } TEST(THelp, RemoveHelp) { CLI::App app{"My prog"}; app.set_help_flag(); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("My prog")); EXPECT_THAT(help, Not(HasSubstr("-h,--help"))); EXPECT_THAT(help, Not(HasSubstr("Options:"))); EXPECT_THAT(help, HasSubstr("Usage:")); std::vector input{"--help"}; try { app.parse(input); } catch(const CLI::ParseError &e) { EXPECT_EQ(static_cast(CLI::ExitCodes::ExtrasError), e.get_exit_code()); } } TEST(THelp, NoHelp) { CLI::App app{"My prog"}; app.set_help_flag(); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("My prog")); EXPECT_THAT(help, Not(HasSubstr("-h,--help"))); EXPECT_THAT(help, Not(HasSubstr("Options:"))); EXPECT_THAT(help, HasSubstr("Usage:")); std::vector input{"--help"}; try { app.parse(input); } catch(const CLI::ParseError &e) { EXPECT_EQ(static_cast(CLI::ExitCodes::ExtrasError), e.get_exit_code()); } } TEST(THelp, CustomHelp) { CLI::App app{"My prog"}; CLI::Option *help_option = app.set_help_flag("--yelp", "display help and exit"); EXPECT_EQ(app.get_help_ptr(), help_option); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("My prog")); EXPECT_THAT(help, Not(HasSubstr("-h,--help"))); EXPECT_THAT(help, HasSubstr("--yelp")); EXPECT_THAT(help, HasSubstr("Options:")); EXPECT_THAT(help, HasSubstr("Usage:")); std::vector input{"--yelp"}; try { app.parse(input); } catch(const CLI::CallForHelp &e) { EXPECT_EQ(static_cast(CLI::ExitCodes::Success), e.get_exit_code()); } } TEST(THelp, NiceName) { CLI::App app; int x; auto long_name = app.add_option("-s,--long,-q,--other,that", x); auto short_name = app.add_option("more,-x,-y", x); auto positional = app.add_option("posit", x); EXPECT_EQ(long_name->get_name(), "--long"); EXPECT_EQ(short_name->get_name(), "-x"); EXPECT_EQ(positional->get_name(), "posit"); } TEST(Exit, ErrorWithHelp) { CLI::App app{"My prog"}; std::vector input{"-h"}; try { app.parse(input); } catch(const CLI::CallForHelp &e) { EXPECT_EQ(static_cast(CLI::ExitCodes::Success), e.get_exit_code()); } } TEST(Exit, ErrorWithAllHelp) { CLI::App app{"My prog"}; app.set_help_all_flag("--help-all", "All help"); std::vector input{"--help-all"}; try { app.parse(input); } catch(const CLI::CallForAllHelp &e) { EXPECT_EQ(static_cast(CLI::ExitCodes::Success), e.get_exit_code()); } } TEST(Exit, ErrorWithoutHelp) { CLI::App app{"My prog"}; std::vector input{"--none"}; try { app.parse(input); } catch(const CLI::ParseError &e) { EXPECT_EQ(static_cast(CLI::ExitCodes::ExtrasError), e.get_exit_code()); } } TEST(Exit, ExitCodes) { CLI::App app; auto i = static_cast(CLI::ExitCodes::ExtrasError); EXPECT_EQ(0, app.exit(CLI::Success())); EXPECT_EQ(0, app.exit(CLI::CallForHelp())); EXPECT_EQ(i, app.exit(CLI::ExtrasError({"Thing"}))); EXPECT_EQ(42, app.exit(CLI::RuntimeError(42))); EXPECT_EQ(1, app.exit(CLI::RuntimeError())); // Not sure if a default here is a good thing } struct CapturedHelp : public ::testing::Test { CLI::App app{"My Test Program"}; std::stringstream out; std::stringstream err; int run(const CLI::Error &e) { return app.exit(e, out, err); } void reset() { out.clear(); err.clear(); } }; TEST_F(CapturedHelp, Sucessful) { EXPECT_EQ(run(CLI::Success()), 0); EXPECT_EQ(out.str(), ""); EXPECT_EQ(err.str(), ""); } TEST_F(CapturedHelp, JustAnError) { EXPECT_EQ(run(CLI::RuntimeError(42)), 42); EXPECT_EQ(out.str(), ""); EXPECT_EQ(err.str(), ""); } TEST_F(CapturedHelp, CallForHelp) { EXPECT_EQ(run(CLI::CallForHelp()), 0); EXPECT_EQ(out.str(), app.help()); EXPECT_EQ(err.str(), ""); } TEST_F(CapturedHelp, CallForAllHelp) { EXPECT_EQ(run(CLI::CallForAllHelp()), 0); EXPECT_EQ(out.str(), app.help("", CLI::AppFormatMode::All)); EXPECT_EQ(err.str(), ""); } TEST_F(CapturedHelp, CallForAllHelpOutput) { app.add_subcommand("one"); CLI::App *sub = app.add_subcommand("two"); sub->add_flag("--three"); EXPECT_EQ(run(CLI::CallForAllHelp()), 0); EXPECT_EQ(out.str(), app.help("", CLI::AppFormatMode::All)); EXPECT_EQ(err.str(), ""); EXPECT_THAT(out.str(), HasSubstr("one")); EXPECT_THAT(out.str(), HasSubstr("two")); EXPECT_THAT(out.str(), HasSubstr("--three")); } TEST_F(CapturedHelp, NewFormattedHelp) { app.formatter([](const CLI::App *, std::string, CLI::AppFormatMode) { return "New Help"; }); EXPECT_EQ(run(CLI::CallForHelp()), 0); EXPECT_EQ(out.str(), "New Help"); EXPECT_EQ(err.str(), ""); } TEST_F(CapturedHelp, NormalError) { EXPECT_EQ(run(CLI::ExtrasError({"Thing"})), static_cast(CLI::ExitCodes::ExtrasError)); EXPECT_EQ(out.str(), ""); EXPECT_THAT(err.str(), HasSubstr("for more information")); EXPECT_THAT(err.str(), Not(HasSubstr("ExtrasError"))); EXPECT_THAT(err.str(), HasSubstr("Thing")); EXPECT_THAT(err.str(), Not(HasSubstr("Usage"))); } TEST_F(CapturedHelp, RepacedError) { app.set_failure_message(CLI::FailureMessage::help); EXPECT_EQ(run(CLI::ExtrasError({"Thing"})), static_cast(CLI::ExitCodes::ExtrasError)); EXPECT_EQ(out.str(), ""); EXPECT_THAT(err.str(), Not(HasSubstr("for more information"))); EXPECT_THAT(err.str(), HasSubstr("ERROR: ExtrasError")); EXPECT_THAT(err.str(), HasSubstr("Thing")); EXPECT_THAT(err.str(), HasSubstr("Usage")); } // #87 TEST(THelp, CustomDoubleOption) { std::pair custom_opt; CLI::App app; auto opt = app.add_option("posit", [&custom_opt](CLI::results_t vals) { custom_opt = {stol(vals.at(0)), stod(vals.at(1))}; return true; }); opt->set_custom_option("INT FLOAT", 2); EXPECT_THAT(app.help(), Not(HasSubstr("x 2"))); } TEST(THelp, AccessDescription) { CLI::App app{"My description goes here"}; EXPECT_EQ(app.get_description(), "My description goes here"); } TEST(THelp, CleanNeeds) { CLI::App app; int x; auto a_name = app.add_option("-a,--alpha", x); app.add_option("-b,--boo", x)->needs(a_name); EXPECT_THAT(app.help(), Not(HasSubstr("Requires"))); EXPECT_THAT(app.help(), Not(HasSubstr("Needs: -a,--alpha"))); EXPECT_THAT(app.help(), HasSubstr("Needs: --alpha")); } TEST(THelp, RequiredPrintout) { CLI::App app; int x; app.add_option("-a,--alpha", x)->required(); EXPECT_THAT(app.help(), HasSubstr(" REQUIRED")); } TEST(THelp, GroupOrder) { CLI::App app; app.add_flag("--one")->group("zee"); app.add_flag("--two")->group("aee"); std::string help = app.help(); auto zee_loc = help.find("zee"); auto aee_loc = help.find("aee"); EXPECT_NE(zee_loc, std::string::npos); EXPECT_NE(aee_loc, std::string::npos); EXPECT_LT(zee_loc, aee_loc); } TEST(THelp, ValidatorsText) { CLI::App app; std::string filename; int x; unsigned int y; app.add_option("--f1", filename)->check(CLI::ExistingFile); app.add_option("--f3", x)->check(CLI::Range(1, 4)); app.add_option("--f4", y)->check(CLI::Range(12)); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("FILE")); EXPECT_THAT(help, HasSubstr("INT in [1 - 4]")); EXPECT_THAT(help, HasSubstr("INT in [0 - 12]")); // Loses UINT EXPECT_THAT(help, Not(HasSubstr("TEXT"))); } TEST(THelp, ValidatorsNonPathText) { CLI::App app; std::string filename; app.add_option("--f2", filename)->check(CLI::NonexistentPath); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("PATH")); EXPECT_THAT(help, Not(HasSubstr("TEXT"))); } TEST(THelp, ValidatorsDirText) { CLI::App app; std::string filename; app.add_option("--f2", filename)->check(CLI::ExistingDirectory); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("DIR")); EXPECT_THAT(help, Not(HasSubstr("TEXT"))); } TEST(THelp, ValidatorsPathText) { CLI::App app; std::string filename; app.add_option("--f2", filename)->check(CLI::ExistingPath); std::string help = app.help(); EXPECT_THAT(help, HasSubstr("PATH")); EXPECT_THAT(help, Not(HasSubstr("TEXT"))); } TEST(THelp, CombinedValidatorsText) { CLI::App app; std::string filename; app.add_option("--f1", filename)->check(CLI::ExistingFile | CLI::ExistingDirectory); // This would be nice if it put something other than string, but would it be path or file? // Can't programatically tell! // (Users can use ExistingPath, by the way) std::string help = app.help(); EXPECT_THAT(help, HasSubstr("TEXT")); EXPECT_THAT(help, Not(HasSubstr("PATH"))); EXPECT_THAT(help, Not(HasSubstr("FILE"))); } // Don't do this in real life, please TEST(THelp, CombinedValidatorsPathyText) { CLI::App app; std::string filename; app.add_option("--f1", filename)->check(CLI::ExistingPath | CLI::NonexistentPath); // Combining validators with the same type string is OK std::string help = app.help(); EXPECT_THAT(help, Not(HasSubstr("TEXT"))); EXPECT_THAT(help, HasSubstr("PATH")); }