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 | |
9 | #include "llvm/ADT/STLExtras.h" |
10 | #include "llvm/Option/Arg.h" |
11 | #include "llvm/Option/ArgList.h" |
12 | #include "llvm/Option/OptTable.h" |
13 | #include "llvm/Option/Option.h" |
14 | #include "gtest/gtest.h" |
15 | |
16 | using namespace llvm; |
17 | using namespace llvm::opt; |
18 | |
19 | #if defined(__clang__) |
20 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
21 | #endif |
22 | |
23 | enum ID { |
24 | OPT_INVALID = 0, // This is not an option ID. |
25 | #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), |
26 | #include "Opts.inc" |
27 | LastOption |
28 | #undef OPTION |
29 | }; |
30 | |
31 | #define PREFIX(NAME, VALUE) \ |
32 | static constexpr StringLiteral NAME##_init[] = VALUE; \ |
33 | static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \ |
34 | std::size(NAME##_init) - 1); |
35 | #include "Opts.inc" |
36 | #undef PREFIX |
37 | |
38 | static constexpr const StringLiteral PrefixTable_init[] = |
39 | #define PREFIX_UNION(VALUES) VALUES |
40 | #include "Opts.inc" |
41 | #undef PREFIX_UNION |
42 | ; |
43 | static constexpr const ArrayRef<StringLiteral> |
44 | PrefixTable(PrefixTable_init, std::size(PrefixTable_init) - 1); |
45 | |
46 | enum OptionFlags { |
47 | OptFlag1 = (1 << 4), |
48 | OptFlag2 = (1 << 5), |
49 | OptFlag3 = (1 << 6) |
50 | }; |
51 | |
52 | enum OptionVisibility { |
53 | SubtoolVis = (1 << 2), |
54 | MultiLineVis = (1 << 3), |
55 | }; |
56 | |
57 | static constexpr OptTable::Info InfoTable[] = { |
58 | #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), |
59 | #include "Opts.inc" |
60 | #undef OPTION |
61 | }; |
62 | |
63 | namespace { |
64 | class TestOptTable : public GenericOptTable { |
65 | public: |
66 | TestOptTable(bool IgnoreCase = false) |
67 | : GenericOptTable(InfoTable, IgnoreCase) {} |
68 | }; |
69 | |
70 | class TestPrecomputedOptTable : public PrecomputedOptTable { |
71 | public: |
72 | TestPrecomputedOptTable(bool IgnoreCase = false) |
73 | : PrecomputedOptTable(InfoTable, PrefixTable, IgnoreCase) {} |
74 | }; |
75 | } |
76 | |
77 | const char *Args[] = { |
78 | "-A" , |
79 | "-Bhi" , |
80 | "--C=desu" , |
81 | "-C" , "bye" , |
82 | "-D,adena" , |
83 | "-E" , "apple" , "bloom" , |
84 | "-Fblarg" , |
85 | "-F" , "42" , |
86 | "-Gchuu" , "2" |
87 | }; |
88 | |
89 | // Test fixture |
90 | template <typename T> class OptTableTest : public ::testing::Test {}; |
91 | |
92 | template <typename T> class DISABLED_OptTableTest : public ::testing::Test {}; |
93 | |
94 | // Test both precomputed and computed OptTables with the same suite of tests. |
95 | using OptTableTestTypes = |
96 | ::testing::Types<TestOptTable, TestPrecomputedOptTable>; |
97 | |
98 | TYPED_TEST_SUITE(OptTableTest, OptTableTestTypes, ); |
99 | TYPED_TEST_SUITE(DISABLED_OptTableTest, OptTableTestTypes, ); |
100 | |
101 | TYPED_TEST(OptTableTest, OptionParsing) { |
102 | TypeParam T; |
103 | unsigned MAI, MAC; |
104 | InputArgList AL = T.ParseArgs(Args, MAI, MAC); |
105 | |
106 | // Check they all exist. |
107 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
108 | EXPECT_TRUE(AL.hasArg(OPT_B)); |
109 | EXPECT_TRUE(AL.hasArg(OPT_C)); |
110 | EXPECT_TRUE(AL.hasArg(OPT_D)); |
111 | EXPECT_TRUE(AL.hasArg(OPT_E)); |
112 | EXPECT_TRUE(AL.hasArg(OPT_F)); |
113 | EXPECT_TRUE(AL.hasArg(OPT_G)); |
114 | |
115 | // Check the values. |
116 | EXPECT_EQ("hi" , AL.getLastArgValue(OPT_B)); |
117 | EXPECT_EQ("bye" , AL.getLastArgValue(OPT_C)); |
118 | EXPECT_EQ("adena" , AL.getLastArgValue(OPT_D)); |
119 | std::vector<std::string> Es = AL.getAllArgValues(Id: OPT_E); |
120 | EXPECT_EQ("apple" , Es[0]); |
121 | EXPECT_EQ("bloom" , Es[1]); |
122 | EXPECT_EQ("42" , AL.getLastArgValue(OPT_F)); |
123 | std::vector<std::string> Gs = AL.getAllArgValues(Id: OPT_G); |
124 | EXPECT_EQ("chuu" , Gs[0]); |
125 | EXPECT_EQ("2" , Gs[1]); |
126 | |
127 | // Check the help text. |
128 | std::string Help; |
129 | raw_string_ostream RSO(Help); |
130 | T.printHelp(RSO, "test" , "title!" ); |
131 | EXPECT_NE(std::string::npos, Help.find("-A" )); |
132 | |
133 | // Check usage line. |
134 | T.printHelp(RSO, "name [options] file..." , "title!" ); |
135 | EXPECT_NE(std::string::npos, Help.find("USAGE: name [options] file...\n" )); |
136 | |
137 | // Test aliases. |
138 | auto Cs = AL.filtered(OPT_C); |
139 | ASSERT_NE(Cs.begin(), Cs.end()); |
140 | EXPECT_EQ("desu" , StringRef((*Cs.begin())->getValue())); |
141 | ArgStringList ASL; |
142 | (*Cs.begin())->render(AL, ASL); |
143 | ASSERT_EQ(2u, ASL.size()); |
144 | EXPECT_EQ("-C" , StringRef(ASL[0])); |
145 | EXPECT_EQ("desu" , StringRef(ASL[1])); |
146 | } |
147 | |
148 | TYPED_TEST(OptTableTest, ParseWithFlagExclusions) { |
149 | TypeParam T; |
150 | unsigned MAI, MAC; |
151 | |
152 | // Exclude flag3 to avoid parsing as OPT_SLASH_C. |
153 | InputArgList AL = T.ParseArgs(Args, MAI, MAC, |
154 | /*FlagsToInclude=*/0, |
155 | /*FlagsToExclude=*/OptFlag3); |
156 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
157 | EXPECT_TRUE(AL.hasArg(OPT_C)); |
158 | EXPECT_FALSE(AL.hasArg(OPT_SLASH_C)); |
159 | |
160 | // Exclude flag1 to avoid parsing as OPT_C. |
161 | AL = T.ParseArgs(Args, MAI, MAC, |
162 | /*FlagsToInclude=*/0, |
163 | /*FlagsToExclude=*/OptFlag1); |
164 | EXPECT_TRUE(AL.hasArg(OPT_B)); |
165 | EXPECT_FALSE(AL.hasArg(OPT_C)); |
166 | EXPECT_TRUE(AL.hasArg(OPT_SLASH_C)); |
167 | |
168 | const char *NewArgs[] = { "/C" , "foo" , "--C=bar" }; |
169 | AL = T.ParseArgs(NewArgs, MAI, MAC); |
170 | EXPECT_TRUE(AL.hasArg(OPT_SLASH_C)); |
171 | EXPECT_TRUE(AL.hasArg(OPT_C)); |
172 | EXPECT_EQ("foo" , AL.getLastArgValue(OPT_SLASH_C)); |
173 | EXPECT_EQ("bar" , AL.getLastArgValue(OPT_C)); |
174 | } |
175 | |
176 | TYPED_TEST(OptTableTest, ParseWithVisibility) { |
177 | TypeParam T; |
178 | unsigned MAI, MAC; |
179 | |
180 | const char *STArgs[] = {"-A" , "-Q" , "-R" }; |
181 | |
182 | // With no visibility specified, we find all of the arguments. |
183 | InputArgList AL = T.ParseArgs(STArgs, MAI, MAC); |
184 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
185 | EXPECT_TRUE(AL.hasArg(OPT_Q)); |
186 | EXPECT_TRUE(AL.hasArg(OPT_R)); |
187 | |
188 | // Default visibility omits SubtoolVis. |
189 | AL = T.ParseArgs(STArgs, MAI, MAC, Visibility(DefaultVis)); |
190 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
191 | EXPECT_FALSE(AL.hasArg(OPT_Q)); |
192 | EXPECT_TRUE(AL.hasArg(OPT_R)); |
193 | |
194 | // ~SubtoolVis still finds arguments that are visible in Default. |
195 | AL = T.ParseArgs(STArgs, MAI, MAC, Visibility(~SubtoolVis)); |
196 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
197 | EXPECT_FALSE(AL.hasArg(OPT_Q)); |
198 | EXPECT_TRUE(AL.hasArg(OPT_R)); |
199 | |
200 | // Only SubtoolVis. |
201 | AL = T.ParseArgs(STArgs, MAI, MAC, Visibility(SubtoolVis)); |
202 | EXPECT_FALSE(AL.hasArg(OPT_A)); |
203 | EXPECT_TRUE(AL.hasArg(OPT_Q)); |
204 | EXPECT_TRUE(AL.hasArg(OPT_R)); |
205 | |
206 | // Both Default and SubtoolVis are found. |
207 | AL = T.ParseArgs(STArgs, MAI, MAC, Visibility(DefaultVis | SubtoolVis)); |
208 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
209 | EXPECT_TRUE(AL.hasArg(OPT_Q)); |
210 | EXPECT_TRUE(AL.hasArg(OPT_R)); |
211 | } |
212 | |
213 | TYPED_TEST(OptTableTest, ParseAliasInGroup) { |
214 | TypeParam T; |
215 | unsigned MAI, MAC; |
216 | |
217 | const char *MyArgs[] = { "-I" }; |
218 | InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); |
219 | EXPECT_TRUE(AL.hasArg(OPT_H)); |
220 | } |
221 | |
222 | TYPED_TEST(OptTableTest, AliasArgs) { |
223 | TypeParam T; |
224 | unsigned MAI, MAC; |
225 | |
226 | const char *MyArgs[] = { "-J" , "-Joo" }; |
227 | InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); |
228 | EXPECT_TRUE(AL.hasArg(OPT_B)); |
229 | EXPECT_EQ("foo" , AL.getAllArgValues(OPT_B)[0]); |
230 | EXPECT_EQ("bar" , AL.getAllArgValues(OPT_B)[1]); |
231 | } |
232 | |
233 | TYPED_TEST(OptTableTest, IgnoreCase) { |
234 | TypeParam T(true); |
235 | unsigned MAI, MAC; |
236 | |
237 | const char *MyArgs[] = { "-a" , "-joo" }; |
238 | InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); |
239 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
240 | EXPECT_TRUE(AL.hasArg(OPT_B)); |
241 | } |
242 | |
243 | #if defined(__clang__) |
244 | // Disable the warning that triggers on exactly what is being tested. |
245 | #pragma clang diagnostic push |
246 | #pragma clang diagnostic ignored "-Wself-move" |
247 | #endif |
248 | |
249 | TYPED_TEST(OptTableTest, InputArgListSelfAssign) { |
250 | TypeParam T; |
251 | unsigned MAI, MAC; |
252 | InputArgList AL = T.ParseArgs(Args, MAI, MAC, |
253 | /*FlagsToInclude=*/0, |
254 | /*FlagsToExclude=*/OptFlag3); |
255 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
256 | EXPECT_TRUE(AL.hasArg(OPT_C)); |
257 | EXPECT_FALSE(AL.hasArg(OPT_SLASH_C)); |
258 | |
259 | AL = std::move(AL); |
260 | |
261 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
262 | EXPECT_TRUE(AL.hasArg(OPT_C)); |
263 | EXPECT_FALSE(AL.hasArg(OPT_SLASH_C)); |
264 | } |
265 | |
266 | #if defined(__clang__) |
267 | #pragma clang diagnostic pop |
268 | #endif |
269 | |
270 | TYPED_TEST(OptTableTest, DoNotIgnoreCase) { |
271 | TypeParam T; |
272 | unsigned MAI, MAC; |
273 | |
274 | const char *MyArgs[] = { "-a" , "-joo" }; |
275 | InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); |
276 | EXPECT_FALSE(AL.hasArg(OPT_A)); |
277 | EXPECT_FALSE(AL.hasArg(OPT_B)); |
278 | } |
279 | |
280 | TYPED_TEST(OptTableTest, SlurpEmpty) { |
281 | TypeParam T; |
282 | unsigned MAI, MAC; |
283 | |
284 | const char *MyArgs[] = { "-A" , "-slurp" }; |
285 | InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); |
286 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
287 | EXPECT_TRUE(AL.hasArg(OPT_Slurp)); |
288 | EXPECT_EQ(0U, AL.getAllArgValues(OPT_Slurp).size()); |
289 | } |
290 | |
291 | TYPED_TEST(OptTableTest, Slurp) { |
292 | TypeParam T; |
293 | unsigned MAI, MAC; |
294 | |
295 | const char *MyArgs[] = { "-A" , "-slurp" , "-B" , "--" , "foo" }; |
296 | InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); |
297 | EXPECT_EQ(AL.size(), 2U); |
298 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
299 | EXPECT_FALSE(AL.hasArg(OPT_B)); |
300 | EXPECT_TRUE(AL.hasArg(OPT_Slurp)); |
301 | EXPECT_EQ(3U, AL.getAllArgValues(OPT_Slurp).size()); |
302 | EXPECT_EQ("-B" , AL.getAllArgValues(OPT_Slurp)[0]); |
303 | EXPECT_EQ("--" , AL.getAllArgValues(OPT_Slurp)[1]); |
304 | EXPECT_EQ("foo" , AL.getAllArgValues(OPT_Slurp)[2]); |
305 | } |
306 | |
307 | TYPED_TEST(OptTableTest, SlurpJoinedEmpty) { |
308 | TypeParam T; |
309 | unsigned MAI, MAC; |
310 | |
311 | const char *MyArgs[] = { "-A" , "-slurpjoined" }; |
312 | InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); |
313 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
314 | EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined)); |
315 | EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined).size(), 0U); |
316 | } |
317 | |
318 | TYPED_TEST(OptTableTest, SlurpJoinedOneJoined) { |
319 | TypeParam T; |
320 | unsigned MAI, MAC; |
321 | |
322 | const char *MyArgs[] = { "-A" , "-slurpjoinedfoo" }; |
323 | InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); |
324 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
325 | EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined)); |
326 | EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined).size(), 1U); |
327 | EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined)[0], "foo" ); |
328 | } |
329 | |
330 | TYPED_TEST(OptTableTest, SlurpJoinedAndSeparate) { |
331 | TypeParam T; |
332 | unsigned MAI, MAC; |
333 | |
334 | const char *MyArgs[] = { "-A" , "-slurpjoinedfoo" , "bar" , "baz" }; |
335 | InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); |
336 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
337 | EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined)); |
338 | EXPECT_EQ(3U, AL.getAllArgValues(OPT_SlurpJoined).size()); |
339 | EXPECT_EQ("foo" , AL.getAllArgValues(OPT_SlurpJoined)[0]); |
340 | EXPECT_EQ("bar" , AL.getAllArgValues(OPT_SlurpJoined)[1]); |
341 | EXPECT_EQ("baz" , AL.getAllArgValues(OPT_SlurpJoined)[2]); |
342 | } |
343 | |
344 | TYPED_TEST(OptTableTest, SlurpJoinedButSeparate) { |
345 | TypeParam T; |
346 | unsigned MAI, MAC; |
347 | |
348 | const char *MyArgs[] = { "-A" , "-slurpjoined" , "foo" , "bar" , "baz" }; |
349 | InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); |
350 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
351 | EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined)); |
352 | EXPECT_EQ(3U, AL.getAllArgValues(OPT_SlurpJoined).size()); |
353 | EXPECT_EQ("foo" , AL.getAllArgValues(OPT_SlurpJoined)[0]); |
354 | EXPECT_EQ("bar" , AL.getAllArgValues(OPT_SlurpJoined)[1]); |
355 | EXPECT_EQ("baz" , AL.getAllArgValues(OPT_SlurpJoined)[2]); |
356 | } |
357 | |
358 | TYPED_TEST(OptTableTest, FlagAliasToJoined) { |
359 | TypeParam T; |
360 | unsigned MAI, MAC; |
361 | |
362 | // Check that a flag alias provides an empty argument to a joined option. |
363 | const char *MyArgs[] = { "-K" }; |
364 | InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); |
365 | EXPECT_EQ(AL.size(), 1U); |
366 | EXPECT_TRUE(AL.hasArg(OPT_B)); |
367 | EXPECT_EQ(1U, AL.getAllArgValues(OPT_B).size()); |
368 | EXPECT_EQ("" , AL.getAllArgValues(OPT_B)[0]); |
369 | } |
370 | |
371 | TYPED_TEST(OptTableTest, FindNearest) { |
372 | TypeParam T; |
373 | std::string Nearest; |
374 | |
375 | // Options that are too short should not be considered |
376 | // "near" other short options. |
377 | EXPECT_GT(T.findNearest("-A" , Nearest), 4U); |
378 | EXPECT_GT(T.findNearest("/C" , Nearest), 4U); |
379 | EXPECT_GT(T.findNearest("--C=foo" , Nearest), 4U); |
380 | |
381 | // The nearest candidate should mirror the amount of prefix |
382 | // characters used in the original string. |
383 | EXPECT_EQ(1U, T.findNearest("-blorb" , Nearest)); |
384 | EXPECT_EQ(Nearest, "-blorp" ); |
385 | EXPECT_EQ(1U, T.findNearest("--blorm" , Nearest)); |
386 | EXPECT_EQ(Nearest, "--blorp" ); |
387 | EXPECT_EQ(1U, T.findNearest("-blarg" , Nearest)); |
388 | EXPECT_EQ(Nearest, "-blarn" ); |
389 | EXPECT_EQ(1U, T.findNearest("--blarm" , Nearest)); |
390 | EXPECT_EQ(Nearest, "--blarn" ); |
391 | EXPECT_EQ(1U, T.findNearest("-fjormp" , Nearest)); |
392 | EXPECT_EQ(Nearest, "--fjormp" ); |
393 | |
394 | // The nearest candidate respects the prefix and value delimiter |
395 | // of the original string. |
396 | EXPECT_EQ(1U, T.findNearest("/framb:foo" , Nearest)); |
397 | EXPECT_EQ(Nearest, "/cramb:foo" ); |
398 | |
399 | // `--glormp` should have an editing distance > 0 from `--glormp=`. |
400 | EXPECT_GT(T.findNearest("--glorrmp" , Nearest), 0U); |
401 | EXPECT_EQ(Nearest, "--glorrmp=" ); |
402 | EXPECT_EQ(0U, T.findNearest("--glorrmp=foo" , Nearest)); |
403 | |
404 | // `--blurmps` should correct to `--blurmp`, not `--blurmp=`, even though |
405 | // both naively have an editing distance of 1. |
406 | EXPECT_EQ(1U, T.findNearest("--blurmps" , Nearest)); |
407 | EXPECT_EQ(Nearest, "--blurmp" ); |
408 | |
409 | // ...but `--blurmps=foo` should correct to `--blurmp=foo`. |
410 | EXPECT_EQ(1U, T.findNearest("--blurmps=foo" , Nearest)); |
411 | EXPECT_EQ(Nearest, "--blurmp=foo" ); |
412 | |
413 | // Flags should be included and excluded as specified. |
414 | EXPECT_EQ(1U, T.findNearest("-doopf" , Nearest, |
415 | /*FlagsToInclude=*/OptFlag2, |
416 | /*FlagsToExclude=*/0)); |
417 | EXPECT_EQ(Nearest, "-doopf2" ); |
418 | EXPECT_EQ(1U, T.findNearest("-doopf" , Nearest, |
419 | /*FlagsToInclude=*/0, |
420 | /*FlagsToExclude=*/OptFlag2)); |
421 | EXPECT_EQ(Nearest, "-doopf1" ); |
422 | |
423 | // Spelling should respect visibility. |
424 | EXPECT_EQ(1U, T.findNearest("-xyzzy" , Nearest, Visibility(DefaultVis))); |
425 | EXPECT_EQ(Nearest, "-xyzzy2" ); |
426 | EXPECT_EQ(1U, T.findNearest("-xyzzy" , Nearest, Visibility(SubtoolVis))); |
427 | EXPECT_EQ(Nearest, "-xyzzy1" ); |
428 | } |
429 | |
430 | TYPED_TEST(DISABLED_OptTableTest, FindNearestFIXME) { |
431 | TypeParam T; |
432 | std::string Nearest; |
433 | |
434 | // FIXME: Options with joined values should not have those values considered |
435 | // when calculating distance. The test below would fail if run, but it should |
436 | // succeed. |
437 | EXPECT_EQ(1U, T.findNearest("--erbghFoo" , Nearest)); |
438 | EXPECT_EQ(Nearest, "--ermghFoo" ); |
439 | } |
440 | |
441 | TYPED_TEST(OptTableTest, ParseGroupedShortOptions) { |
442 | TypeParam T; |
443 | T.setGroupedShortOptions(true); |
444 | unsigned MAI, MAC; |
445 | |
446 | // Grouped short options can be followed by a long Flag (-Joo), or a non-Flag |
447 | // option (-C=1). |
448 | const char *Args1[] = {"-AIJ" , "-AIJoo" , "-AC=1" }; |
449 | InputArgList AL = T.ParseArgs(Args1, MAI, MAC); |
450 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
451 | EXPECT_TRUE(AL.hasArg(OPT_H)); |
452 | ASSERT_EQ((size_t)2, AL.getAllArgValues(OPT_B).size()); |
453 | EXPECT_EQ("foo" , AL.getAllArgValues(OPT_B)[0]); |
454 | EXPECT_EQ("bar" , AL.getAllArgValues(OPT_B)[1]); |
455 | ASSERT_TRUE(AL.hasArg(OPT_C)); |
456 | EXPECT_EQ("1" , AL.getAllArgValues(OPT_C)[0]); |
457 | |
458 | // Prefer a long option to a short option. |
459 | const char *Args2[] = {"-AB" }; |
460 | InputArgList AL2 = T.ParseArgs(Args2, MAI, MAC); |
461 | EXPECT_TRUE(!AL2.hasArg(OPT_A)); |
462 | EXPECT_TRUE(AL2.hasArg(OPT_AB)); |
463 | |
464 | // Short options followed by a long option. We probably should disallow this. |
465 | const char *Args3[] = {"-AIblorp" }; |
466 | InputArgList AL3 = T.ParseArgs(Args3, MAI, MAC); |
467 | EXPECT_TRUE(AL3.hasArg(OPT_A)); |
468 | EXPECT_TRUE(AL3.hasArg(OPT_Blorp)); |
469 | } |
470 | |
471 | TYPED_TEST(OptTableTest, ParseDashDash) { |
472 | TypeParam T; |
473 | T.setDashDashParsing(true); |
474 | unsigned MAI, MAC; |
475 | |
476 | const char *Args1[] = {"-A" , "--" }; |
477 | InputArgList AL = T.ParseArgs(Args1, MAI, MAC); |
478 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
479 | EXPECT_EQ(size_t(0), AL.getAllArgValues(OPT_INPUT).size()); |
480 | EXPECT_EQ(size_t(0), AL.getAllArgValues(OPT_UNKNOWN).size()); |
481 | |
482 | const char *Args2[] = {"-A" , "--" , "-A" , "--" , "-B" }; |
483 | AL = T.ParseArgs(Args2, MAI, MAC); |
484 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
485 | EXPECT_FALSE(AL.hasArg(OPT_B)); |
486 | const std::vector<std::string> Input = AL.getAllArgValues(OPT_INPUT); |
487 | ASSERT_EQ(size_t(3), Input.size()); |
488 | EXPECT_EQ("-A" , Input[0]); |
489 | EXPECT_EQ("--" , Input[1]); |
490 | EXPECT_EQ("-B" , Input[2]); |
491 | EXPECT_EQ(size_t(0), AL.getAllArgValues(OPT_UNKNOWN).size()); |
492 | |
493 | T.setDashDashParsing(false); |
494 | AL = T.ParseArgs(Args2, MAI, MAC); |
495 | EXPECT_TRUE(AL.hasArg(OPT_A)); |
496 | EXPECT_TRUE(AL.hasArg(OPT_B)); |
497 | EXPECT_EQ(size_t(0), AL.getAllArgValues(OPT_INPUT).size()); |
498 | const std::vector<std::string> Unknown = AL.getAllArgValues(OPT_UNKNOWN); |
499 | ASSERT_EQ(size_t(2), Unknown.size()); |
500 | EXPECT_EQ("--" , Unknown[0]); |
501 | EXPECT_EQ("--" , Unknown[1]); |
502 | } |
503 | |
504 | TYPED_TEST(OptTableTest, UnknownOptions) { |
505 | TypeParam T; |
506 | unsigned MAI, MAC; |
507 | const char *Args[] = {"-u" , "--long" , "0" }; |
508 | for (int I = 0; I < 2; ++I) { |
509 | T.setGroupedShortOptions(I != 0); |
510 | InputArgList AL = T.ParseArgs(Args, MAI, MAC); |
511 | const std::vector<std::string> Unknown = AL.getAllArgValues(OPT_UNKNOWN); |
512 | ASSERT_EQ((size_t)2, Unknown.size()); |
513 | EXPECT_EQ("-u" , Unknown[0]); |
514 | EXPECT_EQ("--long" , Unknown[1]); |
515 | } |
516 | } |
517 | |
518 | TYPED_TEST(OptTableTest, FlagsWithoutValues) { |
519 | TypeParam T; |
520 | T.setGroupedShortOptions(true); |
521 | unsigned MAI, MAC; |
522 | const char *Args[] = {"-A=1" , "-A=" }; |
523 | InputArgList AL = T.ParseArgs(Args, MAI, MAC); |
524 | const std::vector<std::string> Unknown = AL.getAllArgValues(OPT_UNKNOWN); |
525 | ASSERT_EQ((size_t)2, Unknown.size()); |
526 | EXPECT_EQ("-A=1" , Unknown[0]); |
527 | EXPECT_EQ("-A=" , Unknown[1]); |
528 | } |
529 | |
530 | TYPED_TEST(OptTableTest, UnknownGroupedShortOptions) { |
531 | TypeParam T; |
532 | T.setGroupedShortOptions(true); |
533 | unsigned MAI, MAC; |
534 | const char *Args[] = {"-AuzK" , "-AuzK" }; |
535 | InputArgList AL = T.ParseArgs(Args, MAI, MAC); |
536 | const std::vector<std::string> Unknown = AL.getAllArgValues(OPT_UNKNOWN); |
537 | ASSERT_EQ((size_t)4, Unknown.size()); |
538 | EXPECT_EQ("-u" , Unknown[0]); |
539 | EXPECT_EQ("-z" , Unknown[1]); |
540 | EXPECT_EQ("-u" , Unknown[2]); |
541 | EXPECT_EQ("-z" , Unknown[3]); |
542 | } |
543 | |
544 | TYPED_TEST(OptTableTest, PrintMultilineHelpText) { |
545 | TypeParam T; |
546 | std::string Help; |
547 | raw_string_ostream RSO(Help); |
548 | T.printHelp(RSO, "usage" , "title" , /*ShowHidden=*/false, |
549 | /*ShowAllAliases=*/false, Visibility(MultiLineVis)); |
550 | EXPECT_STREQ(Help.c_str(), R"(OVERVIEW: title |
551 | |
552 | USAGE: usage |
553 | |
554 | OPTIONS: |
555 | -multiline-help-with-long-name |
556 | This a help text that has |
557 | multiple lines in it |
558 | and a long name |
559 | -multiline-help This a help text that has |
560 | multiple lines in it |
561 | )" ); |
562 | } |
563 | |