OptionParsingTest.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. //===- unittest/Support/OptionParsingTest.cpp - OptTable tests ------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "llvm/ADT/STLExtras.h"
  9. #include "llvm/Option/Arg.h"
  10. #include "llvm/Option/ArgList.h"
  11. #include "llvm/Option/Option.h"
  12. #include "gtest/gtest.h"
  13. using namespace llvm;
  14. using namespace llvm::opt;
  15. enum ID {
  16. OPT_INVALID = 0, // This is not an option ID.
  17. #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
  18. HELPTEXT, METAVAR, VALUES) \
  19. OPT_##ID,
  20. #include "Opts.inc"
  21. LastOption
  22. #undef OPTION
  23. };
  24. #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
  25. #include "Opts.inc"
  26. #undef PREFIX
  27. enum OptionFlags {
  28. OptFlag1 = (1 << 4),
  29. OptFlag2 = (1 << 5),
  30. OptFlag3 = (1 << 6)
  31. };
  32. static const OptTable::Info InfoTable[] = {
  33. #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
  34. HELPTEXT, METAVAR, VALUES) \
  35. {PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, Option::KIND##Class, \
  36. PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS, VALUES},
  37. #include "Opts.inc"
  38. #undef OPTION
  39. };
  40. namespace {
  41. class TestOptTable : public OptTable {
  42. public:
  43. TestOptTable(bool IgnoreCase = false)
  44. : OptTable(InfoTable, IgnoreCase) {}
  45. };
  46. }
  47. const char *Args[] = {
  48. "-A",
  49. "-Bhi",
  50. "--C=desu",
  51. "-C", "bye",
  52. "-D,adena",
  53. "-E", "apple", "bloom",
  54. "-Fblarg",
  55. "-F", "42",
  56. "-Gchuu", "2"
  57. };
  58. TEST(Option, OptionParsing) {
  59. TestOptTable T;
  60. unsigned MAI, MAC;
  61. InputArgList AL = T.ParseArgs(Args, MAI, MAC);
  62. // Check they all exist.
  63. EXPECT_TRUE(AL.hasArg(OPT_A));
  64. EXPECT_TRUE(AL.hasArg(OPT_B));
  65. EXPECT_TRUE(AL.hasArg(OPT_C));
  66. EXPECT_TRUE(AL.hasArg(OPT_D));
  67. EXPECT_TRUE(AL.hasArg(OPT_E));
  68. EXPECT_TRUE(AL.hasArg(OPT_F));
  69. EXPECT_TRUE(AL.hasArg(OPT_G));
  70. // Check the values.
  71. EXPECT_EQ("hi", AL.getLastArgValue(OPT_B));
  72. EXPECT_EQ("bye", AL.getLastArgValue(OPT_C));
  73. EXPECT_EQ("adena", AL.getLastArgValue(OPT_D));
  74. std::vector<std::string> Es = AL.getAllArgValues(OPT_E);
  75. EXPECT_EQ("apple", Es[0]);
  76. EXPECT_EQ("bloom", Es[1]);
  77. EXPECT_EQ("42", AL.getLastArgValue(OPT_F));
  78. std::vector<std::string> Gs = AL.getAllArgValues(OPT_G);
  79. EXPECT_EQ("chuu", Gs[0]);
  80. EXPECT_EQ("2", Gs[1]);
  81. // Check the help text.
  82. std::string Help;
  83. raw_string_ostream RSO(Help);
  84. T.PrintHelp(RSO, "test", "title!");
  85. EXPECT_NE(std::string::npos, Help.find("-A"));
  86. // Check usage line.
  87. T.PrintHelp(RSO, "name [options] file...", "title!");
  88. EXPECT_NE(std::string::npos, Help.find("USAGE: name [options] file...\n"));
  89. // Test aliases.
  90. auto Cs = AL.filtered(OPT_C);
  91. ASSERT_NE(Cs.begin(), Cs.end());
  92. EXPECT_EQ("desu", StringRef((*Cs.begin())->getValue()));
  93. ArgStringList ASL;
  94. (*Cs.begin())->render(AL, ASL);
  95. ASSERT_EQ(2u, ASL.size());
  96. EXPECT_EQ("-C", StringRef(ASL[0]));
  97. EXPECT_EQ("desu", StringRef(ASL[1]));
  98. }
  99. TEST(Option, ParseWithFlagExclusions) {
  100. TestOptTable T;
  101. unsigned MAI, MAC;
  102. // Exclude flag3 to avoid parsing as OPT_SLASH_C.
  103. InputArgList AL = T.ParseArgs(Args, MAI, MAC,
  104. /*FlagsToInclude=*/0,
  105. /*FlagsToExclude=*/OptFlag3);
  106. EXPECT_TRUE(AL.hasArg(OPT_A));
  107. EXPECT_TRUE(AL.hasArg(OPT_C));
  108. EXPECT_FALSE(AL.hasArg(OPT_SLASH_C));
  109. // Exclude flag1 to avoid parsing as OPT_C.
  110. AL = T.ParseArgs(Args, MAI, MAC,
  111. /*FlagsToInclude=*/0,
  112. /*FlagsToExclude=*/OptFlag1);
  113. EXPECT_TRUE(AL.hasArg(OPT_B));
  114. EXPECT_FALSE(AL.hasArg(OPT_C));
  115. EXPECT_TRUE(AL.hasArg(OPT_SLASH_C));
  116. const char *NewArgs[] = { "/C", "foo", "--C=bar" };
  117. AL = T.ParseArgs(NewArgs, MAI, MAC);
  118. EXPECT_TRUE(AL.hasArg(OPT_SLASH_C));
  119. EXPECT_TRUE(AL.hasArg(OPT_C));
  120. EXPECT_EQ("foo", AL.getLastArgValue(OPT_SLASH_C));
  121. EXPECT_EQ("bar", AL.getLastArgValue(OPT_C));
  122. }
  123. TEST(Option, ParseAliasInGroup) {
  124. TestOptTable T;
  125. unsigned MAI, MAC;
  126. const char *MyArgs[] = { "-I" };
  127. InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
  128. EXPECT_TRUE(AL.hasArg(OPT_H));
  129. }
  130. TEST(Option, AliasArgs) {
  131. TestOptTable T;
  132. unsigned MAI, MAC;
  133. const char *MyArgs[] = { "-J", "-Joo" };
  134. InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
  135. EXPECT_TRUE(AL.hasArg(OPT_B));
  136. EXPECT_EQ("foo", AL.getAllArgValues(OPT_B)[0]);
  137. EXPECT_EQ("bar", AL.getAllArgValues(OPT_B)[1]);
  138. }
  139. TEST(Option, IgnoreCase) {
  140. TestOptTable T(true);
  141. unsigned MAI, MAC;
  142. const char *MyArgs[] = { "-a", "-joo" };
  143. InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
  144. EXPECT_TRUE(AL.hasArg(OPT_A));
  145. EXPECT_TRUE(AL.hasArg(OPT_B));
  146. }
  147. TEST(Option, DoNotIgnoreCase) {
  148. TestOptTable T;
  149. unsigned MAI, MAC;
  150. const char *MyArgs[] = { "-a", "-joo" };
  151. InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
  152. EXPECT_FALSE(AL.hasArg(OPT_A));
  153. EXPECT_FALSE(AL.hasArg(OPT_B));
  154. }
  155. TEST(Option, SlurpEmpty) {
  156. TestOptTable T;
  157. unsigned MAI, MAC;
  158. const char *MyArgs[] = { "-A", "-slurp" };
  159. InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
  160. EXPECT_TRUE(AL.hasArg(OPT_A));
  161. EXPECT_TRUE(AL.hasArg(OPT_Slurp));
  162. EXPECT_EQ(0U, AL.getAllArgValues(OPT_Slurp).size());
  163. }
  164. TEST(Option, Slurp) {
  165. TestOptTable T;
  166. unsigned MAI, MAC;
  167. const char *MyArgs[] = { "-A", "-slurp", "-B", "--", "foo" };
  168. InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
  169. EXPECT_EQ(AL.size(), 2U);
  170. EXPECT_TRUE(AL.hasArg(OPT_A));
  171. EXPECT_FALSE(AL.hasArg(OPT_B));
  172. EXPECT_TRUE(AL.hasArg(OPT_Slurp));
  173. EXPECT_EQ(3U, AL.getAllArgValues(OPT_Slurp).size());
  174. EXPECT_EQ("-B", AL.getAllArgValues(OPT_Slurp)[0]);
  175. EXPECT_EQ("--", AL.getAllArgValues(OPT_Slurp)[1]);
  176. EXPECT_EQ("foo", AL.getAllArgValues(OPT_Slurp)[2]);
  177. }
  178. TEST(Option, SlurpJoinedEmpty) {
  179. TestOptTable T;
  180. unsigned MAI, MAC;
  181. const char *MyArgs[] = { "-A", "-slurpjoined" };
  182. InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
  183. EXPECT_TRUE(AL.hasArg(OPT_A));
  184. EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined));
  185. EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined).size(), 0U);
  186. }
  187. TEST(Option, SlurpJoinedOneJoined) {
  188. TestOptTable T;
  189. unsigned MAI, MAC;
  190. const char *MyArgs[] = { "-A", "-slurpjoinedfoo" };
  191. InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
  192. EXPECT_TRUE(AL.hasArg(OPT_A));
  193. EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined));
  194. EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined).size(), 1U);
  195. EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined)[0], "foo");
  196. }
  197. TEST(Option, SlurpJoinedAndSeparate) {
  198. TestOptTable T;
  199. unsigned MAI, MAC;
  200. const char *MyArgs[] = { "-A", "-slurpjoinedfoo", "bar", "baz" };
  201. InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
  202. EXPECT_TRUE(AL.hasArg(OPT_A));
  203. EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined));
  204. EXPECT_EQ(3U, AL.getAllArgValues(OPT_SlurpJoined).size());
  205. EXPECT_EQ("foo", AL.getAllArgValues(OPT_SlurpJoined)[0]);
  206. EXPECT_EQ("bar", AL.getAllArgValues(OPT_SlurpJoined)[1]);
  207. EXPECT_EQ("baz", AL.getAllArgValues(OPT_SlurpJoined)[2]);
  208. }
  209. TEST(Option, SlurpJoinedButSeparate) {
  210. TestOptTable T;
  211. unsigned MAI, MAC;
  212. const char *MyArgs[] = { "-A", "-slurpjoined", "foo", "bar", "baz" };
  213. InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
  214. EXPECT_TRUE(AL.hasArg(OPT_A));
  215. EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined));
  216. EXPECT_EQ(3U, AL.getAllArgValues(OPT_SlurpJoined).size());
  217. EXPECT_EQ("foo", AL.getAllArgValues(OPT_SlurpJoined)[0]);
  218. EXPECT_EQ("bar", AL.getAllArgValues(OPT_SlurpJoined)[1]);
  219. EXPECT_EQ("baz", AL.getAllArgValues(OPT_SlurpJoined)[2]);
  220. }
  221. TEST(Option, FlagAliasToJoined) {
  222. TestOptTable T;
  223. unsigned MAI, MAC;
  224. // Check that a flag alias provides an empty argument to a joined option.
  225. const char *MyArgs[] = { "-K" };
  226. InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC);
  227. EXPECT_EQ(AL.size(), 1U);
  228. EXPECT_TRUE(AL.hasArg(OPT_B));
  229. EXPECT_EQ(1U, AL.getAllArgValues(OPT_B).size());
  230. EXPECT_EQ("", AL.getAllArgValues(OPT_B)[0]);
  231. }
  232. TEST(Option, FindNearest) {
  233. TestOptTable T;
  234. std::string Nearest;
  235. // Options that are too short should not be considered
  236. // "near" other short options.
  237. EXPECT_GT(T.findNearest("-A", Nearest), 4U);
  238. EXPECT_GT(T.findNearest("/C", Nearest), 4U);
  239. EXPECT_GT(T.findNearest("--C=foo", Nearest), 4U);
  240. // The nearest candidate should mirror the amount of prefix
  241. // characters used in the original string.
  242. EXPECT_EQ(1U, T.findNearest("-blorb", Nearest));
  243. EXPECT_EQ(Nearest, "-blorp");
  244. EXPECT_EQ(1U, T.findNearest("--blorm", Nearest));
  245. EXPECT_EQ(Nearest, "--blorp");
  246. EXPECT_EQ(1U, T.findNearest("-blarg", Nearest));
  247. EXPECT_EQ(Nearest, "-blarn");
  248. EXPECT_EQ(1U, T.findNearest("--blarm", Nearest));
  249. EXPECT_EQ(Nearest, "--blarn");
  250. EXPECT_EQ(1U, T.findNearest("-fjormp", Nearest));
  251. EXPECT_EQ(Nearest, "--fjormp");
  252. // The nearest candidate respects the prefix and value delimiter
  253. // of the original string.
  254. EXPECT_EQ(1U, T.findNearest("/framb:foo", Nearest));
  255. EXPECT_EQ(Nearest, "/cramb:foo");
  256. // `--glormp` should have an editing distance > 0 from `--glormp=`.
  257. EXPECT_GT(T.findNearest("--glorrmp", Nearest), 0U);
  258. EXPECT_EQ(Nearest, "--glorrmp=");
  259. EXPECT_EQ(0U, T.findNearest("--glorrmp=foo", Nearest));
  260. // `--blurmps` should correct to `--blurmp`, not `--blurmp=`, even though
  261. // both naively have an editing distance of 1.
  262. EXPECT_EQ(1U, T.findNearest("--blurmps", Nearest));
  263. EXPECT_EQ(Nearest, "--blurmp");
  264. // ...but `--blurmps=foo` should correct to `--blurmp=foo`.
  265. EXPECT_EQ(1U, T.findNearest("--blurmps=foo", Nearest));
  266. EXPECT_EQ(Nearest, "--blurmp=foo");
  267. // Flags should be included and excluded as specified.
  268. EXPECT_EQ(1U, T.findNearest("-doopf", Nearest, /*FlagsToInclude=*/OptFlag2));
  269. EXPECT_EQ(Nearest, "-doopf2");
  270. EXPECT_EQ(1U, T.findNearest("-doopf", Nearest,
  271. /*FlagsToInclude=*/0,
  272. /*FlagsToExclude=*/OptFlag2));
  273. EXPECT_EQ(Nearest, "-doopf1");
  274. }
  275. TEST(DISABLED_Option, FindNearestFIXME) {
  276. TestOptTable T;
  277. std::string Nearest;
  278. // FIXME: Options with joined values should not have those values considered
  279. // when calculating distance. The test below would fail if run, but it should
  280. // succeed.
  281. EXPECT_EQ(1U, T.findNearest("--erbghFoo", Nearest));
  282. EXPECT_EQ(Nearest, "--ermghFoo");
  283. }