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