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