1 // Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
2 // under NSF AWARD 1414736 and by the respective contributors.
3 // All rights reserved.
4 //
5 // SPDX-License-Identifier: BSD-3-Clause
6 
7 #ifdef CLI11_SINGLE_FILE
8 #include "CLI11.hpp"
9 #else
10 #include "CLI/CLI.hpp"
11 #endif
12 
13 #include "catch.hpp"
14 #include <fstream>
15 
16 using Catch::Matchers::Contains;
17 
18 TEST_CASE("THelp: Basic", "[help]") {
19     CLI::App app{"My prog"};
20 
21     std::string help = app.help();
22 
23     CHECK_THAT(help, Contains("My prog"));
24     CHECK_THAT(help, Contains("-h,--help"));
25     CHECK_THAT(help, Contains("Options:"));
26     CHECK_THAT(help, Contains("Usage:"));
27 }
28 
29 TEST_CASE("THelp: Footer", "[help]") {
30     CLI::App app{"My prog"};
31     app.footer("Report bugs to bugs@example.com");
32 
33     std::string help = app.help();
34 
35     CHECK_THAT(help, Contains("My prog"));
36     CHECK_THAT(help, Contains("-h,--help"));
37     CHECK_THAT(help, Contains("Options:"));
38     CHECK_THAT(help, Contains("Usage:"));
39     CHECK_THAT(help, Contains("Report bugs to bugs@example.com"));
40 }
41 
42 TEST_CASE("THelp: FooterCallback", "[help]") {
43     CLI::App app{"My prog"};
__anonf33f93150102() 44     app.footer([]() { return "Report bugs to bugs@example.com"; });
45 
46     std::string help = app.help();
47 
48     CHECK_THAT(help, Contains("My prog"));
49     CHECK_THAT(help, Contains("-h,--help"));
50     CHECK_THAT(help, Contains("Options:"));
51     CHECK_THAT(help, Contains("Usage:"));
52     CHECK_THAT(help, Contains("Report bugs to bugs@example.com"));
53 }
54 
55 TEST_CASE("THelp: FooterCallbackBoth", "[help]") {
56     CLI::App app{"My prog"};
__anonf33f93150202() 57     app.footer([]() { return "Report bugs to bugs@example.com"; });
58     app.footer(" foot!!!!");
59     std::string help = app.help();
60 
61     CHECK_THAT(help, Contains("My prog"));
62     CHECK_THAT(help, Contains("-h,--help"));
63     CHECK_THAT(help, Contains("Options:"));
64     CHECK_THAT(help, Contains("Usage:"));
65     CHECK_THAT(help, Contains("Report bugs to bugs@example.com"));
66     CHECK_THAT(help, Contains("foot!!!!"));
67 }
68 
69 TEST_CASE("THelp: OptionalPositional", "[help]") {
70     CLI::App app{"My prog", "program"};
71 
72     std::string x;
73     app.add_option("something", x, "My option here");
74 
75     std::string help = app.help();
76 
77     CHECK_THAT(help, Contains("My prog"));
78     CHECK_THAT(help, Contains("-h,--help"));
79     CHECK_THAT(help, Contains("Options:"));
80     CHECK_THAT(help, Contains("Positionals:"));
81     CHECK_THAT(help, Contains("something TEXT"));
82     CHECK_THAT(help, Contains("My option here"));
83     CHECK_THAT(help, Contains("Usage: program [OPTIONS] [something]"));
84 }
85 
86 TEST_CASE("THelp: Hidden", "[help]") {
87     CLI::App app{"My prog"};
88 
89     std::string x;
90     app.add_option("something", x, "My option here")->group("");
91     std::string y;
92     app.add_option("--another", y)->group("");
93 
94     std::string help = app.help();
95 
96     CHECK_THAT(help, Contains("My prog"));
97     CHECK_THAT(help, Contains("-h,--help"));
98     CHECK_THAT(help, Contains("Options:"));
99     CHECK_THAT(help, !Contains("[something]"));
100     CHECK_THAT(help, !Contains("something "));
101     CHECK_THAT(help, !Contains("another"));
102 }
103 
104 TEST_CASE("THelp: deprecatedOptions", "[help]") {
105     CLI::App app{"My prog"};
106 
107     std::string x;
108     auto soption = app.add_option("--something", x, "My option here");
109     app.add_option("--something_else", x, "My option here");
110     std::string y;
111     app.add_option("--another", y);
112 
113     CLI::deprecate_option(soption, "something_else");
114 
115     std::string help = app.help();
116 
117     CHECK_THAT(help, Contains("DEPRECATED"));
118     CHECK_THAT(help, Contains("something"));
119     CHECK_NOTHROW(app.parse("--something deprecated"));
120 }
121 
122 TEST_CASE("THelp: deprecatedOptions2", "[help]") {
123     CLI::App app{"My prog"};
124 
125     std::string x;
126     app.add_option("--something", x, "My option here");
127     app.add_option("--something_else", x, "My option here");
128     std::string y;
129     app.add_option("--another", y);
130 
131     CLI::deprecate_option(&app, "--something");
132 
133     std::string help = app.help();
134 
135     CHECK_THAT(help, Contains("DEPRECATED"));
136     CHECK_THAT(help, Contains("something"));
137     CHECK_NOTHROW(app.parse("--something deprecated"));
138 }
139 
140 TEST_CASE("THelp: deprecatedOptions3", "[help]") {
141     CLI::App app{"My prog"};
142 
143     std::string x;
144     app.add_option("--something", x, "Some Description");
145     app.add_option("--something_else", x, "Some other description");
146     std::string y;
147     app.add_option("--another", y);
148 
149     CLI::deprecate_option(app, "--something", "--something_else");
150 
151     std::string help = app.help();
152 
153     CHECK_THAT(help, Contains("DEPRECATED"));
154     CHECK_THAT(help, Contains("'--something_else' instead"));
155     CHECK_NOTHROW(app.parse("--something deprecated"));
156 }
157 
158 TEST_CASE("THelp: retiredOptions", "[help]") {
159     CLI::App app{"My prog"};
160 
161     std::string x;
162     auto opt1 = app.add_option("--something", x, "My option here");
163     app.add_option("--something_else", x, "My option here");
164     std::string y;
165     app.add_option("--another", y);
166 
167     CLI::retire_option(app, opt1);
168 
169     std::string help = app.help();
170 
171     CHECK_THAT(help, Contains("RETIRED"));
172     CHECK_THAT(help, Contains("something"));
173 
174     CHECK_NOTHROW(app.parse("--something old"));
175 }
176 
177 TEST_CASE("THelp: retiredOptions2", "[help]") {
178     CLI::App app{"My prog"};
179 
180     std::string x;
181     app.add_option("--something_else", x, "My option here");
182     std::string y;
183     app.add_option("--another", y);
184 
185     CLI::retire_option(&app, "--something");
186 
187     std::string help = app.help();
188 
189     CHECK_THAT(help, Contains("RETIRED"));
190     CHECK_THAT(help, Contains("something"));
191     CHECK_NOTHROW(app.parse("--something old"));
192 }
193 
194 TEST_CASE("THelp: retiredOptions3", "[help]") {
195     CLI::App app{"My prog"};
196 
197     std::string x;
198     app.add_option("--something", x, "My option here");
199     app.add_option("--something_else", x, "My option here");
200     std::string y;
201     app.add_option("--another", y);
202 
203     CLI::retire_option(app, "--something");
204 
205     std::string help = app.help();
206 
207     CHECK_THAT(help, Contains("RETIRED"));
208     CHECK_THAT(help, Contains("something"));
209 
210     CHECK_NOTHROW(app.parse("--something old"));
211 }
212 
213 TEST_CASE("THelp: HiddenGroup", "[help]") {
214     CLI::App app{"My prog"};
215     // empty option group name should be hidden
216     auto hgroup = app.add_option_group("");
217     std::string x;
218     hgroup->add_option("something", x, "My option here");
219     std::string y;
220     hgroup->add_option("--another", y);
221 
222     std::string help = app.help();
223 
224     CHECK_THAT(help, Contains("My prog"));
225     CHECK_THAT(help, Contains("-h,--help"));
226     CHECK_THAT(help, Contains("Options:"));
227     CHECK_THAT(help, !Contains("[something]"));
228     CHECK_THAT(help, !Contains("something "));
229     CHECK_THAT(help, !Contains("another"));
230 
231     hgroup->group("ghidden");
232 
233     help = app.help();
234 
235     CHECK_THAT(help, Contains("something "));
236     CHECK_THAT(help, Contains("another"));
237 }
238 
239 TEST_CASE("THelp: OptionalPositionalAndOptions", "[help]") {
240     CLI::App app{"My prog", "AnotherProgram"};
241     app.add_flag("-q,--quick");
242 
243     std::string x;
244     app.add_option("something", x, "My option here");
245 
246     std::string help = app.help();
247 
248     CHECK_THAT(help, Contains("My prog"));
249     CHECK_THAT(help, Contains("-h,--help"));
250     CHECK_THAT(help, Contains("Options:"));
251     CHECK_THAT(help, Contains("Usage: AnotherProgram [OPTIONS] [something]"));
252 }
253 
254 TEST_CASE("THelp: RequiredPositionalAndOptions", "[help]") {
255     CLI::App app{"My prog"};
256     app.add_flag("-q,--quick");
257 
258     std::string x;
259     app.add_option("something", x, "My option here")->required();
260 
261     std::string help = app.help();
262 
263     CHECK_THAT(help, Contains("My prog"));
264     CHECK_THAT(help, Contains("-h,--help"));
265     CHECK_THAT(help, Contains("Options:"));
266     CHECK_THAT(help, Contains("Positionals:"));
267     CHECK_THAT(help, Contains("Usage: [OPTIONS] something"));
268 }
269 
270 TEST_CASE("THelp: MultiOpts", "[help]") {
271     CLI::App app{"My prog"};
272     std::vector<int> x, y;
273     app.add_option("-q,--quick", x, "Disc")->expected(2);
274     app.add_option("-v,--vals", y, "Other");
275 
276     std::string help = app.help();
277 
278     CHECK_THAT(help, Contains("My prog"));
279     CHECK_THAT(help, !Contains("Positionals:"));
280     CHECK_THAT(help, Contains("Usage: [OPTIONS]"));
281     CHECK_THAT(help, Contains("INT x 2"));
282     CHECK_THAT(help, Contains("INT ..."));
283 }
284 
285 TEST_CASE("THelp: VectorOpts", "[help]") {
286     CLI::App app{"My prog"};
287     std::vector<int> x = {1, 2};
288     app.add_option("-q,--quick", x)->capture_default_str();
289 
290     std::string help = app.help();
291 
292     CHECK_THAT(help, Contains("INT=[1,2] ..."));
293 }
294 
295 TEST_CASE("THelp: MultiPosOpts", "[help]") {
296     CLI::App app{"My prog"};
297     app.name("program");
298     std::vector<int> x, y;
299     app.add_option("quick", x, "Disc")->expected(2);
300     app.add_option("vals", y, "Other");
301 
302     std::string help = app.help();
303 
304     CHECK_THAT(help, Contains("My prog"));
305     CHECK_THAT(help, Contains("Positionals:"));
306     CHECK_THAT(help, Contains("Usage: program [OPTIONS]"));
307     CHECK_THAT(help, Contains("INT x 2"));
308     CHECK_THAT(help, Contains("INT ..."));
309     CHECK_THAT(help, Contains("[quick(2x)]"));
310     CHECK_THAT(help, Contains("[vals...]"));
311 }
312 
313 TEST_CASE("THelp: EnvName", "[help]") {
314     CLI::App app{"My prog"};
315     std::string input;
316     app.add_option("--something", input)->envname("SOME_ENV");
317 
318     std::string help = app.help();
319 
320     CHECK_THAT(help, Contains("SOME_ENV"));
321 }
322 
323 TEST_CASE("THelp: Needs", "[help]") {
324     CLI::App app{"My prog"};
325 
326     CLI::Option *op1 = app.add_flag("--op1");
327     app.add_flag("--op2")->needs(op1);
328 
329     std::string help = app.help();
330 
331     CHECK_THAT(help, Contains("Needs: --op1"));
332 }
333 
334 TEST_CASE("THelp: NeedsPositional", "[help]") {
335     CLI::App app{"My prog"};
336 
337     int x{0}, y{0};
338 
339     CLI::Option *op1 = app.add_option("op1", x, "one");
340     app.add_option("op2", y, "two")->needs(op1);
341 
342     std::string help = app.help();
343 
344     CHECK_THAT(help, Contains("Positionals:"));
345     CHECK_THAT(help, Contains("Needs: op1"));
346 }
347 
348 TEST_CASE("THelp: Excludes", "[help]") {
349     CLI::App app{"My prog"};
350 
351     CLI::Option *op1 = app.add_flag("--op1");
352     app.add_flag("--op2")->excludes(op1);
353 
354     std::string help = app.help();
355 
356     CHECK_THAT(help, Contains("Excludes: --op1"));
357 }
358 
359 TEST_CASE("THelp: ExcludesPositional", "[help]") {
360     CLI::App app{"My prog"};
361 
362     int x{0}, y{0};
363 
364     CLI::Option *op1 = app.add_option("op1", x);
365     app.add_option("op2", y)->excludes(op1);
366 
367     std::string help = app.help();
368 
369     CHECK_THAT(help, Contains("Positionals:"));
370     CHECK_THAT(help, Contains("Excludes: op1"));
371 }
372 
373 TEST_CASE("THelp: ExcludesSymmetric", "[help]") {
374     CLI::App app{"My prog"};
375 
376     CLI::Option *op1 = app.add_flag("--op1");
377     app.add_flag("--op2")->excludes(op1);
378 
379     std::string help = app.help();
380 
381     CHECK_THAT(help, Contains("Excludes: --op2"));
382 }
383 
384 TEST_CASE("THelp: ManualSetters", "[help]") {
385 
386     CLI::App app{"My prog"};
387 
388     int x{1};
389 
390     CLI::Option *op1 = app.add_option("--op", x);
391     op1->default_str("12");
392     op1->type_name("BIGGLES");
393     CHECK(1 == x);
394 
395     std::string help = app.help();
396 
397     CHECK_THAT(help, Contains("=12"));
398     CHECK_THAT(help, Contains("BIGGLES"));
399 
400     op1->default_val("14");
401     CHECK(14 == x);
402     help = app.help();
403     CHECK_THAT(help, Contains("=14"));
404 
405     op1->default_val(12);
406     CHECK(12 == x);
407     help = app.help();
408     CHECK_THAT(help, Contains("=12"));
409 
410     CHECK(op1->get_run_callback_for_default());
411     op1->run_callback_for_default(false);
412     CHECK(!op1->get_run_callback_for_default());
413 
414     op1->default_val(18);
415     // x should not be modified in this case
416     CHECK(12 == x);
417     help = app.help();
418     CHECK_THAT(help, Contains("=18"));
419 }
420 
421 TEST_CASE("THelp: ManualSetterOverFunction", "[help]") {
422 
423     CLI::App app{"My prog"};
424 
425     int x{1};
426 
427     CLI::Option *op1 = app.add_option("--op1", x)->check(CLI::IsMember({1, 2}));
428     CLI::Option *op2 = app.add_option("--op2", x)->transform(CLI::IsMember({1, 2}));
429     op1->default_str("12");
430     op1->type_name("BIGGLES");
431     op2->type_name("QUIGGLES");
432     CHECK(1 == x);
433 
434     std::string help = app.help();
435     CHECK_THAT(help, Contains("=12"));
436     CHECK_THAT(help, Contains("BIGGLES"));
437     CHECK_THAT(help, Contains("QUIGGLES"));
438     CHECK_THAT(help, Contains("{1,2}"));
439 }
440 
441 TEST_CASE("THelp: Subcom", "[help]") {
442     CLI::App app{"My prog"};
443 
444     auto sub1 = app.add_subcommand("sub1");
445     app.add_subcommand("sub2");
446 
447     std::string help = app.help();
448     CHECK_THAT(help, Contains("Usage: [OPTIONS] [SUBCOMMAND]"));
449 
450     app.require_subcommand();
451 
452     help = app.help();
453     CHECK_THAT(help, Contains("Usage: [OPTIONS] SUBCOMMAND"));
454 
455     help = sub1->help();
456     CHECK_THAT(help, Contains("Usage: sub1"));
457 
458     char x[] = "./myprogram";
459     char y[] = "sub2";
460 
461     std::vector<char *> args = {x, y};
462     app.parse(static_cast<int>(args.size()), args.data());
463 
464     help = app.help();
465     CHECK_THAT(help, Contains("Usage: ./myprogram sub2"));
466 }
467 
468 TEST_CASE("THelp: Subcom_alias", "[help]") {
469     CLI::App app{"My prog"};
470 
471     auto sub1 = app.add_subcommand("sub1", "Subcommand1 description test");
472     sub1->alias("sub_alias1");
473     sub1->alias("sub_alias2");
474 
475     app.add_subcommand("sub2", "Subcommand2 description test");
476 
477     std::string help = app.help();
478     CHECK_THAT(help, Contains("Usage: [OPTIONS] [SUBCOMMAND]"));
479     CHECK_THAT(help, Contains("sub_alias1"));
480     CHECK_THAT(help, Contains("sub_alias2"));
481 }
482 
483 TEST_CASE("THelp: Subcom_alias_group", "[help]") {
484     CLI::App app{"My prog"};
485 
486     auto sub1 = app.add_subcommand("", "Subcommand1 description test");
487     sub1->alias("sub_alias1");
488     sub1->alias("sub_alias2");
489 
490     app.add_subcommand("sub2", "Subcommand2 description test");
491 
492     std::string help = app.help();
493     CHECK_THAT(help, Contains("Usage: [OPTIONS] [SUBCOMMAND]"));
494     CHECK_THAT(help, Contains("sub_alias1"));
495     CHECK_THAT(help, Contains("sub_alias2"));
496 }
497 
498 TEST_CASE("THelp: MasterName", "[help]") {
499     CLI::App app{"My prog", "MyRealName"};
500 
501     char x[] = "./myprogram";
502 
503     std::vector<char *> args = {x};
504     app.parse(static_cast<int>(args.size()), args.data());
505 
506     CHECK_THAT(app.help(), Contains("Usage: MyRealName"));
507 }
508 
509 TEST_CASE("THelp: IntDefaults", "[help]") {
510     CLI::App app{"My prog"};
511 
512     int one{1}, two{2};
513     app.add_option("--one", one, "Help for one")->capture_default_str();
514     app.add_option("--set", two, "Help for set")->capture_default_str()->check(CLI::IsMember({2, 3, 4}));
515 
516     std::string help = app.help();
517 
518     CHECK_THAT(help, Contains("--one"));
519     CHECK_THAT(help, Contains("--set"));
520     CHECK_THAT(help, Contains("1"));
521     CHECK_THAT(help, Contains("=2"));
522     CHECK_THAT(help, Contains("2,3,4"));
523 }
524 
525 TEST_CASE("THelp: SetLower", "[help]") {
526     CLI::App app{"My prog"};
527     app.option_defaults()->always_capture_default();
528 
529     std::string def{"One"};
530     app.add_option("--set", def, "Help for set")->check(CLI::IsMember({"oNe", "twO", "THREE"}));
531 
532     std::string help = app.help();
533 
534     CHECK_THAT(help, Contains("--set"));
535     CHECK_THAT(help, Contains("=One"));
536     CHECK_THAT(help, Contains("oNe"));
537     CHECK_THAT(help, Contains("twO"));
538     CHECK_THAT(help, Contains("THREE"));
539 }
540 
541 TEST_CASE("THelp: OnlyOneHelp", "[help]") {
542     CLI::App app{"My prog"};
543 
544     // It is not supported to have more than one help flag, last one wins
545     app.set_help_flag("--help", "No short name allowed");
546     app.set_help_flag("--yelp", "Alias for help");
547 
548     std::vector<std::string> input{"--help"};
549     CHECK_THROWS_AS(app.parse(input), CLI::ExtrasError);
550 }
551 
552 TEST_CASE("THelp: MultiHelp", "[help]") {
553     CLI::App app{"My prog"};
554 
555     // It is not supported to have more than one help flag, last one wins
556     app.set_help_flag("--help,-h,-?", "No short name allowed");
557     app.allow_windows_style_options();
558 
559     std::vector<std::string> input{"/?"};
560     CHECK_THROWS_AS(app.parse(input), CLI::CallForHelp);
561 }
562 
563 TEST_CASE("THelp: OnlyOneAllHelp", "[help]") {
564     CLI::App app{"My prog"};
565 
566     // It is not supported to have more than one help flag, last one wins
567     app.set_help_all_flag("--help-all", "No short name allowed");
568     app.set_help_all_flag("--yelp", "Alias for help");
569 
570     std::vector<std::string> input{"--help-all"};
571     CHECK_THROWS_AS(app.parse(input), CLI::ExtrasError);
572 
573     std::vector<std::string> input2{"--yelp"};
574     CHECK_THROWS_AS(app.parse(input2), CLI::CallForAllHelp);
575 
576     // Remove the flag
577     app.set_help_all_flag();
578     std::vector<std::string> input3{"--yelp"};
579     CHECK_THROWS_AS(app.parse(input3), CLI::ExtrasError);
580 }
581 
582 TEST_CASE("THelp: RemoveHelp", "[help]") {
583     CLI::App app{"My prog"};
584     app.set_help_flag();
585 
586     std::string help = app.help();
587 
588     CHECK_THAT(help, Contains("My prog"));
589     CHECK_THAT(help, !Contains("-h,--help"));
590     CHECK_THAT(help, !Contains("Options:"));
591     CHECK_THAT(help, Contains("Usage:"));
592 
593     std::vector<std::string> input{"--help"};
594     try {
595         app.parse(input);
596     } catch(const CLI::ParseError &e) {
597         CHECK(e.get_exit_code() == static_cast<int>(CLI::ExitCodes::ExtrasError));
598     }
599 }
600 
601 TEST_CASE("THelp: RemoveOtherMethodHelp", "[help]") {
602     CLI::App app{"My prog"};
603 
604     // Don't do this. Just in case, let's make sure it works.
605     app.remove_option(const_cast<CLI::Option *>(app.get_help_ptr()));
606 
607     std::string help = app.help();
608 
609     CHECK_THAT(help, Contains("My prog"));
610     CHECK_THAT(help, !Contains("-h,--help"));
611     CHECK_THAT(help, !Contains("Options:"));
612     CHECK_THAT(help, Contains("Usage:"));
613 
614     std::vector<std::string> input{"--help"};
615     try {
616         app.parse(input);
617     } catch(const CLI::ParseError &e) {
618         CHECK(e.get_exit_code() == static_cast<int>(CLI::ExitCodes::ExtrasError));
619     }
620 }
621 
622 TEST_CASE("THelp: RemoveOtherMethodHelpAll", "[help]") {
623     CLI::App app{"My prog"};
624 
625     app.set_help_all_flag("--help-all");
626     // Don't do this. Just in case, let's make sure it works.
627     app.remove_option(const_cast<CLI::Option *>(app.get_help_all_ptr()));
628 
629     std::string help = app.help();
630 
631     CHECK_THAT(help, Contains("My prog"));
632     CHECK_THAT(help, !Contains("--help-all"));
633     CHECK_THAT(help, Contains("Options:"));
634     CHECK_THAT(help, Contains("Usage:"));
635 
636     std::vector<std::string> input{"--help-all"};
637     try {
638         app.parse(input);
639     } catch(const CLI::ParseError &e) {
640         CHECK(e.get_exit_code() == static_cast<int>(CLI::ExitCodes::ExtrasError));
641     }
642 }
643 
644 TEST_CASE("THelp: NoHelp", "[help]") {
645     CLI::App app{"My prog"};
646     app.set_help_flag();
647 
648     std::string help = app.help();
649 
650     CHECK_THAT(help, Contains("My prog"));
651     CHECK_THAT(help, !Contains("-h,--help"));
652     CHECK_THAT(help, !Contains("Options:"));
653     CHECK_THAT(help, Contains("Usage:"));
654 
655     std::vector<std::string> input{"--help"};
656     try {
657         app.parse(input);
658     } catch(const CLI::ParseError &e) {
659         CHECK(e.get_exit_code() == static_cast<int>(CLI::ExitCodes::ExtrasError));
660     }
661 }
662 
663 TEST_CASE("THelp: CustomHelp", "[help]") {
664     CLI::App app{"My prog"};
665 
666     CLI::Option *help_option = app.set_help_flag("--yelp", "display help and exit");
667     CHECK(help_option == app.get_help_ptr());
668 
669     std::string help = app.help();
670 
671     CHECK_THAT(help, Contains("My prog"));
672     CHECK_THAT(help, !Contains("-h,--help"));
673     CHECK_THAT(help, Contains("--yelp"));
674     CHECK_THAT(help, Contains("Options:"));
675     CHECK_THAT(help, Contains("Usage:"));
676 
677     std::vector<std::string> input{"--yelp"};
678     try {
679         app.parse(input);
680     } catch(const CLI::CallForHelp &e) {
681         CHECK(e.get_exit_code() == static_cast<int>(CLI::ExitCodes::Success));
682     }
683 }
684 
685 TEST_CASE("THelp: NextLineShouldBeAlignmentInMultilineDescription", "[help]") {
686     CLI::App app;
687     int i{0};
688     const std::string first{"first line"};
689     const std::string second{"second line"};
690     app.add_option("-i,--int", i, first + "\n" + second);
691 
692     const std::string help = app.help();
693     const auto width = app.get_formatter()->get_column_width();
694     CHECK_THAT(help, Contains(first + "\n" + std::string(width, ' ') + second));
695 }
696 
697 TEST_CASE("THelp: NiceName", "[help]") {
698     CLI::App app;
699 
700     int x{0};
701     auto long_name = app.add_option("-s,--long,-q,--other,that", x);
702     auto short_name = app.add_option("more,-x,-y", x);
703     auto positional = app.add_option("posit", x);
704 
705     CHECK("--long" == long_name->get_name());
706     CHECK("-x" == short_name->get_name());
707     CHECK("posit" == positional->get_name());
708 }
709 
710 TEST_CASE("Exit: ErrorWithHelp", "[help]") {
711     CLI::App app{"My prog"};
712 
713     std::vector<std::string> input{"-h"};
714     try {
715         app.parse(input);
716     } catch(const CLI::CallForHelp &e) {
717         CHECK(e.get_exit_code() == static_cast<int>(CLI::ExitCodes::Success));
718     }
719 }
720 
721 TEST_CASE("Exit: ErrorWithAllHelp", "[help]") {
722     CLI::App app{"My prog"};
723     app.set_help_all_flag("--help-all", "All help");
724 
725     std::vector<std::string> input{"--help-all"};
726     try {
727         app.parse(input);
728     } catch(const CLI::CallForAllHelp &e) {
729         CHECK(e.get_exit_code() == static_cast<int>(CLI::ExitCodes::Success));
730     }
731 }
732 
733 TEST_CASE("Exit: ErrorWithoutHelp", "[help]") {
734     CLI::App app{"My prog"};
735 
736     std::vector<std::string> input{"--none"};
737     try {
738         app.parse(input);
739     } catch(const CLI::ParseError &e) {
740         CHECK(e.get_exit_code() == static_cast<int>(CLI::ExitCodes::ExtrasError));
741     }
742 }
743 
744 TEST_CASE("Exit: ExitCodes", "[help]") {
745     CLI::App app;
746 
747     auto i = static_cast<int>(CLI::ExitCodes::ExtrasError);
748     CHECK(app.exit(CLI::Success()) == 0);
749     CHECK(app.exit(CLI::CallForHelp()) == 0);
750     CHECK(app.exit(CLI::ExtrasError({"Thing"})) == i);
751     CHECK(app.exit(CLI::RuntimeError(42)) == 42);
752     CHECK(app.exit(CLI::RuntimeError()) == 1);
753 }
754 
755 struct CapturedHelp {
756     CLI::App app{"My Test Program"};
757     std::stringstream out{};
758     std::stringstream err{};
759 
runCapturedHelp760     int run(const CLI::Error &e) { return app.exit(e, out, err); }
761 
resetCapturedHelp762     void reset() {
763         out.clear();
764         err.clear();
765     }
766 };
767 
768 TEST_CASE_METHOD(CapturedHelp, "Successful", "[help]") {
769     CHECK(0 == run(CLI::Success()));
770     CHECK("" == out.str());
771     CHECK("" == err.str());
772 }
773 
774 TEST_CASE_METHOD(CapturedHelp, "JustAnError", "[help]") {
775     CHECK(42 == run(CLI::RuntimeError(42)));
776     CHECK("" == out.str());
777     CHECK("" == err.str());
778 }
779 
780 TEST_CASE_METHOD(CapturedHelp, "CallForHelp", "[help]") {
781     CHECK(0 == run(CLI::CallForHelp()));
782     CHECK(app.help() == out.str());
783     CHECK("" == err.str());
784 }
785 TEST_CASE_METHOD(CapturedHelp, "CallForAllHelp", "[help]") {
786     CHECK(0 == run(CLI::CallForAllHelp()));
787     CHECK(app.help("", CLI::AppFormatMode::All) == out.str());
788     CHECK("" == err.str());
789 }
790 TEST_CASE_METHOD(CapturedHelp, "CallForAllHelpOutput", "[help]") {
791     app.set_help_all_flag("--help-all", "Help all");
792     app.add_subcommand("one", "One description");
793     CLI::App *sub = app.add_subcommand("two");
794     sub->add_flag("--three");
795 
796     CHECK(0 == run(CLI::CallForAllHelp()));
797     CHECK(app.help("", CLI::AppFormatMode::All) == out.str());
798     CHECK("" == err.str());
799     CHECK_THAT(out.str(), Contains("one"));
800     CHECK_THAT(out.str(), Contains("two"));
801     CHECK_THAT(out.str(), Contains("--three"));
802 
803     CHECK(out.str() == "My Test Program\n"
804                        "Usage: [OPTIONS] [SUBCOMMAND]\n"
805                        "\n"
806                        "Options:\n"
807                        "  -h,--help                   Print this help message and exit\n"
808                        "  --help-all                  Help all\n"
809                        "\n"
810                        "Subcommands:\n"
811                        "one\n"
812                        "  One description\n\n"
813                        "two\n"
814                        "  Options:\n"
815                        "    --three                     \n\n\n");
816 }
817 TEST_CASE_METHOD(CapturedHelp, "NewFormattedHelp", "[help]") {
__anonf33f93150302(const CLI::App *, std::string, CLI::AppFormatMode) 818     app.formatter_fn([](const CLI::App *, std::string, CLI::AppFormatMode) { return "New Help"; });
819     CHECK(0 == run(CLI::CallForHelp()));
820     CHECK("New Help" == out.str());
821     CHECK("" == err.str());
822 }
823 
824 TEST_CASE_METHOD(CapturedHelp, "NormalError", "[help]") {
825     CHECK(static_cast<int>(CLI::ExitCodes::ExtrasError) == run(CLI::ExtrasError({"Thing"})));
826     CHECK("" == out.str());
827     CHECK_THAT(err.str(), Contains("for more information"));
828     CHECK_THAT(err.str(), !Contains("ExtrasError"));
829     CHECK_THAT(err.str(), Contains("Thing"));
830     CHECK_THAT(err.str(), !Contains(" or "));
831     CHECK_THAT(err.str(), !Contains("Usage"));
832 }
833 
834 TEST_CASE_METHOD(CapturedHelp, "DoubleError", "[help]") {
835     app.set_help_all_flag("--help-all");
836     CHECK(static_cast<int>(CLI::ExitCodes::ExtrasError) == run(CLI::ExtrasError({"Thing"})));
837     CHECK("" == out.str());
838     CHECK_THAT(err.str(), Contains("for more information"));
839     CHECK_THAT(err.str(), Contains(" --help "));
840     CHECK_THAT(err.str(), Contains(" --help-all "));
841     CHECK_THAT(err.str(), Contains(" or "));
842     CHECK_THAT(err.str(), !Contains("ExtrasError"));
843     CHECK_THAT(err.str(), Contains("Thing"));
844     CHECK_THAT(err.str(), !Contains("Usage"));
845 }
846 
847 TEST_CASE_METHOD(CapturedHelp, "AllOnlyError", "[help]") {
848     app.set_help_all_flag("--help-all");
849     app.set_help_flag();
850     CHECK(static_cast<int>(CLI::ExitCodes::ExtrasError) == run(CLI::ExtrasError({"Thing"})));
851     CHECK("" == out.str());
852     CHECK_THAT(err.str(), Contains("for more information"));
853     CHECK_THAT(err.str(), !Contains(" --help "));
854     CHECK_THAT(err.str(), Contains(" --help-all "));
855     CHECK_THAT(err.str(), !Contains(" or "));
856     CHECK_THAT(err.str(), !Contains("ExtrasError"));
857     CHECK_THAT(err.str(), Contains("Thing"));
858     CHECK_THAT(err.str(), !Contains("Usage"));
859 }
860 
861 TEST_CASE_METHOD(CapturedHelp, "ReplacedError", "[help]") {
862     app.failure_message(CLI::FailureMessage::help);
863 
864     CHECK(static_cast<int>(CLI::ExitCodes::ExtrasError) == run(CLI::ExtrasError({"Thing"})));
865     CHECK("" == out.str());
866     CHECK_THAT(err.str(), !Contains("for more information"));
867     CHECK_THAT(err.str(), Contains("ERROR: ExtrasError"));
868     CHECK_THAT(err.str(), Contains("Thing"));
869     CHECK_THAT(err.str(), Contains("Usage"));
870 }
871 
872 // #87
873 TEST_CASE("THelp: CustomDoubleOption", "[help]") {
874 
875     std::pair<int, double> custom_opt;
876 
877     CLI::App app;
878 
__anonf33f93150402(CLI::results_t vals) 879     auto opt = app.add_option("posit", [&custom_opt](CLI::results_t vals) {
880         custom_opt = {stol(vals.at(0)), stod(vals.at(1))};
881         return true;
882     });
883     opt->type_name("INT FLOAT")->type_size(2);
884 
885     CHECK_THAT(app.help(), !Contains("x 2"));
886 }
887 
888 TEST_CASE("THelp: CheckEmptyTypeName", "[help]") {
889     CLI::App app;
890 
891     auto opt = app.add_flag("-f,--flag");
892     std::string name = opt->get_type_name();
893     CHECK(name.empty());
894 }
895 
896 TEST_CASE("THelp: AccessDescription", "[help]") {
897     CLI::App app{"My description goes here"};
898 
899     CHECK("My description goes here" == app.get_description());
900 }
901 
902 TEST_CASE("THelp: SetDescriptionAfterCreation", "[help]") {
903     CLI::App app{""};
904 
905     app.description("My description goes here");
906 
907     CHECK("My description goes here" == app.get_description());
908     CHECK_THAT(app.help(), Contains("My description goes here"));
909 }
910 
911 TEST_CASE("THelp: AccessOptionDescription", "[help]") {
912     CLI::App app{};
913 
914     int x{0};
915     auto opt = app.add_option("-a,--alpha", x, "My description goes here");
916 
917     CHECK("My description goes here" == opt->get_description());
918 }
919 
920 TEST_CASE("THelp: SetOptionDescriptionAfterCreation", "[help]") {
921     CLI::App app{};
922 
923     int x{0};
924     auto opt = app.add_option("-a,--alpha", x);
925     opt->description("My description goes here");
926 
927     CHECK("My description goes here" == opt->get_description());
928     CHECK_THAT(app.help(), Contains("My description goes here"));
929 }
930 
931 TEST_CASE("THelp: CleanNeeds", "[help]") {
932     CLI::App app;
933 
934     int x{0};
935     auto a_name = app.add_option("-a,--alpha", x);
936     app.add_option("-b,--boo", x)->needs(a_name);
937 
938     CHECK_THAT(app.help(), !Contains("Requires"));
939     CHECK_THAT(app.help(), !Contains("Needs: -a,--alpha"));
940     CHECK_THAT(app.help(), Contains("Needs: --alpha"));
941 }
942 
943 TEST_CASE("THelp: RequiredPrintout", "[help]") {
944     CLI::App app;
945 
946     int x{0};
947     app.add_option("-a,--alpha", x)->required();
948 
949     CHECK_THAT(app.help(), Contains(" REQUIRED"));
950 }
951 
952 TEST_CASE("THelp: GroupOrder", "[help]") {
953     CLI::App app;
954 
955     app.add_flag("--one")->group("zee");
956     app.add_flag("--two")->group("aee");
957 
958     std::string help = app.help();
959 
960     auto zee_loc = help.find("zee");
961     auto aee_loc = help.find("aee");
962 
963     CHECK(std::string::npos != zee_loc);
964     CHECK(std::string::npos != aee_loc);
965     CHECK(aee_loc > zee_loc);
966 }
967 
968 TEST_CASE("THelp: ValidatorsText", "[help]") {
969     CLI::App app;
970 
971     std::string filename;
972     int x{0};
973     unsigned int y{0};
974     app.add_option("--f1", filename)->check(CLI::ExistingFile);
975     app.add_option("--f3", x)->check(CLI::Range(1, 4));
976     app.add_option("--f4", y)->check(CLI::Range(12));
977 
978     std::string help = app.help();
979     CHECK_THAT(help, Contains("TEXT:FILE"));
980     CHECK_THAT(help, Contains("INT in [1 - 4]"));
981     CHECK_THAT(help, Contains("UINT:INT in [0 - 12]"));
982 }
983 
984 TEST_CASE("THelp: ValidatorsTextCustom", "[help]") {
985     CLI::App app;
986 
987     std::string filename;
988     app.add_option("--f1", filename)->check(CLI::ExistingFile.description("Existing file"));
989 
990     std::string help = app.help();
991     CHECK_THAT(help, Contains("Existing file"));
992 }
993 
994 TEST_CASE("THelp: ValidatorsNonPathText", "[help]") {
995     CLI::App app;
996 
997     std::string filename;
998     app.add_option("--f2", filename)->check(CLI::NonexistentPath);
999 
1000     std::string help = app.help();
1001     CHECK_THAT(help, Contains("TEXT:PATH"));
1002 }
1003 
1004 TEST_CASE("THelp: ValidatorsDirText", "[help]") {
1005     CLI::App app;
1006 
1007     std::string filename;
1008     app.add_option("--f2", filename)->check(CLI::ExistingDirectory);
1009 
1010     std::string help = app.help();
1011     CHECK_THAT(help, Contains("TEXT:DIR"));
1012 }
1013 
1014 TEST_CASE("THelp: ValidatorsPathText", "[help]") {
1015     CLI::App app;
1016 
1017     std::string filename;
1018     app.add_option("--f2", filename)->check(CLI::ExistingPath);
1019 
1020     std::string help = app.help();
1021     CHECK_THAT(help, Contains("TEXT:PATH"));
1022 }
1023 
1024 TEST_CASE("THelp: CombinedValidatorsText", "[help]") {
1025     CLI::App app;
1026 
1027     std::string filename;
1028     app.add_option("--f1", filename)->check(CLI::ExistingFile | CLI::ExistingDirectory);
1029 
1030     // This would be nice if it put something other than string, but would it be path or file?
1031     // Can't programmatically tell!
1032     // (Users can use ExistingPath, by the way)
1033     std::string help = app.help();
1034     CHECK_THAT(help, Contains("TEXT:(FILE) OR (DIR)"));
1035     CHECK_THAT(help, !Contains("PATH"));
1036 }
1037 
1038 // Don't do this in real life, please
1039 TEST_CASE("THelp: CombinedValidatorsPathyText", "[help]") {
1040     CLI::App app;
1041 
1042     std::string filename;
1043     app.add_option("--f1", filename)->check(CLI::ExistingPath | CLI::NonexistentPath);
1044 
1045     // Combining validators with the same type string is OK
1046     std::string help = app.help();
1047     CHECK_THAT(help, Contains("TEXT:"));
1048     CHECK_THAT(help, Contains("PATH"));
1049 }
1050 
1051 // Don't do this in real life, please (and transform does nothing here)
1052 TEST_CASE("THelp: CombinedValidatorsPathyTextAsTransform", "[help]") {
1053     CLI::App app;
1054 
1055     std::string filename;
1056     app.add_option("--f1", filename)->transform(CLI::ExistingPath | CLI::NonexistentPath);
1057 
1058     // Combining validators with the same type string is OK
1059     std::string help = app.help();
1060     CHECK_THAT(help, Contains("TEXT:(PATH(existing)) OR (PATH"));
1061 }
1062 
1063 // #113 Part 2
1064 TEST_CASE("THelp: ChangingSet", "[help]") {
1065     CLI::App app;
1066 
1067     std::set<int> vals{1, 2, 3};
1068     int val{0};
1069     app.add_option("--val", val)->check(CLI::IsMember(&vals));
1070 
1071     std::string help = app.help();
1072 
1073     CHECK_THAT(help, Contains("1"));
1074     CHECK_THAT(help, !Contains("4"));
1075 
1076     vals.insert(4);
1077     vals.erase(1);
1078 
1079     help = app.help();
1080 
1081     CHECK_THAT(help, !Contains("1"));
1082     CHECK_THAT(help, Contains("4"));
1083 }
1084 
1085 TEST_CASE("THelp: ChangingSetDefaulted", "[help]") {
1086     CLI::App app;
1087 
1088     std::set<int> vals{1, 2, 3};
1089     int val{2};
1090     app.add_option("--val", val, "")->check(CLI::IsMember(&vals))->capture_default_str();
1091 
1092     std::string help = app.help();
1093 
1094     CHECK_THAT(help, Contains("1"));
1095     CHECK_THAT(help, !Contains("4"));
1096 
1097     vals.insert(4);
1098     vals.erase(1);
1099 
1100     help = app.help();
1101 
1102     CHECK_THAT(help, !Contains("1"));
1103     CHECK_THAT(help, Contains("4"));
1104 }
1105 
1106 TEST_CASE("THelp: ChangingCaselessSet", "[help]") {
1107     CLI::App app;
1108 
1109     std::set<std::string> vals{"1", "2", "3"};
1110     std::string val;
1111     app.add_option("--val", val)->check(CLI::IsMember(&vals, CLI::ignore_case));
1112 
1113     std::string help = app.help();
1114 
1115     CHECK_THAT(help, Contains("1"));
1116     CHECK_THAT(help, !Contains("4"));
1117 
1118     vals.insert("4");
1119     vals.erase("1");
1120 
1121     help = app.help();
1122 
1123     CHECK_THAT(help, !Contains("1"));
1124     CHECK_THAT(help, Contains("4"));
1125 }
1126 
1127 TEST_CASE("THelp: ChangingCaselessSetDefaulted", "[help]") {
1128     CLI::App app;
1129     app.option_defaults()->always_capture_default();
1130 
1131     std::set<std::string> vals{"1", "2", "3"};
1132     std::string val = "2";
1133     app.add_option("--val", val)->check(CLI::IsMember(&vals, CLI::ignore_case));
1134 
1135     std::string help = app.help();
1136 
1137     CHECK_THAT(help, Contains("1"));
1138     CHECK_THAT(help, !Contains("4"));
1139 
1140     vals.insert("4");
1141     vals.erase("1");
1142 
1143     help = app.help();
1144 
1145     CHECK_THAT(help, !Contains("1"));
1146     CHECK_THAT(help, Contains("4"));
1147 }
1148 
1149 // New defaults tests (1.8)
1150 
1151 TEST_CASE("THelp: ChangingDefaults", "[help]") {
1152 
1153     CLI::App app;
1154 
1155     std::vector<int> x = {1, 2};
1156     CLI::Option *opt = app.add_option("-q,--quick", x);
1157     x = {3, 4};
1158     CHECK(x[0] == 3);
1159 
1160     opt->capture_default_str();
1161 
1162     x = {5, 6};
1163     std::string help = app.help();
1164 
1165     CHECK_THAT(help, Contains("INT=[3,4] ..."));
1166     CHECK(x[0] == 5);
1167 }
1168 
1169 TEST_CASE("THelp: ChangingDefaultsWithAutoCapture", "[help]") {
1170 
1171     CLI::App app;
1172     app.option_defaults()->always_capture_default();
1173 
1174     std::vector<int> x = {1, 2};
1175     CHECK(x[0] == 1);
1176     app.add_option("-q,--quick", x);
1177     x = {3, 4};
1178     CHECK(x[0] == 3);
1179 
1180     std::string help = app.help();
1181 
1182     CHECK_THAT(help, Contains("INT=[1,2] ..."));
1183 }
1184 
1185 TEST_CASE("THelp: FunctionDefaultString", "[help]") {
1186 
1187     CLI::App app;
1188 
1189     std::vector<int> x = {1, 2};
1190     CLI::Option *opt = app.add_option("-q,--quick", x);
1191 
__anonf33f93150502() 1192     opt->default_function([]() { return std::string("Powerful"); });
1193     opt->capture_default_str();
1194 
1195     std::string help = app.help();
1196 
1197     CHECK_THAT(help, Contains("INT=Powerful"));
1198 }
1199 
1200 TEST_CASE("TVersion: simple_flag", "[help]") {
1201 
1202     CLI::App app;
1203 
1204     app.set_version_flag("-v,--version", "VERSION " CLI11_VERSION);
1205 
1206     auto vers = app.version();
1207     CHECK_THAT(vers, Contains("VERSION"));
1208 
1209     app.set_version_flag();
1210     CHECK(app.version().empty());
1211 }
1212 
1213 TEST_CASE("TVersion: callback_flag", "[help]") {
1214 
1215     CLI::App app;
1216 
__anonf33f93150602() 1217     app.set_version_flag("-v,--version", []() { return std::string("VERSION " CLI11_VERSION); });
1218 
1219     auto vers = app.version();
1220     CHECK_THAT(vers, Contains("VERSION"));
1221 
__anonf33f93150702() 1222     app.set_version_flag("-v", []() { return std::string("VERSION2 " CLI11_VERSION); });
1223     vers = app.version();
1224     CHECK_THAT(vers, Contains("VERSION"));
1225 }
1226 
1227 TEST_CASE("TVersion: help", "[help]") {
1228 
1229     CLI::App app;
1230 
1231     app.set_version_flag("-v,--version", "version_string", "help_for_version");
1232 
1233     auto hvers = app.help();
1234     CHECK_THAT(hvers, Contains("help_for_version"));
1235 
1236     app.set_version_flag(
__anonf33f93150802() 1237         "-v", []() { return std::string("VERSION2 " CLI11_VERSION); }, "help_for_version2");
1238     hvers = app.help();
1239     CHECK_THAT(hvers, Contains("help_for_version2"));
1240 }
1241 
1242 TEST_CASE("TVersion: parse_throw", "[help]") {
1243 
1244     CLI::App app;
1245 
1246     app.set_version_flag("--version", CLI11_VERSION);
1247 
1248     CHECK_THROWS_AS(app.parse("--version"), CLI::CallForVersion);
1249     CHECK_THROWS_AS(app.parse("--version --arg2 5"), CLI::CallForVersion);
1250 
1251     auto ptr = app.get_version_ptr();
1252 
1253     ptr->ignore_case();
1254     try {
1255         app.parse("--Version");
1256     } catch(const CLI::CallForVersion &v) {
1257         CHECK_THAT(CLI11_VERSION, Catch::Equals(v.what()));
1258         CHECK(0 == v.get_exit_code());
1259         const auto &appc = app;
1260         auto cptr = appc.get_version_ptr();
1261         CHECK(1U == cptr->count());
1262     }
1263 }
1264