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__anon926e99df0111::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