Abseil Common Libraries (C++) (grcp 依赖)
https://abseil.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
930 lines
26 KiB
930 lines
26 KiB
// |
|
// 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/usage.h" |
|
#include "absl/flags/reflection.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& operator=(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 { |
|
public: |
|
~ParseTest() override { flags::SetFlagsHelpMode(flags::HelpMode::kNone); } |
|
|
|
private: |
|
absl::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", |
|
}; |
|
|
|
GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}, |
|
{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, |
|
flagfile_flag); |
|
|
|
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, TestSimpleHelpFlagHandling) { |
|
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(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant); |
|
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 3); |
|
} |
|
|
|
// -------------------------------------------------------------------- |
|
|
|
TEST_F(ParseDeathTest, TestSubstringHelpFlagHandling) { |
|
const char* in_args1[] = { |
|
"testbin", |
|
"--help=abcd", |
|
}; |
|
|
|
auto out_args1 = flags::ParseCommandLineImpl( |
|
2, const_cast<char**>(in_args1), flags::ArgvListAction::kRemoveParsedArgs, |
|
flags::UsageFlagsAction::kIgnoreUsage, |
|
flags::OnUndefinedFlag::kAbortIfUndefined); |
|
|
|
EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kMatch); |
|
EXPECT_EQ(flags::GetFlagsHelpMatchSubstr(), "abcd"); |
|
|
|
const char* in_args2[] = {"testbin", "--help", "some_positional_arg"}; |
|
|
|
auto out_args2 = flags::ParseCommandLineImpl( |
|
3, const_cast<char**>(in_args2), flags::ArgvListAction::kRemoveParsedArgs, |
|
flags::UsageFlagsAction::kIgnoreUsage, |
|
flags::OnUndefinedFlag::kAbortIfUndefined); |
|
|
|
EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant); |
|
} |
|
|
|
// -------------------------------------------------------------------- |
|
|
|
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
|
|
|