1 // Copyright 2010 The Kyua Authors. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are 6 // met: 7 // 8 // * Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // * Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // * Neither the name of Google Inc. nor the names of its contributors 14 // may be used to endorse or promote products derived from this software 15 // without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 #include "utils/cmdline/parser.ipp" 30 31 #if defined(HAVE_CONFIG_H) 32 #include "config.h" 33 #endif 34 35 extern "C" { 36 #include <fcntl.h> 37 #include <getopt.h> 38 #include <unistd.h> 39 } 40 41 #include <cstdlib> 42 #include <cstring> 43 #include <fstream> 44 #include <iostream> 45 #include <string> 46 #include <utility> 47 48 #include <atf-c++.hpp> 49 50 #include "utils/cmdline/exceptions.hpp" 51 #include "utils/cmdline/options.hpp" 52 #include "utils/format/macros.hpp" 53 #include "utils/sanity.hpp" 54 55 namespace cmdline = utils::cmdline; 56 57 using cmdline::base_option; 58 using cmdline::bool_option; 59 using cmdline::int_option; 60 using cmdline::parse; 61 using cmdline::parsed_cmdline; 62 using cmdline::string_option; 63 64 65 namespace { 66 67 68 /// Mock option type to check the validate and convert methods sequence. 69 /// 70 /// Instances of this option accept a string argument that must be either "zero" 71 /// or "one". These are validated and converted to integers. 72 class mock_option : public base_option { 73 public: 74 /// Constructs the new option. 75 /// 76 /// \param long_name_ The long name for the option. All other option 77 /// properties are irrelevant for the tests using this, so they are set 78 /// to arbitrary values. 79 mock_option(const char* long_name_) : 80 base_option(long_name_, "Irrelevant description", "arg") 81 { 82 } 83 84 /// The type of the argument of this option. 85 typedef int option_type; 86 87 /// Checks that the user-provided option is valid. 88 /// 89 /// \param str The user argument; must be "zero" or "one". 90 /// 91 /// \throw cmdline::option_argument_value_error If str is not valid. 92 void 93 validate(const std::string& str) const 94 { 95 if (str != "zero" && str != "one") 96 throw cmdline::option_argument_value_error(F("--%s") % long_name(), 97 str, "Unknown value"); 98 } 99 100 /// Converts the user-provided argument to our native integer type. 101 /// 102 /// \param str The user argument; must be "zero" or "one". 103 /// 104 /// \return 0 if the input is "zero", or 1 if the input is "one". 105 /// 106 /// \throw std::runtime_error If str is not valid. In real life, this 107 /// should be a precondition because validate() has already ensured that 108 /// the values passed to convert() are correct. However, we raise an 109 /// exception here because we are actually validating that this code 110 /// sequence holds true. 111 static int 112 convert(const std::string& str) 113 { 114 if (str == "zero") 115 return 0; 116 else if (str == "one") 117 return 1; 118 else { 119 // This would generally be an assertion but, given that this is 120 // test code, we want to catch any errors regardless of how the 121 // binary is built. 122 throw std::runtime_error("Value not validated properly."); 123 } 124 } 125 }; 126 127 128 /// Redirects stdout and stderr to a file. 129 /// 130 /// This fails the test case in case of any error. 131 /// 132 /// \param file The name of the file to redirect stdout and stderr to. 133 /// 134 /// \return A copy of the old stdout and stderr file descriptors. 135 static std::pair< int, int > 136 mock_stdfds(const char* file) 137 { 138 std::cout.flush(); 139 std::cerr.flush(); 140 141 const int oldout = ::dup(STDOUT_FILENO); 142 ATF_REQUIRE(oldout != -1); 143 const int olderr = ::dup(STDERR_FILENO); 144 ATF_REQUIRE(olderr != -1); 145 146 const int fd = ::open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644); 147 ATF_REQUIRE(fd != -1); 148 ATF_REQUIRE(::dup2(fd, STDOUT_FILENO) != -1); 149 ATF_REQUIRE(::dup2(fd, STDERR_FILENO) != -1); 150 ::close(fd); 151 152 return std::make_pair(oldout, olderr); 153 } 154 155 156 /// Restores stdout and stderr after a call to mock_stdfds. 157 /// 158 /// \param oldfds The copy of the previous stdout and stderr as returned by the 159 /// call to mock_fds(). 160 static void 161 restore_stdfds(const std::pair< int, int >& oldfds) 162 { 163 ATF_REQUIRE(::dup2(oldfds.first, STDOUT_FILENO) != -1); 164 ::close(oldfds.first); 165 ATF_REQUIRE(::dup2(oldfds.second, STDERR_FILENO) != -1); 166 ::close(oldfds.second); 167 } 168 169 170 /// Checks whether a '+:' prefix to the short options of getopt_long works. 171 /// 172 /// It turns out that the getopt_long(3) implementation of Ubuntu 10.04.1 (and 173 /// very likely other distributions) does not properly report a missing argument 174 /// to a second long option as such. Instead of returning ':' when the second 175 /// long option provided on the command line does not carry a required argument, 176 /// it will mistakenly return '?' which translates to "unknown option". 177 /// 178 /// As a result of this bug, we cannot properly detect that 'flag2' requires an 179 /// argument in a command line like: 'progname --flag1=foo --flag2'. 180 /// 181 /// I am not sure if we could fully workaround the issue in the implementation 182 /// of our library. For the time being I am just using this bug detection in 183 /// the test cases to prevent failures that are not really our fault. 184 /// 185 /// \return bool True if getopt_long is broken and does not interpret '+:' 186 /// correctly; False otherwise. 187 static bool 188 is_getopt_long_pluscolon_broken(void) 189 { 190 struct ::option long_options[] = { 191 { "flag1", 1, NULL, '1' }, 192 { "flag2", 1, NULL, '2' }, 193 { NULL, 0, NULL, 0 } 194 }; 195 196 const int argc = 3; 197 char* argv[4]; 198 argv[0] = ::strdup("progname"); 199 argv[1] = ::strdup("--flag1=a"); 200 argv[2] = ::strdup("--flag2"); 201 argv[3] = NULL; 202 203 const int old_opterr = ::opterr; 204 ::opterr = 0; 205 206 bool got_colon = false; 207 208 int opt; 209 while ((opt = ::getopt_long(argc, argv, "+:", long_options, NULL)) != -1) { 210 switch (opt) { 211 case '1': break; 212 case '2': break; 213 case ':': got_colon = true; break; 214 case '?': break; 215 default: UNREACHABLE; break; 216 } 217 } 218 219 ::opterr = old_opterr; 220 ::optind = 1; 221 #if defined(HAVE_GETOPT_WITH_OPTRESET) 222 ::optreset = 1; 223 #endif 224 225 for (char** arg = &argv[0]; *arg != NULL; arg++) 226 std::free(*arg); 227 228 return !got_colon; 229 } 230 231 232 } // anonymous namespace 233 234 235 ATF_TEST_CASE_WITHOUT_HEAD(progname__no_options); 236 ATF_TEST_CASE_BODY(progname__no_options) 237 { 238 const int argc = 1; 239 const char* const argv[] = {"progname", NULL}; 240 std::vector< const base_option* > options; 241 const parsed_cmdline cmdline = parse(argc, argv, options); 242 243 ATF_REQUIRE(cmdline.arguments().empty()); 244 } 245 246 247 ATF_TEST_CASE_WITHOUT_HEAD(progname__some_options); 248 ATF_TEST_CASE_BODY(progname__some_options) 249 { 250 const int argc = 1; 251 const char* const argv[] = {"progname", NULL}; 252 const string_option a('a', "a_option", "Foo", NULL); 253 const string_option b('b', "b_option", "Bar", "arg", "foo"); 254 const string_option c("c_option", "Baz", NULL); 255 const string_option d("d_option", "Wohoo", "arg", "bar"); 256 std::vector< const base_option* > options; 257 options.push_back(&a); 258 options.push_back(&b); 259 options.push_back(&c); 260 options.push_back(&d); 261 const parsed_cmdline cmdline = parse(argc, argv, options); 262 263 ATF_REQUIRE_EQ("foo", cmdline.get_option< string_option >("b_option")); 264 ATF_REQUIRE_EQ("bar", cmdline.get_option< string_option >("d_option")); 265 ATF_REQUIRE(cmdline.arguments().empty()); 266 } 267 268 269 ATF_TEST_CASE_WITHOUT_HEAD(some_args__no_options); 270 ATF_TEST_CASE_BODY(some_args__no_options) 271 { 272 const int argc = 5; 273 const char* const argv[] = {"progname", "foo", "-c", "--opt", "bar", NULL}; 274 std::vector< const base_option* > options; 275 const parsed_cmdline cmdline = parse(argc, argv, options); 276 277 ATF_REQUIRE(!cmdline.has_option("c")); 278 ATF_REQUIRE(!cmdline.has_option("opt")); 279 ATF_REQUIRE_EQ(4, cmdline.arguments().size()); 280 ATF_REQUIRE_EQ("foo", cmdline.arguments()[0]); 281 ATF_REQUIRE_EQ("-c", cmdline.arguments()[1]); 282 ATF_REQUIRE_EQ("--opt", cmdline.arguments()[2]); 283 ATF_REQUIRE_EQ("bar", cmdline.arguments()[3]); 284 } 285 286 287 ATF_TEST_CASE_WITHOUT_HEAD(some_args__some_options); 288 ATF_TEST_CASE_BODY(some_args__some_options) 289 { 290 const int argc = 5; 291 const char* const argv[] = {"progname", "foo", "-c", "--opt", "bar", NULL}; 292 const string_option c('c', "opt", "Description", NULL); 293 std::vector< const base_option* > options; 294 options.push_back(&c); 295 const parsed_cmdline cmdline = parse(argc, argv, options); 296 297 ATF_REQUIRE(!cmdline.has_option("c")); 298 ATF_REQUIRE(!cmdline.has_option("opt")); 299 ATF_REQUIRE_EQ(4, cmdline.arguments().size()); 300 ATF_REQUIRE_EQ("foo", cmdline.arguments()[0]); 301 ATF_REQUIRE_EQ("-c", cmdline.arguments()[1]); 302 ATF_REQUIRE_EQ("--opt", cmdline.arguments()[2]); 303 ATF_REQUIRE_EQ("bar", cmdline.arguments()[3]); 304 } 305 306 307 ATF_TEST_CASE_WITHOUT_HEAD(some_options__all_known); 308 ATF_TEST_CASE_BODY(some_options__all_known) 309 { 310 const int argc = 14; 311 const char* const argv[] = { 312 "progname", 313 "-a", 314 "-bvalue_b", 315 "-c", "value_c", 316 //"-d", // Options with default optional values are unsupported. 317 "-evalue_e", // Has default; overriden. 318 "--f_long", 319 "--g_long=value_g", 320 "--h_long", "value_h", 321 //"--i_long", // Options with default optional values are unsupported. 322 "--j_long", "value_j", // Has default; overriden as separate argument. 323 "arg1", "arg2", NULL, 324 }; 325 const bool_option a('a', "a_long", ""); 326 const string_option b('b', "b_long", "Description", "arg"); 327 const string_option c('c', "c_long", "ABCD", "foo"); 328 const string_option d('d', "d_long", "Description", "bar", "default_d"); 329 const string_option e('e', "e_long", "Description", "baz", "default_e"); 330 const bool_option f("f_long", "Description"); 331 const string_option g("g_long", "Description", "arg"); 332 const string_option h("h_long", "Description", "foo"); 333 const string_option i("i_long", "EFGH", "bar", "default_i"); 334 const string_option j("j_long", "Description", "baz", "default_j"); 335 std::vector< const base_option* > options; 336 options.push_back(&a); 337 options.push_back(&b); 338 options.push_back(&c); 339 options.push_back(&d); 340 options.push_back(&e); 341 options.push_back(&f); 342 options.push_back(&g); 343 options.push_back(&h); 344 options.push_back(&i); 345 options.push_back(&j); 346 const parsed_cmdline cmdline = parse(argc, argv, options); 347 348 ATF_REQUIRE(cmdline.has_option("a_long")); 349 ATF_REQUIRE_EQ("value_b", cmdline.get_option< string_option >("b_long")); 350 ATF_REQUIRE_EQ("value_c", cmdline.get_option< string_option >("c_long")); 351 ATF_REQUIRE_EQ("default_d", cmdline.get_option< string_option >("d_long")); 352 ATF_REQUIRE_EQ("value_e", cmdline.get_option< string_option >("e_long")); 353 ATF_REQUIRE(cmdline.has_option("f_long")); 354 ATF_REQUIRE_EQ("value_g", cmdline.get_option< string_option >("g_long")); 355 ATF_REQUIRE_EQ("value_h", cmdline.get_option< string_option >("h_long")); 356 ATF_REQUIRE_EQ("default_i", cmdline.get_option< string_option >("i_long")); 357 ATF_REQUIRE_EQ("value_j", cmdline.get_option< string_option >("j_long")); 358 ATF_REQUIRE_EQ(2, cmdline.arguments().size()); 359 ATF_REQUIRE_EQ("arg1", cmdline.arguments()[0]); 360 ATF_REQUIRE_EQ("arg2", cmdline.arguments()[1]); 361 } 362 363 364 ATF_TEST_CASE_WITHOUT_HEAD(some_options__multi); 365 ATF_TEST_CASE_BODY(some_options__multi) 366 { 367 const int argc = 9; 368 const char* const argv[] = { 369 "progname", 370 "-a1", 371 "-bvalue1", 372 "-a2", 373 "--a_long=3", 374 "-bvalue2", 375 "--b_long=value3", 376 "arg1", "arg2", NULL, 377 }; 378 const int_option a('a', "a_long", "Description", "arg"); 379 const string_option b('b', "b_long", "Description", "arg"); 380 std::vector< const base_option* > options; 381 options.push_back(&a); 382 options.push_back(&b); 383 const parsed_cmdline cmdline = parse(argc, argv, options); 384 385 { 386 ATF_REQUIRE_EQ(3, cmdline.get_option< int_option >("a_long")); 387 const std::vector< int > multi = 388 cmdline.get_multi_option< int_option >("a_long"); 389 ATF_REQUIRE_EQ(3, multi.size()); 390 ATF_REQUIRE_EQ(1, multi[0]); 391 ATF_REQUIRE_EQ(2, multi[1]); 392 ATF_REQUIRE_EQ(3, multi[2]); 393 } 394 395 { 396 ATF_REQUIRE_EQ("value3", cmdline.get_option< string_option >("b_long")); 397 const std::vector< std::string > multi = 398 cmdline.get_multi_option< string_option >("b_long"); 399 ATF_REQUIRE_EQ(3, multi.size()); 400 ATF_REQUIRE_EQ("value1", multi[0]); 401 ATF_REQUIRE_EQ("value2", multi[1]); 402 ATF_REQUIRE_EQ("value3", multi[2]); 403 } 404 } 405 406 407 ATF_TEST_CASE_WITHOUT_HEAD(subcommands); 408 ATF_TEST_CASE_BODY(subcommands) 409 { 410 const int argc = 5; 411 const char* const argv[] = {"progname", "--flag1", "subcommand", 412 "--flag2", "arg", NULL}; 413 const bool_option flag1("flag1", ""); 414 std::vector< const base_option* > options; 415 options.push_back(&flag1); 416 const parsed_cmdline cmdline = parse(argc, argv, options); 417 418 ATF_REQUIRE( cmdline.has_option("flag1")); 419 ATF_REQUIRE(!cmdline.has_option("flag2")); 420 ATF_REQUIRE_EQ(3, cmdline.arguments().size()); 421 ATF_REQUIRE_EQ("subcommand", cmdline.arguments()[0]); 422 ATF_REQUIRE_EQ("--flag2", cmdline.arguments()[1]); 423 ATF_REQUIRE_EQ("arg", cmdline.arguments()[2]); 424 425 const bool_option flag2("flag2", ""); 426 std::vector< const base_option* > options2; 427 options2.push_back(&flag2); 428 const parsed_cmdline cmdline2 = parse(cmdline.arguments(), options2); 429 430 ATF_REQUIRE(!cmdline2.has_option("flag1")); 431 ATF_REQUIRE( cmdline2.has_option("flag2")); 432 ATF_REQUIRE_EQ(1, cmdline2.arguments().size()); 433 ATF_REQUIRE_EQ("arg", cmdline2.arguments()[0]); 434 } 435 436 437 ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__short); 438 ATF_TEST_CASE_BODY(missing_option_argument_error__short) 439 { 440 const int argc = 3; 441 const char* const argv[] = {"progname", "-a3", "-b", NULL}; 442 const string_option flag1('a', "flag1", "Description", "arg"); 443 const string_option flag2('b', "flag2", "Description", "arg"); 444 std::vector< const base_option* > options; 445 options.push_back(&flag1); 446 options.push_back(&flag2); 447 448 try { 449 parse(argc, argv, options); 450 fail("missing_option_argument_error not raised"); 451 } catch (const cmdline::missing_option_argument_error& e) { 452 ATF_REQUIRE_EQ("-b", e.option()); 453 } catch (const cmdline::unknown_option_error& e) { 454 if (is_getopt_long_pluscolon_broken()) 455 expect_fail("Your getopt_long is broken"); 456 fail("Got unknown_option_error instead of " 457 "missing_option_argument_error"); 458 } 459 } 460 461 462 ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__shortblock); 463 ATF_TEST_CASE_BODY(missing_option_argument_error__shortblock) 464 { 465 const int argc = 3; 466 const char* const argv[] = {"progname", "-ab3", "-ac", NULL}; 467 const bool_option flag1('a', "flag1", "Description"); 468 const string_option flag2('b', "flag2", "Description", "arg"); 469 const string_option flag3('c', "flag2", "Description", "arg"); 470 std::vector< const base_option* > options; 471 options.push_back(&flag1); 472 options.push_back(&flag2); 473 options.push_back(&flag3); 474 475 try { 476 parse(argc, argv, options); 477 fail("missing_option_argument_error not raised"); 478 } catch (const cmdline::missing_option_argument_error& e) { 479 ATF_REQUIRE_EQ("-c", e.option()); 480 } catch (const cmdline::unknown_option_error& e) { 481 if (is_getopt_long_pluscolon_broken()) 482 expect_fail("Your getopt_long is broken"); 483 fail("Got unknown_option_error instead of " 484 "missing_option_argument_error"); 485 } 486 } 487 488 489 ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__long); 490 ATF_TEST_CASE_BODY(missing_option_argument_error__long) 491 { 492 const int argc = 3; 493 const char* const argv[] = {"progname", "--flag1=a", "--flag2", NULL}; 494 const string_option flag1("flag1", "Description", "arg"); 495 const string_option flag2("flag2", "Description", "arg"); 496 std::vector< const base_option* > options; 497 options.push_back(&flag1); 498 options.push_back(&flag2); 499 500 try { 501 parse(argc, argv, options); 502 fail("missing_option_argument_error not raised"); 503 } catch (const cmdline::missing_option_argument_error& e) { 504 ATF_REQUIRE_EQ("--flag2", e.option()); 505 } catch (const cmdline::unknown_option_error& e) { 506 if (is_getopt_long_pluscolon_broken()) 507 expect_fail("Your getopt_long is broken"); 508 fail("Got unknown_option_error instead of " 509 "missing_option_argument_error"); 510 } 511 } 512 513 514 ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__short); 515 ATF_TEST_CASE_BODY(unknown_option_error__short) 516 { 517 const int argc = 3; 518 const char* const argv[] = {"progname", "-a", "-b", NULL}; 519 const bool_option flag1('a', "flag1", "Description"); 520 std::vector< const base_option* > options; 521 options.push_back(&flag1); 522 523 try { 524 parse(argc, argv, options); 525 fail("unknown_option_error not raised"); 526 } catch (const cmdline::unknown_option_error& e) { 527 ATF_REQUIRE_EQ("-b", e.option()); 528 } 529 } 530 531 532 ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__shortblock); 533 ATF_TEST_CASE_BODY(unknown_option_error__shortblock) 534 { 535 const int argc = 3; 536 const char* const argv[] = {"progname", "-a", "-bdc", NULL}; 537 const bool_option flag1('a', "flag1", "Description"); 538 const bool_option flag2('b', "flag2", "Description"); 539 const bool_option flag3('c', "flag3", "Description"); 540 std::vector< const base_option* > options; 541 options.push_back(&flag1); 542 options.push_back(&flag2); 543 options.push_back(&flag3); 544 545 try { 546 parse(argc, argv, options); 547 fail("unknown_option_error not raised"); 548 } catch (const cmdline::unknown_option_error& e) { 549 ATF_REQUIRE_EQ("-d", e.option()); 550 } 551 } 552 553 554 ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__long); 555 ATF_TEST_CASE_BODY(unknown_option_error__long) 556 { 557 const int argc = 3; 558 const char* const argv[] = {"progname", "--flag1=a", "--flag2", NULL}; 559 const string_option flag1("flag1", "Description", "arg"); 560 std::vector< const base_option* > options; 561 options.push_back(&flag1); 562 563 try { 564 parse(argc, argv, options); 565 fail("unknown_option_error not raised"); 566 } catch (const cmdline::unknown_option_error& e) { 567 ATF_REQUIRE_EQ("--flag2", e.option()); 568 } 569 } 570 571 572 ATF_TEST_CASE_WITHOUT_HEAD(unknown_plus_option_error); 573 ATF_TEST_CASE_BODY(unknown_plus_option_error) 574 { 575 const int argc = 2; 576 const char* const argv[] = {"progname", "-+", NULL}; 577 const cmdline::options_vector options; 578 579 try { 580 parse(argc, argv, options); 581 fail("unknown_option_error not raised"); 582 } catch (const cmdline::unknown_option_error& e) { 583 ATF_REQUIRE_EQ("-+", e.option()); 584 } catch (const cmdline::missing_option_argument_error& e) { 585 fail("Looks like getopt_long thinks a + option is defined and it " 586 "even requires an argument"); 587 } 588 } 589 590 591 ATF_TEST_CASE_WITHOUT_HEAD(option_types); 592 ATF_TEST_CASE_BODY(option_types) 593 { 594 const int argc = 3; 595 const char* const argv[] = {"progname", "--flag1=a", "--flag2=one", NULL}; 596 const string_option flag1("flag1", "The flag1", "arg"); 597 const mock_option flag2("flag2"); 598 std::vector< const base_option* > options; 599 options.push_back(&flag1); 600 options.push_back(&flag2); 601 602 const parsed_cmdline cmdline = parse(argc, argv, options); 603 604 ATF_REQUIRE(cmdline.has_option("flag1")); 605 ATF_REQUIRE(cmdline.has_option("flag2")); 606 ATF_REQUIRE_EQ("a", cmdline.get_option< string_option >("flag1")); 607 ATF_REQUIRE_EQ(1, cmdline.get_option< mock_option >("flag2")); 608 } 609 610 611 ATF_TEST_CASE_WITHOUT_HEAD(option_validation_error); 612 ATF_TEST_CASE_BODY(option_validation_error) 613 { 614 const int argc = 3; 615 const char* const argv[] = {"progname", "--flag1=zero", "--flag2=foo", 616 NULL}; 617 const mock_option flag1("flag1"); 618 const mock_option flag2("flag2"); 619 std::vector< const base_option* > options; 620 options.push_back(&flag1); 621 options.push_back(&flag2); 622 623 try { 624 parse(argc, argv, options); 625 fail("option_argument_value_error not raised"); 626 } catch (const cmdline::option_argument_value_error& e) { 627 ATF_REQUIRE_EQ("--flag2", e.option()); 628 ATF_REQUIRE_EQ("foo", e.argument()); 629 } 630 } 631 632 633 ATF_TEST_CASE_WITHOUT_HEAD(silent_errors); 634 ATF_TEST_CASE_BODY(silent_errors) 635 { 636 const int argc = 2; 637 const char* const argv[] = {"progname", "-h", NULL}; 638 cmdline::options_vector options; 639 640 try { 641 std::pair< int, int > oldfds = mock_stdfds("output.txt"); 642 try { 643 parse(argc, argv, options); 644 } catch (...) { 645 restore_stdfds(oldfds); 646 throw; 647 } 648 restore_stdfds(oldfds); 649 fail("unknown_option_error not raised"); 650 } catch (const cmdline::unknown_option_error& e) { 651 ATF_REQUIRE_EQ("-h", e.option()); 652 } 653 654 std::ifstream input("output.txt"); 655 ATF_REQUIRE(input); 656 657 bool has_output = false; 658 std::string line; 659 while (std::getline(input, line).good()) { 660 std::cout << line << '\n'; 661 has_output = true; 662 } 663 664 if (has_output) 665 fail("getopt_long printed messages on stdout/stderr by itself"); 666 } 667 668 669 ATF_INIT_TEST_CASES(tcs) 670 { 671 ATF_ADD_TEST_CASE(tcs, progname__no_options); 672 ATF_ADD_TEST_CASE(tcs, progname__some_options); 673 ATF_ADD_TEST_CASE(tcs, some_args__no_options); 674 ATF_ADD_TEST_CASE(tcs, some_args__some_options); 675 ATF_ADD_TEST_CASE(tcs, some_options__all_known); 676 ATF_ADD_TEST_CASE(tcs, some_options__multi); 677 ATF_ADD_TEST_CASE(tcs, subcommands); 678 ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__short); 679 ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__shortblock); 680 ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__long); 681 ATF_ADD_TEST_CASE(tcs, unknown_option_error__short); 682 ATF_ADD_TEST_CASE(tcs, unknown_option_error__shortblock); 683 ATF_ADD_TEST_CASE(tcs, unknown_option_error__long); 684 ATF_ADD_TEST_CASE(tcs, unknown_plus_option_error); 685 ATF_ADD_TEST_CASE(tcs, option_types); 686 ATF_ADD_TEST_CASE(tcs, option_validation_error); 687 ATF_ADD_TEST_CASE(tcs, silent_errors); 688 } 689