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