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