1 //
2 // Copyright 2019 The Abseil Authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      https://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 #include "absl/flags/parse.h"
17 
18 #include <stdlib.h>
19 
20 #include <fstream>
21 #include <string>
22 #include <vector>
23 
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"
37 
38 #ifdef _WIN32
39 #include <windows.h>
40 #endif
41 
42 namespace {
43 
44 using absl::base_internal::ScopedSetEnv;
45 
46 struct UDT {
47   UDT() = default;
48   UDT(const UDT&) = default;
49   UDT& operator=(const UDT&) = default;
UDT__anon5e8436390111::UDT50   UDT(int v) : value(v) {}  // NOLINT
51 
52   int value;
53 };
54 
AbslParseFlag(absl::string_view in,UDT * udt,std::string * err)55 bool AbslParseFlag(absl::string_view in, UDT* udt, std::string* err) {
56   if (in == "A") {
57     udt->value = 1;
58     return true;
59   }
60   if (in == "AAA") {
61     udt->value = 10;
62     return true;
63   }
64 
65   *err = "Use values A, AAA instead";
66   return false;
67 }
AbslUnparseFlag(const UDT & udt)68 std::string AbslUnparseFlag(const UDT& udt) {
69   return udt.value == 1 ? "A" : "AAA";
70 }
71 
GetTestTmpDirEnvVar(const char * const env_var_name)72 std::string GetTestTmpDirEnvVar(const char* const env_var_name) {
73 #ifdef _WIN32
74   char buf[MAX_PATH];
75   auto get_res = GetEnvironmentVariableA(env_var_name, buf, sizeof(buf));
76   if (get_res >= sizeof(buf) || get_res == 0) {
77     return "";
78   }
79 
80   return std::string(buf, get_res);
81 #else
82   const char* val = ::getenv(env_var_name);
83   if (val == nullptr) {
84     return "";
85   }
86 
87   return val;
88 #endif
89 }
90 
GetTestTempDir()91 const std::string& GetTestTempDir() {
92   static std::string* temp_dir_name = []() -> std::string* {
93     std::string* res = new std::string(GetTestTmpDirEnvVar("TEST_TMPDIR"));
94 
95     if (res->empty()) {
96       *res = GetTestTmpDirEnvVar("TMPDIR");
97     }
98 
99     if (res->empty()) {
100 #ifdef _WIN32
101       char temp_path_buffer[MAX_PATH];
102 
103       auto len = GetTempPathA(MAX_PATH, temp_path_buffer);
104       if (len < MAX_PATH && len != 0) {
105         std::string temp_dir_name = temp_path_buffer;
106         if (!absl::EndsWith(temp_dir_name, "\\")) {
107           temp_dir_name.push_back('\\');
108         }
109         absl::StrAppend(&temp_dir_name, "parse_test.", GetCurrentProcessId());
110         if (CreateDirectoryA(temp_dir_name.c_str(), nullptr)) {
111           *res = temp_dir_name;
112         }
113       }
114 #else
115       char temp_dir_template[] = "/tmp/parse_test.XXXXXX";
116       if (auto* unique_name = ::mkdtemp(temp_dir_template)) {
117         *res = unique_name;
118       }
119 #endif
120     }
121 
122     if (res->empty()) {
123       ABSL_INTERNAL_LOG(FATAL,
124                         "Failed to make temporary directory for data files");
125     }
126 
127 #ifdef _WIN32
128     *res += "\\";
129 #else
130     *res += "/";
131 #endif
132 
133     return res;
134   }();
135 
136   return *temp_dir_name;
137 }
138 
139 struct FlagfileData {
140   const absl::string_view file_name;
141   const absl::Span<const char* const> file_lines;
142 };
143 
144 // clang-format off
145 constexpr const char* const ff1_data[] = {
146     "# comment    ",
147     "  # comment  ",
148     "",
149     "     ",
150     "--int_flag=-1",
151     "  --string_flag=q2w2  ",
152     "  ##   ",
153     "  --double_flag=0.1",
154     "--bool_flag=Y  "
155 };
156 
157 constexpr const char* const ff2_data[] = {
158     "# Setting legacy flag",
159     "--legacy_int=1111",
160     "--legacy_bool",
161     "--nobool_flag",
162     "--legacy_str=aqsw",
163     "--int_flag=100",
164     "   ## ============="
165 };
166 // clang-format on
167 
168 // Builds flagfile flag in the flagfile_flag buffer and returns it. This
169 // function also creates a temporary flagfile based on FlagfileData input.
170 // We create a flagfile in a temporary directory with the name specified in
171 // FlagfileData and populate it with lines specifed in FlagfileData. If $0 is
172 // referenced in any of the lines in FlagfileData they are replaced with
173 // temporary directory location. This way we can test inclusion of one flagfile
174 // from another flagfile.
GetFlagfileFlag(const std::vector<FlagfileData> & ffd,std::string & flagfile_flag)175 const char* GetFlagfileFlag(const std::vector<FlagfileData>& ffd,
176                             std::string& flagfile_flag) {
177   flagfile_flag = "--flagfile=";
178   absl::string_view separator;
179   for (const auto& flagfile_data : ffd) {
180     std::string flagfile_name =
181         absl::StrCat(GetTestTempDir(), flagfile_data.file_name);
182 
183     std::ofstream flagfile_out(flagfile_name);
184     for (auto line : flagfile_data.file_lines) {
185       flagfile_out << absl::Substitute(line, GetTestTempDir()) << "\n";
186     }
187 
188     absl::StrAppend(&flagfile_flag, separator, flagfile_name);
189     separator = ",";
190   }
191 
192   return flagfile_flag.c_str();
193 }
194 
195 }  // namespace
196 
197 ABSL_FLAG(int, int_flag, 1, "");
198 ABSL_FLAG(double, double_flag, 1.1, "");
199 ABSL_FLAG(std::string, string_flag, "a", "");
200 ABSL_FLAG(bool, bool_flag, false, "");
201 ABSL_FLAG(UDT, udt_flag, -1, "");
202 ABSL_RETIRED_FLAG(int, legacy_int, 1, "");
203 ABSL_RETIRED_FLAG(bool, legacy_bool, false, "");
204 ABSL_RETIRED_FLAG(std::string, legacy_str, "l", "");
205 
206 namespace {
207 
208 namespace flags = absl::flags_internal;
209 using testing::ElementsAreArray;
210 
211 class ParseTest : public testing::Test {
212  public:
~ParseTest()213   ~ParseTest() override { flags::SetFlagsHelpMode(flags::HelpMode::kNone); }
214 
215  private:
216   absl::FlagSaver flag_saver_;
217 };
218 
219 // --------------------------------------------------------------------
220 
221 template <int N>
InvokeParse(const char * (& in_argv)[N])222 std::vector<char*> InvokeParse(const char* (&in_argv)[N]) {
223   return absl::ParseCommandLine(N, const_cast<char**>(in_argv));
224 }
225 
226 // --------------------------------------------------------------------
227 
228 template <int N>
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)229 void TestParse(const char* (&in_argv)[N], int int_flag_value,
230                double double_flag_val, absl::string_view string_flag_val,
231                bool bool_flag_val, int exp_position_args = 0) {
232   auto out_args = InvokeParse(in_argv);
233 
234   EXPECT_EQ(out_args.size(), 1 + exp_position_args);
235   EXPECT_STREQ(out_args[0], "testbin");
236 
237   EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), int_flag_value);
238   EXPECT_NEAR(absl::GetFlag(FLAGS_double_flag), double_flag_val, 0.0001);
239   EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), string_flag_val);
240   EXPECT_EQ(absl::GetFlag(FLAGS_bool_flag), bool_flag_val);
241 }
242 
243 // --------------------------------------------------------------------
244 
TEST_F(ParseTest,TestEmptyArgv)245 TEST_F(ParseTest, TestEmptyArgv) {
246   const char* in_argv[] = {"testbin"};
247 
248   auto out_args = InvokeParse(in_argv);
249 
250   EXPECT_EQ(out_args.size(), 1);
251   EXPECT_STREQ(out_args[0], "testbin");
252 }
253 
254 // --------------------------------------------------------------------
255 
TEST_F(ParseTest,TestValidIntArg)256 TEST_F(ParseTest, TestValidIntArg) {
257   const char* in_args1[] = {
258       "testbin",
259       "--int_flag=10",
260   };
261   TestParse(in_args1, 10, 1.1, "a", false);
262 
263   const char* in_args2[] = {
264       "testbin",
265       "-int_flag=020",
266   };
267   TestParse(in_args2, 20, 1.1, "a", false);
268 
269   const char* in_args3[] = {
270       "testbin",
271       "--int_flag",
272       "-30",
273   };
274   TestParse(in_args3, -30, 1.1, "a", false);
275 
276   const char* in_args4[] = {
277       "testbin",
278       "-int_flag",
279       "0x21",
280   };
281   TestParse(in_args4, 33, 1.1, "a", false);
282 }
283 
284 // --------------------------------------------------------------------
285 
TEST_F(ParseTest,TestValidDoubleArg)286 TEST_F(ParseTest, TestValidDoubleArg) {
287   const char* in_args1[] = {
288       "testbin",
289       "--double_flag=2.3",
290   };
291   TestParse(in_args1, 1, 2.3, "a", false);
292 
293   const char* in_args2[] = {
294       "testbin",
295       "--double_flag=0x1.2",
296   };
297   TestParse(in_args2, 1, 1.125, "a", false);
298 
299   const char* in_args3[] = {
300       "testbin",
301       "--double_flag",
302       "99.7",
303   };
304   TestParse(in_args3, 1, 99.7, "a", false);
305 
306   const char* in_args4[] = {
307       "testbin",
308       "--double_flag",
309       "0x20.1",
310   };
311   TestParse(in_args4, 1, 32.0625, "a", false);
312 }
313 
314 // --------------------------------------------------------------------
315 
TEST_F(ParseTest,TestValidStringArg)316 TEST_F(ParseTest, TestValidStringArg) {
317   const char* in_args1[] = {
318       "testbin",
319       "--string_flag=aqswde",
320   };
321   TestParse(in_args1, 1, 1.1, "aqswde", false);
322 
323   const char* in_args2[] = {
324       "testbin",
325       "-string_flag=a=b=c",
326   };
327   TestParse(in_args2, 1, 1.1, "a=b=c", false);
328 
329   const char* in_args3[] = {
330       "testbin",
331       "--string_flag",
332       "zaxscd",
333   };
334   TestParse(in_args3, 1, 1.1, "zaxscd", false);
335 
336   const char* in_args4[] = {
337       "testbin",
338       "-string_flag",
339       "--int_flag",
340   };
341   TestParse(in_args4, 1, 1.1, "--int_flag", false);
342 
343   const char* in_args5[] = {
344       "testbin",
345       "--string_flag",
346       "--no_a_flag=11",
347   };
348   TestParse(in_args5, 1, 1.1, "--no_a_flag=11", false);
349 }
350 
351 // --------------------------------------------------------------------
352 
TEST_F(ParseTest,TestValidBoolArg)353 TEST_F(ParseTest, TestValidBoolArg) {
354   const char* in_args1[] = {
355       "testbin",
356       "--bool_flag",
357   };
358   TestParse(in_args1, 1, 1.1, "a", true);
359 
360   const char* in_args2[] = {
361       "testbin",
362       "--nobool_flag",
363   };
364   TestParse(in_args2, 1, 1.1, "a", false);
365 
366   const char* in_args3[] = {
367       "testbin",
368       "--bool_flag=true",
369   };
370   TestParse(in_args3, 1, 1.1, "a", true);
371 
372   const char* in_args4[] = {
373       "testbin",
374       "-bool_flag=false",
375   };
376   TestParse(in_args4, 1, 1.1, "a", false);
377 }
378 
379 // --------------------------------------------------------------------
380 
TEST_F(ParseTest,TestValidUDTArg)381 TEST_F(ParseTest, TestValidUDTArg) {
382   const char* in_args1[] = {
383       "testbin",
384       "--udt_flag=A",
385   };
386   InvokeParse(in_args1);
387 
388   EXPECT_EQ(absl::GetFlag(FLAGS_udt_flag).value, 1);
389 
390   const char* in_args2[] = {"testbin", "--udt_flag", "AAA"};
391   InvokeParse(in_args2);
392 
393   EXPECT_EQ(absl::GetFlag(FLAGS_udt_flag).value, 10);
394 }
395 
396 // --------------------------------------------------------------------
397 
TEST_F(ParseTest,TestValidMultipleArg)398 TEST_F(ParseTest, TestValidMultipleArg) {
399   const char* in_args1[] = {
400       "testbin",           "--bool_flag",       "--int_flag=2",
401       "--double_flag=0.1", "--string_flag=asd",
402   };
403   TestParse(in_args1, 2, 0.1, "asd", true);
404 
405   const char* in_args2[] = {
406       "testbin", "--string_flag=", "--nobool_flag", "--int_flag",
407       "-011",    "--double_flag",  "-1e-2",
408   };
409   TestParse(in_args2, -11, -0.01, "", false);
410 
411   const char* in_args3[] = {
412       "testbin",          "--int_flag",         "-0", "--string_flag", "\"\"",
413       "--bool_flag=true", "--double_flag=1e18",
414   };
415   TestParse(in_args3, 0, 1e18, "\"\"", true);
416 }
417 
418 // --------------------------------------------------------------------
419 
TEST_F(ParseTest,TestPositionalArgs)420 TEST_F(ParseTest, TestPositionalArgs) {
421   const char* in_args1[] = {
422       "testbin",
423       "p1",
424       "p2",
425   };
426   TestParse(in_args1, 1, 1.1, "a", false, 2);
427 
428   auto out_args1 = InvokeParse(in_args1);
429 
430   EXPECT_STREQ(out_args1[1], "p1");
431   EXPECT_STREQ(out_args1[2], "p2");
432 
433   const char* in_args2[] = {
434       "testbin",
435       "--int_flag=2",
436       "p1",
437   };
438   TestParse(in_args2, 2, 1.1, "a", false, 1);
439 
440   auto out_args2 = InvokeParse(in_args2);
441 
442   EXPECT_STREQ(out_args2[1], "p1");
443 
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);
447 
448   auto out_args3 = InvokeParse(in_args3);
449 
450   EXPECT_STREQ(out_args3[1], "p1");
451   EXPECT_STREQ(out_args3[2], "p2");
452   EXPECT_STREQ(out_args3[3], "true");
453 
454   const char* in_args4[] = {
455       "testbin",
456       "--",
457       "p1",
458       "p2",
459   };
460   TestParse(in_args4, 3, 1.1, "a", true, 2);
461 
462   auto out_args4 = InvokeParse(in_args4);
463 
464   EXPECT_STREQ(out_args4[1], "p1");
465   EXPECT_STREQ(out_args4[2], "p2");
466 
467   const char* in_args5[] = {
468       "testbin", "p1", "--int_flag=4", "--", "--bool_flag", "false", "p2",
469   };
470   TestParse(in_args5, 4, 1.1, "a", true, 4);
471 
472   auto out_args5 = InvokeParse(in_args5);
473 
474   EXPECT_STREQ(out_args5[1], "p1");
475   EXPECT_STREQ(out_args5[2], "--bool_flag");
476   EXPECT_STREQ(out_args5[3], "false");
477   EXPECT_STREQ(out_args5[4], "p2");
478 }
479 
480 // --------------------------------------------------------------------
481 
482 using ParseDeathTest = ParseTest;
483 
TEST_F(ParseDeathTest,TestUndefinedArg)484 TEST_F(ParseDeathTest, TestUndefinedArg) {
485   const char* in_args1[] = {
486       "testbin",
487       "--undefined_flag",
488   };
489   EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
490                             "Unknown command line flag 'undefined_flag'");
491 
492   const char* in_args2[] = {
493       "testbin",
494       "--noprefixed_flag",
495   };
496   EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2),
497                             "Unknown command line flag 'noprefixed_flag'");
498 
499   const char* in_args3[] = {
500       "testbin",
501       "--Int_flag=1",
502   };
503   EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args3),
504                             "Unknown command line flag 'Int_flag'");
505 }
506 
507 // --------------------------------------------------------------------
508 
TEST_F(ParseDeathTest,TestInvalidBoolFlagFormat)509 TEST_F(ParseDeathTest, TestInvalidBoolFlagFormat) {
510   const char* in_args1[] = {
511       "testbin",
512       "--bool_flag=",
513   };
514   EXPECT_DEATH_IF_SUPPORTED(
515       InvokeParse(in_args1),
516       "Missing the value after assignment for the boolean flag 'bool_flag'");
517 
518   const char* in_args2[] = {
519       "testbin",
520       "--nobool_flag=true",
521   };
522   EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2),
523                "Negative form with assignment is not valid for the boolean "
524                "flag 'bool_flag'");
525 }
526 
527 // --------------------------------------------------------------------
528 
TEST_F(ParseDeathTest,TestInvalidNonBoolFlagFormat)529 TEST_F(ParseDeathTest, TestInvalidNonBoolFlagFormat) {
530   const char* in_args1[] = {
531       "testbin",
532       "--nostring_flag",
533   };
534   EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
535                "Negative form is not valid for the flag 'string_flag'");
536 
537   const char* in_args2[] = {
538       "testbin",
539       "--int_flag",
540   };
541   EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2),
542                "Missing the value for the flag 'int_flag'");
543 }
544 
545 // --------------------------------------------------------------------
546 
TEST_F(ParseDeathTest,TestInvalidUDTFlagFormat)547 TEST_F(ParseDeathTest, TestInvalidUDTFlagFormat) {
548   const char* in_args1[] = {
549       "testbin",
550       "--udt_flag=1",
551   };
552   EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
553                "Illegal value '1' specified for flag 'udt_flag'; Use values A, "
554                "AAA instead");
555 
556   const char* in_args2[] = {
557       "testbin",
558       "--udt_flag",
559       "AA",
560   };
561   EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2),
562                "Illegal value 'AA' specified for flag 'udt_flag'; Use values "
563                "A, AAA instead");
564 }
565 
566 // --------------------------------------------------------------------
567 
TEST_F(ParseTest,TestLegacyFlags)568 TEST_F(ParseTest, TestLegacyFlags) {
569   const char* in_args1[] = {
570       "testbin",
571       "--legacy_int=11",
572   };
573   TestParse(in_args1, 1, 1.1, "a", false);
574 
575   const char* in_args2[] = {
576       "testbin",
577       "--legacy_bool",
578   };
579   TestParse(in_args2, 1, 1.1, "a", false);
580 
581   const char* in_args3[] = {
582       "testbin",       "--legacy_int", "22",           "--int_flag=2",
583       "--legacy_bool", "true",         "--legacy_str", "--string_flag=qwe",
584   };
585   TestParse(in_args3, 2, 1.1, "a", false, 1);
586 }
587 
588 // --------------------------------------------------------------------
589 
TEST_F(ParseTest,TestSimpleValidFlagfile)590 TEST_F(ParseTest, TestSimpleValidFlagfile) {
591   std::string flagfile_flag;
592 
593   const char* in_args1[] = {
594       "testbin",
595       GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
596                       flagfile_flag),
597   };
598   TestParse(in_args1, -1, 0.1, "q2w2  ", true);
599 
600   const char* in_args2[] = {
601       "testbin",
602       GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}},
603                       flagfile_flag),
604   };
605   TestParse(in_args2, 100, 0.1, "q2w2  ", false);
606 }
607 
608 // --------------------------------------------------------------------
609 
TEST_F(ParseTest,TestValidMultiFlagfile)610 TEST_F(ParseTest, TestValidMultiFlagfile) {
611   std::string flagfile_flag;
612 
613   const char* in_args1[] = {
614       "testbin",
615       GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)},
616                        {"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
617                       flagfile_flag),
618   };
619   TestParse(in_args1, -1, 0.1, "q2w2  ", true);
620 }
621 
622 // --------------------------------------------------------------------
623 
TEST_F(ParseTest,TestFlagfileMixedWithRegularFlags)624 TEST_F(ParseTest, TestFlagfileMixedWithRegularFlags) {
625   std::string flagfile_flag;
626 
627   const char* in_args1[] = {
628       "testbin", "--int_flag=3",
629       GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
630                       flagfile_flag),
631       "-double_flag=0.2"};
632   TestParse(in_args1, -1, 0.2, "q2w2  ", true);
633 }
634 
635 // --------------------------------------------------------------------
636 
TEST_F(ParseTest,TestFlagfileInFlagfile)637 TEST_F(ParseTest, TestFlagfileInFlagfile) {
638   std::string flagfile_flag;
639 
640   constexpr const char* const ff3_data[] = {
641       "--flagfile=$0/parse_test.ff1",
642       "--flagfile=$0/parse_test.ff2",
643   };
644 
645   GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)},
646                    {"parse_test.ff1", absl::MakeConstSpan(ff1_data)}},
647                       flagfile_flag);
648 
649   const char* in_args1[] = {
650       "testbin",
651       GetFlagfileFlag({{"parse_test.ff3", absl::MakeConstSpan(ff3_data)}},
652                       flagfile_flag),
653   };
654   TestParse(in_args1, 100, 0.1, "q2w2  ", false);
655 }
656 
657 // --------------------------------------------------------------------
658 
TEST_F(ParseDeathTest,TestInvalidFlagfiles)659 TEST_F(ParseDeathTest, TestInvalidFlagfiles) {
660   std::string flagfile_flag;
661 
662   constexpr const char* const ff4_data[] = {
663     "--unknown_flag=10"
664   };
665 
666   const char* in_args1[] = {
667       "testbin",
668       GetFlagfileFlag({{"parse_test.ff4",
669                         absl::MakeConstSpan(ff4_data)}}, flagfile_flag),
670   };
671   EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
672                "Unknown command line flag 'unknown_flag'");
673 
674   constexpr const char* const ff5_data[] = {
675     "--int_flag 10",
676   };
677 
678   const char* in_args2[] = {
679       "testbin",
680       GetFlagfileFlag({{"parse_test.ff5",
681                         absl::MakeConstSpan(ff5_data)}}, flagfile_flag),
682   };
683   EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2),
684                "Unknown command line flag 'int_flag 10'");
685 
686   constexpr const char* const ff6_data[] = {
687       "--int_flag=10", "--", "arg1", "arg2", "arg3",
688   };
689 
690   const char* in_args3[] = {
691       "testbin",
692       GetFlagfileFlag({{"parse_test.ff6", absl::MakeConstSpan(ff6_data)}},
693                       flagfile_flag),
694   };
695   EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args3),
696                "Flagfile can't contain position arguments or --");
697 
698   const char* in_args4[] = {
699       "testbin",
700       "--flagfile=invalid_flag_file",
701   };
702   EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args4),
703                             "Can't open flagfile invalid_flag_file");
704 
705   constexpr const char* const ff7_data[] = {
706       "--int_flag=10",
707       "*bin*",
708       "--str_flag=aqsw",
709   };
710 
711   const char* in_args5[] = {
712       "testbin",
713       GetFlagfileFlag({{"parse_test.ff7", absl::MakeConstSpan(ff7_data)}},
714                       flagfile_flag),
715   };
716   EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args5),
717                "Unexpected line in the flagfile .*: \\*bin\\*");
718 }
719 
720 // --------------------------------------------------------------------
721 
TEST_F(ParseTest,TestReadingRequiredFlagsFromEnv)722 TEST_F(ParseTest, TestReadingRequiredFlagsFromEnv) {
723   const char* in_args1[] = {"testbin",
724                             "--fromenv=int_flag,bool_flag,string_flag"};
725 
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");
729 
730   TestParse(in_args1, 33, 1.1, "AQ12", true);
731 }
732 
733 // --------------------------------------------------------------------
734 
TEST_F(ParseDeathTest,TestReadingUnsetRequiredFlagsFromEnv)735 TEST_F(ParseDeathTest, TestReadingUnsetRequiredFlagsFromEnv) {
736   const char* in_args1[] = {"testbin", "--fromenv=int_flag"};
737 
738   EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
739                "FLAGS_int_flag not found in environment");
740 }
741 
742 // --------------------------------------------------------------------
743 
TEST_F(ParseDeathTest,TestRecursiveFlagsFromEnv)744 TEST_F(ParseDeathTest, TestRecursiveFlagsFromEnv) {
745   const char* in_args1[] = {"testbin", "--fromenv=tryfromenv"};
746 
747   ScopedSetEnv set_tryfromenv("FLAGS_tryfromenv", "int_flag");
748 
749   EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1),
750                             "Infinite recursion on flag tryfromenv");
751 }
752 
753 // --------------------------------------------------------------------
754 
TEST_F(ParseTest,TestReadingOptionalFlagsFromEnv)755 TEST_F(ParseTest, TestReadingOptionalFlagsFromEnv) {
756   const char* in_args1[] = {
757       "testbin", "--tryfromenv=int_flag,bool_flag,string_flag,other_flag"};
758 
759   ScopedSetEnv set_int_flag("FLAGS_int_flag", "17");
760   ScopedSetEnv set_bool_flag("FLAGS_bool_flag", "Y");
761 
762   TestParse(in_args1, 17, 1.1, "a", true);
763 }
764 
765 // --------------------------------------------------------------------
766 
TEST_F(ParseTest,TestReadingFlagsFromEnvMoxedWithRegularFlags)767 TEST_F(ParseTest, TestReadingFlagsFromEnvMoxedWithRegularFlags) {
768   const char* in_args1[] = {
769       "testbin",
770       "--bool_flag=T",
771       "--tryfromenv=int_flag,bool_flag",
772       "--int_flag=-21",
773   };
774 
775   ScopedSetEnv set_int_flag("FLAGS_int_flag", "-15");
776   ScopedSetEnv set_bool_flag("FLAGS_bool_flag", "F");
777 
778   TestParse(in_args1, -21, 1.1, "a", false);
779 }
780 
781 // --------------------------------------------------------------------
782 
TEST_F(ParseTest,TestKeepParsedArgs)783 TEST_F(ParseTest, TestKeepParsedArgs) {
784   const char* in_args1[] = {
785       "testbin",        "arg1", "--bool_flag",
786       "--int_flag=211", "arg2", "--double_flag=1.1",
787       "--string_flag",  "asd",  "--",
788       "arg3",           "arg4",
789   };
790 
791   auto out_args1 = InvokeParse(in_args1);
792 
793   EXPECT_THAT(
794       out_args1,
795       ElementsAreArray({absl::string_view("testbin"), absl::string_view("arg1"),
796                         absl::string_view("arg2"), absl::string_view("arg3"),
797                         absl::string_view("arg4")}));
798 
799   auto out_args2 = flags::ParseCommandLineImpl(
800       11, const_cast<char**>(in_args1), flags::ArgvListAction::kKeepParsedArgs,
801       flags::UsageFlagsAction::kHandleUsage,
802       flags::OnUndefinedFlag::kAbortIfUndefined);
803 
804   EXPECT_THAT(
805       out_args2,
806       ElementsAreArray({absl::string_view("testbin"),
807                         absl::string_view("--bool_flag"),
808                         absl::string_view("--int_flag=211"),
809                         absl::string_view("--double_flag=1.1"),
810                         absl::string_view("--string_flag"),
811                         absl::string_view("asd"), absl::string_view("--"),
812                         absl::string_view("arg1"), absl::string_view("arg2"),
813                         absl::string_view("arg3"), absl::string_view("arg4")}));
814 }
815 
816 // --------------------------------------------------------------------
817 
TEST_F(ParseTest,TestIgnoreUndefinedFlags)818 TEST_F(ParseTest, TestIgnoreUndefinedFlags) {
819   const char* in_args1[] = {
820       "testbin",
821       "arg1",
822       "--undef_flag=aa",
823       "--int_flag=21",
824   };
825 
826   auto out_args1 = flags::ParseCommandLineImpl(
827       4, const_cast<char**>(in_args1), flags::ArgvListAction::kRemoveParsedArgs,
828       flags::UsageFlagsAction::kHandleUsage,
829       flags::OnUndefinedFlag::kIgnoreUndefined);
830 
831   EXPECT_THAT(out_args1, ElementsAreArray({absl::string_view("testbin"),
832                                            absl::string_view("arg1")}));
833 
834   EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 21);
835 
836   const char* in_args2[] = {
837       "testbin",
838       "arg1",
839       "--undef_flag=aa",
840       "--string_flag=AA",
841   };
842 
843   auto out_args2 = flags::ParseCommandLineImpl(
844       4, const_cast<char**>(in_args2), flags::ArgvListAction::kKeepParsedArgs,
845       flags::UsageFlagsAction::kHandleUsage,
846       flags::OnUndefinedFlag::kIgnoreUndefined);
847 
848   EXPECT_THAT(
849       out_args2,
850       ElementsAreArray(
851           {absl::string_view("testbin"), absl::string_view("--undef_flag=aa"),
852            absl::string_view("--string_flag=AA"), absl::string_view("arg1")}));
853 
854   EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "AA");
855 }
856 
857 // --------------------------------------------------------------------
858 
TEST_F(ParseDeathTest,TestSimpleHelpFlagHandling)859 TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) {
860   const char* in_args1[] = {
861       "testbin",
862       "--help",
863   };
864 
865   EXPECT_EXIT(InvokeParse(in_args1), testing::ExitedWithCode(1), "");
866 
867   const char* in_args2[] = {
868       "testbin",
869       "--help",
870       "--int_flag=3",
871   };
872 
873   auto out_args2 = flags::ParseCommandLineImpl(
874       3, const_cast<char**>(in_args2), flags::ArgvListAction::kRemoveParsedArgs,
875       flags::UsageFlagsAction::kIgnoreUsage,
876       flags::OnUndefinedFlag::kAbortIfUndefined);
877 
878   EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
879   EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 3);
880 }
881 
882 // --------------------------------------------------------------------
883 
TEST_F(ParseDeathTest,TestSubstringHelpFlagHandling)884 TEST_F(ParseDeathTest, TestSubstringHelpFlagHandling) {
885   const char* in_args1[] = {
886       "testbin",
887       "--help=abcd",
888   };
889 
890   auto out_args1 = flags::ParseCommandLineImpl(
891       2, const_cast<char**>(in_args1), flags::ArgvListAction::kRemoveParsedArgs,
892       flags::UsageFlagsAction::kIgnoreUsage,
893       flags::OnUndefinedFlag::kAbortIfUndefined);
894 
895   EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kMatch);
896   EXPECT_EQ(flags::GetFlagsHelpMatchSubstr(), "abcd");
897 
898   const char* in_args2[] = {"testbin", "--help", "some_positional_arg"};
899 
900   auto out_args2 = flags::ParseCommandLineImpl(
901       3, const_cast<char**>(in_args2), flags::ArgvListAction::kRemoveParsedArgs,
902       flags::UsageFlagsAction::kIgnoreUsage,
903       flags::OnUndefinedFlag::kAbortIfUndefined);
904 
905   EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
906 }
907 
908 // --------------------------------------------------------------------
909 
TEST_F(ParseTest,WasPresentOnCommandLine)910 TEST_F(ParseTest, WasPresentOnCommandLine) {
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",
916   };
917 
918   InvokeParse(in_args1);
919 
920   EXPECT_TRUE(flags::WasPresentOnCommandLine("bool_flag"));
921   EXPECT_TRUE(flags::WasPresentOnCommandLine("int_flag"));
922   EXPECT_TRUE(flags::WasPresentOnCommandLine("double_flag"));
923   EXPECT_TRUE(flags::WasPresentOnCommandLine("string_flag"));
924   EXPECT_FALSE(flags::WasPresentOnCommandLine("some_flag"));
925   EXPECT_FALSE(flags::WasPresentOnCommandLine("another_flag"));
926 }
927 
928 // --------------------------------------------------------------------
929 
930 }  // namespace
931