Commit cfc389d4e46d7ed0ac65cc0849b213d19843a79a

Authored by Henry Fredrick Schreiner
1 parent 4fca03d0

Tests, fix extra options after subcommand going to parent

CHANGELOG.md
1 ## Version 0.5 (in progress) 1 ## Version 0.5 (in progress)
2 2
3 -* `->ignore_case()` added to subcommands, options, and `add_set_ignore_case` (untested). Subcommand inherit setting from parent App on creation. 3 +* `->ignore_case()` added to subcommands, options, and `add_set_ignore_case`. Subcommand inherit setting from parent App on creation.
4 * Subcommands now can be "chained", that is, left over arguments can now include subcommands that then get parsed. Subcommands are now a list (`get_subcommands`). Added `got_subcommand(App_or_name)` to check for subcommands. (untested) 4 * Subcommands now can be "chained", that is, left over arguments can now include subcommands that then get parsed. Subcommands are now a list (`get_subcommands`). Added `got_subcommand(App_or_name)` to check for subcommands. (untested)
5 * Added `.allow_extras()` to disable error on failure. Parse returns a vector of leftover options. Renamed error to `ExtrasError`, and now triggers on extra options too. (untested) 5 * Added `.allow_extras()` to disable error on failure. Parse returns a vector of leftover options. Renamed error to `ExtrasError`, and now triggers on extra options too. (untested)
6 * Added `require_subcommand` to `App`, to simplify forcing subcommands. Do not "chain" with `add_subcommand`, since that is the subcommand, not the master `App`. 6 * Added `require_subcommand` to `App`, to simplify forcing subcommands. Do not "chain" with `add_subcommand`, since that is the subcommand, not the master `App`.
README.md
@@ -43,7 +43,6 @@ This library was built to supply the Application object for the GooFit CUDA/OMP @@ -43,7 +43,6 @@ This library was built to supply the Application object for the GooFit CUDA/OMP
43 * Evaluate compatibility with [ROOT](https://root.cern.ch)'s TApplication object. 43 * Evaluate compatibility with [ROOT](https://root.cern.ch)'s TApplication object.
44 * Add tests: Add way for subclasses to return remaining options rather than throwing error 44 * Add tests: Add way for subclasses to return remaining options rather than throwing error
45 * Chained subcommands are not tested, once a subcommand is given the rest of the options go to that subcommand, rather than allowing multiple subcommands. This is currently intentional behavior, but multiple base level subcommands, like [`Click`](http://click.pocoo.org) supports, might be considered in the future. 45 * Chained subcommands are not tested, once a subcommand is given the rest of the options go to that subcommand, rather than allowing multiple subcommands. This is currently intentional behavior, but multiple base level subcommands, like [`Click`](http://click.pocoo.org) supports, might be considered in the future.
46 -* Test and refine support for case insensitive set and/or subcommands?  
47 * Throw error if `ignore_case` causes non-unique matches 46 * Throw error if `ignore_case` causes non-unique matches
48 47
49 See the [changelog](./CHANGELOG.md) or [GitHub releases](https://github.com/henryiii/CLI11/releases) for details. 48 See the [changelog](./CHANGELOG.md) or [GitHub releases](https://github.com/henryiii/CLI11/releases) for details.
include/CLI/App.hpp
@@ -600,12 +600,23 @@ protected: @@ -600,12 +600,23 @@ protected:
600 _parse_subcommand(args); 600 _parse_subcommand(args);
601 break; 601 break;
602 case detail::Classifer::LONG: 602 case detail::Classifer::LONG:
603 - _parse_long(args); 603 + // If already parsed a subcommand, don't accept options
  604 + if(selected_subcommands.size() > 0) {
  605 + missing.emplace_back(classifer, args.back());
  606 + args.pop_back();
  607 + } else
  608 + _parse_long(args);
604 break; 609 break;
605 case detail::Classifer::SHORT: 610 case detail::Classifer::SHORT:
606 - _parse_short(args); 611 + // If already parsed a subcommand, don't accept options
  612 + if(selected_subcommands.size() > 0) {
  613 + missing.emplace_back(classifer, args.back());
  614 + args.pop_back();
  615 + } else
  616 + _parse_short(args);
607 break; 617 break;
608 case detail::Classifer::NONE: 618 case detail::Classifer::NONE:
  619 + // Probably a positional or something for a parent (sub)command
609 missing.emplace_back(classifer, args.back()); 620 missing.emplace_back(classifer, args.back());
610 args.pop_back(); 621 args.pop_back();
611 } 622 }
tests/AppTest.cpp
@@ -325,6 +325,47 @@ TEST_F(TApp, InSet) { @@ -325,6 +325,47 @@ TEST_F(TApp, InSet) {
325 EXPECT_THROW(run(), CLI::ConversionError); 325 EXPECT_THROW(run(), CLI::ConversionError);
326 } 326 }
327 327
  328 +TEST_F(TApp, InIntSet) {
  329 +
  330 + int choice;
  331 + app.add_set("-q,--quick", choice, {1, 2, 3});
  332 +
  333 + args = {"--quick", "2"};
  334 +
  335 + EXPECT_NO_THROW(run());
  336 + EXPECT_EQ(2, choice);
  337 +
  338 + app.reset();
  339 +
  340 + args = {"--quick", "4"};
  341 + EXPECT_THROW(run(), CLI::ConversionError);
  342 +}
  343 +
  344 +TEST_F(TApp, InSetIgnoreCase) {
  345 +
  346 + std::string choice;
  347 + app.add_set_ignore_case("-q,--quick", choice, {"one", "Two", "THREE"});
  348 +
  349 + args = {"--quick", "One"};
  350 + EXPECT_NO_THROW(run());
  351 + EXPECT_EQ("one", choice);
  352 +
  353 + app.reset();
  354 + args = {"--quick", "two"};
  355 + EXPECT_NO_THROW(run());
  356 + EXPECT_EQ("Two", choice); // Keeps caps from set
  357 +
  358 + app.reset();
  359 + args = {"--quick", "ThrEE"};
  360 + EXPECT_NO_THROW(run());
  361 + EXPECT_EQ("THREE", choice); // Keeps caps from set
  362 +
  363 +
  364 + app.reset();
  365 + args = {"--quick", "four"};
  366 + EXPECT_THROW(run(), CLI::ConversionError);
  367 +}
  368 +
328 TEST_F(TApp, VectorFixedString) { 369 TEST_F(TApp, VectorFixedString) {
329 std::vector<std::string> strvec; 370 std::vector<std::string> strvec;
330 std::vector<std::string> answer{"mystring", "mystring2", "mystring3"}; 371 std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
tests/SubcommandTest.cpp
@@ -18,6 +18,10 @@ TEST_F(TApp, BasicSubcommands) { @@ -18,6 +18,10 @@ TEST_F(TApp, BasicSubcommands) {
18 args = {"sub2"}; 18 args = {"sub2"};
19 EXPECT_NO_THROW(run()); 19 EXPECT_NO_THROW(run());
20 EXPECT_EQ(sub2, app.get_subcommands().at(0)); 20 EXPECT_EQ(sub2, app.get_subcommands().at(0));
  21 +
  22 + app.reset();
  23 + args = {"SUb2"};
  24 + EXPECT_THROW(run(), CLI::ExtrasError);
21 } 25 }
22 26
23 27
@@ -98,6 +102,30 @@ TEST_F(SubcommandProgram, SpareSub) { @@ -98,6 +102,30 @@ TEST_F(SubcommandProgram, SpareSub) {
98 EXPECT_THROW(run(), CLI::ExtrasError); 102 EXPECT_THROW(run(), CLI::ExtrasError);
99 } 103 }
100 104
  105 +TEST_F(SubcommandProgram, Multiple) {
  106 + args = {"-d", "start", "-ffilename", "stop"};
  107 +
  108 + EXPECT_NO_THROW(run());
  109 + EXPECT_EQ(2, app.get_subcommands().size());
  110 + EXPECT_EQ(1, dummy);
  111 + EXPECT_EQ("filename", file);
  112 +}
  113 +
  114 +TEST_F(SubcommandProgram, MultipleOtherOrder) {
  115 + args = {"start", "-d", "-ffilename", "stop"};
  116 +
  117 + EXPECT_THROW(run(), CLI::ExtrasError);
  118 +}
  119 +
  120 +TEST_F(SubcommandProgram, MultipleArgs) {
  121 + args = {"start", "stop"};
  122 +
  123 + EXPECT_NO_THROW(run());
  124 +
  125 + EXPECT_EQ(2, app.get_subcommands().size());
  126 +
  127 +}
  128 +
101 TEST_F(SubcommandProgram, CaseCheck) { 129 TEST_F(SubcommandProgram, CaseCheck) {
102 args = {"Start"}; 130 args = {"Start"};
103 EXPECT_THROW(run(), CLI::ExtrasError); 131 EXPECT_THROW(run(), CLI::ExtrasError);
@@ -117,3 +145,35 @@ TEST_F(SubcommandProgram, CaseCheck) { @@ -117,3 +145,35 @@ TEST_F(SubcommandProgram, CaseCheck) {
117 EXPECT_NO_THROW(run()); 145 EXPECT_NO_THROW(run());
118 } 146 }
119 147
  148 +TEST_F(TApp, SubcomInheritCaseCheck) {
  149 + app.ignore_case();
  150 + auto sub1 = app.add_subcommand("sub1");
  151 + auto sub2 = app.add_subcommand("sub2");
  152 +
  153 + EXPECT_NO_THROW(run());
  154 + EXPECT_EQ(0, app.get_subcommands().size());
  155 +
  156 + app.reset();
  157 + args = {"SuB1"};
  158 + EXPECT_NO_THROW(run());
  159 + EXPECT_EQ(sub1, app.get_subcommands().at(0));
  160 +
  161 + app.reset();
  162 + EXPECT_EQ(0, app.get_subcommands().size());
  163 +
  164 + args = {"sUb2"};
  165 + EXPECT_NO_THROW(run());
  166 + EXPECT_EQ(sub2, app.get_subcommands().at(0));
  167 +}
  168 +
  169 +TEST_F(SubcommandProgram, HelpOrder) {
  170 +
  171 + args = {"-h"};
  172 + EXPECT_THROW(run(), CLI::CallForHelp);
  173 +
  174 + args = {"start", "-h"};
  175 + EXPECT_THROW(run(), CLI::CallForHelp);
  176 +
  177 + args = {"-h", "start"};
  178 + EXPECT_THROW(run(), CLI::CallForHelp);
  179 +}