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;
65 *
err =
"Use values A, AAA instead";
69 return udt.value == 1 ?
"A" :
"AAA";
72 std::string GetTestTmpDirEnvVar(
const char*
const env_var_name) {
75 auto get_res = GetEnvironmentVariableA(env_var_name,
buf,
sizeof(
buf));
76 if (get_res >=
sizeof(
buf) || get_res == 0) {
82 const char* val =
::getenv(env_var_name);
96 *res = GetTestTmpDirEnvVar(
"TMPDIR");
101 char temp_path_buffer[MAX_PATH];
103 auto len = GetTempPathA(MAX_PATH, temp_path_buffer);
104 if (
len < MAX_PATH &&
len != 0) {
107 temp_dir_name.push_back(
'\\');
110 if (CreateDirectoryA(temp_dir_name.c_str(),
nullptr)) {
111 *res = temp_dir_name;
115 char temp_dir_template[] =
"/tmp/parse_test.XXXXXX";
116 if (
auto* unique_name = ::
mkdtemp(temp_dir_template)) {
124 "Failed to make temporary directory for data files");
136 return *temp_dir_name;
139 struct FlagfileData {
145 constexpr
const char*
const ff1_data[] = {
151 " --string_flag=q2w2 ",
153 " --double_flag=0.1",
157 constexpr
const char*
const ff2_data[] = {
158 "# Setting legacy flag",
175 const char* GetFlagfileFlag(
const std::vector<FlagfileData>& ffd,
177 flagfile_flag =
"--flagfile=";
179 for (
const auto& flagfile_data : ffd) {
181 absl::StrCat(GetTestTempDir(), flagfile_data.file_name);
183 std::ofstream flagfile_out(flagfile_name);
184 for (
auto line : flagfile_data.file_lines) {
192 return flagfile_flag.c_str();
222 std::vector<char*> InvokeParse(
const char* (&in_argv)[
N]) {
229 void TestParse(
const char* (&in_argv)[
N],
int int_flag_value,
231 bool bool_flag_val,
int exp_position_args = 0) {
232 auto out_args = InvokeParse(in_argv);
234 EXPECT_EQ(out_args.size(), 1 + exp_position_args);
246 const char* in_argv[] = {
"testbin"};
248 auto out_args = InvokeParse(in_argv);
257 const char* in_args1[] = {
261 TestParse(in_args1, 10, 1.1,
"a",
false);
263 const char* in_args2[] = {
267 TestParse(in_args2, 20, 1.1,
"a",
false);
269 const char* in_args3[] = {
274 TestParse(in_args3, -30, 1.1,
"a",
false);
276 const char* in_args4[] = {
281 TestParse(in_args4, 33, 1.1,
"a",
false);
287 const char* in_args1[] = {
293 const char* in_args2[] = {
295 "--double_flag=0x1.2",
297 TestParse(in_args2, 1, 1.125,
"a",
false);
299 const char* in_args3[] = {
304 TestParse(in_args3, 1, 99.7,
"a",
false);
306 const char* in_args4[] = {
311 TestParse(in_args4, 1, 32.0625,
"a",
false);
317 const char* in_args1[] = {
319 "--string_flag=aqswde",
321 TestParse(in_args1, 1, 1.1,
"aqswde",
false);
323 const char* in_args2[] = {
325 "-string_flag=a=b=c",
327 TestParse(in_args2, 1, 1.1,
"a=b=c",
false);
329 const char* in_args3[] = {
334 TestParse(in_args3, 1, 1.1,
"zaxscd",
false);
336 const char* in_args4[] = {
341 TestParse(in_args4, 1, 1.1,
"--int_flag",
false);
343 const char* in_args5[] = {
348 TestParse(in_args5, 1, 1.1,
"--no_a_flag=11",
false);
354 const char* in_args1[] = {
360 const char* in_args2[] = {
366 const char* in_args3[] = {
372 const char* in_args4[] = {
382 const char* in_args1[] = {
386 InvokeParse(in_args1);
390 const char* in_args2[] = {
"testbin",
"--udt_flag",
"AAA"};
391 InvokeParse(in_args2);
399 const char* in_args1[] = {
400 "testbin",
"--bool_flag",
"--int_flag=2",
401 "--double_flag=0.1",
"--string_flag=asd",
403 TestParse(in_args1, 2, 0.1,
"asd",
true);
405 const char* in_args2[] = {
406 "testbin",
"--string_flag=",
"--nobool_flag",
"--int_flag",
407 "-011",
"--double_flag",
"-1e-2",
409 TestParse(in_args2, -11, -0.01,
"",
false);
411 const char* in_args3[] = {
412 "testbin",
"--int_flag",
"-0",
"--string_flag",
"\"\"",
413 "--bool_flag=true",
"--double_flag=1e18",
415 TestParse(in_args3, 0, 1e18,
"\"\"",
true);
421 const char* in_args1[] = {
426 TestParse(in_args1, 1, 1.1,
"a",
false, 2);
428 auto out_args1 = InvokeParse(in_args1);
433 const char* in_args2[] = {
438 TestParse(in_args2, 2, 1.1,
"a",
false, 1);
440 auto out_args2 = InvokeParse(in_args2);
444 const char* in_args3[] = {
"testbin",
"p1",
"--int_flag=3",
445 "p2",
"--bool_flag",
"true"};
446 TestParse(in_args3, 3, 1.1,
"a",
true, 3);
448 auto out_args3 = InvokeParse(in_args3);
454 const char* in_args4[] = {
460 TestParse(in_args4, 3, 1.1,
"a",
true, 2);
462 auto out_args4 = InvokeParse(in_args4);
467 const char* in_args5[] = {
468 "testbin",
"p1",
"--int_flag=4",
"--",
"--bool_flag",
"false",
"p2",
470 TestParse(in_args5, 4, 1.1,
"a",
true, 4);
472 auto out_args5 = InvokeParse(in_args5);
484 TEST_F(ParseDeathTest, TestUndefinedArg) {
485 const char* in_args1[] = {
490 "Unknown command line flag 'undefined_flag'");
492 const char* in_args2[] = {
497 "Unknown command line flag 'noprefixed_flag'");
499 const char* in_args3[] = {
504 "Unknown command line flag 'Int_flag'");
509 TEST_F(ParseDeathTest, TestInvalidBoolFlagFormat) {
510 const char* in_args1[] = {
515 InvokeParse(in_args1),
516 "Missing the value after assignment for the boolean flag 'bool_flag'");
518 const char* in_args2[] = {
520 "--nobool_flag=true",
523 "Negative form with assignment is not valid for the boolean "
529 TEST_F(ParseDeathTest, TestInvalidNonBoolFlagFormat) {
530 const char* in_args1[] = {
535 "Negative form is not valid for the flag 'string_flag'");
537 const char* in_args2[] = {
542 "Missing the value for the flag 'int_flag'");
547 TEST_F(ParseDeathTest, TestInvalidUDTFlagFormat) {
548 const char* in_args1[] = {
553 "Illegal value '1' specified for flag 'udt_flag'; Use values A, "
556 const char* in_args2[] = {
562 "Illegal value 'AA' specified for flag 'udt_flag'; Use values "
569 const char* in_args1[] = {
575 const char* in_args2[] = {
581 const char* in_args3[] = {
582 "testbin",
"--legacy_int",
"22",
"--int_flag=2",
583 "--legacy_bool",
"true",
"--legacy_str",
"--string_flag=qwe",
585 TestParse(in_args3, 2, 1.1,
"a",
false, 1);
593 const char* in_args1[] = {
598 TestParse(in_args1, -1, 0.1,
"q2w2 ",
true);
600 const char* in_args2[] = {
605 TestParse(in_args2, 100, 0.1,
"q2w2 ",
false);
613 const char* in_args1[] = {
619 TestParse(in_args1, -1, 0.1,
"q2w2 ",
true);
627 const char* in_args1[] = {
628 "testbin",
"--int_flag=3",
632 TestParse(in_args1, -1, 0.2,
"q2w2 ",
true);
640 constexpr
const char*
const ff3_data[] = {
641 "--flagfile=$0/parse_test.ff1",
642 "--flagfile=$0/parse_test.ff2",
649 const char* in_args1[] = {
654 TestParse(in_args1, 100, 0.1,
"q2w2 ",
false);
659 TEST_F(ParseDeathTest, TestInvalidFlagfiles) {
662 constexpr
const char*
const ff4_data[] = {
666 const char* in_args1[] = {
668 GetFlagfileFlag({{
"parse_test.ff4",
672 "Unknown command line flag 'unknown_flag'");
674 constexpr
const char*
const ff5_data[] = {
678 const char* in_args2[] = {
680 GetFlagfileFlag({{
"parse_test.ff5",
684 "Unknown command line flag 'int_flag 10'");
686 constexpr
const char*
const ff6_data[] = {
687 "--int_flag=10",
"--",
"arg1",
"arg2",
"arg3",
690 const char* in_args3[] = {
696 "Flagfile can't contain position arguments or --");
698 const char* in_args4[] = {
700 "--flagfile=invalid_flag_file",
703 "Can't open flagfile invalid_flag_file");
705 constexpr
const char*
const ff7_data[] = {
711 const char* in_args5[] = {
717 "Unexpected line in the flagfile .*: \\*bin\\*");
723 const char* in_args1[] = {
"testbin",
724 "--fromenv=int_flag,bool_flag,string_flag"};
726 ScopedSetEnv set_int_flag(
"FLAGS_int_flag",
"33");
727 ScopedSetEnv set_bool_flag(
"FLAGS_bool_flag",
"True");
728 ScopedSetEnv set_string_flag(
"FLAGS_string_flag",
"AQ12");
730 TestParse(in_args1, 33, 1.1,
"AQ12",
true);
735 TEST_F(ParseDeathTest, TestReadingUnsetRequiredFlagsFromEnv) {
736 const char* in_args1[] = {
"testbin",
"--fromenv=int_flag"};
739 "FLAGS_int_flag not found in environment");
744 TEST_F(ParseDeathTest, TestRecursiveFlagsFromEnv) {
745 const char* in_args1[] = {
"testbin",
"--fromenv=tryfromenv"};
747 ScopedSetEnv set_tryfromenv(
"FLAGS_tryfromenv",
"int_flag");
750 "Infinite recursion on flag tryfromenv");
756 const char* in_args1[] = {
757 "testbin",
"--tryfromenv=int_flag,bool_flag,string_flag,other_flag"};
759 ScopedSetEnv set_int_flag(
"FLAGS_int_flag",
"17");
760 ScopedSetEnv set_bool_flag(
"FLAGS_bool_flag",
"Y");
768 const char* in_args1[] = {
771 "--tryfromenv=int_flag,bool_flag",
775 ScopedSetEnv set_int_flag(
"FLAGS_int_flag",
"-15");
776 ScopedSetEnv set_bool_flag(
"FLAGS_bool_flag",
"F");
778 TestParse(in_args1, -21, 1.1,
"a",
false);
784 const char* in_args1[] = {
785 "testbin",
"arg1",
"--bool_flag",
786 "--int_flag=211",
"arg2",
"--double_flag=1.1",
787 "--string_flag",
"asd",
"--",
791 auto out_args1 = InvokeParse(in_args1);
800 11,
const_cast<char**
>(in_args1), flags::ArgvListAction::kKeepParsedArgs,
801 flags::UsageFlagsAction::kHandleUsage,
802 flags::OnUndefinedFlag::kAbortIfUndefined);
819 const char* in_args1[] = {
827 4,
const_cast<char**
>(in_args1), flags::ArgvListAction::kRemoveParsedArgs,
828 flags::UsageFlagsAction::kHandleUsage,
829 flags::OnUndefinedFlag::kIgnoreUndefined);
836 const char* in_args2[] = {
844 4,
const_cast<char**
>(in_args2), flags::ArgvListAction::kKeepParsedArgs,
845 flags::UsageFlagsAction::kHandleUsage,
846 flags::OnUndefinedFlag::kIgnoreUndefined);
859 TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) {
860 const char* in_args1[] = {
865 EXPECT_EXIT(InvokeParse(in_args1), testing::ExitedWithCode(1),
"");
867 const char* in_args2[] = {
874 3,
const_cast<char**
>(in_args2), flags::ArgvListAction::kRemoveParsedArgs,
875 flags::UsageFlagsAction::kIgnoreUsage,
876 flags::OnUndefinedFlag::kAbortIfUndefined);
884 TEST_F(ParseDeathTest, TestSubstringHelpFlagHandling) {
885 const char* in_args1[] = {
891 2,
const_cast<char**
>(in_args1), flags::ArgvListAction::kRemoveParsedArgs,
892 flags::UsageFlagsAction::kIgnoreUsage,
893 flags::OnUndefinedFlag::kAbortIfUndefined);
898 const char* in_args2[] = {
"testbin",
"--help",
"some_positional_arg"};
901 3,
const_cast<char**
>(in_args2), flags::ArgvListAction::kRemoveParsedArgs,
902 flags::UsageFlagsAction::kIgnoreUsage,
903 flags::OnUndefinedFlag::kAbortIfUndefined);
911 const char* in_args1[] = {
912 "testbin",
"arg1",
"--bool_flag",
913 "--int_flag=211",
"arg2",
"--double_flag=1.1",
914 "--string_flag",
"asd",
"--",
915 "--some_flag",
"arg4",
918 InvokeParse(in_args1);