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         http://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.Assert.assertEquals;
21  import static org.junit.Assert.assertNull;
22  import static org.junit.Assert.fail;
23  
24  import java.io.ByteArrayOutputStream;
25  import java.io.PrintWriter;
26  import java.io.StringWriter;
27  
28  import org.junit.Test;
29  
30  /**
31   * Test case for the HelpFormatter class.
32   */
33  public class HelpFormatterTest {
34      private static final String EOL = System.lineSeparator();
35  
36      @Test
37      public void testAccessors() {
38          final HelpFormatter formatter = new HelpFormatter();
39  
40          formatter.setArgName("argname");
41          assertEquals("arg name", "argname", formatter.getArgName());
42  
43          formatter.setDescPadding(3);
44          assertEquals("desc padding", 3, formatter.getDescPadding());
45  
46          formatter.setLeftPadding(7);
47          assertEquals("left padding", 7, formatter.getLeftPadding());
48  
49          formatter.setLongOptPrefix("~~");
50          assertEquals("long opt prefix", "~~", formatter.getLongOptPrefix());
51  
52          formatter.setNewLine("\n");
53          assertEquals("new line", "\n", formatter.getNewLine());
54  
55          formatter.setOptPrefix("~");
56          assertEquals("opt prefix", "~", formatter.getOptPrefix());
57  
58          formatter.setSyntaxPrefix("-> ");
59          assertEquals("syntax prefix", "-> ", formatter.getSyntaxPrefix());
60  
61          formatter.setWidth(80);
62          assertEquals("width", 80, formatter.getWidth());
63      }
64  
65      @Test
66      public void testAutomaticUsage() {
67          final HelpFormatter hf = new HelpFormatter();
68          Options options;
69          String expected = "usage: app [-a]";
70          final ByteArrayOutputStream out = new ByteArrayOutputStream();
71          final PrintWriter pw = new PrintWriter(out);
72  
73          options = new Options().addOption("a", false, "aaaa aaaa aaaa aaaa aaaa");
74          hf.printUsage(pw, 60, "app", options);
75          pw.flush();
76          assertEquals("simple auto usage", expected, out.toString().trim());
77          out.reset();
78  
79          expected = "usage: app [-a] [-b]";
80          options = new Options().addOption("a", false, "aaaa aaaa aaaa aaaa aaaa").addOption("b", false, "bbb");
81          hf.printUsage(pw, 60, "app", options);
82          pw.flush();
83          assertEquals("simple auto usage", expected, out.toString().trim());
84          out.reset();
85      }
86  
87      @Test
88      public void testDefaultArgName() {
89          final Option option = Option.builder("f").hasArg().required(true).build();
90  
91          final Options options = new Options();
92          options.addOption(option);
93  
94          final StringWriter out = new StringWriter();
95  
96          final HelpFormatter formatter = new HelpFormatter();
97          formatter.setArgName("argument");
98          formatter.printUsage(new PrintWriter(out), 80, "app", options);
99  
100         assertEquals("usage: app -f <argument>" + EOL, out.toString());
101     }
102 
103     @Test
104     public void testFindWrapPos() {
105         final HelpFormatter hf = new HelpFormatter();
106 
107         String text = "This is a test.";
108         // text width should be max 8; the wrap position is 7
109         assertEquals("wrap position", 7, hf.findWrapPos(text, 8, 0));
110 
111         // starting from 8 must give -1 - the wrap pos is after end
112         assertEquals("wrap position 2", -1, hf.findWrapPos(text, 8, 8));
113 
114         // words longer than the width are cut
115         text = "aaaa aa";
116         assertEquals("wrap position 3", 3, hf.findWrapPos(text, 3, 0));
117 
118         // last word length is equal to the width
119         text = "aaaaaa aaaaaa";
120         assertEquals("wrap position 4", 6, hf.findWrapPos(text, 6, 0));
121         assertEquals("wrap position 4", -1, hf.findWrapPos(text, 6, 7));
122 
123         text = "aaaaaa\n aaaaaa";
124         assertEquals("wrap position 5", 7, hf.findWrapPos(text, 6, 0));
125 
126         text = "aaaaaa\t aaaaaa";
127         assertEquals("wrap position 6", 7, hf.findWrapPos(text, 6, 0));
128     }
129 
130     @Test
131     public void testHeaderStartingWithLineSeparator() {
132         // related to Bugzilla #21215
133         final Options options = new Options();
134         final HelpFormatter formatter = new HelpFormatter();
135         final String header = EOL + "Header";
136         final String footer = "Footer";
137         final StringWriter out = new StringWriter();
138         formatter.printHelp(new PrintWriter(out), 80, "foobar", header, options, 2, 2, footer, true);
139         //@formatter:off
140         assertEquals(
141                 "usage: foobar" + EOL +
142                 "" + EOL +
143                 "Header" + EOL +
144                 "" + EOL +
145                 "Footer" + EOL,
146                 out.toString());
147         //@formatter:on
148     }
149 
150     @Test
151     public void testHelpWithLongOptSeparator() {
152         final Options options = new Options();
153         options.addOption("f", true, "the file");
154         options.addOption(Option.builder("s").longOpt("size").desc("the size").hasArg().argName("SIZE").build());
155         options.addOption(Option.builder().longOpt("age").desc("the age").hasArg().build());
156 
157         final HelpFormatter formatter = new HelpFormatter();
158         assertEquals(HelpFormatter.DEFAULT_LONG_OPT_SEPARATOR, formatter.getLongOptSeparator());
159         formatter.setLongOptSeparator("=");
160         assertEquals("=", formatter.getLongOptSeparator());
161 
162         final StringWriter out = new StringWriter();
163 
164         formatter.printHelp(new PrintWriter(out), 80, "create", "header", options, 2, 2, "footer");
165 
166         //@formatter:off
167         assertEquals(
168                 "usage: create" + EOL +
169                 "header" + EOL +
170                 "     --age=<arg>    the age" + EOL +
171                 "  -f <arg>          the file" + EOL +
172                 "  -s,--size=<SIZE>  the size" + EOL +
173                 "footer" + EOL,
174                 out.toString());
175         //@formatter:on
176     }
177 
178     @Test
179     public void testIndentedHeaderAndFooter() {
180         // related to CLI-207
181         final Options options = new Options();
182         final HelpFormatter formatter = new HelpFormatter();
183         final String header = "  Header1\n  Header2";
184         final String footer = "  Footer1\n  Footer2";
185         final StringWriter out = new StringWriter();
186         formatter.printHelp(new PrintWriter(out), 80, "foobar", header, options, 2, 2, footer, true);
187         //@formatter:off
188         assertEquals(
189                 "usage: foobar" + EOL +
190                 "  Header1" + EOL +
191                 "  Header2" + EOL +
192                 "" + EOL +
193                 "  Footer1" + EOL +
194                 "  Footer2" + EOL,
195                 out.toString());
196         //@formatter:on
197     }
198 
199     @Test
200     public void testOptionWithoutShortFormat() {
201         // related to Bugzilla #19383 (CLI-67)
202         final Options options = new Options();
203         options.addOption(new Option("a", "aaa", false, "aaaaaaa"));
204         options.addOption(new Option(null, "bbb", false, "bbbbbbb"));
205         options.addOption(new Option("c", null, false, "ccccccc"));
206 
207         final HelpFormatter formatter = new HelpFormatter();
208         final StringWriter out = new StringWriter();
209         formatter.printHelp(new PrintWriter(out), 80, "foobar", "", options, 2, 2, "", true);
210         //@formatter:off
211         assertEquals(
212                 "usage: foobar [-a] [--bbb] [-c]" + EOL +
213                 "  -a,--aaa  aaaaaaa" + EOL +
214                 "     --bbb  bbbbbbb" + EOL +
215                 "  -c        ccccccc" + EOL,
216                 out.toString());
217         //@formatter:on
218     }
219 
220     @Test
221     public void testOptionWithoutShortFormat2() {
222         // related to Bugzilla #27635 (CLI-26)
223         final Option help = new Option("h", "help", false, "print this message");
224         final Option version = new Option("v", "version", false, "print version information");
225         final Option newRun = new Option("n", "new", false, "Create NLT cache entries only for new items");
226         final Option trackerRun = new Option("t", "tracker", false, "Create NLT cache entries only for tracker items");
227 
228         //@formatter:off
229         final Option timeLimit = Option.builder("l")
230                                  .longOpt("limit")
231                                  .hasArg()
232                                  .valueSeparator()
233                                  .desc("Set time limit for execution, in mintues")
234                                  .build();
235 
236         final Option age = Option.builder("a").longOpt("age")
237                                         .hasArg()
238                                         .valueSeparator()
239                                         .desc("Age (in days) of cache item before being recomputed")
240                                         .build();
241 
242         final Option server = Option.builder("s").longOpt("server")
243                                            .hasArg()
244                                            .valueSeparator()
245                                            .desc("The NLT server address")
246                                            .build();
247 
248         final Option numResults = Option.builder("r").longOpt("results")
249                                                .hasArg()
250                                                .valueSeparator()
251                                                .desc("Number of results per item")
252                                                .build();
253 
254         final Option configFile = Option.builder().longOpt("config")
255                                             .hasArg()
256                                             .valueSeparator()
257                                             .desc("Use the specified configuration file")
258                                             .build();
259         //@formatter:on
260 
261         final Options mOptions = new Options();
262         mOptions.addOption(help);
263         mOptions.addOption(version);
264         mOptions.addOption(newRun);
265         mOptions.addOption(trackerRun);
266         mOptions.addOption(timeLimit);
267         mOptions.addOption(age);
268         mOptions.addOption(server);
269         mOptions.addOption(numResults);
270         mOptions.addOption(configFile);
271 
272         final HelpFormatter formatter = new HelpFormatter();
273         final String eol = System.lineSeparator();
274         final StringWriter out = new StringWriter();
275         formatter.printHelp(new PrintWriter(out), 80, "commandline", "header", mOptions, 2, 2, "footer", true);
276         //@formatter:off
277         assertEquals(
278                 "usage: commandline [-a <arg>] [--config <arg>] [-h] [-l <arg>] [-n] [-r <arg>]" + eol +
279                 "       [-s <arg>] [-t] [-v]" + eol +
280                 "header" + eol +
281                 "  -a,--age <arg>      Age (in days) of cache item before being recomputed" + eol +
282                 "     --config <arg>   Use the specified configuration file" + eol +
283                 "  -h,--help           print this message" + eol +
284                 "  -l,--limit <arg>    Set time limit for execution, in mintues" + eol +
285                 "  -n,--new            Create NLT cache entries only for new items" + eol +
286                 "  -r,--results <arg>  Number of results per item" + eol +
287                 "  -s,--server <arg>   The NLT server address" + eol +
288                 "  -t,--tracker        Create NLT cache entries only for tracker items" + eol +
289                 "  -v,--version        print version information" + eol +
290                 "footer" + eol,
291                 out.toString());
292         //@formatter:on
293     }
294 
295     @Test
296     public void testPrintHelpNewlineFooter() {
297         final HelpFormatter formatter = new HelpFormatter();
298         final ByteArrayOutputStream out = new ByteArrayOutputStream();
299         final PrintWriter pw = new PrintWriter(out);
300 
301         final Options options = new Options();
302         options.addOption("a", "b");
303 
304         formatter.printHelp(
305             pw,
306             80,
307             "test" + EOL,
308             "header" + EOL,
309             options,
310             0,
311             0,
312             EOL
313         );
314         final String expected = "usage: test" + EOL +
315                           "header" + EOL +
316                           "-ab" + EOL +
317                           EOL;
318         pw.flush();
319         assertEquals("footer newline", expected, out.toString());
320     }
321 
322     @Test
323     public void testPrintHelpNewlineHeader() {
324         final HelpFormatter formatter = new HelpFormatter();
325         final ByteArrayOutputStream out = new ByteArrayOutputStream();
326         final PrintWriter pw = new PrintWriter(out);
327 
328         final Options options = new Options();
329         options.addOption("a", "b");
330 
331         formatter.printHelp(
332             pw,
333             80,
334             "test" + EOL,
335             EOL,
336             options,
337             0,
338             0,
339             "footer" + EOL
340         );
341         final String expected = "usage: test" + EOL +
342                           EOL +
343                           "-ab" + EOL +
344                           "footer" + EOL;
345         pw.flush();
346         assertEquals("header newline", expected, out.toString());
347     }
348 
349     @Test
350     public void testPrintHelpWithEmptySyntax() {
351         final HelpFormatter formatter = new HelpFormatter();
352         try {
353             formatter.printHelp(null, new Options());
354             fail("null command line syntax should be rejected");
355         } catch (final IllegalArgumentException e) {
356             // expected
357         }
358 
359         try {
360             formatter.printHelp("", new Options());
361             fail("empty command line syntax should be rejected");
362         } catch (final IllegalArgumentException e) {
363             // expected
364         }
365     }
366 
367     @Test
368     public void testPrintOptionGroupUsage() {
369         final OptionGroup group = new OptionGroup();
370         group.addOption(Option.builder("a").build());
371         group.addOption(Option.builder("b").build());
372         group.addOption(Option.builder("c").build());
373 
374         final Options options = new Options();
375         options.addOptionGroup(group);
376 
377         final StringWriter out = new StringWriter();
378 
379         final HelpFormatter formatter = new HelpFormatter();
380         formatter.printUsage(new PrintWriter(out), 80, "app", options);
381 
382         assertEquals("usage: app [-a | -b | -c]" + EOL, out.toString());
383     }
384 
385     @Test
386     public void testPrintOptions() {
387         final StringBuffer sb = new StringBuffer();
388         final HelpFormatter hf = new HelpFormatter();
389         final int leftPad = 1;
390         final int descPad = 3;
391         final String lpad = hf.createPadding(leftPad);
392         final String dpad = hf.createPadding(descPad);
393         Options options;
394         String expected;
395 
396         options = new Options().addOption("a", false, "aaaa aaaa aaaa aaaa aaaa");
397         expected = lpad + "-a" + dpad + "aaaa aaaa aaaa aaaa aaaa";
398         hf.renderOptions(sb, 60, options, leftPad, descPad);
399         assertEquals("simple non-wrapped option", expected, sb.toString());
400 
401         int nextLineTabStop = leftPad + descPad + "-a".length();
402         expected = lpad + "-a" + dpad + "aaaa aaaa aaaa" + EOL + hf.createPadding(nextLineTabStop) + "aaaa aaaa";
403         sb.setLength(0);
404         hf.renderOptions(sb, nextLineTabStop + 17, options, leftPad, descPad);
405         assertEquals("simple wrapped option", expected, sb.toString());
406 
407         options = new Options().addOption("a", "aaa", false, "dddd dddd dddd dddd");
408         expected = lpad + "-a,--aaa" + dpad + "dddd dddd dddd dddd";
409         sb.setLength(0);
410         hf.renderOptions(sb, 60, options, leftPad, descPad);
411         assertEquals("long non-wrapped option", expected, sb.toString());
412 
413         nextLineTabStop = leftPad + descPad + "-a,--aaa".length();
414         expected = lpad + "-a,--aaa" + dpad + "dddd dddd" + EOL + hf.createPadding(nextLineTabStop) + "dddd dddd";
415         sb.setLength(0);
416         hf.renderOptions(sb, 25, options, leftPad, descPad);
417         assertEquals("long wrapped option", expected, sb.toString());
418 
419         options = new Options().addOption("a", "aaa", false, "dddd dddd dddd dddd").addOption("b", false, "feeee eeee eeee eeee");
420         expected = lpad + "-a,--aaa" + dpad + "dddd dddd" + EOL + hf.createPadding(nextLineTabStop) + "dddd dddd" + EOL + lpad + "-b      " + dpad
421             + "feeee eeee" + EOL + hf.createPadding(nextLineTabStop) + "eeee eeee";
422         sb.setLength(0);
423         hf.renderOptions(sb, 25, options, leftPad, descPad);
424         assertEquals("multiple wrapped options", expected, sb.toString());
425     }
426 
427     @Test
428     public void testPrintOptionWithEmptyArgNameUsage() {
429         final Option option = new Option("f", true, null);
430         option.setArgName("");
431         option.setRequired(true);
432 
433         final Options options = new Options();
434         options.addOption(option);
435 
436         final StringWriter out = new StringWriter();
437 
438         final HelpFormatter formatter = new HelpFormatter();
439         formatter.printUsage(new PrintWriter(out), 80, "app", options);
440 
441         assertEquals("usage: app -f" + EOL, out.toString());
442     }
443 
444     @Test
445     public void testPrintRequiredOptionGroupUsage() {
446         final OptionGroup group = new OptionGroup();
447         group.addOption(Option.builder("a").build());
448         group.addOption(Option.builder("b").build());
449         group.addOption(Option.builder("c").build());
450         group.setRequired(true);
451 
452         final Options options = new Options();
453         options.addOptionGroup(group);
454 
455         final StringWriter out = new StringWriter();
456 
457         final HelpFormatter formatter = new HelpFormatter();
458         formatter.printUsage(new PrintWriter(out), 80, "app", options);
459 
460         assertEquals("usage: app -a | -b | -c" + EOL, out.toString());
461     }
462 
463     // uses the test for CLI-131 to implement CLI-155
464     @Test
465     public void testPrintSortedUsage() {
466         final Options opts = new Options();
467         opts.addOption(new Option("a", "first"));
468         opts.addOption(new Option("b", "second"));
469         opts.addOption(new Option("c", "third"));
470 
471         final HelpFormatter helpFormatter = new HelpFormatter();
472         helpFormatter.setOptionComparator((opt1, opt2) -> opt2.getKey().compareToIgnoreCase(opt1.getKey()));
473 
474         final StringWriter out = new StringWriter();
475         helpFormatter.printUsage(new PrintWriter(out), 80, "app", opts);
476 
477         assertEquals("usage: app [-c] [-b] [-a]" + EOL, out.toString());
478     }
479 
480     @Test
481     public void testPrintSortedUsageWithNullComparator() {
482         final Options opts = new Options();
483         opts.addOption(new Option("c", "first"));
484         opts.addOption(new Option("b", "second"));
485         opts.addOption(new Option("a", "third"));
486 
487         final HelpFormatter helpFormatter = new HelpFormatter();
488         helpFormatter.setOptionComparator(null);
489 
490         final StringWriter out = new StringWriter();
491         helpFormatter.printUsage(new PrintWriter(out), 80, "app", opts);
492 
493         assertEquals("usage: app [-c] [-b] [-a]" + EOL, out.toString());
494     }
495 
496     // This test ensures the options are properly sorted
497     // See https://issues.apache.org/jira/browse/CLI-131
498     @Test
499     public void testPrintUsage() {
500         final Option optionA = new Option("a", "first");
501         final Option optionB = new Option("b", "second");
502         final Option optionC = new Option("c", "third");
503         final Options opts = new Options();
504         opts.addOption(optionA);
505         opts.addOption(optionB);
506         opts.addOption(optionC);
507         final HelpFormatter helpFormatter = new HelpFormatter();
508         final ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
509         try (PrintWriter printWriter = new PrintWriter(bytesOut)) {
510             helpFormatter.printUsage(printWriter, 80, "app", opts);
511         }
512         assertEquals("usage: app [-a] [-b] [-c]" + EOL, bytesOut.toString());
513     }
514 
515     @Test
516     public void testRenderWrappedTextMultiLine() {
517         // multi line text
518         final int width = 16;
519         final int padding = 0;
520         //@formatter:off
521         final String expected = "aaaa aaaa aaaa" + EOL +
522                                 "aaaaaa" + EOL +
523                                 "aaaaa";
524         //@formatter:on
525 
526         final StringBuffer sb = new StringBuffer();
527         new HelpFormatter().renderWrappedText(sb, width, padding, expected);
528         assertEquals("multi line text", expected, sb.toString());
529     }
530 
531     @Test
532     public void testRenderWrappedTextMultiLinePadded() {
533         // multi-line padded text
534         final int width = 16;
535         final int padding = 4;
536         //@formatter:off
537         final String text = "aaaa aaaa aaaa" + EOL +
538                       "aaaaaa" + EOL +
539                       "aaaaa";
540         final String expected = "aaaa aaaa aaaa" + EOL +
541                           "    aaaaaa" + EOL +
542                           "    aaaaa";
543         //@formatter:on
544 
545         final StringBuffer sb = new StringBuffer();
546         new HelpFormatter().renderWrappedText(sb, width, padding, text);
547         assertEquals("multi-line padded text", expected, sb.toString());
548     }
549 
550     @Test
551     public void testRenderWrappedTextSingleLine() {
552         // single line text
553         final int width = 12;
554         final int padding = 0;
555         final String text = "This is a test.";
556         final String expected = "This is a" + EOL + "test.";
557 
558         final StringBuffer sb = new StringBuffer();
559         new HelpFormatter().renderWrappedText(sb, width, padding, text);
560         assertEquals("single line text", expected, sb.toString());
561     }
562 
563     @Test
564     public void testRenderWrappedTextSingleLinePadded() {
565         // single line padded text
566         final int width = 12;
567         final int padding = 4;
568         final String text = "This is a test.";
569         final String expected = "This is a" + EOL + "    test.";
570 
571         final StringBuffer sb = new StringBuffer();
572         new HelpFormatter().renderWrappedText(sb, width, padding, text);
573         assertEquals("single line padded text", expected, sb.toString());
574     }
575 
576     @Test
577     public void testRenderWrappedTextSingleLinePadded2() {
578         // single line padded text 2
579         final int width = 53;
580         final int padding = 24;
581         //@formatter:off
582         final String text = "  -p,--period <PERIOD>  PERIOD is time duration of form " +
583                             "DATE[-DATE] where DATE has form YYYY[MM[DD]]";
584         final String expected = "  -p,--period <PERIOD>  PERIOD is time duration of" + EOL +
585                                 "                        form DATE[-DATE] where DATE" + EOL +
586                                 "                        has form YYYY[MM[DD]]";
587         //@formatter:on
588 
589         final StringBuffer sb = new StringBuffer();
590         new HelpFormatter().renderWrappedText(sb, width, padding, text);
591         assertEquals("single line padded text 2", expected, sb.toString());
592     }
593 
594     @Test
595     public void testRenderWrappedTextWordCut() {
596         final int width = 7;
597         final int padding = 0;
598         final String text = "Thisisatest.";
599         final String expected = "Thisisa" + EOL + "test.";
600 
601         final StringBuffer sb = new StringBuffer();
602         new HelpFormatter().renderWrappedText(sb, width, padding, text);
603         assertEquals("cut and wrap", expected, sb.toString());
604     }
605 
606     @Test
607     public void testRtrim() {
608         final HelpFormatter formatter = new HelpFormatter();
609 
610         assertNull(formatter.rtrim(null));
611         assertEquals("", formatter.rtrim(""));
612         assertEquals("  foo", formatter.rtrim("  foo  "));
613     }
614 
615     @Test
616     public void testUsageWithLongOptSeparator() {
617         final Options options = new Options();
618         options.addOption("f", true, "the file");
619         options.addOption(Option.builder("s").longOpt("size").desc("the size").hasArg().argName("SIZE").build());
620         options.addOption(Option.builder().longOpt("age").desc("the age").hasArg().build());
621 
622         final HelpFormatter formatter = new HelpFormatter();
623         formatter.setLongOptSeparator("=");
624 
625         final StringWriter out = new StringWriter();
626 
627         formatter.printUsage(new PrintWriter(out), 80, "create", options);
628 
629         assertEquals("usage: create [--age=<arg>] [-f <arg>] [-s <SIZE>]", out.toString().trim());
630     }
631 }