parse_test.cc (31527B)
1 // 2 // Copyright 2019 The Abseil Authors. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // https://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 #include "absl/flags/parse.h" 17 18 #include <stdlib.h> 19 20 #include <fstream> 21 #include <iostream> 22 #include <string> 23 #include <vector> 24 25 #include "gmock/gmock.h" 26 #include "gtest/gtest.h" 27 #include "absl/base/internal/scoped_set_env.h" 28 #include "absl/flags/config.h" 29 #include "absl/flags/flag.h" 30 #include "absl/flags/internal/parse.h" 31 #include "absl/flags/internal/usage.h" 32 #include "absl/flags/reflection.h" 33 #include "absl/log/log.h" 34 #include "absl/strings/str_cat.h" 35 #include "absl/strings/string_view.h" 36 #include "absl/strings/substitute.h" 37 #include "absl/types/span.h" 38 39 #ifdef _WIN32 40 #include <windows.h> 41 #endif 42 43 // Define 125 similar flags to test kMaxHints for flag suggestions. 44 #define FLAG_MULT(x) F3(x) 45 #define TEST_FLAG_HEADER FLAG_HEADER_ 46 47 #define F(name) ABSL_FLAG(int, name, 0, "") 48 49 #define F1(name) \ 50 F(name##1); \ 51 F(name##2); \ 52 F(name##3); \ 53 F(name##4); \ 54 F(name##5) 55 /**/ 56 #define F2(name) \ 57 F1(name##1); \ 58 F1(name##2); \ 59 F1(name##3); \ 60 F1(name##4); \ 61 F1(name##5) 62 /**/ 63 #define F3(name) \ 64 F2(name##1); \ 65 F2(name##2); \ 66 F2(name##3); \ 67 F2(name##4); \ 68 F2(name##5) 69 /**/ 70 71 FLAG_MULT(TEST_FLAG_HEADER); 72 73 namespace { 74 75 using absl::base_internal::ScopedSetEnv; 76 77 struct UDT { 78 UDT() = default; 79 UDT(const UDT&) = default; 80 UDT& operator=(const UDT&) = default; 81 UDT(int v) : value(v) {} // NOLINT 82 83 int value; 84 }; 85 86 bool AbslParseFlag(absl::string_view in, UDT* udt, std::string* err) { 87 if (in == "A") { 88 udt->value = 1; 89 return true; 90 } 91 if (in == "AAA") { 92 udt->value = 10; 93 return true; 94 } 95 96 *err = "Use values A, AAA instead"; 97 return false; 98 } 99 std::string AbslUnparseFlag(const UDT& udt) { 100 return udt.value == 1 ? "A" : "AAA"; 101 } 102 103 std::string GetTestTmpDirEnvVar(const char* const env_var_name) { 104 #ifdef _WIN32 105 char buf[MAX_PATH]; 106 auto get_res = GetEnvironmentVariableA(env_var_name, buf, sizeof(buf)); 107 if (get_res >= sizeof(buf) || get_res == 0) { 108 return ""; 109 } 110 111 return std::string(buf, get_res); 112 #else 113 const char* val = ::getenv(env_var_name); 114 if (val == nullptr) { 115 return ""; 116 } 117 118 return val; 119 #endif 120 } 121 122 const std::string& GetTestTempDir() { 123 static std::string* temp_dir_name = []() -> std::string* { 124 std::string* res = new std::string(GetTestTmpDirEnvVar("TEST_TMPDIR")); 125 126 if (res->empty()) { 127 *res = GetTestTmpDirEnvVar("TMPDIR"); 128 } 129 130 if (res->empty()) { 131 #ifdef _WIN32 132 char temp_path_buffer[MAX_PATH]; 133 134 auto len = GetTempPathA(MAX_PATH, temp_path_buffer); 135 if (len < MAX_PATH && len != 0) { 136 std::string temp_dir_name = temp_path_buffer; 137 if (!absl::EndsWith(temp_dir_name, "\\")) { 138 temp_dir_name.push_back('\\'); 139 } 140 absl::StrAppend(&temp_dir_name, "parse_test.", GetCurrentProcessId()); 141 if (CreateDirectoryA(temp_dir_name.c_str(), nullptr)) { 142 *res = temp_dir_name; 143 } 144 } 145 #else 146 char temp_dir_template[] = "/tmp/parse_test.XXXXXX"; 147 if (auto* unique_name = ::mkdtemp(temp_dir_template)) { 148 *res = unique_name; 149 } 150 #endif 151 } 152 153 if (res->empty()) { 154 LOG(FATAL) << "Failed to make temporary directory for data files"; 155 } 156 157 #ifdef _WIN32 158 *res += "\\"; 159 #else 160 *res += "/"; 161 #endif 162 163 return res; 164 }(); 165 166 return *temp_dir_name; 167 } 168 169 struct FlagfileData { 170 const absl::string_view file_name; 171 const absl::Span<const char* const> file_lines; 172 }; 173 174 // clang-format off 175 constexpr const char* const ff1_data[] = { 176 "# comment ", 177 " # comment ", 178 "", 179 " ", 180 "--int_flag=-1", 181 " --string_flag=q2w2 ", 182 " ## ", 183 " --double_flag=0.1", 184 "--bool_flag=Y " 185 }; 186 187 constexpr const char* const ff2_data[] = { 188 "# Setting legacy flag", 189 "--legacy_int=1111", 190 "--legacy_bool", 191 "--nobool_flag", 192 "--legacy_str=aqsw", 193 "--int_flag=100", 194 " ## =============" 195 }; 196 // clang-format on 197 198 // Builds flagfile flag in the flagfile_flag buffer and returns it. This 199 // function also creates a temporary flagfile based on FlagfileData input. 200 // We create a flagfile in a temporary directory with the name specified in 201 // FlagfileData and populate it with lines specified in FlagfileData. If $0 is 202 // referenced in any of the lines in FlagfileData they are replaced with 203 // temporary directory location. This way we can test inclusion of one flagfile 204 // from another flagfile. 205 const char* GetFlagfileFlag(const std::vector<FlagfileData>& ffd, 206 std::string& flagfile_flag) { 207 flagfile_flag = "--flagfile="; 208 absl::string_view separator; 209 for (const auto& flagfile_data : ffd) { 210 std::string flagfile_name = 211 absl::StrCat(GetTestTempDir(), flagfile_data.file_name); 212 213 std::ofstream flagfile_out(flagfile_name); 214 for (auto line : flagfile_data.file_lines) { 215 flagfile_out << absl::Substitute(line, GetTestTempDir()) << "\n"; 216 } 217 218 absl::StrAppend(&flagfile_flag, separator, flagfile_name); 219 separator = ","; 220 } 221 222 return flagfile_flag.c_str(); 223 } 224 225 } // namespace 226 227 ABSL_FLAG(int, int_flag, 1, ""); 228 ABSL_FLAG(double, double_flag, 1.1, ""); 229 ABSL_FLAG(std::string, string_flag, "a", ""); 230 ABSL_FLAG(bool, bool_flag, false, ""); 231 ABSL_FLAG(UDT, udt_flag, -1, ""); 232 ABSL_RETIRED_FLAG(int, legacy_int, 1, ""); 233 ABSL_RETIRED_FLAG(bool, legacy_bool, false, ""); 234 ABSL_RETIRED_FLAG(std::string, legacy_str, "l", ""); 235 236 namespace { 237 238 namespace flags = absl::flags_internal; 239 using testing::AllOf; 240 using testing::ElementsAreArray; 241 using testing::HasSubstr; 242 243 class ParseTest : public testing::Test { 244 public: 245 ~ParseTest() override { flags::SetFlagsHelpMode(flags::HelpMode::kNone); } 246 247 void SetUp() override { 248 #if ABSL_FLAGS_STRIP_NAMES 249 GTEST_SKIP() << "This test requires flag names to be present"; 250 #endif 251 } 252 253 private: 254 absl::FlagSaver flag_saver_; 255 }; 256 257 // -------------------------------------------------------------------- 258 259 template <int N> 260 flags::HelpMode InvokeParseAbslOnlyImpl(const char* (&in_argv)[N]) { 261 std::vector<char*> positional_args; 262 std::vector<absl::UnrecognizedFlag> unrecognized_flags; 263 264 return flags::ParseAbseilFlagsOnlyImpl(N, const_cast<char**>(in_argv), 265 positional_args, unrecognized_flags, 266 flags::UsageFlagsAction::kHandleUsage); 267 } 268 269 // -------------------------------------------------------------------- 270 271 template <int N> 272 void InvokeParseAbslOnly(const char* (&in_argv)[N]) { 273 std::vector<char*> positional_args; 274 std::vector<absl::UnrecognizedFlag> unrecognized_flags; 275 276 absl::ParseAbseilFlagsOnly(2, const_cast<char**>(in_argv), positional_args, 277 unrecognized_flags); 278 } 279 280 // -------------------------------------------------------------------- 281 282 template <int N> 283 std::vector<char*> InvokeParseCommandLineImpl(const char* (&in_argv)[N]) { 284 return flags::ParseCommandLineImpl( 285 N, const_cast<char**>(in_argv), flags::UsageFlagsAction::kHandleUsage, 286 flags::OnUndefinedFlag::kAbortIfUndefined, std::cerr); 287 } 288 289 // -------------------------------------------------------------------- 290 291 template <int N> 292 std::vector<char*> InvokeParse(const char* (&in_argv)[N]) { 293 return absl::ParseCommandLine(N, const_cast<char**>(in_argv)); 294 } 295 296 // -------------------------------------------------------------------- 297 298 template <int N> 299 void TestParse(const char* (&in_argv)[N], int int_flag_value, 300 double double_flag_val, absl::string_view string_flag_val, 301 bool bool_flag_val, int exp_position_args = 0) { 302 auto out_args = InvokeParse(in_argv); 303 304 EXPECT_EQ(out_args.size(), 1 + exp_position_args); 305 EXPECT_STREQ(out_args[0], "testbin"); 306 307 EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), int_flag_value); 308 EXPECT_NEAR(absl::GetFlag(FLAGS_double_flag), double_flag_val, 0.0001); 309 EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), string_flag_val); 310 EXPECT_EQ(absl::GetFlag(FLAGS_bool_flag), bool_flag_val); 311 } 312 313 // -------------------------------------------------------------------- 314 315 TEST_F(ParseTest, TestEmptyArgv) { 316 const char* in_argv[] = {"testbin"}; 317 318 auto out_args = InvokeParse(in_argv); 319 320 EXPECT_EQ(out_args.size(), 1); 321 EXPECT_STREQ(out_args[0], "testbin"); 322 } 323 324 // -------------------------------------------------------------------- 325 326 TEST_F(ParseTest, TestValidIntArg) { 327 const char* in_args1[] = { 328 "testbin", 329 "--int_flag=10", 330 }; 331 TestParse(in_args1, 10, 1.1, "a", false); 332 333 const char* in_args2[] = { 334 "testbin", 335 "-int_flag=020", 336 }; 337 TestParse(in_args2, 20, 1.1, "a", false); 338 339 const char* in_args3[] = { 340 "testbin", 341 "--int_flag", 342 "-30", 343 }; 344 TestParse(in_args3, -30, 1.1, "a", false); 345 346 const char* in_args4[] = { 347 "testbin", 348 "-int_flag", 349 "0x21", 350 }; 351 TestParse(in_args4, 33, 1.1, "a", false); 352 } 353 354 // -------------------------------------------------------------------- 355 356 TEST_F(ParseTest, TestValidDoubleArg) { 357 const char* in_args1[] = { 358 "testbin", 359 "--double_flag=2.3", 360 }; 361 TestParse(in_args1, 1, 2.3, "a", false); 362 363 const char* in_args2[] = { 364 "testbin", 365 "--double_flag=0x1.2", 366 }; 367 TestParse(in_args2, 1, 1.125, "a", false); 368 369 const char* in_args3[] = { 370 "testbin", 371 "--double_flag", 372 "99.7", 373 }; 374 TestParse(in_args3, 1, 99.7, "a", false); 375 376 const char* in_args4[] = { 377 "testbin", 378 "--double_flag", 379 "0x20.1", 380 }; 381 TestParse(in_args4, 1, 32.0625, "a", false); 382 } 383 384 // -------------------------------------------------------------------- 385 386 TEST_F(ParseTest, TestValidStringArg) { 387 const char* in_args1[] = { 388 "testbin", 389 "--string_flag=aqswde", 390 }; 391 TestParse(in_args1, 1, 1.1, "aqswde", false); 392 393 const char* in_args2[] = { 394 "testbin", 395 "-string_flag=a=b=c", 396 }; 397 TestParse(in_args2, 1, 1.1, "a=b=c", false); 398 399 const char* in_args3[] = { 400 "testbin", 401 "--string_flag", 402 "zaxscd", 403 }; 404 TestParse(in_args3, 1, 1.1, "zaxscd", false); 405 406 const char* in_args4[] = { 407 "testbin", 408 "-string_flag", 409 "--int_flag", 410 }; 411 TestParse(in_args4, 1, 1.1, "--int_flag", false); 412 413 const char* in_args5[] = { 414 "testbin", 415 "--string_flag", 416 "--no_a_flag=11", 417 }; 418 TestParse(in_args5, 1, 1.1, "--no_a_flag=11", false); 419 } 420 421 // -------------------------------------------------------------------- 422 423 TEST_F(ParseTest, TestValidBoolArg) { 424 const char* in_args1[] = { 425 "testbin", 426 "--bool_flag", 427 }; 428 TestParse(in_args1, 1, 1.1, "a", true); 429 430 const char* in_args2[] = { 431 "testbin", 432 "--nobool_flag", 433 }; 434 TestParse(in_args2, 1, 1.1, "a", false); 435 436 const char* in_args3[] = { 437 "testbin", 438 "--bool_flag=true", 439 }; 440 TestParse(in_args3, 1, 1.1, "a", true); 441 442 const char* in_args4[] = { 443 "testbin", 444 "-bool_flag=false", 445 }; 446 TestParse(in_args4, 1, 1.1, "a", false); 447 } 448 449 // -------------------------------------------------------------------- 450 451 TEST_F(ParseTest, TestValidUDTArg) { 452 const char* in_args1[] = { 453 "testbin", 454 "--udt_flag=A", 455 }; 456 InvokeParse(in_args1); 457 458 EXPECT_EQ(absl::GetFlag(FLAGS_udt_flag).value, 1); 459 460 const char* in_args2[] = {"testbin", "--udt_flag", "AAA"}; 461 InvokeParse(in_args2); 462 463 EXPECT_EQ(absl::GetFlag(FLAGS_udt_flag).value, 10); 464 } 465 466 // -------------------------------------------------------------------- 467 468 TEST_F(ParseTest, TestValidMultipleArg) { 469 const char* in_args1[] = { 470 "testbin", "--bool_flag", "--int_flag=2", 471 "--double_flag=0.1", "--string_flag=asd", 472 }; 473 TestParse(in_args1, 2, 0.1, "asd", true); 474 475 const char* in_args2[] = { 476 "testbin", "--string_flag=", "--nobool_flag", "--int_flag", 477 "-011", "--double_flag", "-1e-2", 478 }; 479 TestParse(in_args2, -11, -0.01, "", false); 480 481 const char* in_args3[] = { 482 "testbin", "--int_flag", "-0", "--string_flag", "\"\"", 483 "--bool_flag=true", "--double_flag=1e18", 484 }; 485 TestParse(in_args3, 0, 1e18, "\"\"", true); 486 } 487 488 // -------------------------------------------------------------------- 489 490 TEST_F(ParseTest, TestPositionalArgs) { 491 const char* in_args1[] = { 492 "testbin", 493 "p1", 494 "p2", 495 }; 496 TestParse(in_args1, 1, 1.1, "a", false, 2); 497 498 auto out_args1 = InvokeParse(in_args1); 499 500 EXPECT_STREQ(out_args1[1], "p1"); 501 EXPECT_STREQ(out_args1[2], "p2"); 502 503 const char* in_args2[] = { 504 "testbin", 505 "--int_flag=2", 506 "p1", 507 }; 508 TestParse(in_args2, 2, 1.1, "a", false, 1); 509 510 auto out_args2 = InvokeParse(in_args2); 511 512 EXPECT_STREQ(out_args2[1], "p1"); 513 514 const char* in_args3[] = {"testbin", "p1", "--int_flag=3", 515 "p2", "--bool_flag", "true"}; 516 TestParse(in_args3, 3, 1.1, "a", true, 3); 517 518 auto out_args3 = InvokeParse(in_args3); 519 520 EXPECT_STREQ(out_args3[1], "p1"); 521 EXPECT_STREQ(out_args3[2], "p2"); 522 EXPECT_STREQ(out_args3[3], "true"); 523 524 const char* in_args4[] = { 525 "testbin", 526 "--", 527 "p1", 528 "p2", 529 }; 530 TestParse(in_args4, 3, 1.1, "a", true, 2); 531 532 auto out_args4 = InvokeParse(in_args4); 533 534 EXPECT_STREQ(out_args4[1], "p1"); 535 EXPECT_STREQ(out_args4[2], "p2"); 536 537 const char* in_args5[] = { 538 "testbin", "p1", "--int_flag=4", "--", "--bool_flag", "false", "p2", 539 }; 540 TestParse(in_args5, 4, 1.1, "a", true, 4); 541 542 auto out_args5 = InvokeParse(in_args5); 543 544 EXPECT_STREQ(out_args5[1], "p1"); 545 EXPECT_STREQ(out_args5[2], "--bool_flag"); 546 EXPECT_STREQ(out_args5[3], "false"); 547 EXPECT_STREQ(out_args5[4], "p2"); 548 } 549 550 // -------------------------------------------------------------------- 551 552 using ParseDeathTest = ParseTest; 553 554 TEST_F(ParseDeathTest, TestUndefinedArg) { 555 const char* in_args1[] = { 556 "testbin", 557 "--undefined_flag", 558 }; 559 EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1), 560 "Unknown command line flag 'undefined_flag'"); 561 562 const char* in_args2[] = { 563 "testbin", 564 "--noprefixed_flag", 565 }; 566 EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2), 567 "Unknown command line flag 'noprefixed_flag'"); 568 569 const char* in_args3[] = { 570 "testbin", 571 "--Int_flag=1", 572 }; 573 EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args3), 574 "Unknown command line flag 'Int_flag'"); 575 } 576 577 // -------------------------------------------------------------------- 578 579 TEST_F(ParseDeathTest, TestInvalidBoolFlagFormat) { 580 const char* in_args1[] = { 581 "testbin", 582 "--bool_flag=", 583 }; 584 EXPECT_DEATH_IF_SUPPORTED( 585 InvokeParse(in_args1), 586 "Missing the value after assignment for the boolean flag 'bool_flag'"); 587 588 const char* in_args2[] = { 589 "testbin", 590 "--nobool_flag=true", 591 }; 592 EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2), 593 "Negative form with assignment is not valid for the boolean " 594 "flag 'bool_flag'"); 595 } 596 597 // -------------------------------------------------------------------- 598 599 TEST_F(ParseDeathTest, TestInvalidNonBoolFlagFormat) { 600 const char* in_args1[] = { 601 "testbin", 602 "--nostring_flag", 603 }; 604 EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1), 605 "Negative form is not valid for the flag 'string_flag'"); 606 607 const char* in_args2[] = { 608 "testbin", 609 "--int_flag", 610 }; 611 EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2), 612 "Missing the value for the flag 'int_flag'"); 613 } 614 615 // -------------------------------------------------------------------- 616 617 TEST_F(ParseDeathTest, TestInvalidUDTFlagFormat) { 618 const char* in_args1[] = { 619 "testbin", 620 "--udt_flag=1", 621 }; 622 EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1), 623 "Illegal value '1' specified for flag 'udt_flag'; Use values A, " 624 "AAA instead"); 625 626 const char* in_args2[] = { 627 "testbin", 628 "--udt_flag", 629 "AA", 630 }; 631 EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2), 632 "Illegal value 'AA' specified for flag 'udt_flag'; Use values " 633 "A, AAA instead"); 634 } 635 636 // -------------------------------------------------------------------- 637 638 TEST_F(ParseDeathTest, TestFlagSuggestions) { 639 const char* in_args1[] = { 640 "testbin", 641 "--legacy_boo", 642 }; 643 EXPECT_DEATH_IF_SUPPORTED( 644 InvokeParse(in_args1), 645 "Unknown command line flag 'legacy_boo'. Did you mean: legacy_bool ?"); 646 647 const char* in_args2[] = {"testbin", "--foo", "--undefok=foo1"}; 648 EXPECT_DEATH_IF_SUPPORTED( 649 InvokeParse(in_args2), 650 "Unknown command line flag 'foo'. Did you mean: foo1 \\(undefok\\)?"); 651 652 const char* in_args3[] = { 653 "testbin", 654 "--nolegacy_ino", 655 }; 656 EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args3), 657 "Unknown command line flag 'nolegacy_ino'. Did " 658 "you mean: nolegacy_bool, legacy_int ?"); 659 } 660 661 // -------------------------------------------------------------------- 662 663 TEST_F(ParseTest, GetHints) { 664 EXPECT_THAT(absl::flags_internal::GetMisspellingHints("legacy_boo"), 665 testing::ContainerEq(std::vector<std::string>{"legacy_bool"})); 666 EXPECT_THAT(absl::flags_internal::GetMisspellingHints("nolegacy_itn"), 667 testing::ContainerEq(std::vector<std::string>{"legacy_int"})); 668 EXPECT_THAT(absl::flags_internal::GetMisspellingHints("nolegacy_int1"), 669 testing::ContainerEq(std::vector<std::string>{"legacy_int"})); 670 EXPECT_THAT(absl::flags_internal::GetMisspellingHints("nolegacy_int"), 671 testing::ContainerEq(std::vector<std::string>{"legacy_int"})); 672 EXPECT_THAT(absl::flags_internal::GetMisspellingHints("nolegacy_ino"), 673 testing::ContainerEq( 674 std::vector<std::string>{"nolegacy_bool", "legacy_int"})); 675 EXPECT_THAT( 676 absl::flags_internal::GetMisspellingHints("FLAG_HEADER_000").size(), 100); 677 } 678 679 // -------------------------------------------------------------------- 680 681 TEST_F(ParseTest, TestLegacyFlags) { 682 const char* in_args1[] = { 683 "testbin", 684 "--legacy_int=11", 685 }; 686 TestParse(in_args1, 1, 1.1, "a", false); 687 688 const char* in_args2[] = { 689 "testbin", 690 "--legacy_bool", 691 }; 692 TestParse(in_args2, 1, 1.1, "a", false); 693 694 const char* in_args3[] = { 695 "testbin", "--legacy_int", "22", "--int_flag=2", 696 "--legacy_bool", "true", "--legacy_str", "--string_flag=qwe", 697 }; 698 TestParse(in_args3, 2, 1.1, "a", false, 1); 699 } 700 701 // -------------------------------------------------------------------- 702 703 TEST_F(ParseTest, TestSimpleValidFlagfile) { 704 std::string flagfile_flag; 705 706 const char* in_args1[] = { 707 "testbin", 708 GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, 709 flagfile_flag), 710 }; 711 TestParse(in_args1, -1, 0.1, "q2w2 ", true); 712 713 const char* in_args2[] = { 714 "testbin", 715 GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}}, 716 flagfile_flag), 717 }; 718 TestParse(in_args2, 100, 0.1, "q2w2 ", false); 719 } 720 721 // -------------------------------------------------------------------- 722 723 TEST_F(ParseTest, TestValidMultiFlagfile) { 724 std::string flagfile_flag; 725 726 const char* in_args1[] = { 727 "testbin", 728 GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}, 729 {"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, 730 flagfile_flag), 731 }; 732 TestParse(in_args1, -1, 0.1, "q2w2 ", true); 733 } 734 735 // -------------------------------------------------------------------- 736 737 TEST_F(ParseTest, TestFlagfileMixedWithRegularFlags) { 738 std::string flagfile_flag; 739 740 const char* in_args1[] = { 741 "testbin", "--int_flag=3", 742 GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, 743 flagfile_flag), 744 "-double_flag=0.2"}; 745 TestParse(in_args1, -1, 0.2, "q2w2 ", true); 746 } 747 748 // -------------------------------------------------------------------- 749 750 TEST_F(ParseTest, TestFlagfileInFlagfile) { 751 std::string flagfile_flag; 752 753 constexpr const char* const ff3_data[] = { 754 "--flagfile=$0/parse_test.ff1", 755 "--flagfile=$0/parse_test.ff2", 756 }; 757 758 GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}, 759 {"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, 760 flagfile_flag); 761 762 const char* in_args1[] = { 763 "testbin", 764 GetFlagfileFlag({{"parse_test.ff3", absl::MakeConstSpan(ff3_data)}}, 765 flagfile_flag), 766 }; 767 TestParse(in_args1, 100, 0.1, "q2w2 ", false); 768 } 769 770 // -------------------------------------------------------------------- 771 772 TEST_F(ParseDeathTest, TestInvalidFlagfiles) { 773 std::string flagfile_flag; 774 775 constexpr const char* const ff4_data[] = { 776 "--unknown_flag=10" 777 }; 778 779 const char* in_args1[] = { 780 "testbin", 781 GetFlagfileFlag({{"parse_test.ff4", 782 absl::MakeConstSpan(ff4_data)}}, flagfile_flag), 783 }; 784 EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1), 785 "Unknown command line flag 'unknown_flag'"); 786 787 constexpr const char* const ff5_data[] = { 788 "--int_flag 10", 789 }; 790 791 const char* in_args2[] = { 792 "testbin", 793 GetFlagfileFlag({{"parse_test.ff5", 794 absl::MakeConstSpan(ff5_data)}}, flagfile_flag), 795 }; 796 EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2), 797 "Unknown command line flag 'int_flag 10'"); 798 799 constexpr const char* const ff6_data[] = { 800 "--int_flag=10", "--", "arg1", "arg2", "arg3", 801 }; 802 803 const char* in_args3[] = { 804 "testbin", 805 GetFlagfileFlag({{"parse_test.ff6", absl::MakeConstSpan(ff6_data)}}, 806 flagfile_flag), 807 }; 808 EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args3), 809 "Flagfile can't contain position arguments or --"); 810 811 const char* in_args4[] = { 812 "testbin", 813 "--flagfile=invalid_flag_file", 814 }; 815 EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args4), 816 "Can't open flagfile invalid_flag_file"); 817 818 constexpr const char* const ff7_data[] = { 819 "--int_flag=10", 820 "*bin*", 821 "--str_flag=aqsw", 822 }; 823 824 const char* in_args5[] = { 825 "testbin", 826 GetFlagfileFlag({{"parse_test.ff7", absl::MakeConstSpan(ff7_data)}}, 827 flagfile_flag), 828 }; 829 EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args5), 830 "Unexpected line in the flagfile .*: \\*bin\\*"); 831 } 832 833 // -------------------------------------------------------------------- 834 835 TEST_F(ParseTest, TestReadingRequiredFlagsFromEnv) { 836 const char* in_args1[] = {"testbin", 837 "--fromenv=int_flag,bool_flag,string_flag"}; 838 839 ScopedSetEnv set_int_flag("FLAGS_int_flag", "33"); 840 ScopedSetEnv set_bool_flag("FLAGS_bool_flag", "True"); 841 ScopedSetEnv set_string_flag("FLAGS_string_flag", "AQ12"); 842 843 TestParse(in_args1, 33, 1.1, "AQ12", true); 844 } 845 846 // -------------------------------------------------------------------- 847 848 TEST_F(ParseDeathTest, TestReadingUnsetRequiredFlagsFromEnv) { 849 const char* in_args1[] = {"testbin", "--fromenv=int_flag"}; 850 851 EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1), 852 "FLAGS_int_flag not found in environment"); 853 } 854 855 // -------------------------------------------------------------------- 856 857 TEST_F(ParseDeathTest, TestRecursiveFlagsFromEnv) { 858 const char* in_args1[] = {"testbin", "--fromenv=tryfromenv"}; 859 860 ScopedSetEnv set_tryfromenv("FLAGS_tryfromenv", "int_flag"); 861 862 EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1), 863 "Infinite recursion on flag tryfromenv"); 864 } 865 866 // -------------------------------------------------------------------- 867 868 TEST_F(ParseTest, TestReadingOptionalFlagsFromEnv) { 869 const char* in_args1[] = { 870 "testbin", "--tryfromenv=int_flag,bool_flag,string_flag,other_flag"}; 871 872 ScopedSetEnv set_int_flag("FLAGS_int_flag", "17"); 873 ScopedSetEnv set_bool_flag("FLAGS_bool_flag", "Y"); 874 875 TestParse(in_args1, 17, 1.1, "a", true); 876 } 877 878 // -------------------------------------------------------------------- 879 880 TEST_F(ParseTest, TestReadingFlagsFromEnvMoxedWithRegularFlags) { 881 const char* in_args1[] = { 882 "testbin", 883 "--bool_flag=T", 884 "--tryfromenv=int_flag,bool_flag", 885 "--int_flag=-21", 886 }; 887 888 ScopedSetEnv set_int_flag("FLAGS_int_flag", "-15"); 889 ScopedSetEnv set_bool_flag("FLAGS_bool_flag", "F"); 890 891 TestParse(in_args1, -21, 1.1, "a", false); 892 } 893 894 // -------------------------------------------------------------------- 895 896 TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) { 897 const char* in_args1[] = { 898 "testbin", 899 "--help", 900 }; 901 902 EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args1), flags::HelpMode::kImportant); 903 EXPECT_EXIT(InvokeParse(in_args1), testing::ExitedWithCode(1), ""); 904 905 const char* in_args2[] = { 906 "testbin", 907 "--help", 908 "--int_flag=3", 909 }; 910 911 EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args2), flags::HelpMode::kImportant); 912 EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 3); 913 914 const char* in_args3[] = {"testbin", "--help", "some_positional_arg"}; 915 916 EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args3), flags::HelpMode::kImportant); 917 } 918 919 // -------------------------------------------------------------------- 920 921 TEST_F(ParseTest, TestSubstringHelpFlagHandling) { 922 const char* in_args1[] = { 923 "testbin", 924 "--help=abcd", 925 }; 926 927 EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args1), flags::HelpMode::kMatch); 928 EXPECT_EQ(flags::GetFlagsHelpMatchSubstr(), "abcd"); 929 } 930 931 // -------------------------------------------------------------------- 932 933 TEST_F(ParseDeathTest, TestVersionHandling) { 934 const char* in_args1[] = { 935 "testbin", 936 "--version", 937 }; 938 939 EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args1), flags::HelpMode::kVersion); 940 } 941 942 // -------------------------------------------------------------------- 943 944 TEST_F(ParseTest, TestCheckArgsHandling) { 945 const char* in_args1[] = {"testbin", "--only_check_args", "--int_flag=211"}; 946 947 EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args1), flags::HelpMode::kOnlyCheckArgs); 948 EXPECT_EXIT(InvokeParseAbslOnly(in_args1), testing::ExitedWithCode(0), ""); 949 EXPECT_EXIT(InvokeParse(in_args1), testing::ExitedWithCode(0), ""); 950 951 const char* in_args2[] = {"testbin", "--only_check_args", "--unknown_flag=a"}; 952 953 EXPECT_EQ(InvokeParseAbslOnlyImpl(in_args2), flags::HelpMode::kOnlyCheckArgs); 954 EXPECT_EXIT(InvokeParseAbslOnly(in_args2), testing::ExitedWithCode(0), ""); 955 EXPECT_EXIT(InvokeParse(in_args2), testing::ExitedWithCode(1), ""); 956 } 957 958 // -------------------------------------------------------------------- 959 960 TEST_F(ParseTest, WasPresentOnCommandLine) { 961 const char* in_args1[] = { 962 "testbin", "arg1", "--bool_flag", 963 "--int_flag=211", "arg2", "--double_flag=1.1", 964 "--string_flag", "asd", "--", 965 "--some_flag", "arg4", 966 }; 967 968 InvokeParse(in_args1); 969 970 EXPECT_TRUE(flags::WasPresentOnCommandLine("bool_flag")); 971 EXPECT_TRUE(flags::WasPresentOnCommandLine("int_flag")); 972 EXPECT_TRUE(flags::WasPresentOnCommandLine("double_flag")); 973 EXPECT_TRUE(flags::WasPresentOnCommandLine("string_flag")); 974 EXPECT_FALSE(flags::WasPresentOnCommandLine("some_flag")); 975 EXPECT_FALSE(flags::WasPresentOnCommandLine("another_flag")); 976 } 977 978 // -------------------------------------------------------------------- 979 980 TEST_F(ParseTest, ParseAbseilFlagsOnlySuccess) { 981 const char* in_args[] = { 982 "testbin", 983 "arg1", 984 "--bool_flag", 985 "--int_flag=211", 986 "arg2", 987 "--double_flag=1.1", 988 "--undef_flag1", 989 "--undef_flag2=123", 990 "--string_flag", 991 "asd", 992 "--", 993 "--some_flag", 994 "arg4", 995 }; 996 997 std::vector<char*> positional_args; 998 std::vector<absl::UnrecognizedFlag> unrecognized_flags; 999 1000 absl::ParseAbseilFlagsOnly(13, const_cast<char**>(in_args), positional_args, 1001 unrecognized_flags); 1002 EXPECT_THAT(positional_args, 1003 ElementsAreArray( 1004 {absl::string_view("testbin"), absl::string_view("arg1"), 1005 absl::string_view("arg2"), absl::string_view("--some_flag"), 1006 absl::string_view("arg4")})); 1007 EXPECT_THAT(unrecognized_flags, 1008 ElementsAreArray( 1009 {absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv, 1010 "undef_flag1"), 1011 absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv, 1012 "undef_flag2")})); 1013 } 1014 1015 // -------------------------------------------------------------------- 1016 1017 TEST_F(ParseDeathTest, ParseAbseilFlagsOnlyFailure) { 1018 const char* in_args[] = { 1019 "testbin", 1020 "--int_flag=21.1", 1021 }; 1022 1023 EXPECT_DEATH_IF_SUPPORTED( 1024 InvokeParseAbslOnly(in_args), 1025 "Illegal value '21.1' specified for flag 'int_flag'"); 1026 } 1027 1028 // -------------------------------------------------------------------- 1029 1030 TEST_F(ParseTest, UndefOkFlagsAreIgnored) { 1031 const char* in_args[] = { 1032 "testbin", "--undef_flag1", 1033 "--undef_flag2=123", "--undefok=undef_flag2", 1034 "--undef_flag3", "value", 1035 }; 1036 1037 std::vector<char*> positional_args; 1038 std::vector<absl::UnrecognizedFlag> unrecognized_flags; 1039 1040 absl::ParseAbseilFlagsOnly(6, const_cast<char**>(in_args), positional_args, 1041 unrecognized_flags); 1042 EXPECT_THAT(positional_args, ElementsAreArray({absl::string_view("testbin"), 1043 absl::string_view("value")})); 1044 EXPECT_THAT(unrecognized_flags, 1045 ElementsAreArray( 1046 {absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv, 1047 "undef_flag1"), 1048 absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv, 1049 "undef_flag3")})); 1050 } 1051 1052 // -------------------------------------------------------------------- 1053 1054 TEST_F(ParseTest, AllUndefOkFlagsAreIgnored) { 1055 const char* in_args[] = { 1056 "testbin", 1057 "--undef_flag1", 1058 "--undef_flag2=123", 1059 "--undefok=undef_flag2,undef_flag1,undef_flag3", 1060 "--undef_flag3", 1061 "value", 1062 "--", 1063 "--undef_flag4", 1064 }; 1065 1066 std::vector<char*> positional_args; 1067 std::vector<absl::UnrecognizedFlag> unrecognized_flags; 1068 1069 absl::ParseAbseilFlagsOnly(8, const_cast<char**>(in_args), positional_args, 1070 unrecognized_flags); 1071 EXPECT_THAT(positional_args, 1072 ElementsAreArray({absl::string_view("testbin"), 1073 absl::string_view("value"), 1074 absl::string_view("--undef_flag4")})); 1075 EXPECT_THAT(unrecognized_flags, testing::IsEmpty()); 1076 } 1077 1078 // -------------------------------------------------------------------- 1079 1080 TEST_F(ParseDeathTest, ExitOnUnrecognizedFlagPrintsHelp) { 1081 const char* in_args[] = { 1082 "testbin", 1083 "--undef_flag1", 1084 "--help=int_flag", 1085 }; 1086 1087 EXPECT_EXIT(InvokeParseCommandLineImpl(in_args), testing::ExitedWithCode(1), 1088 AllOf(HasSubstr("Unknown command line flag 'undef_flag1'"), 1089 HasSubstr("Try --helpfull to get a list of all flags"))); 1090 } 1091 1092 // -------------------------------------------------------------------- 1093 1094 } // namespace