Commit 17e7d60c18473c3c74902c61b548f94b9db40c6a
Committed by
GitHub
1 parent
eba619fc
fix: several small fixes and added tests (#666)
* add a few tests related to github issues * change how the default is displayed in the help message prev was =XXXX, this was confusing in some cases particularly with flags or with multiple option names. Now is [default=XXXX] which makes it clearer what the value represents. * Try to fix RTTI issue * style: pre-commit.ci fixes * Fix subcommand callbacks being called multiple times if in an option group * style: pre-commit.ci fixes * remove extra group call * change [default=XXXXD] to just [XXXXX] for the default specification * update changelog Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Showing
8 changed files
with
94 additions
and
16 deletions
CHANGELOG.md
| ... | ... | @@ -45,6 +45,16 @@ is not passed, or every time the option is parsed. |
| 45 | 45 | [#656]: https://github.com/CLIUtils/CLI11/pull/656 |
| 46 | 46 | [#657]: https://github.com/CLIUtils/CLI11/pull/657 |
| 47 | 47 | |
| 48 | +## Version 2.1.3: Bug Fixes and Tweaks | |
| 49 | + | |
| 50 | +* Change the way the default value is displayed in the included help text generation from `=XXXXX` to `[XXXXX]` to clean up some situations in which the text looked awkward and unclear [#666][] | |
| 51 | +* Fix a bug where a subcommand callback could be executed multiple times if it was a member of an option group [#666][] | |
| 52 | +* Fix an issue where the detection of RTTI being disabled on certain visual studio platforms did not disable the use of dynamic cast calls [#666][] | |
| 53 | +* Add additional tests concerning the use of aliases for option groups in config files [#666][] | |
| 54 | +* Resolve strict-overflow warning on some GCC compilers [#666][] | |
| 55 | + | |
| 56 | +[#666]: https://github.com/CLIUtils/CLI11/pull/666 | |
| 57 | + | |
| 48 | 58 | ## Version 2.0: Simplification |
| 49 | 59 | |
| 50 | 60 | This version focuses on cleaning up deprecated functionality, and some minor | ... | ... |
azure-pipelines.yml
include/CLI/App.hpp
| ... | ... | @@ -1213,8 +1213,8 @@ class App { |
| 1213 | 1213 | } |
| 1214 | 1214 | |
| 1215 | 1215 | std::vector<std::string> args; |
| 1216 | - args.reserve(static_cast<std::size_t>(argc) - 1); | |
| 1217 | - for(int i = argc - 1; i > 0; i--) | |
| 1216 | + args.reserve(static_cast<std::size_t>(argc) - 1U); | |
| 1217 | + for(auto i = static_cast<std::size_t>(argc) - 1U; i > 0U; --i) | |
| 1218 | 1218 | args.emplace_back(argv[i]); |
| 1219 | 1219 | parse(std::move(args)); |
| 1220 | 1220 | } |
| ... | ... | @@ -1543,7 +1543,7 @@ class App { |
| 1543 | 1543 | /// Access the config formatter as a configBase pointer |
| 1544 | 1544 | std::shared_ptr<ConfigBase> get_config_formatter_base() const { |
| 1545 | 1545 | // This is safer as a dynamic_cast if we have RTTI, as Config -> ConfigBase |
| 1546 | -#if defined(__cpp_rtti) || (defined(__GXX_RTTI) && __GXX_RTTI) || (defined(_HAS_STATIC_RTTI) && (_HAS_STATIC_RTTI == 0)) | |
| 1546 | +#if CLI11_USE_STATIC_RTTI == 0 | |
| 1547 | 1547 | return std::dynamic_pointer_cast<ConfigBase>(config_formatter_); |
| 1548 | 1548 | #else |
| 1549 | 1549 | return std::static_pointer_cast<ConfigBase>(config_formatter_); |
| ... | ... | @@ -1945,7 +1945,9 @@ class App { |
| 1945 | 1945 | } |
| 1946 | 1946 | // run the callbacks for the received subcommands |
| 1947 | 1947 | for(App *subc : get_subcommands()) { |
| 1948 | - subc->run_callback(true, suppress_final_callback); | |
| 1948 | + if(subc->parent_ == this) { | |
| 1949 | + subc->run_callback(true, suppress_final_callback); | |
| 1950 | + } | |
| 1949 | 1951 | } |
| 1950 | 1952 | // now run callbacks for option_groups |
| 1951 | 1953 | for(auto &subc : subcommands_) { | ... | ... |
include/CLI/Formatter.hpp
| ... | ... | @@ -249,7 +249,7 @@ inline std::string Formatter::make_option_opts(const Option *opt) const { |
| 249 | 249 | if(!opt->get_type_name().empty()) |
| 250 | 250 | out << " " << get_label(opt->get_type_name()); |
| 251 | 251 | if(!opt->get_default_str().empty()) |
| 252 | - out << "=" << opt->get_default_str(); | |
| 252 | + out << " [" << opt->get_default_str() << "] "; | |
| 253 | 253 | if(opt->get_expected_max() == detail::expected_max_vector_size) |
| 254 | 254 | out << " ..."; |
| 255 | 255 | else if(opt->get_expected_min() > 1) | ... | ... |
include/CLI/Macros.hpp
| ... | ... | @@ -41,4 +41,20 @@ |
| 41 | 41 | #define CLI11_DEPRECATED(reason) __attribute__((deprecated(reason))) |
| 42 | 42 | #endif |
| 43 | 43 | |
| 44 | +/** detection of rtti */ | |
| 45 | +#ifndef CLI11_USE_STATIC_RTTI | |
| 46 | +#if(defined(_HAS_STATIC_RTTI) && _HAS_STATIC_RTTI) | |
| 47 | +#define CLI11_USE_STATIC_RTTI 1 | |
| 48 | +#elif defined(__cpp_rtti) | |
| 49 | +#if(defined(_CPPRTTI) && _CPPRTTI == 0) | |
| 50 | +#define CLI11_USE_STATIC_RTTI 1 | |
| 51 | +#else | |
| 52 | +#define CLI11_USE_STATIC_RTTI 0 | |
| 53 | +#endif | |
| 54 | +#elif(defined(__GCC_RTTI) && __GXX_RTTI) | |
| 55 | +#define CLI11_USE_STATIC_RTTI 0 | |
| 56 | +#else | |
| 57 | +#define CLI11_USE_STATIC_RTTI 1 | |
| 58 | +#endif | |
| 59 | +#endif | |
| 44 | 60 | // [CLI11:macros_hpp:end] | ... | ... |
tests/ConfigFileTest.cpp
| ... | ... | @@ -1216,6 +1216,30 @@ TEST_CASE_METHOD(TApp, "IniLayeredCustomSectionSeparator", "[config]") { |
| 1216 | 1216 | CHECK(!*subcom); |
| 1217 | 1217 | } |
| 1218 | 1218 | |
| 1219 | +TEST_CASE_METHOD(TApp, "IniLayeredOptionGroupAlias", "[config]") { | |
| 1220 | + | |
| 1221 | + TempFile tmpini{"TestIniTmp.ini"}; | |
| 1222 | + | |
| 1223 | + app.set_config("--config", tmpini); | |
| 1224 | + | |
| 1225 | + { | |
| 1226 | + std::ofstream out{tmpini}; | |
| 1227 | + out << "[default]" << std::endl; | |
| 1228 | + out << "val=1" << std::endl; | |
| 1229 | + out << "[ogroup]" << std::endl; | |
| 1230 | + out << "val2=2" << std::endl; | |
| 1231 | + } | |
| 1232 | + int one{0}, two{0}; | |
| 1233 | + app.add_option("--val", one); | |
| 1234 | + auto subcom = app.add_option_group("ogroup")->alias("ogroup"); | |
| 1235 | + subcom->add_option("--val2", two); | |
| 1236 | + | |
| 1237 | + run(); | |
| 1238 | + | |
| 1239 | + CHECK(one == 1); | |
| 1240 | + CHECK(two == 2); | |
| 1241 | +} | |
| 1242 | + | |
| 1219 | 1243 | TEST_CASE_METHOD(TApp, "IniSubcommandConfigurable", "[config]") { |
| 1220 | 1244 | |
| 1221 | 1245 | TempFile tmpini{"TestIniTmp.ini"}; | ... | ... |
tests/HelpTest.cpp
| ... | ... | @@ -289,7 +289,8 @@ TEST_CASE("THelp: VectorOpts", "[help]") { |
| 289 | 289 | |
| 290 | 290 | std::string help = app.help(); |
| 291 | 291 | |
| 292 | - CHECK_THAT(help, Contains("INT=[1,2] ...")); | |
| 292 | + CHECK_THAT(help, Contains("[1,2]")); | |
| 293 | + CHECK_THAT(help, Contains(" ...")); | |
| 293 | 294 | } |
| 294 | 295 | |
| 295 | 296 | TEST_CASE("THelp: MultiPosOpts", "[help]") { |
| ... | ... | @@ -394,18 +395,18 @@ TEST_CASE("THelp: ManualSetters", "[help]") { |
| 394 | 395 | |
| 395 | 396 | std::string help = app.help(); |
| 396 | 397 | |
| 397 | - CHECK_THAT(help, Contains("=12")); | |
| 398 | + CHECK_THAT(help, Contains("[12]")); | |
| 398 | 399 | CHECK_THAT(help, Contains("BIGGLES")); |
| 399 | 400 | |
| 400 | 401 | op1->default_val("14"); |
| 401 | 402 | CHECK(14 == x); |
| 402 | 403 | help = app.help(); |
| 403 | - CHECK_THAT(help, Contains("=14")); | |
| 404 | + CHECK_THAT(help, Contains("[14]")); | |
| 404 | 405 | |
| 405 | 406 | op1->default_val(12); |
| 406 | 407 | CHECK(12 == x); |
| 407 | 408 | help = app.help(); |
| 408 | - CHECK_THAT(help, Contains("=12")); | |
| 409 | + CHECK_THAT(help, Contains("[12]")); | |
| 409 | 410 | |
| 410 | 411 | CHECK(op1->get_run_callback_for_default()); |
| 411 | 412 | op1->run_callback_for_default(false); |
| ... | ... | @@ -415,7 +416,7 @@ TEST_CASE("THelp: ManualSetters", "[help]") { |
| 415 | 416 | // x should not be modified in this case |
| 416 | 417 | CHECK(12 == x); |
| 417 | 418 | help = app.help(); |
| 418 | - CHECK_THAT(help, Contains("=18")); | |
| 419 | + CHECK_THAT(help, Contains("[18]")); | |
| 419 | 420 | } |
| 420 | 421 | |
| 421 | 422 | TEST_CASE("THelp: ManualSetterOverFunction", "[help]") { |
| ... | ... | @@ -432,7 +433,7 @@ TEST_CASE("THelp: ManualSetterOverFunction", "[help]") { |
| 432 | 433 | CHECK(1 == x); |
| 433 | 434 | |
| 434 | 435 | std::string help = app.help(); |
| 435 | - CHECK_THAT(help, Contains("=12")); | |
| 436 | + CHECK_THAT(help, Contains("[12]")); | |
| 436 | 437 | CHECK_THAT(help, Contains("BIGGLES")); |
| 437 | 438 | CHECK_THAT(help, Contains("QUIGGLES")); |
| 438 | 439 | CHECK_THAT(help, Contains("{1,2}")); |
| ... | ... | @@ -518,7 +519,7 @@ TEST_CASE("THelp: IntDefaults", "[help]") { |
| 518 | 519 | CHECK_THAT(help, Contains("--one")); |
| 519 | 520 | CHECK_THAT(help, Contains("--set")); |
| 520 | 521 | CHECK_THAT(help, Contains("1")); |
| 521 | - CHECK_THAT(help, Contains("=2")); | |
| 522 | + CHECK_THAT(help, Contains("[2]")); | |
| 522 | 523 | CHECK_THAT(help, Contains("2,3,4")); |
| 523 | 524 | } |
| 524 | 525 | |
| ... | ... | @@ -532,7 +533,7 @@ TEST_CASE("THelp: SetLower", "[help]") { |
| 532 | 533 | std::string help = app.help(); |
| 533 | 534 | |
| 534 | 535 | CHECK_THAT(help, Contains("--set")); |
| 535 | - CHECK_THAT(help, Contains("=One")); | |
| 536 | + CHECK_THAT(help, Contains("[One]")); | |
| 536 | 537 | CHECK_THAT(help, Contains("oNe")); |
| 537 | 538 | CHECK_THAT(help, Contains("twO")); |
| 538 | 539 | CHECK_THAT(help, Contains("THREE")); |
| ... | ... | @@ -893,6 +894,14 @@ TEST_CASE("THelp: CheckEmptyTypeName", "[help]") { |
| 893 | 894 | CHECK(name.empty()); |
| 894 | 895 | } |
| 895 | 896 | |
| 897 | +TEST_CASE("THelp: FlagDefaults", "[help]") { | |
| 898 | + CLI::App app; | |
| 899 | + | |
| 900 | + app.add_flag("-t,--not{false}")->default_str("false"); | |
| 901 | + auto str = app.help(); | |
| 902 | + CHECK_THAT(str, Contains("--not{false}")); | |
| 903 | +} | |
| 904 | + | |
| 896 | 905 | TEST_CASE("THelp: AccessDescription", "[help]") { |
| 897 | 906 | CLI::App app{"My description goes here"}; |
| 898 | 907 | |
| ... | ... | @@ -1162,7 +1171,9 @@ TEST_CASE("THelp: ChangingDefaults", "[help]") { |
| 1162 | 1171 | x = {5, 6}; |
| 1163 | 1172 | std::string help = app.help(); |
| 1164 | 1173 | |
| 1165 | - CHECK_THAT(help, Contains("INT=[3,4] ...")); | |
| 1174 | + CHECK_THAT(help, Contains("[[3,4]]")); | |
| 1175 | + CHECK_THAT(help, Contains("...")); | |
| 1176 | + CHECK_THAT(help, Contains("INT")); | |
| 1166 | 1177 | CHECK(x[0] == 5); |
| 1167 | 1178 | } |
| 1168 | 1179 | |
| ... | ... | @@ -1179,7 +1190,8 @@ TEST_CASE("THelp: ChangingDefaultsWithAutoCapture", "[help]") { |
| 1179 | 1190 | |
| 1180 | 1191 | std::string help = app.help(); |
| 1181 | 1192 | |
| 1182 | - CHECK_THAT(help, Contains("INT=[1,2] ...")); | |
| 1193 | + CHECK_THAT(help, Contains("[[1,2]]")); | |
| 1194 | + CHECK_THAT(help, Contains("...")); | |
| 1183 | 1195 | } |
| 1184 | 1196 | |
| 1185 | 1197 | TEST_CASE("THelp: FunctionDefaultString", "[help]") { |
| ... | ... | @@ -1194,7 +1206,7 @@ TEST_CASE("THelp: FunctionDefaultString", "[help]") { |
| 1194 | 1206 | |
| 1195 | 1207 | std::string help = app.help(); |
| 1196 | 1208 | |
| 1197 | - CHECK_THAT(help, Contains("INT=Powerful")); | |
| 1209 | + CHECK_THAT(help, Contains("[Powerful]")); | |
| 1198 | 1210 | } |
| 1199 | 1211 | |
| 1200 | 1212 | TEST_CASE("TVersion: simple_flag", "[help]") { | ... | ... |
tests/SubcommandTest.cpp
| ... | ... | @@ -1955,3 +1955,16 @@ TEST_CASE_METHOD(TApp, "MultiFinalCallbackCounts", "[subcom]") { |
| 1955 | 1955 | CHECK(subsub_final == 1); |
| 1956 | 1956 | } |
| 1957 | 1957 | } |
| 1958 | + | |
| 1959 | +// From gitter issue | |
| 1960 | +TEST_CASE_METHOD(TApp, "SubcommandInOptionGroupCallbackCount", "[subcom]") { | |
| 1961 | + | |
| 1962 | + int subcount{0}; | |
| 1963 | + auto group1 = app.add_option_group("FirstGroup"); | |
| 1964 | + | |
| 1965 | + group1->add_subcommand("g1c1")->callback([&subcount]() { ++subcount; }); | |
| 1966 | + | |
| 1967 | + args = {"g1c1"}; | |
| 1968 | + run(); | |
| 1969 | + CHECK(subcount == 1); | |
| 1970 | +} | ... | ... |