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