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