parse_test.cc 26 KB

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