Commit cfc389d4e46d7ed0ac65cc0849b213d19843a79a

Authored by Henry Fredrick Schreiner
1 parent 4fca03d0

Tests, fix extra options after subcommand going to parent

CHANGELOG.md
1 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 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 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 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 43 * Evaluate compatibility with [ROOT](https://root.cern.ch)'s TApplication object.
44 44 * Add tests: Add way for subclasses to return remaining options rather than throwing error
45 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 46 * Throw error if `ignore_case` causes non-unique matches
48 47  
49 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 600 _parse_subcommand(args);
601 601 break;
602 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 609 break;
605 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 617 break;
608 618 case detail::Classifer::NONE:
  619 + // Probably a positional or something for a parent (sub)command
609 620 missing.emplace_back(classifer, args.back());
610 621 args.pop_back();
611 622 }
... ...
tests/AppTest.cpp
... ... @@ -325,6 +325,47 @@ TEST_F(TApp, InSet) {
325 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 369 TEST_F(TApp, VectorFixedString) {
329 370 std::vector<std::string> strvec;
330 371 std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
... ...
tests/SubcommandTest.cpp
... ... @@ -18,6 +18,10 @@ TEST_F(TApp, BasicSubcommands) {
18 18 args = {"sub2"};
19 19 EXPECT_NO_THROW(run());
20 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 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 129 TEST_F(SubcommandProgram, CaseCheck) {
102 130 args = {"Start"};
103 131 EXPECT_THROW(run(), CLI::ExtrasError);
... ... @@ -117,3 +145,35 @@ TEST_F(SubcommandProgram, CaseCheck) {
117 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 +}
... ...