16 #include "absl/flags/parse.h"
24 #include "gmock/gmock.h"
25 #include "gtest/gtest.h"
26 #include "absl/base/internal/raw_logging.h"
27 #include "absl/base/internal/scoped_set_env.h"
28 #include "absl/flags/declare.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/strings/str_cat.h"
34 #include "absl/strings/string_view.h"
35 #include "absl/strings/substitute.h"
36 #include "absl/types/span.h"
48 UDT(
const UDT&) =
default;
64 *
err =
"Use values A, AAA instead";
68 return udt.value == 1 ?
"A" :
"AAA";
71 std::string GetTestTmpDirEnvVar(
const char*
const env_var_name) {
74 auto get_res = GetEnvironmentVariableA(env_var_name,
buf,
sizeof(
buf));
75 if (get_res >=
sizeof(
buf) || get_res == 0) {
81 const char* val =
::getenv(env_var_name);
95 *res = GetTestTmpDirEnvVar(
"TMPDIR");
100 char temp_path_buffer[MAX_PATH];
102 auto len = GetTempPathA(MAX_PATH, temp_path_buffer);
103 if (
len < MAX_PATH &&
len != 0) {
106 temp_dir_name.push_back(
'\\');
109 if (CreateDirectoryA(temp_dir_name.c_str(),
nullptr)) {
110 *res = temp_dir_name;
114 char temp_dir_template[] =
"/tmp/parse_test.XXXXXX";
115 if (
auto* unique_name = ::
mkdtemp(temp_dir_template)) {
123 "Failed to make temporary directory for data files");
135 return *temp_dir_name;
138 struct FlagfileData {
144 constexpr
const char*
const ff1_data[] = {
150 " --string_flag=q2w2 ",
152 " --double_flag=0.1",
156 constexpr
const char*
const ff2_data[] = {
157 "# Setting legacy flag",
174 const char* GetFlagfileFlag(
const std::vector<FlagfileData>& ffd,
176 flagfile_flag =
"--flagfile=";
178 for (
const auto& flagfile_data : ffd) {
180 absl::StrCat(GetTestTempDir(), flagfile_data.file_name);
182 std::ofstream flagfile_out(flagfile_name);
183 for (
auto line : flagfile_data.file_lines) {
191 return flagfile_flag.c_str();
221 std::vector<char*> InvokeParse(
const char* (&in_argv)[
N]) {
228 void TestParse(
const char* (&in_argv)[
N],
int int_flag_value,
230 bool bool_flag_val,
int exp_position_args = 0) {
231 auto out_args = InvokeParse(in_argv);
233 EXPECT_EQ(out_args.size(), 1 + exp_position_args);
245 const char* in_argv[] = {
"testbin"};
247 auto out_args = InvokeParse(in_argv);
256 const char* in_args1[] = {
260 TestParse(in_args1, 10, 1.1,
"a",
false);
262 const char* in_args2[] = {
266 TestParse(in_args2, 20, 1.1,
"a",
false);
268 const char* in_args3[] = {
273 TestParse(in_args3, -30, 1.1,
"a",
false);
275 const char* in_args4[] = {
280 TestParse(in_args4, 33, 1.1,
"a",
false);
286 const char* in_args1[] = {
292 const char* in_args2[] = {
294 "--double_flag=0x1.2",
296 TestParse(in_args2, 1, 1.125,
"a",
false);
298 const char* in_args3[] = {
303 TestParse(in_args3, 1, 99.7,
"a",
false);
305 const char* in_args4[] = {
310 TestParse(in_args4, 1, 32.0625,
"a",
false);
316 const char* in_args1[] = {
318 "--string_flag=aqswde",
320 TestParse(in_args1, 1, 1.1,
"aqswde",
false);
322 const char* in_args2[] = {
324 "-string_flag=a=b=c",
326 TestParse(in_args2, 1, 1.1,
"a=b=c",
false);
328 const char* in_args3[] = {
333 TestParse(in_args3, 1, 1.1,
"zaxscd",
false);
335 const char* in_args4[] = {
340 TestParse(in_args4, 1, 1.1,
"--int_flag",
false);
342 const char* in_args5[] = {
347 TestParse(in_args5, 1, 1.1,
"--no_a_flag=11",
false);
353 const char* in_args1[] = {
359 const char* in_args2[] = {
365 const char* in_args3[] = {
371 const char* in_args4[] = {
381 const char* in_args1[] = {
385 InvokeParse(in_args1);
389 const char* in_args2[] = {
"testbin",
"--udt_flag",
"AAA"};
390 InvokeParse(in_args2);
398 const char* in_args1[] = {
399 "testbin",
"--bool_flag",
"--int_flag=2",
400 "--double_flag=0.1",
"--string_flag=asd",
402 TestParse(in_args1, 2, 0.1,
"asd",
true);
404 const char* in_args2[] = {
405 "testbin",
"--string_flag=",
"--nobool_flag",
"--int_flag",
406 "-011",
"--double_flag",
"-1e-2",
408 TestParse(in_args2, -11, -0.01,
"",
false);
410 const char* in_args3[] = {
411 "testbin",
"--int_flag",
"-0",
"--string_flag",
"\"\"",
412 "--bool_flag=true",
"--double_flag=1e18",
414 TestParse(in_args3, 0, 1e18,
"\"\"",
true);
420 const char* in_args1[] = {
425 TestParse(in_args1, 1, 1.1,
"a",
false, 2);
427 auto out_args1 = InvokeParse(in_args1);
432 const char* in_args2[] = {
437 TestParse(in_args2, 2, 1.1,
"a",
false, 1);
439 auto out_args2 = InvokeParse(in_args2);
443 const char* in_args3[] = {
"testbin",
"p1",
"--int_flag=3",
444 "p2",
"--bool_flag",
"true"};
445 TestParse(in_args3, 3, 1.1,
"a",
true, 3);
447 auto out_args3 = InvokeParse(in_args3);
453 const char* in_args4[] = {
459 TestParse(in_args4, 3, 1.1,
"a",
true, 2);
461 auto out_args4 = InvokeParse(in_args4);
466 const char* in_args5[] = {
467 "testbin",
"p1",
"--int_flag=4",
"--",
"--bool_flag",
"false",
"p2",
469 TestParse(in_args5, 4, 1.1,
"a",
true, 4);
471 auto out_args5 = InvokeParse(in_args5);
483 TEST_F(ParseDeathTest, TestUndefinedArg) {
484 const char* in_args1[] = {
489 "Unknown command line flag 'undefined_flag'");
491 const char* in_args2[] = {
496 "Unknown command line flag 'noprefixed_flag'");
498 const char* in_args3[] = {
503 "Unknown command line flag 'Int_flag'");
508 TEST_F(ParseDeathTest, TestInvalidBoolFlagFormat) {
509 const char* in_args1[] = {
514 InvokeParse(in_args1),
515 "Missing the value after assignment for the boolean flag 'bool_flag'");
517 const char* in_args2[] = {
519 "--nobool_flag=true",
522 "Negative form with assignment is not valid for the boolean "
528 TEST_F(ParseDeathTest, TestInvalidNonBoolFlagFormat) {
529 const char* in_args1[] = {
534 "Negative form is not valid for the flag 'string_flag'");
536 const char* in_args2[] = {
541 "Missing the value for the flag 'int_flag'");
546 TEST_F(ParseDeathTest, TestInvalidUDTFlagFormat) {
547 const char* in_args1[] = {
552 "Illegal value '1' specified for flag 'udt_flag'; Use values A, "
555 const char* in_args2[] = {
561 "Illegal value 'AA' specified for flag 'udt_flag'; Use values "
568 const char* in_args1[] = {
574 const char* in_args2[] = {
580 const char* in_args3[] = {
581 "testbin",
"--legacy_int",
"22",
"--int_flag=2",
582 "--legacy_bool",
"true",
"--legacy_str",
"--string_flag=qwe",
584 TestParse(in_args3, 2, 1.1,
"a",
false, 1);
592 const char* in_args1[] = {
597 TestParse(in_args1, -1, 0.1,
"q2w2 ",
true);
599 const char* in_args2[] = {
604 TestParse(in_args2, 100, 0.1,
"q2w2 ",
false);
612 const char* in_args1[] = {
618 TestParse(in_args1, -1, 0.1,
"q2w2 ",
true);
626 const char* in_args1[] = {
627 "testbin",
"--int_flag=3",
631 TestParse(in_args1, -1, 0.2,
"q2w2 ",
true);
639 constexpr
const char*
const ff3_data[] = {
640 "--flagfile=$0/parse_test.ff1",
641 "--flagfile=$0/parse_test.ff2",
648 const char* in_args1[] = {
653 TestParse(in_args1, 100, 0.1,
"q2w2 ",
false);
658 TEST_F(ParseDeathTest, TestInvalidFlagfiles) {
661 constexpr
const char*
const ff4_data[] = {
665 const char* in_args1[] = {
667 GetFlagfileFlag({{
"parse_test.ff4",
671 "Unknown command line flag 'unknown_flag'");
673 constexpr
const char*
const ff5_data[] = {
677 const char* in_args2[] = {
679 GetFlagfileFlag({{
"parse_test.ff5",
683 "Unknown command line flag 'int_flag 10'");
685 constexpr
const char*
const ff6_data[] = {
686 "--int_flag=10",
"--",
"arg1",
"arg2",
"arg3",
689 const char* in_args3[] = {
695 "Flagfile can't contain position arguments or --");
697 const char* in_args4[] = {
699 "--flagfile=invalid_flag_file",
702 "Can't open flagfile invalid_flag_file");
704 constexpr
const char*
const ff7_data[] = {
710 const char* in_args5[] = {
716 "Unexpected line in the flagfile .*: \\*bin\\*");
722 const char* in_args1[] = {
"testbin",
723 "--fromenv=int_flag,bool_flag,string_flag"};
725 ScopedSetEnv set_int_flag(
"FLAGS_int_flag",
"33");
726 ScopedSetEnv set_bool_flag(
"FLAGS_bool_flag",
"True");
727 ScopedSetEnv set_string_flag(
"FLAGS_string_flag",
"AQ12");
729 TestParse(in_args1, 33, 1.1,
"AQ12",
true);
734 TEST_F(ParseDeathTest, TestReadingUnsetRequiredFlagsFromEnv) {
735 const char* in_args1[] = {
"testbin",
"--fromenv=int_flag"};
738 "FLAGS_int_flag not found in environment");
743 TEST_F(ParseDeathTest, TestRecursiveFlagsFromEnv) {
744 const char* in_args1[] = {
"testbin",
"--fromenv=tryfromenv"};
746 ScopedSetEnv set_tryfromenv(
"FLAGS_tryfromenv",
"int_flag");
749 "Infinite recursion on flag tryfromenv");
755 const char* in_args1[] = {
756 "testbin",
"--tryfromenv=int_flag,bool_flag,string_flag,other_flag"};
758 ScopedSetEnv set_int_flag(
"FLAGS_int_flag",
"17");
759 ScopedSetEnv set_bool_flag(
"FLAGS_bool_flag",
"Y");
767 const char* in_args1[] = {
770 "--tryfromenv=int_flag,bool_flag",
774 ScopedSetEnv set_int_flag(
"FLAGS_int_flag",
"-15");
775 ScopedSetEnv set_bool_flag(
"FLAGS_bool_flag",
"F");
777 TestParse(in_args1, -21, 1.1,
"a",
false);
783 const char* in_args1[] = {
784 "testbin",
"arg1",
"--bool_flag",
785 "--int_flag=211",
"arg2",
"--double_flag=1.1",
786 "--string_flag",
"asd",
"--",
790 auto out_args1 = InvokeParse(in_args1);
799 11,
const_cast<char**
>(in_args1), flags::ArgvListAction::kKeepParsedArgs,
800 flags::UsageFlagsAction::kHandleUsage,
801 flags::OnUndefinedFlag::kAbortIfUndefined);
818 const char* in_args1[] = {
826 4,
const_cast<char**
>(in_args1), flags::ArgvListAction::kRemoveParsedArgs,
827 flags::UsageFlagsAction::kHandleUsage,
828 flags::OnUndefinedFlag::kIgnoreUndefined);
835 const char* in_args2[] = {
843 4,
const_cast<char**
>(in_args2), flags::ArgvListAction::kKeepParsedArgs,
844 flags::UsageFlagsAction::kHandleUsage,
845 flags::OnUndefinedFlag::kIgnoreUndefined);
858 TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) {
859 const char* in_args1[] = {
864 EXPECT_EXIT(InvokeParse(in_args1), testing::ExitedWithCode(1),
"");
866 const char* in_args2[] = {
873 3,
const_cast<char**
>(in_args2), flags::ArgvListAction::kRemoveParsedArgs,
874 flags::UsageFlagsAction::kIgnoreUsage,
875 flags::OnUndefinedFlag::kAbortIfUndefined);
883 TEST_F(ParseDeathTest, TestSubstringHelpFlagHandling) {
884 const char* in_args1[] = {
890 2,
const_cast<char**
>(in_args1), flags::ArgvListAction::kRemoveParsedArgs,
891 flags::UsageFlagsAction::kIgnoreUsage,
892 flags::OnUndefinedFlag::kAbortIfUndefined);
897 const char* in_args2[] = {
"testbin",
"--help",
"some_positional_arg"};
900 3,
const_cast<char**
>(in_args2), flags::ArgvListAction::kRemoveParsedArgs,
901 flags::UsageFlagsAction::kIgnoreUsage,
902 flags::OnUndefinedFlag::kAbortIfUndefined);
910 const char* in_args1[] = {
911 "testbin",
"arg1",
"--bool_flag",
912 "--int_flag=211",
"arg2",
"--double_flag=1.1",
913 "--string_flag",
"asd",
"--",
914 "--some_flag",
"arg4",
917 InvokeParse(in_args1);