tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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