//
// 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(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