command_line_test.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "common/command_line.h"
  5. #include <gmock/gmock.h>
  6. #include <gtest/gtest.h>
  7. #include "common/error_test_helpers.h"
  8. #include "llvm/Support/FormatVariadic.h"
  9. #include "testing/base/test_raw_ostream.h"
  10. namespace Carbon::CommandLine {
  11. namespace {
  12. using ::Carbon::Testing::IsError;
  13. using ::Carbon::Testing::IsSuccess;
  14. using ::Carbon::Testing::TestRawOstream;
  15. using ::testing::ElementsAre;
  16. using ::testing::Eq;
  17. using ::testing::StrEq;
  18. constexpr CommandInfo TestCommandInfo = {
  19. .name = "test",
  20. .help = "A test command.",
  21. .help_epilogue = "TODO",
  22. };
  23. enum class TestEnum : int8_t {
  24. Val1,
  25. Val2,
  26. };
  27. enum class TestSubcommand : int8_t {
  28. Sub1,
  29. Sub2,
  30. };
  31. TEST(ArgParserTest, BasicCommand) {
  32. bool flag = false;
  33. int integer_option = -1;
  34. llvm::StringRef string_option = "";
  35. auto parse = [&](llvm::ArrayRef<llvm::StringRef> args) {
  36. return Parse(args, llvm::errs(), TestCommandInfo, [&](auto& b) {
  37. b.AddFlag({.name = "flag"}, [&](auto& arg_b) { arg_b.Set(&flag); });
  38. b.AddIntegerOption({.name = "option1"},
  39. [&](auto& arg_b) { arg_b.Set(&integer_option); });
  40. b.AddStringOption({.name = "option2"},
  41. [&](auto& arg_b) { arg_b.Set(&string_option); });
  42. b.Do([] {});
  43. });
  44. };
  45. EXPECT_THAT(parse({"--flag", "--option2=value", "--option1=42"}),
  46. IsSuccess(Eq(ParseResult::Success)));
  47. EXPECT_TRUE(flag);
  48. EXPECT_THAT(integer_option, Eq(42));
  49. EXPECT_THAT(string_option, StrEq("value"));
  50. }
  51. TEST(ArgParserTest, BooleanFlags) {
  52. bool flag = false;
  53. auto parse = [&](llvm::ArrayRef<llvm::StringRef> args, llvm::raw_ostream& s) {
  54. return Parse(args, s, TestCommandInfo, [&](auto& b) {
  55. b.AddFlag({.name = "flag"}, [&](auto& arg_b) { arg_b.Set(&flag); });
  56. b.Do([] {});
  57. });
  58. };
  59. EXPECT_THAT(parse({"--no-flag"}, llvm::errs()),
  60. IsSuccess(Eq(ParseResult::Success)));
  61. EXPECT_FALSE(flag);
  62. EXPECT_THAT(parse({"--flag", "--no-flag"}, llvm::errs()),
  63. IsSuccess(Eq(ParseResult::Success)));
  64. EXPECT_FALSE(flag);
  65. EXPECT_THAT(parse({"--no-flag", "--flag"}, llvm::errs()),
  66. IsSuccess(Eq(ParseResult::Success)));
  67. EXPECT_TRUE(flag);
  68. EXPECT_THAT(parse({"--no-flag", "--flag", "--flag=false"}, llvm::errs()),
  69. IsSuccess(Eq(ParseResult::Success)));
  70. EXPECT_FALSE(flag);
  71. EXPECT_THAT(parse({"--no-flag", "--flag=true"}, llvm::errs()),
  72. IsSuccess(Eq(ParseResult::Success)));
  73. EXPECT_TRUE(flag);
  74. EXPECT_THAT(parse({"--no-flag", "--flag=true", "--no-flag"}, llvm::errs()),
  75. IsSuccess(Eq(ParseResult::Success)));
  76. EXPECT_FALSE(flag);
  77. TestRawOstream os;
  78. EXPECT_THAT(
  79. parse({"--no-flag=true"}, os),
  80. IsError(StrEq("cannot specify a value when using a flag name prefixed "
  81. "with `no-` -- that prefix implies a value of `false`")));
  82. EXPECT_THAT(
  83. parse({"--no-flag=false"}, os),
  84. IsError(StrEq("cannot specify a value when using a flag name prefixed "
  85. "with `no-` -- that prefix implies a value of `false`")));
  86. }
  87. TEST(ArgParserTest, ArgDefaults) {
  88. bool flag = false;
  89. int integer_option = -1;
  90. llvm::StringRef string_option = "";
  91. auto parse = [&](llvm::ArrayRef<llvm::StringRef> args, llvm::raw_ostream& s) {
  92. return Parse(args, s, TestCommandInfo, [&](auto& b) {
  93. b.AddFlag({.name = "flag"}, [&](auto& arg_b) {
  94. arg_b.Default(true);
  95. arg_b.Set(&flag);
  96. });
  97. b.AddIntegerOption({.name = "option1"}, [&](auto& arg_b) {
  98. arg_b.Default(7);
  99. arg_b.Set(&integer_option);
  100. });
  101. b.AddStringOption({.name = "option2"}, [&](auto& arg_b) {
  102. arg_b.Default("default");
  103. arg_b.Set(&string_option);
  104. });
  105. b.Do([] {});
  106. });
  107. };
  108. EXPECT_THAT(parse({}, llvm::errs()), IsSuccess(Eq(ParseResult::Success)));
  109. EXPECT_TRUE(flag);
  110. EXPECT_THAT(integer_option, Eq(7));
  111. EXPECT_THAT(string_option, StrEq("default"));
  112. EXPECT_THAT(parse({"--option1", "--option2"}, llvm::errs()),
  113. IsSuccess(Eq(ParseResult::Success)));
  114. EXPECT_TRUE(flag);
  115. EXPECT_THAT(integer_option, Eq(7));
  116. EXPECT_THAT(string_option, StrEq("default"));
  117. EXPECT_THAT(parse({"--option1=42", "--option2=explicit"}, llvm::errs()),
  118. IsSuccess(Eq(ParseResult::Success)));
  119. EXPECT_TRUE(flag);
  120. EXPECT_THAT(integer_option, Eq(42));
  121. EXPECT_THAT(string_option, StrEq("explicit"));
  122. EXPECT_THAT(
  123. parse({"--option1=42", "--option2=explicit", "--option1", "--option2"},
  124. llvm::errs()),
  125. IsSuccess(Eq(ParseResult::Success)));
  126. EXPECT_TRUE(flag);
  127. EXPECT_THAT(integer_option, Eq(7));
  128. EXPECT_THAT(string_option, StrEq("default"));
  129. }
  130. TEST(ArgParserTest, ShortArgs) {
  131. bool flag = false;
  132. bool example = false;
  133. int integer_option = -1;
  134. auto parse = [&](llvm::ArrayRef<llvm::StringRef> args, llvm::raw_ostream& s) {
  135. return Parse(args, s, TestCommandInfo, [&](auto& b) {
  136. b.AddFlag({.name = "flag", .short_name = "f"},
  137. [&](auto& arg_b) { arg_b.Set(&flag); });
  138. b.AddFlag({.name = "example", .short_name = "x"},
  139. [&](auto& arg_b) { arg_b.Set(&example); });
  140. b.AddIntegerOption({.name = "option1", .short_name = "o"},
  141. [&](auto& arg_b) {
  142. arg_b.Default(123);
  143. arg_b.Set(&integer_option);
  144. });
  145. b.AddIntegerOption({.name = "option2", .short_name = "z"},
  146. [&](auto& arg_b) { arg_b.Set(&integer_option); });
  147. b.Do([] {});
  148. });
  149. };
  150. EXPECT_THAT(parse({"-f", "-o=42", "-x"}, llvm::errs()),
  151. IsSuccess(Eq(ParseResult::Success)));
  152. EXPECT_TRUE(flag);
  153. EXPECT_TRUE(example);
  154. EXPECT_THAT(integer_option, Eq(42));
  155. flag = false;
  156. example = false;
  157. EXPECT_THAT(parse({"--option1=13", "-xfo"}, llvm::errs()),
  158. IsSuccess(Eq(ParseResult::Success)));
  159. EXPECT_TRUE(flag);
  160. EXPECT_TRUE(example);
  161. EXPECT_THAT(integer_option, Eq(123));
  162. TestRawOstream os;
  163. EXPECT_THAT(
  164. parse({"-xzf"}, os),
  165. IsError(StrEq("option `-z` (short for `--option2`) requires a value to "
  166. "be provided and none was")));
  167. EXPECT_THAT(
  168. parse({"--option2"}, os),
  169. IsError(StrEq(
  170. "option `--option2` requires a value to be provided and none was")));
  171. EXPECT_THAT(
  172. parse({"-xz=123"}, os),
  173. IsError(StrEq("cannot provide a value to the group of multiple short "
  174. "options `-xz=...`; values must be provided to a single "
  175. "option, using either the short or long spelling")));
  176. }
  177. TEST(ArgParserTest, PositionalArgs) {
  178. bool flag = false;
  179. llvm::StringRef string_option = "";
  180. llvm::StringRef source_string;
  181. llvm::StringRef dest_string;
  182. auto parse = [&](llvm::ArrayRef<llvm::StringRef> args, llvm::raw_ostream& s) {
  183. return Parse(args, s, TestCommandInfo, [&](auto& b) {
  184. b.AddFlag({.name = "flag"}, [&](auto& arg_b) { arg_b.Set(&flag); });
  185. b.AddStringOption({.name = "option"},
  186. [&](auto& arg_b) { arg_b.Set(&string_option); });
  187. b.AddStringPositionalArg({.name = "source"}, [&](auto& arg_b) {
  188. arg_b.Set(&source_string);
  189. arg_b.Required(true);
  190. });
  191. b.AddStringPositionalArg({.name = "dest"}, [&](auto& arg_b) {
  192. arg_b.Set(&dest_string);
  193. arg_b.Required(true);
  194. });
  195. b.Do([] {});
  196. });
  197. };
  198. TestRawOstream os;
  199. EXPECT_THAT(parse({"--flag", "--option=x"}, os),
  200. IsError(StrEq(
  201. "not all required positional arguments were provided; first "
  202. "missing and required positional argument: `source`")));
  203. EXPECT_THAT(parse({"src", "--flag", "--option=value", "dst"}, llvm::errs()),
  204. IsSuccess(Eq(ParseResult::Success)));
  205. EXPECT_TRUE(flag);
  206. EXPECT_THAT(string_option, StrEq("value"));
  207. EXPECT_THAT(source_string, StrEq("src"));
  208. EXPECT_THAT(dest_string, StrEq("dst"));
  209. EXPECT_THAT(parse({"src2", "--", "dst2"}, llvm::errs()),
  210. IsSuccess(Eq(ParseResult::Success)));
  211. EXPECT_THAT(source_string, StrEq("src2"));
  212. EXPECT_THAT(dest_string, StrEq("dst2"));
  213. EXPECT_THAT(parse({"-", "--", "-"}, llvm::errs()),
  214. IsSuccess(Eq(ParseResult::Success)));
  215. EXPECT_THAT(source_string, StrEq("-"));
  216. EXPECT_THAT(dest_string, StrEq("-"));
  217. }
  218. TEST(ArgParserTest, PositionalAppendArgs) {
  219. bool flag = false;
  220. llvm::StringRef string_option = "";
  221. llvm::SmallVector<llvm::StringRef> source_strings;
  222. llvm::SmallVector<llvm::StringRef> dest_strings;
  223. auto parse = [&](llvm::ArrayRef<llvm::StringRef> args, llvm::raw_ostream& s) {
  224. return Parse(args, s, TestCommandInfo, [&](auto& b) {
  225. b.AddFlag({.name = "flag"}, [&](auto& arg_b) { arg_b.Set(&flag); });
  226. b.AddStringOption({.name = "option"},
  227. [&](auto& arg_b) { arg_b.Set(&string_option); });
  228. b.AddStringPositionalArg({.name = "sources"}, [&](auto& arg_b) {
  229. arg_b.Append(&source_strings);
  230. arg_b.Required(true);
  231. });
  232. b.AddStringPositionalArg({.name = "dest"}, [&](auto& arg_b) {
  233. arg_b.Append(&dest_strings);
  234. arg_b.Required(true);
  235. });
  236. b.Do([] {});
  237. });
  238. };
  239. TestRawOstream os;
  240. EXPECT_THAT(parse({"--flag", "--option=x"}, os),
  241. IsError(StrEq(
  242. "not all required positional arguments were provided; first "
  243. "missing and required positional argument: `sources`")));
  244. EXPECT_THAT(
  245. parse({"src1", "--flag", "src2", "--option=value", "--", "--dst--"},
  246. llvm::errs()),
  247. IsSuccess(Eq(ParseResult::Success)));
  248. EXPECT_TRUE(flag);
  249. EXPECT_THAT(string_option, StrEq("value"));
  250. EXPECT_THAT(source_strings, ElementsAre(StrEq("src1"), StrEq("src2")));
  251. EXPECT_THAT(dest_strings, ElementsAre(StrEq("--dst--")));
  252. source_strings.clear();
  253. dest_strings.clear();
  254. EXPECT_THAT(
  255. parse({"--", "--src1--", "--src2--", "--", "dst1", "dst2"}, llvm::errs()),
  256. IsSuccess(Eq(ParseResult::Success)));
  257. EXPECT_THAT(source_strings,
  258. ElementsAre(StrEq("--src1--"), StrEq("--src2--")));
  259. EXPECT_THAT(dest_strings, ElementsAre(StrEq("dst1"), StrEq("dst2")));
  260. source_strings.clear();
  261. dest_strings.clear();
  262. EXPECT_THAT(parse({"--", "--", "dst1", "dst2"}, llvm::errs()),
  263. IsSuccess(Eq(ParseResult::Success)));
  264. EXPECT_THAT(source_strings, ElementsAre());
  265. EXPECT_THAT(dest_strings, ElementsAre(StrEq("dst1"), StrEq("dst2")));
  266. }
  267. TEST(ArgParserTest, BasicSubcommands) {
  268. bool flag = false;
  269. llvm::StringRef option1 = "";
  270. llvm::StringRef option2 = "";
  271. TestSubcommand subcommand;
  272. bool subsub_command = false;
  273. auto parse = [&](llvm::ArrayRef<llvm::StringRef> args, llvm::raw_ostream& s) {
  274. return Parse(args, s, TestCommandInfo, [&](auto& b) {
  275. b.AddSubcommand({.name = "sub1"}, [&](auto& sub_b) {
  276. sub_b.AddFlag({.name = "flag"}, [&](auto& arg_b) { arg_b.Set(&flag); });
  277. sub_b.AddStringOption({.name = "option"},
  278. [&](auto& arg_b) { arg_b.Set(&option1); });
  279. sub_b.Do([&] { subcommand = TestSubcommand::Sub1; });
  280. });
  281. b.AddSubcommand({.name = "sub2"}, [&](auto& sub_b) {
  282. sub_b.AddStringOption({.name = "option"},
  283. [&](auto& arg_b) { arg_b.Set(&option1); });
  284. sub_b.AddSubcommand({.name = "subsub"}, [&](auto& subsub_b) {
  285. subsub_b.AddStringOption({.name = "option"},
  286. [&](auto& arg_b) { arg_b.Set(&option2); });
  287. subsub_b.Do([&] { subsub_command = true; });
  288. });
  289. sub_b.Do([&] { subcommand = TestSubcommand::Sub2; });
  290. });
  291. b.RequiresSubcommand();
  292. });
  293. };
  294. TestRawOstream os;
  295. EXPECT_THAT(parse({}, os),
  296. IsError(StrEq("no subcommand specified; available subcommands: "
  297. "`sub1`, `sub2`, or `help`")));
  298. EXPECT_THAT(parse({"--flag"}, os), IsError(StrEq("unknown option `--flag`")));
  299. EXPECT_THAT(parse({"sub3"}, os),
  300. IsError(StrEq("invalid subcommand `sub3`; available subcommands: "
  301. "`sub1`, `sub2`, or `help`")));
  302. EXPECT_THAT(parse({"--flag", "sub1"}, os),
  303. IsError(StrEq("unknown option `--flag`")));
  304. EXPECT_THAT(parse({"sub1", "--flag"}, llvm::errs()),
  305. IsSuccess(Eq(ParseResult::Success)));
  306. EXPECT_THAT(subcommand, Eq(TestSubcommand::Sub1));
  307. EXPECT_TRUE(flag);
  308. EXPECT_THAT(parse({"sub2", "--option=value"}, llvm::errs()),
  309. IsSuccess(Eq(ParseResult::Success)));
  310. EXPECT_THAT(subcommand, Eq(TestSubcommand::Sub2));
  311. EXPECT_THAT(option1, StrEq("value"));
  312. EXPECT_THAT(parse({"sub2", "--option=abc", "subsub42", "--option=xyz"}, os),
  313. IsError(StrEq("invalid subcommand `subsub42`; available "
  314. "subcommands: `subsub` or `help`")));
  315. EXPECT_THAT(
  316. parse({"sub2", "--option=abc", "subsub", "--option=xyz"}, llvm::errs()),
  317. IsSuccess(Eq(ParseResult::Success)));
  318. EXPECT_TRUE(subsub_command);
  319. EXPECT_THAT(option1, StrEq("abc"));
  320. EXPECT_THAT(option2, StrEq("xyz"));
  321. }
  322. TEST(ArgParserTest, Appending) {
  323. llvm::SmallVector<int> integers;
  324. llvm::SmallVector<llvm::StringRef> strings;
  325. auto parse = [&](llvm::ArrayRef<llvm::StringRef> args, llvm::raw_ostream& s) {
  326. return Parse(args, s, TestCommandInfo, [&](auto& b) {
  327. b.AddIntegerOption({.name = "int"},
  328. [&](auto& arg_b) { arg_b.Append(&integers); });
  329. b.AddStringOption({.name = "str"},
  330. [&](auto& arg_b) { arg_b.Append(&strings); });
  331. b.Do([] {});
  332. });
  333. };
  334. EXPECT_THAT(parse({}, llvm::errs()), IsSuccess(Eq(ParseResult::Success)));
  335. EXPECT_THAT(integers, ElementsAre());
  336. EXPECT_THAT(strings, ElementsAre());
  337. EXPECT_THAT(parse({"--int=1", "--int=2", "--int=3"}, llvm::errs()),
  338. IsSuccess(Eq(ParseResult::Success)));
  339. EXPECT_THAT(integers, ElementsAre(Eq(1), Eq(2), Eq(3)));
  340. EXPECT_THAT(strings, ElementsAre());
  341. EXPECT_THAT(parse({"--str=a", "--str=b", "--str=c"}, llvm::errs()),
  342. IsSuccess(Eq(ParseResult::Success)));
  343. EXPECT_THAT(integers, ElementsAre(Eq(1), Eq(2), Eq(3)));
  344. EXPECT_THAT(strings, ElementsAre(StrEq("a"), StrEq("b"), StrEq("c")));
  345. EXPECT_THAT(parse({"--str=d", "--int=4", "--str=e"}, llvm::errs()),
  346. IsSuccess(Eq(ParseResult::Success)));
  347. EXPECT_THAT(integers, ElementsAre(Eq(1), Eq(2), Eq(3), Eq(4)));
  348. EXPECT_THAT(strings, ElementsAre(StrEq("a"), StrEq("b"), StrEq("c"),
  349. StrEq("d"), StrEq("e")));
  350. }
  351. TEST(ArgParserTest, PositionalAppending) {
  352. llvm::SmallVector<llvm::StringRef> option_strings;
  353. llvm::SmallVector<llvm::StringRef> strings1;
  354. llvm::SmallVector<llvm::StringRef> strings2;
  355. llvm::SmallVector<llvm::StringRef> strings3;
  356. auto parse = [&](llvm::ArrayRef<llvm::StringRef> args, llvm::raw_ostream& s) {
  357. return Parse(args, s, TestCommandInfo, [&](auto& b) {
  358. b.AddStringOption({.name = "opt"},
  359. [&](auto& arg_b) { arg_b.Append(&option_strings); });
  360. b.AddStringPositionalArg({.name = "s1"},
  361. [&](auto& arg_b) { arg_b.Append(&strings1); });
  362. b.AddStringPositionalArg({.name = "s2"},
  363. [&](auto& arg_b) { arg_b.Append(&strings2); });
  364. b.AddStringPositionalArg({.name = "s3"},
  365. [&](auto& arg_b) { arg_b.Append(&strings3); });
  366. b.Do([] {});
  367. });
  368. };
  369. EXPECT_THAT(parse({"a", "--opt=x", "b", "--opt=y", "--", "c", "--opt=z", "d",
  370. "--", "e", "f"},
  371. llvm::errs()),
  372. IsSuccess(Eq(ParseResult::Success)));
  373. EXPECT_THAT(option_strings, ElementsAre(StrEq("x"), StrEq("y")));
  374. EXPECT_THAT(strings1, ElementsAre(StrEq("a"), StrEq("b")));
  375. EXPECT_THAT(strings2, ElementsAre(StrEq("c"), StrEq("--opt=z"), StrEq("d")));
  376. EXPECT_THAT(strings3, ElementsAre(StrEq("e"), StrEq("f")));
  377. }
  378. static auto ParseOneOfOption(llvm::ArrayRef<llvm::StringRef> args,
  379. llvm::raw_ostream& s,
  380. llvm::function_ref<void(OneOfArgBuilder&)> build)
  381. -> ErrorOr<ParseResult> {
  382. return Parse(args, s, TestCommandInfo, [&](auto& b) {
  383. b.AddOneOfOption({.name = "option"}, build);
  384. b.Do([] {});
  385. });
  386. }
  387. TEST(ArgParserTest, OneOfOption) {
  388. int value = 0;
  389. auto parse = [&](llvm::ArrayRef<llvm::StringRef> args, llvm::raw_ostream& s) {
  390. return ParseOneOfOption(args, s, [&](auto& arg_b) {
  391. arg_b.SetOneOf(
  392. {
  393. arg_b.OneOfValue("x", 1),
  394. arg_b.OneOfValue("y", 2),
  395. arg_b.OneOfValue("z", 3),
  396. },
  397. &value);
  398. });
  399. };
  400. EXPECT_THAT(parse({"--option=x"}, llvm::errs()),
  401. IsSuccess(Eq(ParseResult::Success)));
  402. EXPECT_THAT(value, Eq(1));
  403. EXPECT_THAT(parse({"--option=y"}, llvm::errs()),
  404. IsSuccess(Eq(ParseResult::Success)));
  405. EXPECT_THAT(value, Eq(2));
  406. EXPECT_THAT(parse({"--option=z"}, llvm::errs()),
  407. IsSuccess(Eq(ParseResult::Success)));
  408. EXPECT_THAT(value, Eq(3));
  409. TestRawOstream os;
  410. EXPECT_THAT(
  411. parse({"--option"}, os),
  412. IsError(StrEq(
  413. "option `--option` requires a value to be provided and none was")));
  414. constexpr const char* ErrorStr =
  415. "option `--option={0}` has an invalid value `{0}`; valid values are: "
  416. "`x`, `y`, or `z`";
  417. EXPECT_THAT(parse({"--option=a"}, os),
  418. IsError(StrEq(llvm::formatv(ErrorStr, "a"))));
  419. EXPECT_THAT(parse({"--option="}, os),
  420. IsError(StrEq(llvm::formatv(ErrorStr, ""))));
  421. EXPECT_THAT(parse({"--option=xx"}, os),
  422. IsError(StrEq(llvm::formatv(ErrorStr, "xx"))));
  423. EXPECT_THAT(parse({"--option=\xFF"}, os),
  424. IsError(StrEq(llvm::formatv(ErrorStr, "\\FF"))));
  425. EXPECT_THAT(parse({llvm::StringRef("--option=\0", 10)}, os),
  426. IsError(StrEq(llvm::formatv(ErrorStr, "\\00"))));
  427. }
  428. TEST(ArgParserTest, OneOfOptionWithTwoOptions) {
  429. int value = 0;
  430. TestRawOstream os;
  431. EXPECT_THAT(ParseOneOfOption({"--option=z"}, os,
  432. [&](auto& arg_b) {
  433. arg_b.SetOneOf(
  434. {
  435. arg_b.OneOfValue("x", 1),
  436. arg_b.OneOfValue("y", 2),
  437. },
  438. &value);
  439. }),
  440. IsError(StrEq("option `--option=z` has an invalid value `z`; "
  441. "valid values are: `x` or `y`")));
  442. }
  443. TEST(ArgParserTest, OneOfOptionWithOneOption) {
  444. int value = 0;
  445. TestRawOstream os;
  446. EXPECT_THAT(ParseOneOfOption({"--option=z"}, os,
  447. [&](auto& arg_b) {
  448. arg_b.SetOneOf(
  449. {
  450. arg_b.OneOfValue("x", 1),
  451. },
  452. &value);
  453. }),
  454. IsError(StrEq("option `--option=z` has an invalid value `z`; "
  455. "valid values are: `x`")));
  456. }
  457. TEST(ArgParserTest, OneOfOptionAppending) {
  458. llvm::SmallVector<int> values;
  459. auto parse = [&](llvm::ArrayRef<llvm::StringRef> args, llvm::raw_ostream& s) {
  460. return ParseOneOfOption(args, s, [&](auto& arg_b) {
  461. arg_b.AppendOneOf(
  462. {
  463. arg_b.OneOfValue("x", 1),
  464. arg_b.OneOfValue("y", 2),
  465. arg_b.OneOfValue("z", 3),
  466. },
  467. &values);
  468. });
  469. };
  470. EXPECT_THAT(parse({}, llvm::errs()), IsSuccess(Eq(ParseResult::Success)));
  471. EXPECT_THAT(values, ElementsAre());
  472. EXPECT_THAT(parse({"--option=x", "--option=y", "--option=z"}, llvm::errs()),
  473. IsSuccess(Eq(ParseResult::Success)));
  474. EXPECT_THAT(values, ElementsAre(Eq(1), Eq(2), Eq(3)));
  475. EXPECT_THAT(parse({"--option=y", "--option=y", "--option=x"}, llvm::errs()),
  476. IsSuccess(Eq(ParseResult::Success)));
  477. EXPECT_THAT(values, ElementsAre(Eq(1), Eq(2), Eq(3), Eq(2), Eq(2), Eq(1)));
  478. }
  479. TEST(ArgParserTest, RequiredArgs) {
  480. int integer_option;
  481. llvm::StringRef string_option;
  482. TestSubcommand subcommand;
  483. int integer_sub_option;
  484. llvm::StringRef string_sub_option;
  485. TestEnum enum_sub_option;
  486. auto parse = [&](llvm::ArrayRef<llvm::StringRef> args, llvm::raw_ostream& s) {
  487. return Parse(args, s, TestCommandInfo, [&](auto& b) {
  488. b.AddIntegerOption({.name = "opt1"}, [&](auto& arg_b) {
  489. arg_b.Required(true);
  490. arg_b.Set(&integer_option);
  491. });
  492. b.AddStringOption({.name = "opt2"}, [&](auto& arg_b) {
  493. arg_b.Required(true);
  494. arg_b.Set(&string_option);
  495. });
  496. b.AddSubcommand({.name = "sub1"}, [&](auto& sub_b) {
  497. sub_b.AddIntegerOption({.name = "sub-opt1"}, [&](auto& arg_b) {
  498. arg_b.Required(true);
  499. arg_b.Set(&integer_sub_option);
  500. });
  501. sub_b.AddStringOption({.name = "sub-opt2"}, [&](auto& arg_b) {
  502. arg_b.Required(true);
  503. arg_b.Set(&string_sub_option);
  504. });
  505. sub_b.Do([&] { subcommand = TestSubcommand::Sub1; });
  506. });
  507. b.AddSubcommand({.name = "sub2"}, [&](auto& sub_b) {
  508. sub_b.AddIntegerOption({.name = "sub-opt1"}, [&](auto& arg_b) {
  509. arg_b.Required(true);
  510. arg_b.Set(&integer_sub_option);
  511. });
  512. sub_b.AddStringOption({.name = "sub-opt2"}, [&](auto& arg_b) {
  513. arg_b.Required(true);
  514. arg_b.Set(&string_sub_option);
  515. });
  516. sub_b.AddOneOfOption({.name = "sub-opt3"}, [&](auto& arg_b) {
  517. arg_b.Required(true);
  518. arg_b.SetOneOf(
  519. {
  520. arg_b.OneOfValue("a", TestEnum::Val1),
  521. arg_b.OneOfValue("b", TestEnum::Val2),
  522. },
  523. &enum_sub_option);
  524. });
  525. sub_b.Do([&] { subcommand = TestSubcommand::Sub2; });
  526. });
  527. b.Do([] {});
  528. });
  529. };
  530. TestRawOstream os;
  531. EXPECT_THAT(parse({}, os),
  532. IsError(StrEq("required options not provided: --opt1, --opt2")));
  533. EXPECT_THAT(parse({"--opt2=xyz"}, os),
  534. IsError(StrEq("required options not provided: --opt1")));
  535. EXPECT_THAT(parse({"--opt2=xyz", "--opt1=42"}, llvm::errs()),
  536. IsSuccess(Eq(ParseResult::Success)));
  537. EXPECT_THAT(integer_option, Eq(42));
  538. EXPECT_THAT(string_option, StrEq("xyz"));
  539. EXPECT_THAT(parse({"--opt2=xyz", "--opt1=42", "sub2"}, os),
  540. IsError(StrEq("required options not provided: --sub-opt1, "
  541. "--sub-opt2, --sub-opt3")));
  542. EXPECT_THAT(
  543. parse({"--opt2=xyz", "--opt1=42", "sub2", "--sub-opt3=b"}, os),
  544. IsError(StrEq("required options not provided: --sub-opt1, --sub-opt2")));
  545. EXPECT_THAT(parse({"--opt2=xyz", "--opt1=42", "sub2", "--sub-opt3=b",
  546. "--sub-opt1=13"},
  547. os),
  548. IsError(StrEq("required options not provided: --sub-opt2")));
  549. EXPECT_THAT(parse({"--opt2=xyz", "--opt1=42", "sub2", "--sub-opt3=b",
  550. "--sub-opt1=13", "--sub-opt2=abc"},
  551. llvm::errs()),
  552. IsSuccess(Eq(ParseResult::Success)));
  553. EXPECT_THAT(integer_option, Eq(42));
  554. EXPECT_THAT(string_option, StrEq("xyz"));
  555. EXPECT_THAT(subcommand, Eq(TestSubcommand::Sub2));
  556. EXPECT_THAT(integer_sub_option, Eq(13));
  557. EXPECT_THAT(string_sub_option, StrEq("abc"));
  558. EXPECT_THAT(enum_sub_option, Eq(TestEnum::Val2));
  559. }
  560. TEST(ArgParserTest, HelpAndVersion) {
  561. bool flag = false;
  562. int storage = -1;
  563. llvm::StringRef string = "";
  564. auto parse = [&](llvm::ArrayRef<llvm::StringRef> args, llvm::raw_ostream& s) {
  565. return Parse( // Force line break.
  566. args, s,
  567. {
  568. .name = "test",
  569. .version = "Test Command -- version 1.2.3",
  570. .build_info = R"""(
  571. Build timestamp: )""" __TIMESTAMP__ R"""(
  572. Build config: test-config-info
  573. )""",
  574. .help = R"""(
  575. A test command.
  576. Lots more information about the test command.
  577. )""",
  578. .help_epilogue = R"""(
  579. Closing remarks.
  580. )""",
  581. },
  582. [&](auto& b) {
  583. b.AddFlag(
  584. {
  585. .name = "flag",
  586. .short_name = "f",
  587. .help = R"""(
  588. A boolean flag.
  589. )""",
  590. },
  591. [&](auto& arg_b) { arg_b.Set(&flag); });
  592. b.AddFlag(
  593. {
  594. .name = "hidden_flag",
  595. .help = R"""(
  596. A *hidden* boolean flag.
  597. )""",
  598. },
  599. [&](auto& arg_b) {
  600. arg_b.HelpHidden(true);
  601. arg_b.Set(&flag);
  602. });
  603. b.AddSubcommand(
  604. {
  605. .name = "edit",
  606. .help = R"""(
  607. Edit the widget.
  608. This will take the provided widgets and edit them.
  609. )""",
  610. .help_epilogue = R"""(
  611. That's all.
  612. )""",
  613. },
  614. [&](auto& sub_b) {
  615. sub_b.AddIntegerOption(
  616. {
  617. .name = "option",
  618. .short_name = "o",
  619. .help = R"""(
  620. An integer option.
  621. )""",
  622. },
  623. [&](auto& arg_b) { arg_b.Set(&storage); });
  624. sub_b.Do([] {});
  625. });
  626. b.AddSubcommand(
  627. {
  628. .name = "run",
  629. .help = R"""(
  630. Run wombats across the screen.
  631. This will cause several wombats to run across your screen.
  632. )""",
  633. .help_epilogue = R"""(
  634. Or it won't, who knows.
  635. )""",
  636. },
  637. [&](auto& sub_b) {
  638. sub_b.AddStringOption(
  639. {
  640. .name = "option",
  641. .short_name = "o",
  642. .help = R"""(
  643. A string option.
  644. )""",
  645. },
  646. [&](auto& arg_b) { arg_b.Set(&string); });
  647. sub_b.AddOneOfOption(
  648. {
  649. .name = "one-of-option",
  650. .help = R"""(
  651. A one-of option.
  652. )""",
  653. },
  654. [&](auto& arg_b) {
  655. arg_b.SetOneOf(
  656. {
  657. arg_b.OneOfValue("x", 1),
  658. arg_b.OneOfValue("y", 2),
  659. arg_b.OneOfValue("z", 3),
  660. },
  661. &storage);
  662. });
  663. sub_b.Do([] {});
  664. });
  665. b.AddSubcommand(
  666. {
  667. .name = "hidden",
  668. .help = R"""(
  669. A hidden subcommand.
  670. )""",
  671. },
  672. [&](auto& sub_b) {
  673. sub_b.HelpHidden(true);
  674. sub_b.Do([] {});
  675. });
  676. b.RequiresSubcommand();
  677. });
  678. };
  679. TestRawOstream os;
  680. EXPECT_THAT(parse({"--flag", "--help"}, os),
  681. IsSuccess(Eq(ParseResult::MetaSuccess)));
  682. std::string help_flag_output = os.TakeStr();
  683. EXPECT_THAT(help_flag_output, StrEq(llvm::StringRef(R"""(
  684. Test Command -- version 1.2.3
  685. A test command.
  686. Lots more information about the test command.
  687. Build info:
  688. Build timestamp: )""" __TIMESTAMP__ R"""(
  689. Build config: test-config-info
  690. Usage:
  691. test [-f] edit [--option=...]
  692. test [-f] run [OPTIONS]
  693. Subcommands:
  694. edit
  695. Edit the widget.
  696. This will take the provided widgets and edit them.
  697. run
  698. Run wombats across the screen.
  699. This will cause several wombats to run across your screen.
  700. help
  701. Prints help information for the command, including a description, command line usage, and details of each subcommand and option that can be provided.
  702. version
  703. Prints the version of this command.
  704. Command options:
  705. -f, --flag
  706. A boolean flag.
  707. Closing remarks.
  708. )""")
  709. .ltrim('\n')));
  710. EXPECT_THAT(parse({"help"}, os), IsSuccess(Eq(ParseResult::MetaSuccess)));
  711. EXPECT_THAT(os.TakeStr(), StrEq(help_flag_output));
  712. EXPECT_THAT(parse({"--version"}, os),
  713. IsSuccess(Eq(ParseResult::MetaSuccess)));
  714. std::string version_flag_output = os.TakeStr();
  715. EXPECT_THAT(version_flag_output, StrEq(llvm::StringRef(R"""(
  716. Test Command -- version 1.2.3
  717. Build timestamp: )""" __TIMESTAMP__ R"""(
  718. Build config: test-config-info
  719. )""")
  720. .ltrim('\n')));
  721. EXPECT_THAT(parse({"version"}, os), IsSuccess(Eq(ParseResult::MetaSuccess)));
  722. EXPECT_THAT(os.TakeStr(), StrEq(version_flag_output));
  723. EXPECT_THAT(parse({"--flag", "edit", "--option=42", "--help"}, os),
  724. IsSuccess(Eq(ParseResult::MetaSuccess)));
  725. std::string edit_help_output = os.TakeStr();
  726. EXPECT_THAT(edit_help_output, StrEq(llvm::StringRef(R"""(
  727. Edit the widget.
  728. This will take the provided widgets and edit them.
  729. Subcommand `edit` usage:
  730. test [-f] edit [--option=...]
  731. Subcommand `edit` options:
  732. -o, --option=...
  733. An integer option.
  734. --help[=(full|short)]
  735. Prints help information for the subcommand, including a description, command line usage, and details of each option that can be provided.
  736. Possible values:
  737. - full (default)
  738. - short
  739. That's all.
  740. )""")
  741. .ltrim('\n')));
  742. EXPECT_THAT(parse({"help", "edit"}, os),
  743. IsSuccess(Eq(ParseResult::MetaSuccess)));
  744. EXPECT_THAT(os.TakeStr(), StrEq(edit_help_output));
  745. EXPECT_THAT(parse({"--flag", "run", "--option=abc", "--help"}, os),
  746. IsSuccess(Eq(ParseResult::MetaSuccess)));
  747. std::string run_help_output = os.TakeStr();
  748. EXPECT_THAT(run_help_output, StrEq(llvm::StringRef(R"""(
  749. Run wombats across the screen.
  750. This will cause several wombats to run across your screen.
  751. Subcommand `run` usage:
  752. test [-f] run [OPTIONS]
  753. Subcommand `run` options:
  754. -o, --option=...
  755. A string option.
  756. --one-of-option=(x|y|z)
  757. A one-of option.
  758. Possible values:
  759. - x
  760. - y
  761. - z
  762. --help[=(full|short)]
  763. Prints help information for the subcommand, including a description, command line usage, and details of each option that can be provided.
  764. Possible values:
  765. - full (default)
  766. - short
  767. Or it won't, who knows.
  768. )""")
  769. .ltrim('\n')));
  770. EXPECT_THAT(parse({"help", "run"}, os),
  771. IsSuccess(Eq(ParseResult::MetaSuccess)));
  772. EXPECT_THAT(os.TakeStr(), StrEq(run_help_output));
  773. }
  774. TEST(ArgParserTest, HelpMarkdownLike) {
  775. bool flag = false;
  776. auto parse = [&](llvm::ArrayRef<llvm::StringRef> args, llvm::raw_ostream& s) {
  777. return Parse( // Force line break.
  778. args, s, {.name = "test"}, [&](auto& b) {
  779. b.AddFlag(
  780. {
  781. .name = "flag",
  782. .help = R"""(
  783. A boolean flag.
  784. Preformatted
  785. code....
  786. ........
  787. But
  788. here
  789. lines
  790. are
  791. collapsed.
  792. ```
  793. x
  794. y
  795. z
  796. ```
  797. )""",
  798. },
  799. [&](auto& arg_b) { arg_b.Set(&flag); });
  800. b.Do([] {});
  801. });
  802. };
  803. TestRawOstream os;
  804. EXPECT_THAT(parse({"--help"}, os), IsSuccess(Eq(ParseResult::MetaSuccess)));
  805. EXPECT_THAT(os.TakeStr(), StrEq(llvm::StringRef(R"""(
  806. Usage:
  807. test [--flag]
  808. Options:
  809. --flag
  810. A boolean flag.
  811. Preformatted
  812. code....
  813. ........
  814. But here lines are collapsed.
  815. ```
  816. x
  817. y
  818. z
  819. ```
  820. --help[=(full|short)]
  821. Prints help information for the command, including a description, command line usage, and details of each option that can be provided.
  822. Possible values:
  823. - full (default)
  824. - short
  825. )""")
  826. .ltrim('\n')));
  827. }
  828. } // namespace
  829. } // namespace Carbon::CommandLine