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