about summary refs log tree commit diff
path: root/third_party/abseil_cpp/absl/flags/parse_test.cc
diff options
context:
space:
mode:
authorVincent Ambo <tazjin@google.com>2020-05-20T01·32+0100
committerVincent Ambo <tazjin@google.com>2020-05-20T01·32+0100
commitfc8dc48020ac5b52731d0828a96ea4d2526c77ba (patch)
tree353204eea3268095a9ad3f5345720f32c2615c69 /third_party/abseil_cpp/absl/flags/parse_test.cc
parentffb2ae54beb5796cd408fbe15d2d2da09ff37adf (diff)
parent768eb2ca2857342673fcd462792ce04b8bac3fa3 (diff)
Add 'third_party/abseil_cpp/' from commit '768eb2ca2857342673fcd462792ce04b8bac3fa3' r/781
git-subtree-dir: third_party/abseil_cpp
git-subtree-mainline: ffb2ae54beb5796cd408fbe15d2d2da09ff37adf
git-subtree-split: 768eb2ca2857342673fcd462792ce04b8bac3fa3
Diffstat (limited to 'third_party/abseil_cpp/absl/flags/parse_test.cc')
-rw-r--r--third_party/abseil_cpp/absl/flags/parse_test.cc894
1 files changed, 894 insertions, 0 deletions
diff --git a/third_party/abseil_cpp/absl/flags/parse_test.cc b/third_party/abseil_cpp/absl/flags/parse_test.cc
new file mode 100644
index 000000000000..e6a53ae6cb43
--- /dev/null
+++ b/third_party/abseil_cpp/absl/flags/parse_test.cc
@@ -0,0 +1,894 @@
+//
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/flags/parse.h"
+
+#include <stdlib.h>
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/internal/scoped_set_env.h"
+#include "absl/flags/declare.h"
+#include "absl/flags/flag.h"
+#include "absl/flags/internal/parse.h"
+#include "absl/flags/internal/registry.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/strings/substitute.h"
+#include "absl/types/span.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+namespace {
+
+using absl::base_internal::ScopedSetEnv;
+
+struct UDT {
+  UDT() = default;
+  UDT(const UDT&) = default;
+  UDT(int v) : value(v) {}  // NOLINT
+
+  int value;
+};
+
+bool AbslParseFlag(absl::string_view in, UDT* udt, std::string* err) {
+  if (in == "A") {
+    udt->value = 1;
+    return true;
+  }
+  if (in == "AAA") {
+    udt->value = 10;
+    return true;
+  }
+
+  *err = "Use values A, AAA instead";
+  return false;
+}
+std::string AbslUnparseFlag(const UDT& udt) {
+  return udt.value == 1 ? "A" : "AAA";
+}
+
+std::string GetTestTmpDirEnvVar(const char* const env_var_name) {
+#ifdef _WIN32
+  char buf[MAX_PATH];
+  auto get_res = GetEnvironmentVariableA(env_var_name, buf, sizeof(buf));
+  if (get_res >= sizeof(buf) || get_res == 0) {
+    return "";
+  }
+
+  return std::string(buf, get_res);
+#else
+  const char* val = ::getenv(env_var_name);
+  if (val == nullptr) {
+    return "";
+  }
+
+  return val;
+#endif
+}
+
+const std::string& GetTestTempDir() {
+  static std::string* temp_dir_name = []() -> std::string* {
+    std::string* res = new std::string(GetTestTmpDirEnvVar("TEST_TMPDIR"));
+
+    if (res->empty()) {
+      *res = GetTestTmpDirEnvVar("TMPDIR");
+    }
+
+    if (res->empty()) {
+#ifdef _WIN32
+      char temp_path_buffer[MAX_PATH];
+
+      auto len = GetTempPathA(MAX_PATH, temp_path_buffer);
+      if (len < MAX_PATH && len != 0) {
+        std::string temp_dir_name = temp_path_buffer;
+        if (!absl::EndsWith(temp_dir_name, "\\")) {
+          temp_dir_name.push_back('\\');
+        }
+        absl::StrAppend(&temp_dir_name, "parse_test.", GetCurrentProcessId());
+        if (CreateDirectoryA(temp_dir_name.c_str(), nullptr)) {
+          *res = temp_dir_name;
+        }
+      }
+#else
+      char temp_dir_template[] = "/tmp/parse_test.XXXXXX";
+      if (auto* unique_name = ::mkdtemp(temp_dir_template)) {
+        *res = unique_name;
+      }
+#endif
+    }
+
+    if (res->empty()) {
+      ABSL_INTERNAL_LOG(FATAL,
+                        "Failed to make temporary directory for data files");
+    }
+
+#ifdef _WIN32
+    *res += "\\";
+#else
+    *res += "/";
+#endif
+
+    return res;
+  }();
+
+  return *temp_dir_name;
+}
+
+struct FlagfileData {
+  const absl::string_view file_name;
+  const absl::Span<const char* const> file_lines;
+};
+
+// clang-format off
+constexpr const char* const ff1_data[] = {
+    "# comment    ",
+    "  # comment  ",
+    "",
+    "     ",
+    "--int_flag=-1",
+    "  --string_flag=q2w2  ",
+    "  ##   ",
+    "  --double_flag=0.1",
+    "--bool_flag=Y  "
+};
+
+constexpr const char* const ff2_data[] = {
+    "# Setting legacy flag",
+    "--legacy_int=1111",
+    "--legacy_bool",
+    "--nobool_flag",
+    "--legacy_str=aqsw",
+    "--int_flag=100",
+    "   ## ============="
+};
+// clang-format on
+
+// Builds flagfile flag in the flagfile_flag buffer and returns it. This
+// function also creates a temporary flagfile based on FlagfileData input.
+// We create a flagfile in a temporary directory with the name specified in
+// FlagfileData and populate it with lines specifed in FlagfileData. If $0 is
+// referenced in any of the lines in FlagfileData they are replaced with
+// temporary directory location. This way we can test inclusion of one flagfile
+// from another flagfile.
+const char* GetFlagfileFlag(const std::vector<FlagfileData>& ffd,
+                            std::string* flagfile_flag) {
+  *flagfile_flag = "--flagfile=";
+  absl::string_view separator;
+  for (const auto& flagfile_data : ffd) {
+    std::string flagfile_name =
+        absl::StrCat(GetTestTempDir(), flagfile_data.file_name);
+
+    std::ofstream flagfile_out(flagfile_name);
+    for (auto line : flagfile_data.file_lines) {
+      flagfile_out << absl::Substitute(line, GetTestTempDir()) << "\n";
+    }
+
+    absl::StrAppend(flagfile_flag, separator, flagfile_name);
+    separator = ",";
+  }
+
+  return flagfile_flag->c_str();
+}
+
+}  // namespace
+
+ABSL_FLAG(int, int_flag, 1, "");
+ABSL_FLAG(double, double_flag, 1.1, "");
+ABSL_FLAG(std::string, string_flag, "a", "");
+ABSL_FLAG(bool, bool_flag, false, "");
+ABSL_FLAG(UDT, udt_flag, -1, "");
+ABSL_RETIRED_FLAG(int, legacy_int, 1, "");
+ABSL_RETIRED_FLAG(bool, legacy_bool, false, "");
+ABSL_RETIRED_FLAG(std::string, legacy_str, "l", "");
+
+namespace {
+
+namespace flags = absl::flags_internal;
+using testing::ElementsAreArray;
+
+class ParseTest : public testing::Test {
+ private:
+  flags::FlagSaver flag_saver_;
+};
+
+// --------------------------------------------------------------------
+
+template <int N>
+std::vector<char*> InvokeParse(const char* (&in_argv)[N]) {
+  return absl::ParseCommandLine(N, const_cast<char**>(in_argv));
+}
+
+// --------------------------------------------------------------------
+
+template <int N>
+void TestParse(const char* (&in_argv)[N], int int_flag_value,
+               double double_flag_val, absl::string_view string_flag_val,
+               bool bool_flag_val, int exp_position_args = 0) {
+  auto out_args = InvokeParse(in_argv);
+
+  EXPECT_EQ(out_args.size(), 1 + exp_position_args);
+  EXPECT_STREQ(out_args[0], "testbin");
+
+  EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), int_flag_value);
+  EXPECT_NEAR(absl::GetFlag(FLAGS_double_flag), double_flag_val, 0.0001);
+  EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), string_flag_val);
+  EXPECT_EQ(absl::GetFlag(FLAGS_bool_flag), bool_flag_val);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestEmptyArgv) {
+  const char* in_argv[] = {"testbin"};
+
+  auto out_args = InvokeParse(in_argv);
+
+  EXPECT_EQ(out_args.size(), 1);
+  EXPECT_STREQ(out_args[0], "testbin");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestValidIntArg) {
+  const char* in_args1[] = {
+      "testbin",
+      "--int_flag=10",
+  };
+  TestParse(in_args1, 10, 1.1, "a", false);
+
+  const char* in_args2[] = {
+      "testbin",
+      "-int_flag=020",
+  };
+  TestParse(in_args2, 20, 1.1, "a", false);
+
+  const char* in_args3[] = {
+      "testbin",
+      "--int_flag",
+      "-30",
+  };
+  TestParse(in_args3, -30, 1.1, "a", false);
+
+  const char* in_args4[] = {
+      "testbin",
+      "-int_flag",
+      "0x21",
+  };
+  TestParse(in_args4, 33, 1.1, "a", false);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestValidDoubleArg) {
+  const char* in_args1[] = {
+      "testbin",
+      "--double_flag=2.3",
+  };
+  TestParse(in_args1, 1, 2.3, "a", false);
+
+  const char* in_args2[] = {
+      "testbin",
+      "--double_flag=0x1.2",
+  };
+  TestParse(in_args2, 1, 1.125, "a", false);
+
+  const char* in_args3[] = {
+      "testbin",
+      "--double_flag",
+      "99.7",
+  };
+  TestParse(in_args3, 1, 99.7, "a", false);
+
+  const char* in_args4[] = {
+      "testbin",
+      "--double_flag",
+      "0x20.1",
+  };
+  TestParse(in_args4, 1, 32.0625, "a", false);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestValidStringArg) {
+  const char* in_args1[] = {
+      "testbin",
+      "--string_flag=aqswde",
+  };
+  TestParse(in_args1, 1, 1.1, "aqswde", false);
+
+  const char* in_args2[] = {
+      "testbin",
+      "-string_flag=a=b=c",
+  };
+  TestParse(in_args2, 1, 1.1, "a=b=c", false);
+
+  const char* in_args3[] = {
+      "testbin",
+      "--string_flag",
+      "zaxscd",
+  };
+  TestParse(in_args3, 1, 1.1, "zaxscd", false);
+
+  const char* in_args4[] = {
+      "testbin",
+      "-string_flag",
+      "--int_flag",
+  };
+  TestParse(in_args4, 1, 1.1, "--int_flag", false);
+
+  const char* in_args5[] = {
+      "testbin",
+      "--string_flag",
+      "--no_a_flag=11",
+  };
+  TestParse(in_args5, 1, 1.1, "--no_a_flag=11", false);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestValidBoolArg) {
+  const char* in_args1[] = {
+      "testbin",
+      "--bool_flag",
+  };
+  TestParse(in_args1, 1, 1.1, "a", true);
+
+  const char* in_args2[] = {
+      "testbin",
+      "--nobool_flag",
+  };
+  TestParse(in_args2, 1, 1.1, "a", false);
+
+  const char* in_args3[] = {
+      "testbin",
+      "--bool_flag=true",
+  };
+  TestParse(in_args3, 1, 1.1, "a", true);
+
+  const char* in_args4[] = {
+      "testbin",
+      "-bool_flag=false",
+  };
+  TestParse(in_args4, 1, 1.1, "a", false);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestValidUDTArg) {
+  const char* in_args1[] = {
+      "testbin",
+      "--udt_flag=A",
+  };
+  InvokeParse(in_args1);
+
+  EXPECT_EQ(absl::GetFlag(FLAGS_udt_flag).value, 1);
+
+  const char* in_args2[] = {"testbin", "--udt_flag", "AAA"};
+  InvokeParse(in_args2);
+
+  EXPECT_EQ(absl::GetFlag(FLAGS_udt_flag).value, 10);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestValidMultipleArg) {
+  const char* in_args1[] = {
+      "testbin",           "--bool_flag",       "--int_flag=2",
+      "--double_flag=0.1", "--string_flag=asd",
+  };
+  TestParse(in_args1, 2, 0.1, "asd", true);
+
+  const char* in_args2[] = {
+      "testbin", "--string_flag=", "--nobool_flag", "--int_flag",
+      "-011",    "--double_flag",  "-1e-2",
+  };
+  TestParse(in_args2, -11, -0.01, "", false);
+
+  const char* in_args3[] = {
+      "testbin",          "--int_flag",         "-0", "--string_flag", "\"\"",
+      "--bool_flag=true", "--double_flag=1e18",
+  };
+  TestParse(in_args3, 0, 1e18, "\"\"", true);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestPositionalArgs) {
+  const char* in_args1[] = {
+      "testbin",
+      "p1",
+      "p2",
+  };
+  TestParse(in_args1, 1, 1.1, "a", false, 2);
+
+  auto out_args1 = InvokeParse(in_args1);
+
+  EXPECT_STREQ(out_args1[1], "p1");
+  EXPECT_STREQ(out_args1[2], "p2");
+
+  const char* in_args2[] = {
+      "testbin",
+      "--int_flag=2",
+      "p1",
+  };
+  TestParse(in_args2, 2, 1.1, "a", false, 1);
+
+  auto out_args2 = InvokeParse(in_args2);
+
+  EXPECT_STREQ(out_args2[1], "p1");
+
+  const char* in_args3[] = {"testbin", "p1",          "--int_flag=3",
+                            "p2",      "--bool_flag", "true"};
+  TestParse(in_args3, 3, 1.1, "a", true, 3);
+
+  auto out_args3 = InvokeParse(in_args3);
+
+  EXPECT_STREQ(out_args3[1], "p1");
+  EXPECT_STREQ(out_args3[2], "p2");
+  EXPECT_STREQ(out_args3[3], "true");
+
+  const char* in_args4[] = {
+      "testbin",
+      "--",
+      "p1",
+      "p2",
+  };
+  TestParse(in_args4, 3, 1.1, "a", true, 2);
+
+  auto out_args4 = InvokeParse(in_args4);
+
+  EXPECT_STREQ(out_args4[1], "p1");
+  EXPECT_STREQ(out_args4[2], "p2");
+
+  const char* in_args5[] = {
+      "testbin", "p1", "--int_flag=4", "--", "--bool_flag", "false", "p2",
+  };
+  TestParse(in_args5, 4, 1.1, "a", true, 4);
+
+  auto out_args5 = InvokeParse(in_args5);
+
+  EXPECT_STREQ(out_args5[1], "p1");
+  EXPECT_STREQ(out_args5[2], "--bool_flag");
+  EXPECT_STREQ(out_args5[3], "false");
+  EXPECT_STREQ(out_args5[4], "p2");
+}
+
+// --------------------------------------------------------------------
+
+using ParseDeathTest = ParseTest;
+
+TEST_F(ParseDeathTest, TestUndefinedArg) {
+  const char* in_args1[] = {
+      "testbin",
+      "--undefined_flag",
+  };
+  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
+                            "Unknown command line flag 'undefined_flag'");
+
+  const char* in_args2[] = {
+      "testbin",
+      "--noprefixed_flag",
+  };
+  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2),
+                            "Unknown command line flag 'noprefixed_flag'");
+
+  const char* in_args3[] = {
+      "testbin",
+      "--Int_flag=1",
+  };
+  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args3),
+                            "Unknown command line flag 'Int_flag'");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseDeathTest, TestInvalidBoolFlagFormat) {
+  const char* in_args1[] = {
+      "testbin",
+      "--bool_flag=",
+  };
+  EXPECT_DEATH_IF_SUPPORTED(
+      InvokeParse(in_args1),
+      "Missing the value after assignment for the boolean flag 'bool_flag'");
+
+  const char* in_args2[] = {
+      "testbin",
+      "--nobool_flag=true",
+  };
+  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2),
+               "Negative form with assignment is not valid for the boolean "
+               "flag 'bool_flag'");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseDeathTest, TestInvalidNonBoolFlagFormat) {
+  const char* in_args1[] = {
+      "testbin",
+      "--nostring_flag",
+  };
+  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
+               "Negative form is not valid for the flag 'string_flag'");
+
+  const char* in_args2[] = {
+      "testbin",
+      "--int_flag",
+  };
+  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2),
+               "Missing the value for the flag 'int_flag'");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseDeathTest, TestInvalidUDTFlagFormat) {
+  const char* in_args1[] = {
+      "testbin",
+      "--udt_flag=1",
+  };
+  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
+               "Illegal value '1' specified for flag 'udt_flag'; Use values A, "
+               "AAA instead");
+
+  const char* in_args2[] = {
+      "testbin",
+      "--udt_flag",
+      "AA",
+  };
+  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2),
+               "Illegal value 'AA' specified for flag 'udt_flag'; Use values "
+               "A, AAA instead");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestLegacyFlags) {
+  const char* in_args1[] = {
+      "testbin",
+      "--legacy_int=11",
+  };
+  TestParse(in_args1, 1, 1.1, "a", false);
+
+  const char* in_args2[] = {
+      "testbin",
+      "--legacy_bool",
+  };
+  TestParse(in_args2, 1, 1.1, "a", false);
+
+  const char* in_args3[] = {
+      "testbin",       "--legacy_int", "22",           "--int_flag=2",
+      "--legacy_bool", "true",         "--legacy_str", "--string_flag=qwe",
+  };
+  TestParse(in_args3, 2, 1.1, "a", false, 1);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestSimpleValidFlagfile) {
+  std::string flagfile_flag;
+
+  const char* in_args1[] = {
+      "testbin",
+      GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
+                      &flagfile_flag),
+  };
+  TestParse(in_args1, -1, 0.1, "q2w2  ", true);
+
+  const char* in_args2[] = {
+      "testbin",
+      GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}},
+                      &flagfile_flag),
+  };
+  TestParse(in_args2, 100, 0.1, "q2w2  ", false);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestValidMultiFlagfile) {
+  std::string flagfile_flag;
+
+  const char* in_args1[] = {
+      "testbin",
+      GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)},
+                       {"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
+                      &flagfile_flag),
+  };
+  TestParse(in_args1, -1, 0.1, "q2w2  ", true);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestFlagfileMixedWithRegularFlags) {
+  std::string flagfile_flag;
+
+  const char* in_args1[] = {
+      "testbin", "--int_flag=3",
+      GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
+                      &flagfile_flag),
+      "-double_flag=0.2"};
+  TestParse(in_args1, -1, 0.2, "q2w2  ", true);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestFlagfileInFlagfile) {
+  std::string flagfile_flag;
+
+  constexpr const char* const ff3_data[] = {
+      "--flagfile=$0/parse_test.ff1",
+      "--flagfile=$0/parse_test.ff2",
+  };
+
+  const char* in_args1[] = {
+      "testbin",
+      GetFlagfileFlag({{"parse_test.ff3", absl::MakeConstSpan(ff3_data)}},
+                      &flagfile_flag),
+  };
+  TestParse(in_args1, 100, 0.1, "q2w2  ", false);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseDeathTest, TestInvalidFlagfiles) {
+  std::string flagfile_flag;
+
+  constexpr const char* const ff4_data[] = {
+    "--unknown_flag=10"
+  };
+
+  const char* in_args1[] = {
+      "testbin",
+      GetFlagfileFlag({{"parse_test.ff4",
+                        absl::MakeConstSpan(ff4_data)}}, &flagfile_flag),
+  };
+  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
+               "Unknown command line flag 'unknown_flag'");
+
+  constexpr const char* const ff5_data[] = {
+    "--int_flag 10",
+  };
+
+  const char* in_args2[] = {
+      "testbin",
+      GetFlagfileFlag({{"parse_test.ff5",
+                        absl::MakeConstSpan(ff5_data)}}, &flagfile_flag),
+  };
+  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2),
+               "Unknown command line flag 'int_flag 10'");
+
+  constexpr const char* const ff6_data[] = {
+      "--int_flag=10", "--", "arg1", "arg2", "arg3",
+  };
+
+  const char* in_args3[] = {
+      "testbin",
+      GetFlagfileFlag({{"parse_test.ff6", absl::MakeConstSpan(ff6_data)}},
+                      &flagfile_flag),
+  };
+  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args3),
+               "Flagfile can't contain position arguments or --");
+
+  const char* in_args4[] = {
+      "testbin",
+      "--flagfile=invalid_flag_file",
+  };
+  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args4),
+                            "Can't open flagfile invalid_flag_file");
+
+  constexpr const char* const ff7_data[] = {
+      "--int_flag=10",
+      "*bin*",
+      "--str_flag=aqsw",
+  };
+
+  const char* in_args5[] = {
+      "testbin",
+      GetFlagfileFlag({{"parse_test.ff7", absl::MakeConstSpan(ff7_data)}},
+                      &flagfile_flag),
+  };
+  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args5),
+               "Unexpected line in the flagfile .*: \\*bin\\*");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestReadingRequiredFlagsFromEnv) {
+  const char* in_args1[] = {"testbin",
+                            "--fromenv=int_flag,bool_flag,string_flag"};
+
+  ScopedSetEnv set_int_flag("FLAGS_int_flag", "33");
+  ScopedSetEnv set_bool_flag("FLAGS_bool_flag", "True");
+  ScopedSetEnv set_string_flag("FLAGS_string_flag", "AQ12");
+
+  TestParse(in_args1, 33, 1.1, "AQ12", true);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseDeathTest, TestReadingUnsetRequiredFlagsFromEnv) {
+  const char* in_args1[] = {"testbin", "--fromenv=int_flag"};
+
+  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
+               "FLAGS_int_flag not found in environment");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseDeathTest, TestRecursiveFlagsFromEnv) {
+  const char* in_args1[] = {"testbin", "--fromenv=tryfromenv"};
+
+  ScopedSetEnv set_tryfromenv("FLAGS_tryfromenv", "int_flag");
+
+  EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
+                            "Infinite recursion on flag tryfromenv");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestReadingOptionalFlagsFromEnv) {
+  const char* in_args1[] = {
+      "testbin", "--tryfromenv=int_flag,bool_flag,string_flag,other_flag"};
+
+  ScopedSetEnv set_int_flag("FLAGS_int_flag", "17");
+  ScopedSetEnv set_bool_flag("FLAGS_bool_flag", "Y");
+
+  TestParse(in_args1, 17, 1.1, "a", true);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestReadingFlagsFromEnvMoxedWithRegularFlags) {
+  const char* in_args1[] = {
+      "testbin",
+      "--bool_flag=T",
+      "--tryfromenv=int_flag,bool_flag",
+      "--int_flag=-21",
+  };
+
+  ScopedSetEnv set_int_flag("FLAGS_int_flag", "-15");
+  ScopedSetEnv set_bool_flag("FLAGS_bool_flag", "F");
+
+  TestParse(in_args1, -21, 1.1, "a", false);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestKeepParsedArgs) {
+  const char* in_args1[] = {
+      "testbin",        "arg1", "--bool_flag",
+      "--int_flag=211", "arg2", "--double_flag=1.1",
+      "--string_flag",  "asd",  "--",
+      "arg3",           "arg4",
+  };
+
+  auto out_args1 = InvokeParse(in_args1);
+
+  EXPECT_THAT(
+      out_args1,
+      ElementsAreArray({absl::string_view("testbin"), absl::string_view("arg1"),
+                        absl::string_view("arg2"), absl::string_view("arg3"),
+                        absl::string_view("arg4")}));
+
+  auto out_args2 = flags::ParseCommandLineImpl(
+      11, const_cast<char**>(in_args1), flags::ArgvListAction::kKeepParsedArgs,
+      flags::UsageFlagsAction::kHandleUsage,
+      flags::OnUndefinedFlag::kAbortIfUndefined);
+
+  EXPECT_THAT(
+      out_args2,
+      ElementsAreArray({absl::string_view("testbin"),
+                        absl::string_view("--bool_flag"),
+                        absl::string_view("--int_flag=211"),
+                        absl::string_view("--double_flag=1.1"),
+                        absl::string_view("--string_flag"),
+                        absl::string_view("asd"), absl::string_view("--"),
+                        absl::string_view("arg1"), absl::string_view("arg2"),
+                        absl::string_view("arg3"), absl::string_view("arg4")}));
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, TestIgnoreUndefinedFlags) {
+  const char* in_args1[] = {
+      "testbin",
+      "arg1",
+      "--undef_flag=aa",
+      "--int_flag=21",
+  };
+
+  auto out_args1 = flags::ParseCommandLineImpl(
+      4, const_cast<char**>(in_args1), flags::ArgvListAction::kRemoveParsedArgs,
+      flags::UsageFlagsAction::kHandleUsage,
+      flags::OnUndefinedFlag::kIgnoreUndefined);
+
+  EXPECT_THAT(out_args1, ElementsAreArray({absl::string_view("testbin"),
+                                           absl::string_view("arg1")}));
+
+  EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 21);
+
+  const char* in_args2[] = {
+      "testbin",
+      "arg1",
+      "--undef_flag=aa",
+      "--string_flag=AA",
+  };
+
+  auto out_args2 = flags::ParseCommandLineImpl(
+      4, const_cast<char**>(in_args2), flags::ArgvListAction::kKeepParsedArgs,
+      flags::UsageFlagsAction::kHandleUsage,
+      flags::OnUndefinedFlag::kIgnoreUndefined);
+
+  EXPECT_THAT(
+      out_args2,
+      ElementsAreArray(
+          {absl::string_view("testbin"), absl::string_view("--undef_flag=aa"),
+           absl::string_view("--string_flag=AA"), absl::string_view("arg1")}));
+
+  EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "AA");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseDeathTest, TestHelpFlagHandling) {
+  const char* in_args1[] = {
+      "testbin",
+      "--help",
+  };
+
+  EXPECT_EXIT(InvokeParse(in_args1), testing::ExitedWithCode(1), "");
+
+  const char* in_args2[] = {
+      "testbin",
+      "--help",
+      "--int_flag=3",
+  };
+
+  auto out_args2 = flags::ParseCommandLineImpl(
+      3, const_cast<char**>(in_args2), flags::ArgvListAction::kRemoveParsedArgs,
+      flags::UsageFlagsAction::kIgnoreUsage,
+      flags::OnUndefinedFlag::kAbortIfUndefined);
+
+  EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 3);
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(ParseTest, WasPresentOnCommandLine) {
+  const char* in_args1[] = {
+      "testbin",        "arg1", "--bool_flag",
+      "--int_flag=211", "arg2", "--double_flag=1.1",
+      "--string_flag",  "asd",  "--",
+      "--some_flag",    "arg4",
+  };
+
+  InvokeParse(in_args1);
+
+  EXPECT_TRUE(flags::WasPresentOnCommandLine("bool_flag"));
+  EXPECT_TRUE(flags::WasPresentOnCommandLine("int_flag"));
+  EXPECT_TRUE(flags::WasPresentOnCommandLine("double_flag"));
+  EXPECT_TRUE(flags::WasPresentOnCommandLine("string_flag"));
+  EXPECT_FALSE(flags::WasPresentOnCommandLine("some_flag"));
+  EXPECT_FALSE(flags::WasPresentOnCommandLine("another_flag"));
+}
+
+// --------------------------------------------------------------------
+
+}  // namespace