Commit 17e7d60c18473c3c74902c61b548f94b9db40c6a

Authored by Philip Top
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>
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
... ... @@ -102,6 +102,7 @@ jobs:
102 102 gcc9:
103 103 containerImage: gcc:9
104 104 cli11.std: 17
  105 + cli11.options: -DCMAKE_CXX_FLAGS="-Wstrict-overflow=5"
105 106 gcc11:
106 107 containerImage: gcc:11
107 108 cli11.std: 20
... ...
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, &quot;IniLayeredCustomSectionSeparator&quot;, &quot;[config]&quot;) {
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(&quot;THelp: VectorOpts&quot;, &quot;[help]&quot;) {
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(&quot;THelp: ManualSetters&quot;, &quot;[help]&quot;) {
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(&quot;THelp: ManualSetters&quot;, &quot;[help]&quot;) {
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(&quot;THelp: ManualSetterOverFunction&quot;, &quot;[help]&quot;) {
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(&quot;THelp: IntDefaults&quot;, &quot;[help]&quot;) {
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(&quot;THelp: SetLower&quot;, &quot;[help]&quot;) {
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(&quot;THelp: CheckEmptyTypeName&quot;, &quot;[help]&quot;) {
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(&quot;THelp: ChangingDefaults&quot;, &quot;[help]&quot;) {
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(&quot;THelp: ChangingDefaultsWithAutoCapture&quot;, &quot;[help]&quot;) {
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(&quot;THelp: FunctionDefaultString&quot;, &quot;[help]&quot;) {
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, &quot;MultiFinalCallbackCounts&quot;, &quot;[subcom]&quot;) {
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 +}
... ...