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