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