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