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