1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.cli;
19
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertFalse;
22 import static org.junit.jupiter.api.Assertions.assertThrows;
23 import static org.junit.jupiter.api.Assertions.assertTrue;
24
25 import java.util.HashSet;
26 import java.util.Set;
27 import java.util.stream.Stream;
28
29 import org.apache.commons.cli.DefaultParser.Builder;
30 import org.junit.jupiter.api.BeforeEach;
31 import org.junit.jupiter.api.Disabled;
32 import org.junit.jupiter.api.Test;
33 import org.junit.jupiter.api.extension.ExtensionContext;
34 import org.junit.jupiter.params.ParameterizedTest;
35 import org.junit.jupiter.params.provider.Arguments;
36 import org.junit.jupiter.params.provider.ArgumentsProvider;
37 import org.junit.jupiter.params.provider.ArgumentsSource;
38
39 class DefaultParserTest extends AbstractParserTestCase {
40
41 static class ExternalArgumentsProvider implements ArgumentsProvider {
42
43 @Override
44 public Stream<? extends Arguments> provideArguments(final ExtensionContext context) {
45 return Stream.of(
46
47
48
49
50
51
52
53
54 Arguments.of(
55 "Long option quote handling DEFAULT behavior",
56 DefaultParser.builder().get(),
57 new String[]{"--bfile", "\"quoted string\""},
58 "quoted string",
59 "b",
60 "Confirm --bfile=\"arg\" strips quotes"
61 ),
62 Arguments.of(
63 "Long option with equals quote handling DEFAULT behavior",
64 DefaultParser.builder().get(),
65 new String[]{"--bfile=\"quoted string\""},
66 "\"quoted string\"",
67 "b",
68 "Confirm --bfile=\"arg\" keeps quotes"
69 ),
70 Arguments.of(
71 "Short option quote handling DEFAULT behavior",
72 DefaultParser.builder().get(),
73 new String[]{"-b", "\"quoted string\""},
74 "quoted string",
75 "b",
76 "Confirm -b\"arg\" strips quotes"
77 ),
78 Arguments.of(
79 "Short option concatenated quote handling DEFAULT behavior",
80 DefaultParser.builder().get(),
81 new String[]{"-b\"quoted string\""},
82 "\"quoted string\"",
83 "b",
84 "Confirm -b\"arg\" keeps quotes"
85 ),
86 Arguments.of(
87 "Long option quote handling WITHOUT strip",
88 DefaultParser.builder().setStripLeadingAndTrailingQuotes(false).get(),
89 new String[]{"--bfile", "\"quoted string\""},
90 "\"quoted string\"",
91 "b",
92 "Confirm --bfile \"arg\" keeps quotes"
93 ),
94 Arguments.of(
95 "Long option with equals quote handling WITHOUT strip",
96 DefaultParser.builder().setStripLeadingAndTrailingQuotes(false).get(),
97 new String[]{"--bfile=\"quoted string\""},
98 "\"quoted string\"",
99 "b",
100 "Confirm --bfile=\"arg\" keeps quotes"
101 ),
102 Arguments.of(
103 "Short option quote handling WITHOUT strip",
104 DefaultParser.builder().setStripLeadingAndTrailingQuotes(false).get(),
105 new String[]{"-b", "\"quoted string\""},
106 "\"quoted string\"",
107 "b",
108 "Confirm -b\"arg\" keeps quotes"
109 ),
110 Arguments.of(
111 "Short option concatenated quote handling WITHOUT strip",
112 DefaultParser.builder().setStripLeadingAndTrailingQuotes(false).get(),
113 new String[]{"-b\"quoted string\""},
114 "\"quoted string\"",
115 "b",
116 "Confirm -b\"arg\" keeps quotes"
117 ),
118 Arguments.of(
119 "Long option quote handling WITH strip",
120 DefaultParser.builder().setStripLeadingAndTrailingQuotes(true).get(),
121 new String[]{"--bfile", "\"quoted string\""},
122 "quoted string",
123 "b",
124 "Confirm --bfile \"arg\" strips quotes"
125 ),
126 Arguments.of(
127 "Long option With Equals Quote Handling WITH Strip",
128 DefaultParser.builder().setStripLeadingAndTrailingQuotes(true).get(),
129 new String[]{"--bfile=\"quoted string\""},
130 "quoted string",
131 "b",
132 "Confirm --bfile=\"arg\" strips quotes"
133 ),
134 Arguments.of(
135 "Short option quote handling WITH strip",
136 DefaultParser.builder().setStripLeadingAndTrailingQuotes(true).get(),
137 new String[]{"-b", "\"quoted string\""},
138 "quoted string",
139 "b",
140 "Confirm -b \"arg\" strips quotes"
141 ),
142 Arguments.of(
143 "Short option concatenated quote handling WITH strip",
144 DefaultParser.builder().setStripLeadingAndTrailingQuotes(true).get(),
145 new String[]{"-b\"quoted string\""},
146 "quoted string",
147 "b",
148 "Confirm -b\"arg\" strips quotes"
149 )
150 );
151 }
152 }
153
154 @Override
155 @BeforeEach
156 public void setUp() {
157 super.setUp();
158 parser = new DefaultParser();
159 }
160
161 @Test
162 void testBuilder() {
163
164 final Builder builder = DefaultParser.builder()
165 .setStripLeadingAndTrailingQuotes(false)
166 .setAllowPartialMatching(false)
167 .setDeprecatedHandler(null);
168
169 parser = builder.build();
170 assertEquals(DefaultParser.class, parser.getClass());
171 parser = builder.get();
172 assertEquals(DefaultParser.class, parser.getClass());
173 }
174
175 @Test
176 void testDeprecated() throws ParseException {
177 final Set<Option> handler = new HashSet<>();
178 parser = DefaultParser.builder().setDeprecatedHandler(handler::add).build();
179 final Option opt1 = Option.builder().option("d1").deprecated().get();
180
181 final Option opt2 = Option.builder().option("d2").deprecated(DeprecatedAttributes.builder()
182 .setForRemoval(true)
183 .setSince("1.0")
184 .setDescription("Do this instead.").get()).get();
185
186 final Option opt3 = Option.builder().option("a").get();
187
188 final CommandLine cl = parser.parse(new Options()
189 .addOption(opt1)
190 .addOption(opt2)
191 .addOption(opt3),
192 new String[] {"-d1", "-d2", "-a"});
193
194
195 assertTrue(cl.hasOption(opt1.getOpt()));
196 assertTrue(cl.hasOption(opt2.getOpt()));
197 assertTrue(cl.hasOption(opt3.getOpt()));
198
199 assertTrue(handler.contains(opt1));
200 assertTrue(handler.contains(opt2));
201 assertFalse(handler.contains(opt3));
202 }
203
204 @Test
205 void testLegacyStopAtNonOption() throws ParseException {
206 final Option a = Option.builder().option("a").longOpt("first-letter").get();
207 final Option b = Option.builder().option("b").longOpt("second-letter").get();
208 final Option c = Option.builder().option("c").longOpt("third-letter").get();
209
210 final Options options = new Options();
211 options.addOption(a);
212 options.addOption(b);
213 options.addOption(c);
214
215 final String[] args = {"-a", "-b", "-c", "-d", "arg1", "arg2"};
216
217 final DefaultParser parser = new DefaultParser();
218
219 final CommandLine commandLine = parser.parse(options, args, null, true);
220 assertEquals(3, commandLine.getOptions().length);
221 assertEquals(3, commandLine.getArgs().length);
222 assertTrue(commandLine.getArgList().contains("-d"));
223 assertTrue(commandLine.getArgList().contains("arg1"));
224 assertTrue(commandLine.getArgList().contains("arg2"));
225
226 final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class, () -> parser.parse(options, args, null, false));
227 assertTrue(e.getMessage().contains("-d"));
228 }
229
230 @Override
231 @Test
232 @Disabled("Test case handled in the parameterized tests as \"DEFAULT behavior\"")
233 void testLongOptionWithEqualsQuoteHandling() throws Exception {
234 }
235
236 @ParameterizedTest(name = "{index}. {0}")
237 @ArgumentsSource(ExternalArgumentsProvider.class)
238 void testParameterized(final String testName, final CommandLineParser parser, final String[] args, final String expected,
239 final String option, final String message) throws Exception {
240 final CommandLine cl = parser.parse(options, args);
241
242 assertEquals(expected, cl.getOptionValue(option), message);
243 }
244
245 @Test
246 void testParseIgnoreHappyPath() throws ParseException {
247 final Option a = Option.builder().option("a").longOpt("first-letter").get();
248 final Option b = Option.builder().option("b").longOpt("second-letter").get();
249 final Option c = Option.builder().option("c").longOpt("third-letter").get();
250 final Option d = Option.builder().option("d").longOpt("fourth-letter").get();
251
252 final Options baseOptions = new Options();
253 baseOptions.addOption(a);
254 baseOptions.addOption(b);
255 final Options specificOptions = new Options();
256 specificOptions.addOption(a);
257 specificOptions.addOption(b);
258 specificOptions.addOption(c);
259 specificOptions.addOption(d);
260
261 final String[] args = {"-a", "-b", "-c", "-d", "arg1", "arg2"};
262
263 final DefaultParser parser = new DefaultParser();
264
265 final CommandLine baseCommandLine = parser.parse(baseOptions, null, DefaultParser.NonOptionAction.IGNORE, args);
266 assertEquals(2, baseCommandLine.getOptions().length);
267 assertEquals(2, baseCommandLine.getArgs().length);
268 assertTrue(baseCommandLine.hasOption("a"));
269 assertTrue(baseCommandLine.hasOption("b"));
270 assertFalse(baseCommandLine.hasOption("c"));
271 assertFalse(baseCommandLine.hasOption("d"));
272 assertFalse(baseCommandLine.getArgList().contains("-a"));
273 assertFalse(baseCommandLine.getArgList().contains("-b"));
274 assertFalse(baseCommandLine.getArgList().contains("-c"));
275 assertFalse(baseCommandLine.getArgList().contains("-d"));
276 assertTrue(baseCommandLine.getArgList().contains("arg1"));
277 assertTrue(baseCommandLine.getArgList().contains("arg2"));
278
279 final CommandLine specificCommandLine = parser.parse(specificOptions, null, DefaultParser.NonOptionAction.THROW, args);
280 assertEquals(4, specificCommandLine.getOptions().length);
281 assertEquals(2, specificCommandLine.getArgs().length);
282 assertTrue(specificCommandLine.hasOption("a"));
283 assertTrue(specificCommandLine.hasOption("b"));
284 assertTrue(specificCommandLine.hasOption("c"));
285 assertTrue(specificCommandLine.hasOption("d"));
286 assertFalse(specificCommandLine.getArgList().contains("-a"));
287 assertFalse(specificCommandLine.getArgList().contains("-b"));
288 assertFalse(specificCommandLine.getArgList().contains("-c"));
289 assertFalse(specificCommandLine.getArgList().contains("-d"));
290 assertTrue(specificCommandLine.getArgList().contains("arg1"));
291 assertTrue(specificCommandLine.getArgList().contains("arg2"));
292 }
293
294 @Test
295 void testParseIgnoreNonHappyPath() throws ParseException {
296 final Option a = Option.builder().option("a").longOpt("first-letter").get();
297 final Option b = Option.builder().option("b").longOpt("second-letter").get();
298 final Option c = Option.builder().option("c").longOpt("third-letter").get();
299
300 final Options baseOptions = new Options();
301 baseOptions.addOption(a);
302 baseOptions.addOption(b);
303 final Options specificOptions = new Options();
304 specificOptions.addOption(a);
305 specificOptions.addOption(b);
306 specificOptions.addOption(c);
307
308 final String[] args = {"-a", "-b", "-c", "-d", "arg1", "arg2"};
309
310 final DefaultParser parser = new DefaultParser();
311
312 final CommandLine baseCommandLine = parser.parse(baseOptions, null, DefaultParser.NonOptionAction.IGNORE, args);
313 assertEquals(2, baseCommandLine.getOptions().length);
314 assertEquals(2, baseCommandLine.getArgs().length);
315
316 final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class,
317 () -> parser.parse(specificOptions, null, DefaultParser.NonOptionAction.THROW, args));
318 assertTrue(e.getMessage().contains("-d"));
319 }
320
321 @Test
322 void testParseNullOption() throws ParseException {
323
324 assertThrows(NullPointerException.class, () -> new DefaultParser().parse(null, null, DefaultParser.NonOptionAction.IGNORE, "-a"));
325 }
326
327 @Test
328 void testParseSkipHappyPath() throws ParseException {
329 final Option a = Option.builder().option("a").longOpt("first-letter").get();
330 final Option b = Option.builder().option("b").longOpt("second-letter").get();
331 final Option c = Option.builder().option("c").longOpt("third-letter").get();
332 final Option d = Option.builder().option("d").longOpt("fourth-letter").get();
333
334 final Options baseOptions = new Options();
335 baseOptions.addOption(a);
336 baseOptions.addOption(b);
337 final Options specificOptions = new Options();
338 specificOptions.addOption(a);
339 specificOptions.addOption(b);
340 specificOptions.addOption(c);
341 specificOptions.addOption(d);
342
343 final String[] args = {"-a", "-b", "-c", "-d", "arg1", "arg2"};
344
345 final DefaultParser parser = new DefaultParser();
346
347 final CommandLine baseCommandLine = parser.parse(baseOptions, null, DefaultParser.NonOptionAction.SKIP, args);
348 assertEquals(2, baseCommandLine.getOptions().length);
349 assertEquals(4, baseCommandLine.getArgs().length);
350 assertTrue(baseCommandLine.hasOption("a"));
351 assertTrue(baseCommandLine.hasOption("b"));
352 assertFalse(baseCommandLine.hasOption("c"));
353 assertFalse(baseCommandLine.hasOption("d"));
354 assertFalse(baseCommandLine.getArgList().contains("-a"));
355 assertFalse(baseCommandLine.getArgList().contains("-b"));
356 assertTrue(baseCommandLine.getArgList().contains("-c"));
357 assertTrue(baseCommandLine.getArgList().contains("-d"));
358 assertTrue(baseCommandLine.getArgList().contains("arg1"));
359 assertTrue(baseCommandLine.getArgList().contains("arg2"));
360
361 final CommandLine specificCommandLine = parser.parse(specificOptions, null, DefaultParser.NonOptionAction.THROW, args);
362 assertEquals(4, specificCommandLine.getOptions().length);
363 assertEquals(2, specificCommandLine.getArgs().length);
364 assertTrue(specificCommandLine.hasOption("a"));
365 assertTrue(specificCommandLine.hasOption("b"));
366 assertTrue(specificCommandLine.hasOption("c"));
367 assertTrue(specificCommandLine.hasOption("d"));
368 assertFalse(specificCommandLine.getArgList().contains("-a"));
369 assertFalse(specificCommandLine.getArgList().contains("-b"));
370 assertFalse(specificCommandLine.getArgList().contains("-c"));
371 assertFalse(specificCommandLine.getArgList().contains("-d"));
372 assertTrue(specificCommandLine.getArgList().contains("arg1"));
373 assertTrue(specificCommandLine.getArgList().contains("arg2"));
374 }
375
376 @Test
377 void testParseSkipNonHappyPath() throws ParseException {
378 final Option a = Option.builder().option("a").longOpt("first-letter").get();
379 final Option b = Option.builder().option("b").longOpt("second-letter").get();
380 final Option c = Option.builder().option("c").longOpt("third-letter").get();
381
382 final Options baseOptions = new Options();
383 baseOptions.addOption(a);
384 baseOptions.addOption(b);
385 final Options specificOptions = new Options();
386 specificOptions.addOption(a);
387 specificOptions.addOption(b);
388 specificOptions.addOption(c);
389
390 final String[] args = {"-a", "-b", "-c", "-d", "arg1", "arg2"};
391
392 final DefaultParser parser = new DefaultParser();
393
394 final CommandLine baseCommandLine = parser.parse(baseOptions, null, DefaultParser.NonOptionAction.SKIP, args);
395 assertEquals(2, baseCommandLine.getOptions().length);
396 assertEquals(4, baseCommandLine.getArgs().length);
397
398 final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class,
399 () -> parser.parse(specificOptions, null, DefaultParser.NonOptionAction.THROW, args));
400 assertTrue(e.getMessage().contains("-d"));
401 }
402
403 @Override
404 @Test
405 @Disabled("Test case handled in the parameterized tests as \"DEFAULT behavior\"")
406 void testShortOptionConcatenatedQuoteHandling() throws Exception {
407 }
408 }