View Javadoc
1   /*
2     Licensed to the Apache Software Foundation (ASF) under one or more
3     contributor license agreements.  See the NOTICE file distributed with
4     this work for additional information regarding copyright ownership.
5     The ASF licenses this file to You under the Apache License, Version 2.0
6     (the "License"); you may not use this file except in compliance with
7     the License.  You may obtain a copy of the License at
8   
9         https://www.apache.org/licenses/LICENSE-2.0
10  
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16   */
17  
18  package org.apache.commons.cli;
19  
20  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertFalse;
23  import static org.junit.jupiter.api.Assertions.assertNotNull;
24  import static org.junit.jupiter.api.Assertions.assertNull;
25  import static org.junit.jupiter.api.Assertions.assertThrows;
26  import static org.junit.jupiter.api.Assertions.assertTrue;
27  
28  import java.util.Arrays;
29  import java.util.List;
30  import java.util.Properties;
31  
32  import org.junit.jupiter.api.BeforeEach;
33  import org.junit.jupiter.api.Test;
34  
35  /**
36   * Abstract test case testing common parser features.
37   *
38   * TODO Needs a rework using JUnit parameterized tests.
39   */
40  public abstract class AbstractParserTestCase {
41  
42      protected CommandLineParser parser;
43  
44      protected Options options;
45  
46      @SuppressWarnings("deprecation")
47      private CommandLine parse(final CommandLineParser parser, final Options options, final String[] args, final Properties properties) throws ParseException {
48          if (parser instanceof Parser) {
49              return ((Parser) parser).parse(options, args, properties);
50          }
51          if (parser instanceof DefaultParser) {
52              return ((DefaultParser) parser).parse(options, args, properties);
53          }
54          throw new UnsupportedOperationException("Default options not supported by this parser");
55      }
56  
57      @BeforeEach
58      public void setUp() {
59          //@formatter:off
60          options = new Options()
61              .addOption("a", "enable-a", false, "turn [a] on or off")
62              .addOption("b", "bfile", true, "set the value of [b]")
63              .addOption("c", "copt", false, "turn [c] on or off");
64          //@formatter:on
65      }
66  
67      @Test
68      void testAmbiguousArgParsing() throws Exception {
69          final String[] args = { "-=-" };
70          final Options options = new Options();
71          assertThrows(UnrecognizedOptionException.class, () -> parser.parse(options, args));
72      }
73  
74      @Test
75      void testAmbiguousLongWithoutEqualSingleDash() throws Exception {
76          final String[] args = { "-b", "-foobar" };
77          final Options options = new Options();
78          options.addOption(OptionBuilder.withLongOpt("foo").hasOptionalArg().create('f'));
79          options.addOption(OptionBuilder.withLongOpt("bar").hasOptionalArg().create('b'));
80          final CommandLine cl = parser.parse(options, args);
81          assertTrue(cl.hasOption("b"));
82          assertTrue(cl.hasOption("f"));
83          assertEquals("bar", cl.getOptionValue("foo"));
84      }
85  
86      @Test
87      void testAmbiguousLongWithoutEqualSingleDash2() throws Exception {
88          final String[] args = { "-b", "-foobar" };
89          final Options options = new Options();
90          options.addOption(Option.builder().longOpt("foo").option("f").optionalArg(true).get());
91          options.addOption(Option.builder().longOpt("bar").option("b").optionalArg(false).get());
92          final CommandLine cl = parser.parse(options, args);
93          assertTrue(cl.hasOption("b"));
94          assertTrue(cl.hasOption("f"));
95          assertEquals("bar", cl.getOptionValue("foo"));
96      }
97  
98      @Test
99      void testAmbiguousPartialLongOption1() throws Exception {
100         final String[] args = { "--ver" };
101         final Options options = new Options();
102         options.addOption(OptionBuilder.withLongOpt("version").create());
103         options.addOption(OptionBuilder.withLongOpt("verbose").create());
104         final AmbiguousOptionException e = assertThrows(AmbiguousOptionException.class, () -> parser.parse(options, args));
105         assertEquals("--ver", e.getOption(), "Partial option");
106         assertNotNull(e.getMatchingOptions(), "Matching options null");
107         assertEquals(2, e.getMatchingOptions().size(), "Matching options size");
108     }
109 
110     @Test
111     void testAmbiguousPartialLongOption2() throws Exception {
112         final String[] args = { "-ver" };
113         final Options options = new Options();
114         options.addOption(OptionBuilder.withLongOpt("version").create());
115         options.addOption(OptionBuilder.withLongOpt("verbose").create());
116         final AmbiguousOptionException e = assertThrows(AmbiguousOptionException.class, () -> parser.parse(options, args));
117         assertEquals("-ver", e.getOption(), "Partial option");
118         assertNotNull(e.getMatchingOptions(), "Matching options null");
119         assertEquals(2, e.getMatchingOptions().size(), "Matching options size");
120     }
121 
122     @Test
123     void testAmbiguousPartialLongOption3() throws Exception {
124         final String[] args = { "--ver=1" };
125         final Options options = new Options();
126         options.addOption(OptionBuilder.withLongOpt("version").create());
127         options.addOption(OptionBuilder.withLongOpt("verbose").hasOptionalArg().create());
128         final AmbiguousOptionException e = assertThrows(AmbiguousOptionException.class, () -> parser.parse(options, args));
129         assertEquals("--ver", e.getOption(), "Partial option");
130         assertNotNull(e.getMatchingOptions(), "Matching options null");
131         assertEquals(2, e.getMatchingOptions().size(), "Matching options size");
132     }
133 
134     @Test
135     void testAmbiguousPartialLongOption4() throws Exception {
136         final String[] args = { "-ver=1" };
137         final Options options = new Options();
138         options.addOption(OptionBuilder.withLongOpt("version").create());
139         options.addOption(OptionBuilder.withLongOpt("verbose").hasOptionalArg().create());
140         final AmbiguousOptionException e = assertThrows(AmbiguousOptionException.class, () -> parser.parse(options, args));
141         assertEquals("-ver", e.getOption(), "Partial option");
142         assertNotNull(e.getMatchingOptions(), "Matching options null");
143         assertEquals(2, e.getMatchingOptions().size(), "Matching options size");
144     }
145 
146     @Test
147     void testArgumentStartingWithHyphen() throws Exception {
148         final String[] args = { "-b", "-foo" };
149         final CommandLine cl = parser.parse(options, args);
150         assertEquals("-foo", cl.getOptionValue("b"));
151     }
152 
153     @Test
154     void testBursting() throws Exception {
155         final String[] args = { "-acbtoast", "foo", "bar" };
156         final CommandLine cl = parser.parse(options, args);
157         assertTrue(cl.hasOption("a"), "Confirm -a is set");
158         assertTrue(cl.hasOption("b"), "Confirm -b is set");
159         assertTrue(cl.hasOption("c"), "Confirm -c is set");
160         assertEquals("toast", cl.getOptionValue("b"), "Confirm arg of -b");
161         assertEquals(2, cl.getArgList().size(), "Confirm size of extra args");
162     }
163 
164     @Test
165     void testDoubleDash1() throws Exception {
166         final String[] args = { "--copt", "--", "-b", "toast" };
167         final CommandLine cl = parser.parse(options, args);
168         assertTrue(cl.hasOption("c"), "Confirm -c is set");
169         assertFalse(cl.hasOption("b"), "Confirm -b is not set");
170         assertEquals(2, cl.getArgList().size(), "Confirm 2 extra args: " + cl.getArgList().size());
171     }
172 
173     @Test
174     void testDoubleDash2() throws Exception {
175         final Options options = new Options();
176         options.addOption(OptionBuilder.hasArg().create('n'));
177         options.addOption(OptionBuilder.create('m'));
178         final MissingArgumentException e = assertThrows(MissingArgumentException.class, () -> parser.parse(options, new String[] { "-n", "--", "-m" }));
179         assertNotNull(e.getOption(), "option null");
180         assertEquals("n", e.getOption().getOpt());
181     }
182 
183     @Test
184     void testLongOptionQuoteHandling() throws Exception {
185         final String[] args = { "--bfile", "\"quoted string\"" };
186         final CommandLine cl = parser.parse(options, args);
187         assertEquals("quoted string", cl.getOptionValue("b"), "Confirm --bfile \"arg\" strips quotes");
188     }
189 
190     @Test
191     void testLongOptionWithEqualsQuoteHandling() throws Exception {
192         final String[] args = { "--bfile=\"quoted string\"" };
193         final CommandLine cl = parser.parse(options, args);
194         assertEquals("quoted string", cl.getOptionValue("b"), "Confirm --bfile=\"arg\" strips quotes");
195     }
196 
197     @Test
198     void testLongWithEqualDoubleDash() throws Exception {
199         final String[] args = { "--foo=bar" };
200         final Options options = new Options();
201         options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
202         final CommandLine cl = parser.parse(options, args);
203         assertEquals("bar", cl.getOptionValue("foo"));
204     }
205 
206     @Test
207     void testLongWithEqualSingleDash() throws Exception {
208         final String[] args = { "-foo=bar" };
209         final Options options = new Options();
210         options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
211         final CommandLine cl = parser.parse(options, args);
212         assertEquals("bar", cl.getOptionValue("foo"));
213     }
214 
215     @Test
216     void testLongWithoutEqualDoubleDash() throws Exception {
217         final String[] args = { "--foobar" };
218         final Options options = new Options();
219         options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
220         final CommandLine cl = parser.parse(options, args, true);
221         assertFalse(cl.hasOption("foo")); // foo isn't expected to be recognized with a double dash
222     }
223 
224     @Test
225     void testLongWithoutEqualSingleDash() throws Exception {
226         final Options options = new Options();
227         options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
228         final CommandLine cl = parser.parse(options, new String[] { "-foobar" });
229         assertEquals("bar", cl.getOptionValue("foo"));
230     }
231 
232     @Test
233     void testLongWithUnexpectedArgument1() throws Exception {
234         final Options options = new Options();
235         options.addOption(OptionBuilder.withLongOpt("foo").create('f'));
236         final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class, () -> parser.parse(options, new String[] { "--foo=bar" }));
237         assertEquals("--foo=bar", e.getOption());
238     }
239 
240     @Test
241     void testLongWithUnexpectedArgument2() throws Exception {
242         final Options options = new Options();
243         options.addOption(OptionBuilder.withLongOpt("foo").create('f'));
244         final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class, () -> parser.parse(options, new String[] { "-foobar" }));
245         assertEquals("-foobar", e.getOption());
246     }
247 
248     @Test
249     void testMissingArg() throws Exception {
250         final MissingArgumentException e = assertThrows(MissingArgumentException.class, () -> parser.parse(options, new String[] { "-b" }));
251         assertEquals("b", e.getOption().getOpt(), "option missing an argument");
252     }
253 
254     @Test
255     void testMissingArgWithBursting() throws Exception {
256         final String[] args = { "-acb" };
257         final MissingArgumentException e = assertThrows(MissingArgumentException.class, () -> parser.parse(options, args));
258         assertEquals("b", e.getOption().getOpt(), "option missing an argument");
259     }
260 
261     @Test
262     void testMissingRequiredGroup() throws Exception {
263         final OptionGroup optionGroup = new OptionGroup();
264         optionGroup.addOption(OptionBuilder.create("a"));
265         optionGroup.addOption(OptionBuilder.create("b"));
266         optionGroup.setRequired(true);
267         final Options options = new Options();
268         options.addOptionGroup(optionGroup);
269         options.addOption(OptionBuilder.isRequired().create("c"));
270         final MissingOptionException e = assertThrows(MissingOptionException.class, () -> parser.parse(options, new String[] { "-c" }));
271         assertEquals(1, e.getMissingOptions().size());
272         assertTrue(e.getMissingOptions().get(0) instanceof OptionGroup);
273     }
274 
275     @Test
276     void testMissingRequiredOption() {
277         final String[] args = { "-a" };
278         final Options options = new Options();
279         options.addOption("a", "enable-a", false, null);
280         options.addOption(OptionBuilder.withLongOpt("bfile").hasArg().isRequired().create('b'));
281         final MissingOptionException e = assertThrows(MissingOptionException.class, () -> parser.parse(options, args));
282         assertEquals("Missing required option: b", e.getMessage(), "Incorrect exception message");
283         assertTrue(e.getMissingOptions().contains("b"));
284     }
285 
286     @Test
287     void testMissingRequiredOptions() {
288         final String[] args = { "-a" };
289         final Options options = new Options();
290         options.addOption("a", "enable-a", false, null);
291         options.addOption(OptionBuilder.withLongOpt("bfile").hasArg().isRequired().create('b'));
292         options.addOption(OptionBuilder.withLongOpt("cfile").hasArg().isRequired().create('c'));
293         final MissingOptionException e = assertThrows(MissingOptionException.class, () -> parser.parse(options, args));
294         assertEquals("Missing required options: b, c", e.getMessage(), "Incorrect exception message");
295         assertTrue(e.getMissingOptions().contains("b"));
296         assertTrue(e.getMissingOptions().contains("c"));
297     }
298 
299     @Test
300     void testMultiple() throws Exception {
301         final String[] args = { "-c", "foobar", "-b", "toast" };
302         CommandLine cl = parser.parse(options, args, true);
303         assertTrue(cl.hasOption("c"), "Confirm -c is set");
304         assertEquals(3, cl.getArgList().size(), "Confirm  3 extra args: " + cl.getArgList().size());
305         cl = parser.parse(options, cl.getArgs());
306         assertFalse(cl.hasOption("c"), "Confirm -c is not set");
307         assertTrue(cl.hasOption("b"), "Confirm -b is set");
308         assertEquals("toast", cl.getOptionValue("b"), "Confirm arg of -b");
309         assertEquals(1, cl.getArgList().size(), "Confirm  1 extra arg: " + cl.getArgList().size());
310         assertEquals("foobar", cl.getArgList().get(0), "Confirm  value of extra arg: " + cl.getArgList().get(0));
311     }
312 
313     @Test
314     void testMultipleWithLong() throws Exception {
315         final String[] args = { "--copt", "foobar", "--bfile", "toast" };
316         CommandLine cl = parser.parse(options, args, true);
317         assertTrue(cl.hasOption("c"), "Confirm -c is set");
318         assertEquals(3, cl.getArgList().size(), "Confirm  3 extra args: " + cl.getArgList().size());
319         cl = parser.parse(options, cl.getArgs());
320         assertFalse(cl.hasOption("c"), "Confirm -c is not set");
321         assertTrue(cl.hasOption("b"), "Confirm -b is set");
322         assertEquals("toast", cl.getOptionValue("b"), "Confirm arg of -b");
323         assertEquals(1, cl.getArgList().size(), "Confirm  1 extra arg: " + cl.getArgList().size());
324         assertEquals("foobar", cl.getArgList().get(0), "Confirm  value of extra arg: " + cl.getArgList().get(0));
325     }
326 
327     @Test
328     void testMultipleWithNull() throws Exception {
329         final String[] args = { null, "-c", null, "foobar", null, "-b", null, "toast", null };
330         CommandLine cl = parser.parse(options, args, true);
331         assertTrue(cl.hasOption("c"), "Confirm -c is set");
332         assertEquals(3, cl.getArgList().size(), "Confirm  3 extra args: " + cl.getArgList().size());
333         cl = parser.parse(options, cl.getArgs());
334         assertFalse(cl.hasOption("c"), "Confirm -c is not set");
335         assertTrue(cl.hasOption("b"), "Confirm -b is set");
336         assertEquals("toast", cl.getOptionValue("b"), "Confirm arg of -b");
337         assertEquals(1, cl.getArgList().size(), "Confirm  1 extra arg: " + cl.getArgList().size());
338         assertEquals("foobar", cl.getArgList().get(0), "Confirm  value of extra arg: " + cl.getArgList().get(0));
339     }
340 
341     @Test
342     void testNegativeArgument() throws Exception {
343         final String[] args = { "-b", "-1" };
344         final CommandLine cl = parser.parse(options, args);
345         assertEquals("-1", cl.getOptionValue("b"));
346     }
347 
348     @Test
349     void testNegativeOption() throws Exception {
350         final String[] args = { "-b", "-1" };
351         options.addOption("1", false, null);
352         final CommandLine cl = parser.parse(options, args);
353         assertEquals("-1", cl.getOptionValue("b"));
354     }
355 
356     @Test
357     void testOptionalArgsOptionBuilder() throws Exception {
358         final Options options = new Options();
359         options.addOption(OptionBuilder.hasOptionalArgs(2).create('i'));
360         final Properties properties = new Properties();
361 
362         CommandLine cmd = parse(parser, options, new String[] { "-i" }, properties);
363         assertTrue(cmd.hasOption("i"));
364         assertNull(cmd.getOptionValues("i"));
365 
366         cmd = parse(parser, options, new String[] { "-i", "paper" }, properties);
367         assertTrue(cmd.hasOption("i"));
368         assertArrayEquals(new String[] { "paper" }, cmd.getOptionValues("i"));
369 
370         cmd = parse(parser, options, new String[] { "-i", "paper", "scissors" }, properties);
371         assertTrue(cmd.hasOption("i"));
372         assertArrayEquals(new String[] { "paper", "scissors" }, cmd.getOptionValues("i"));
373 
374         cmd = parse(parser, options, new String[] { "-i", "paper", "scissors", "rock" }, properties);
375         assertTrue(cmd.hasOption("i"));
376         assertArrayEquals(new String[] { "paper", "scissors" }, cmd.getOptionValues("i"));
377         assertArrayEquals(new String[] { "rock" }, cmd.getArgs());
378     }
379 
380     @Test
381     void testOptionalArgsOptionDotBuilder() throws Exception {
382         final Options options = new Options();
383         options.addOption(Option.builder("i").numberOfArgs(2).optionalArg(true).get());
384         final Properties properties = new Properties();
385 
386         CommandLine cmd = parse(parser, options, new String[] { "-i" }, properties);
387         assertTrue(cmd.hasOption("i"));
388         assertNull(cmd.getOptionValues("i"));
389 
390         cmd = parse(parser, options, new String[] { "-i", "paper" }, properties);
391         assertTrue(cmd.hasOption("i"));
392         assertArrayEquals(new String[] { "paper" }, cmd.getOptionValues("i"));
393 
394         cmd = parse(parser, options, new String[] { "-i", "paper", "scissors" }, properties);
395         assertTrue(cmd.hasOption("i"));
396         assertArrayEquals(new String[] { "paper", "scissors" }, cmd.getOptionValues("i"));
397 
398         cmd = parse(parser, options, new String[] { "-i", "paper", "scissors", "rock" }, properties);
399         assertTrue(cmd.hasOption("i"));
400         assertArrayEquals(new String[] { "paper", "scissors" }, cmd.getOptionValues("i"));
401         assertArrayEquals(new String[] { "rock" }, cmd.getArgs());
402 
403         options.addOption(Option.builder("j").numberOfArgs(3).optionalArg(true).get());
404         cmd = parse(parser, options, new String[] { "-j" }, properties);
405     }
406 
407     @Test
408     void testOptionAndRequiredOption() throws Exception {
409         final String[] args = { "-a", "-b", "file" };
410 
411         final Options options = new Options();
412         options.addOption("a", "enable-a", false, null);
413         options.addOption(OptionBuilder.withLongOpt("bfile").hasArg().isRequired().create('b'));
414 
415         final CommandLine cl = parser.parse(options, args);
416 
417         assertTrue(cl.hasOption("a"), "Confirm -a is set");
418         assertTrue(cl.hasOption("b"), "Confirm -b is set");
419         assertEquals("file", cl.getOptionValue("b"), "Confirm arg of -b");
420         assertTrue(cl.getArgList().isEmpty(), "Confirm NO of extra args");
421     }
422 
423     @Test
424     void testOptionGroup() throws Exception {
425         final OptionGroup optionGroup = new OptionGroup();
426         assertFalse(optionGroup.isSelected());
427         optionGroup.addOption(OptionBuilder.create("a"));
428         optionGroup.addOption(OptionBuilder.create("b"));
429 
430         final Options options = new Options();
431         options.addOptionGroup(optionGroup);
432 
433         parser.parse(options, new String[] { "-b" });
434 
435         assertTrue(optionGroup.isSelected());
436         assertEquals("b", optionGroup.getSelected(), "selected option");
437     }
438 
439     @Test
440     void testOptionGroupLong() throws Exception {
441         final OptionGroup optionGroup = new OptionGroup();
442         optionGroup.addOption(OptionBuilder.withLongOpt("foo").create());
443         optionGroup.addOption(OptionBuilder.withLongOpt("bar").create());
444 
445         final Options options = new Options();
446         options.addOptionGroup(optionGroup);
447 
448         final CommandLine cl = parser.parse(options, new String[] { "--bar" });
449 
450         assertTrue(cl.hasOption("bar"));
451         assertTrue(optionGroup.isSelected());
452         assertEquals("bar", optionGroup.getSelected(), "selected option");
453     }
454 
455     @Test
456     void testPartialLongOptionSingleDash() throws Exception {
457         final String[] args = { "-ver" };
458 
459         final Options options = new Options();
460         options.addOption(OptionBuilder.withLongOpt("version").create());
461         options.addOption(OptionBuilder.hasArg().create('v'));
462 
463         final CommandLine cl = parser.parse(options, args);
464 
465         assertTrue(cl.hasOption("version"), "Confirm --version is set");
466         assertFalse(cl.hasOption("v"), "Confirm -v is not set");
467     }
468 
469     @Test
470     void testPropertiesOption1() throws Exception {
471         final String[] args = { "-Jsource=1.5", "-J", "target", "1.5", "foo" };
472 
473         final Options options = new Options();
474         options.addOption(OptionBuilder.withValueSeparator().hasArgs(2).create('J'));
475 
476         final CommandLine cl = parser.parse(options, args);
477 
478         final List<String> values = Arrays.asList(cl.getOptionValues("J"));
479         assertNotNull(values, "null values");
480         assertEquals(4, values.size(), "number of values");
481         assertEquals("source", values.get(0), "value 1");
482         assertEquals("1.5", values.get(1), "value 2");
483         assertEquals("target", values.get(2), "value 3");
484         assertEquals("1.5", values.get(3), "value 4");
485 
486         final List<?> argsleft = cl.getArgList();
487         assertEquals(1, argsleft.size(), "Should be 1 arg left");
488         assertEquals("foo", argsleft.get(0), "Expecting foo");
489     }
490 
491     @Test
492     void testPropertiesOption2() throws Exception {
493         final String[] args = { "-Dparam1", "-Dparam2=value2", "-D" };
494 
495         final Options options = new Options();
496         options.addOption(OptionBuilder.withValueSeparator().hasOptionalArgs(2).create('D'));
497 
498         final CommandLine cl = parser.parse(options, args);
499 
500         final Properties props = cl.getOptionProperties("D");
501         assertNotNull(props, "null properties");
502         assertEquals(2, props.size(), "number of properties in " + props);
503         assertEquals("true", props.getProperty("param1"), "property 1");
504         assertEquals("value2", props.getProperty("param2"), "property 2");
505 
506         final List<?> argsleft = cl.getArgList();
507         assertEquals(0, argsleft.size(), "Should be no arg left");
508     }
509 
510     @Test
511     void testPropertyOptionFlags() throws Exception {
512         final Options options = new Options();
513         options.addOption("a", false, "toggle -a");
514         options.addOption("c", "c", false, "toggle -c");
515         options.addOption(OptionBuilder.hasOptionalArg().create('e'));
516 
517         Properties properties = new Properties();
518         properties.setProperty("a", "true");
519         properties.setProperty("c", "yes");
520         properties.setProperty("e", "1");
521 
522         CommandLine cmd = parse(parser, options, null, properties);
523         assertTrue(cmd.hasOption("a"));
524         assertTrue(cmd.hasOption("c"));
525         assertTrue(cmd.hasOption("e"));
526 
527         properties = new Properties();
528         properties.setProperty("a", "false");
529         properties.setProperty("c", "no");
530         properties.setProperty("e", "0");
531 
532         cmd = parse(parser, options, null, properties);
533         assertFalse(cmd.hasOption("a"));
534         assertFalse(cmd.hasOption("c"));
535         assertTrue(cmd.hasOption("e")); // this option accepts an argument
536 
537         properties = new Properties();
538         properties.setProperty("a", "TRUE");
539         properties.setProperty("c", "nO");
540         properties.setProperty("e", "TrUe");
541 
542         cmd = parse(parser, options, null, properties);
543         assertTrue(cmd.hasOption("a"));
544         assertFalse(cmd.hasOption("c"));
545         assertTrue(cmd.hasOption("e"));
546 
547         properties = new Properties();
548         properties.setProperty("a", "just a string");
549         properties.setProperty("e", "");
550 
551         cmd = parse(parser, options, null, properties);
552         assertFalse(cmd.hasOption("a"));
553         assertFalse(cmd.hasOption("c"));
554         assertTrue(cmd.hasOption("e"));
555 
556         properties = new Properties();
557         properties.setProperty("a", "0");
558         properties.setProperty("c", "1");
559 
560         cmd = parse(parser, options, null, properties);
561         assertFalse(cmd.hasOption("a"));
562         assertTrue(cmd.hasOption("c"));
563     }
564 
565     @Test
566     void testPropertyOptionGroup() throws Exception {
567         final Options options = new Options();
568 
569         final OptionGroup optionGroup1 = new OptionGroup();
570         optionGroup1.addOption(new Option("a", null));
571         optionGroup1.addOption(new Option("b", null));
572         options.addOptionGroup(optionGroup1);
573 
574         final OptionGroup optionGroup2 = new OptionGroup();
575         optionGroup2.addOption(new Option("x", null));
576         optionGroup2.addOption(new Option("y", null));
577         options.addOptionGroup(optionGroup2);
578 
579         final String[] args = { "-a" };
580 
581         final Properties properties = new Properties();
582         properties.put("b", "true");
583         properties.put("x", "true");
584 
585         final CommandLine cmd = parse(parser, options, args, properties);
586 
587         assertTrue(cmd.hasOption("a"));
588         assertFalse(cmd.hasOption("b"));
589         assertTrue(cmd.hasOption("x"));
590         assertFalse(cmd.hasOption("y"));
591     }
592 
593     @Test
594     void testPropertyOptionMultipleValues() throws Exception {
595         final Options options = new Options();
596         options.addOption(OptionBuilder.hasArgs().withValueSeparator(',').create('k'));
597 
598         final Properties properties = new Properties();
599         properties.setProperty("k", "one,two");
600 
601         final CommandLine cmd = parse(parser, options, null, properties);
602         assertTrue(cmd.hasOption("k"));
603         final String[] values = { "one", "two" };
604         assertArrayEquals(values, cmd.getOptionValues('k'));
605     }
606 
607     @Test
608     void testPropertyOptionRequired() throws Exception {
609         final Options options = new Options();
610         options.addOption(OptionBuilder.isRequired().create("f"));
611 
612         final Properties properties = new Properties();
613         properties.setProperty("f", "true");
614 
615         final CommandLine cmd = parse(parser, options, null, properties);
616         assertTrue(cmd.hasOption("f"));
617     }
618 
619     @Test
620     void testPropertyOptionSingularValue() throws Exception {
621         final Options options = new Options();
622         options.addOption(OptionBuilder.hasOptionalArgs(2).withLongOpt("hide").create());
623 
624         final Properties properties = new Properties();
625         properties.setProperty("hide", "seek");
626 
627         final CommandLine cmd = parse(parser, options, null, properties);
628         assertTrue(cmd.hasOption("hide"));
629         assertEquals("seek", cmd.getOptionValue("hide"));
630         assertFalse(cmd.hasOption("fake"));
631     }
632 
633     @Test
634     void testPropertyOptionUnexpected() throws Exception {
635         final Options options = new Options();
636         final Properties properties = new Properties();
637         properties.setProperty("f", "true");
638         assertThrows(UnrecognizedOptionException.class, () -> parse(parser, options, null, properties));
639     }
640 
641     @Test
642     void testPropertyOverrideValues() throws Exception {
643         final Options options = new Options();
644         options.addOption(OptionBuilder.hasOptionalArgs(2).create('i'));
645         options.addOption(OptionBuilder.hasOptionalArgs().create('j'));
646 
647         final String[] args = { "-j", "found", "-i", "ink" };
648 
649         final Properties properties = new Properties();
650         properties.setProperty("j", "seek");
651 
652         final CommandLine cmd = parse(parser, options, args, properties);
653         assertTrue(cmd.hasOption("j"));
654         assertEquals("found", cmd.getOptionValue("j"));
655         assertTrue(cmd.hasOption("i"));
656         assertEquals("ink", cmd.getOptionValue("i"));
657         assertFalse(cmd.hasOption("fake"));
658     }
659 
660     @Test
661     void testReuseOptionsTwice() throws Exception {
662         final Options options = new Options();
663         options.addOption(OptionBuilder.isRequired().create('v'));
664         // first parsing
665         parser.parse(options, new String[] { "-v" });
666         // second parsing, with the same Options instance and an invalid command line
667         assertThrows(MissingOptionException.class, () -> parser.parse(options, new String[0]));
668     }
669 
670     @Test
671     void testShortOptionConcatenatedQuoteHandling() throws Exception {
672         final CommandLine cl = parser.parse(options, new String[] { "-b\"quoted string\"" });
673         assertEquals("quoted string", cl.getOptionValue("b"), "Confirm -b\"arg\" strips quotes");
674     }
675 
676     @Test
677     void testShortOptionQuoteHandling() throws Exception {
678         final CommandLine cl = parser.parse(options, new String[] { "-b", "\"quoted string\"" });
679         assertEquals("quoted string", cl.getOptionValue("b"), "Confirm -b \"arg\" strips quotes");
680     }
681 
682     @Test
683     void testShortWithEqual() throws Exception {
684         final Options options = new Options();
685         options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
686         final CommandLine cl = parser.parse(options, new String[] { "-f=bar" });
687         assertEquals("bar", cl.getOptionValue("foo"));
688     }
689 
690     @Test
691     void testShortWithoutEqual() throws Exception {
692         final Options options = new Options();
693         options.addOption(OptionBuilder.withLongOpt("foo").hasArg().create('f'));
694         final CommandLine cl = parser.parse(options, new String[] { "-fbar" });
695         assertEquals("bar", cl.getOptionValue("foo"));
696     }
697 
698     @Test
699     void testShortWithUnexpectedArgument() throws Exception {
700         final Options options = new Options();
701         options.addOption(OptionBuilder.withLongOpt("foo").create('f'));
702         final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class, () -> parser.parse(options, new String[] { "-f=bar" }));
703         assertEquals("-f=bar", e.getOption());
704     }
705 
706     @Test
707     void testSimpleLong() throws Exception {
708         final CommandLine cl = parser.parse(options, new String[] { "--enable-a", "--bfile", "toast", "foo", "bar" });
709         assertTrue(cl.hasOption("a"), "Confirm -a is set");
710         assertTrue(cl.hasOption("b"), "Confirm -b is set");
711         assertEquals("toast", cl.getOptionValue("b"), "Confirm arg of -b");
712         assertEquals("toast", cl.getOptionValue("bfile"), "Confirm arg of --bfile");
713         assertEquals(2, cl.getArgList().size(), "Confirm size of extra args");
714     }
715 
716     @Test
717     void testSimpleShort() throws Exception {
718         final CommandLine cl = parser.parse(options, new String[] { "-a", "-b", "toast", "foo", "bar" });
719         assertTrue(cl.hasOption("a"), "Confirm -a is set");
720         assertTrue(cl.hasOption("b"), "Confirm -b is set");
721         assertEquals("toast", cl.getOptionValue("b"), "Confirm arg of -b");
722         assertEquals(2, cl.getArgList().size(), "Confirm size of extra args");
723     }
724 
725     @Test
726     void testSingleDash() throws Exception {
727         final CommandLine cl = parser.parse(options, new String[] { "--copt", "-b", "-", "-a", "-" });
728         assertTrue(cl.hasOption("a"), "Confirm -a is set");
729         assertTrue(cl.hasOption("b"), "Confirm -b is set");
730         assertEquals("-", cl.getOptionValue("b"), "Confirm arg of -b");
731         assertEquals(1, cl.getArgList().size(), "Confirm 1 extra arg: " + cl.getArgList().size());
732         assertEquals("-", cl.getArgList().get(0), "Confirm value of extra arg: " + cl.getArgList().get(0));
733     }
734 
735     @Test
736     void testStopAtExpectedArg() throws Exception {
737         final String[] args = { "-b", "foo" };
738         final CommandLine cl = parser.parse(options, args, true);
739         assertTrue(cl.hasOption('b'), "Confirm -b is set");
740         assertEquals("foo", cl.getOptionValue('b'), "Confirm -b is set");
741         assertTrue(cl.getArgList().isEmpty(), "Confirm no extra args: " + cl.getArgList().size());
742     }
743 
744     @Test
745     void testStopAtNonOptionLong() throws Exception {
746         final String[] args = { "--zop==1", "-abtoast", "--b=bar" };
747         final CommandLine cl = parser.parse(options, args, true);
748         assertFalse(cl.hasOption("a"), "Confirm -a is not set");
749         assertFalse(cl.hasOption("b"), "Confirm -b is not set");
750         assertEquals(3, cl.getArgList().size(), "Confirm  3 extra args: " + cl.getArgList().size());
751     }
752 
753     @Test
754     void testStopAtNonOptionShort() throws Exception {
755         final CommandLine cl = parser.parse(options, new String[] { "-z", "-a", "-btoast" }, true);
756         assertFalse(cl.hasOption("a"), "Confirm -a is not set");
757         assertEquals(3, cl.getArgList().size(), "Confirm  3 extra args: " + cl.getArgList().size());
758     }
759 
760     @Test
761     void testStopAtUnexpectedArg() throws Exception {
762         final CommandLine cl = parser.parse(options, new String[] { "-c", "foober", "-b", "toast" }, true);
763         assertTrue(cl.hasOption("c"), "Confirm -c is set");
764         assertEquals(3, cl.getArgList().size(), "Confirm  3 extra args: " + cl.getArgList().size());
765     }
766 
767     @Test
768     void testStopBursting() throws Exception {
769         final CommandLine cl = parser.parse(options, new String[] { "-azc" }, true);
770         assertTrue(cl.hasOption("a"), "Confirm -a is set");
771         assertFalse(cl.hasOption("c"), "Confirm -c is not set");
772         assertEquals(1, cl.getArgList().size(), "Confirm  1 extra arg: " + cl.getArgList().size());
773         assertTrue(cl.getArgList().contains("zc"));
774     }
775 
776     @Test
777     void testStopBursting2() throws Exception {
778         CommandLine cl = parser.parse(options, new String[] { "-c", "foobar", "-btoast" }, true);
779         assertTrue(cl.hasOption("c"), "Confirm -c is set");
780         assertEquals(2, cl.getArgList().size(), "Confirm  2 extra args: " + cl.getArgList().size());
781         cl = parser.parse(options, cl.getArgs());
782         assertFalse(cl.hasOption("c"), "Confirm -c is not set");
783         assertTrue(cl.hasOption("b"), "Confirm -b is set");
784         assertEquals("toast", cl.getOptionValue("b"), "Confirm arg of -b");
785         assertEquals(1, cl.getArgList().size(), "Confirm  1 extra arg: " + cl.getArgList().size());
786         assertEquals("foobar", cl.getArgList().get(0), "Confirm  value of extra arg: " + cl.getArgList().get(0));
787     }
788 
789     @Test
790     void testUnambiguousPartialLongOption1() throws Exception {
791         final Options options = new Options();
792         options.addOption(OptionBuilder.withLongOpt("version").create());
793         options.addOption(OptionBuilder.withLongOpt("help").create());
794         final CommandLine cl = parser.parse(options, new String[] { "--ver" });
795         assertTrue(cl.hasOption("version"), "Confirm --version is set");
796     }
797 
798     @Test
799     void testUnambiguousPartialLongOption2() throws Exception {
800         final Options options = new Options();
801         options.addOption(OptionBuilder.withLongOpt("version").create());
802         options.addOption(OptionBuilder.withLongOpt("help").create());
803         final CommandLine cl = parser.parse(options, new String[] { "-ver" });
804         assertTrue(cl.hasOption("version"), "Confirm --version is set");
805     }
806 
807     @Test
808     void testUnambiguousPartialLongOption3() throws Exception {
809         final Options options = new Options();
810         options.addOption(OptionBuilder.withLongOpt("verbose").hasOptionalArg().create());
811         options.addOption(OptionBuilder.withLongOpt("help").create());
812         final CommandLine cl = parser.parse(options, new String[] { "--ver=1" });
813         assertTrue(cl.hasOption("verbose"), "Confirm --verbose is set");
814         assertEquals("1", cl.getOptionValue("verbose"));
815     }
816 
817     @Test
818     void testUnambiguousPartialLongOption4() throws Exception {
819         final Options options = new Options();
820         options.addOption(OptionBuilder.withLongOpt("verbose").hasOptionalArg().create());
821         options.addOption(OptionBuilder.withLongOpt("help").create());
822         final CommandLine cl = parser.parse(options, new String[] { "-ver=1" });
823         assertTrue(cl.hasOption("verbose"), "Confirm --verbose is set");
824         assertEquals("1", cl.getOptionValue("verbose"));
825     }
826 
827     @Test
828     void testUnlimitedArgs() throws Exception {
829         final Options options = new Options();
830         options.addOption(OptionBuilder.hasArgs().create("e"));
831         options.addOption(OptionBuilder.hasArgs().create("f"));
832         final CommandLine cl = parser.parse(options, new String[] { "-e", "one", "two", "-f", "alpha" });
833         assertTrue(cl.hasOption("e"), "Confirm -e is set");
834         assertEquals(2, cl.getOptionValues("e").length, "number of arg for -e");
835         assertTrue(cl.hasOption("f"), "Confirm -f is set");
836         assertEquals(1, cl.getOptionValues("f").length, "number of arg for -f");
837     }
838 
839     @Test
840     void testUnrecognizedOption() throws Exception {
841         final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class,
842                 () -> parser.parse(options, new String[] { "-a", "-d", "-b", "toast", "foo", "bar" }));
843         assertEquals("-d", e.getOption());
844     }
845 
846     @Test
847     void testUnrecognizedOptionWithBursting() throws Exception {
848         final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class,
849                 () -> parser.parse(options, new String[] { "-adbtoast", "foo", "bar" }));
850         assertEquals("-adbtoast", e.getOption());
851     }
852 
853     @Test
854     void testWithRequiredOption() throws Exception {
855         final String[] args = { "-b", "file" };
856         final Options options = new Options();
857         options.addOption("a", "enable-a", false, null);
858         options.addOption(OptionBuilder.withLongOpt("bfile").hasArg().isRequired().create('b'));
859         final CommandLine cl = parser.parse(options, args);
860         assertFalse(cl.hasOption("a"), "Confirm -a is NOT set");
861         assertTrue(cl.hasOption("b"), "Confirm -b is set");
862         assertEquals("file", cl.getOptionValue("b"), "Confirm arg of -b");
863         assertTrue(cl.getArgList().isEmpty(), "Confirm NO of extra args");
864     }
865 }