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