1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.cli.help;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertThrows;
21 import static org.junit.jupiter.api.Assertions.assertTrue;
22
23 import java.io.IOException;
24 import java.io.StringReader;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28
29 import org.apache.commons.cli.Option;
30 import org.apache.commons.cli.OptionGroup;
31 import org.apache.commons.cli.Options;
32 import org.apache.commons.cli.example.XhtmlHelpAppendable;
33 import org.apache.commons.io.IOUtils;
34 import org.junit.jupiter.api.Test;
35
36
37
38
39 class HelpFormatterTest {
40
41 private Options getTestGroups() {
42
43 return new Options()
44 .addOptionGroup(new OptionGroup()
45 .addOption(Option.builder("1").longOpt("one").hasArg().desc("English one").get())
46 .addOption(Option.builder().longOpt("aon").hasArg().desc("Irish one").get())
47 .addOption(Option.builder().longOpt("uno").hasArg().desc("Spanish one").get())
48 )
49 .addOptionGroup(new OptionGroup()
50 .addOption(Option.builder().longOpt("two").hasArg().desc("English two").get())
51 .addOption(Option.builder().longOpt("dó").hasArg().desc("Irish twp").get())
52 .addOption(Option.builder().longOpt("dos").hasArg().desc("Spanish two").get())
53 )
54 .addOptionGroup(new OptionGroup()
55 .addOption(Option.builder().longOpt("three").hasArg().desc("English three").get())
56 .addOption(Option.builder().longOpt("trí").hasArg().desc("Irish three").get())
57 .addOption(Option.builder().longOpt("tres").hasArg().desc("Spanish three").get())
58 );
59
60 }
61
62 @Test
63 void testDefault() {
64 final StringBuilder sb = new StringBuilder();
65 final TextHelpAppendable serializer = new TextHelpAppendable(sb);
66 final HelpFormatter formatter = HelpFormatter.builder().setHelpAppendable(serializer).get();
67 assertEquals(serializer, formatter.getSerializer(), "Unexpected helpAppendable tests may fail unexpectedly");
68 assertEquals(AbstractHelpFormatter.DEFAULT_COMPARATOR, formatter.getComparator(), "Unexpected comparator tests may fail unexpectedly");
69 assertEquals(AbstractHelpFormatter.DEFAULT_SYNTAX_PREFIX, formatter.getSyntaxPrefix(), "Unexpected syntax prefix tests may fail unexpectedly");
70 }
71
72 @Test
73 void testPrintHelp() throws IOException {
74 final StringBuilder sb = new StringBuilder();
75 final TextHelpAppendable serializer = new TextHelpAppendable(sb);
76 HelpFormatter formatter = HelpFormatter.builder().setHelpAppendable(serializer).get();
77
78 final Options options = new Options().addOption(Option.builder("a").since("1853").hasArg().desc("aaaa aaaa aaaa aaaa aaaa").get());
79
80 List<String> expected = new ArrayList<>();
81 expected.add(" usage: commandSyntax [-a <arg>]");
82 expected.add("");
83 expected.add(" header");
84 expected.add("");
85 expected.add(" Options Since Description ");
86 expected.add(" -a <arg> 1853 aaaa aaaa aaaa aaaa aaaa");
87 expected.add("");
88 expected.add(" footer");
89 expected.add("");
90
91 formatter.printHelp("commandSyntax", "header", options, "footer", true);
92 List<String> actual = IOUtils.readLines(new StringReader(sb.toString()));
93 assertEquals(expected, actual);
94
95 formatter = HelpFormatter.builder().setShowSince(false).setHelpAppendable(serializer).get();
96 expected = new ArrayList<>();
97 expected.add(" usage: commandSyntax [-a <arg>]");
98 expected.add("");
99 expected.add(" header");
100 expected.add("");
101 expected.add(" Options Description ");
102 expected.add(" -a <arg> aaaa aaaa aaaa aaaa aaaa");
103 expected.add("");
104 expected.add(" footer");
105 expected.add("");
106
107 sb.setLength(0);
108 formatter.printHelp("commandSyntax", "header", options, "footer", true);
109 actual = IOUtils.readLines(new StringReader(sb.toString()));
110 assertEquals(expected, actual);
111
112 sb.setLength(0);
113 formatter.printHelp("commandSyntax", "header", options, "footer", false);
114 expected.set(0, " usage: commandSyntax");
115 actual = IOUtils.readLines(new StringReader(sb.toString()));
116 assertEquals(expected, actual);
117
118 sb.setLength(0);
119 formatter.printHelp("commandSyntax", "", options, "footer", false);
120 expected.remove(3);
121 expected.remove(2);
122 actual = IOUtils.readLines(new StringReader(sb.toString()));
123 assertEquals(expected, actual);
124
125 sb.setLength(0);
126 formatter.printHelp("commandSyntax", null, options, "footer", false);
127 actual = IOUtils.readLines(new StringReader(sb.toString()));
128 assertEquals(expected, actual);
129
130 sb.setLength(0);
131 formatter.printHelp("commandSyntax", null, options, "", false);
132 expected.remove(6);
133 expected.remove(5);
134 actual = IOUtils.readLines(new StringReader(sb.toString()));
135 assertEquals(expected, actual);
136
137 sb.setLength(0);
138 formatter.printHelp("commandSyntax", null, options, null, false);
139 actual = IOUtils.readLines(new StringReader(sb.toString()));
140 assertEquals(expected, actual);
141
142 sb.setLength(0);
143 final HelpFormatter fHelp = formatter;
144 assertThrows(IllegalArgumentException.class, () -> fHelp.printHelp("", "header", options, "footer", true));
145 assertEquals(0, sb.length(), "Should not write to output");
146 assertThrows(IllegalArgumentException.class, () -> fHelp.printHelp(null, "header", options, "footer", true));
147 assertEquals(0, sb.length(), "Should not write to output");
148 }
149
150
151
152
153
154
155
156 @Test
157 void testPrintHelpHeader() throws IOException {
158 HelpFormatter.builder().get().printHelp("CL syntax", "Header", Collections.emptyList(), "Footer", true);
159 HelpFormatter.builder().get().printHelp("CL syntax", "Header\n\n",
160 Collections.emptyList(), "Footer", true);
161 }
162
163 @Test
164 public void testPrintHelpWithIterableOptions() throws IOException {
165 final StringBuilder sb = new StringBuilder();
166 final TextHelpAppendable serializer = new TextHelpAppendable(sb);
167 HelpFormatter formatter = HelpFormatter.builder().setHelpAppendable(serializer).get();
168
169 final List<Option> options = new ArrayList<>();
170 options.add(Option.builder("a").since("1853").hasArg().desc("aaaa aaaa aaaa aaaa aaaa").build());
171
172 List<String> expected = new ArrayList<>();
173 expected.add(" usage: commandSyntax [-a <arg>]");
174 expected.add("");
175 expected.add(" header");
176 expected.add("");
177 expected.add(" Options Since Description ");
178 expected.add(" -a <arg> 1853 aaaa aaaa aaaa aaaa aaaa");
179 expected.add("");
180 expected.add(" footer");
181 expected.add("");
182
183 formatter.printHelp("commandSyntax", "header", options, "footer", true);
184 List<String> actual = IOUtils.readLines(new StringReader(sb.toString()));
185 assertEquals(expected, actual);
186
187 formatter = HelpFormatter.builder().setShowSince(false).setHelpAppendable(serializer).get();
188 expected = new ArrayList<>();
189 expected.add(" usage: commandSyntax [-a <arg>]");
190 expected.add("");
191 expected.add(" header");
192 expected.add("");
193 expected.add(" Options Description ");
194 expected.add(" -a <arg> aaaa aaaa aaaa aaaa aaaa");
195 expected.add("");
196 expected.add(" footer");
197 expected.add("");
198
199 sb.setLength(0);
200 formatter.printHelp("commandSyntax", "header", options, "footer", true);
201 actual = IOUtils.readLines(new StringReader(sb.toString()));
202 assertEquals(expected, actual);
203
204 sb.setLength(0);
205 formatter.printHelp("commandSyntax", "header", options, "footer", false);
206 expected.set(0, " usage: commandSyntax");
207 actual = IOUtils.readLines(new StringReader(sb.toString()));
208 assertEquals(expected, actual);
209
210 sb.setLength(0);
211 formatter.printHelp("commandSyntax", "", options, "footer", false);
212 expected.remove(3);
213 expected.remove(2);
214 actual = IOUtils.readLines(new StringReader(sb.toString()));
215 assertEquals(expected, actual);
216
217 sb.setLength(0);
218 formatter.printHelp("commandSyntax", null, options, "footer", false);
219 actual = IOUtils.readLines(new StringReader(sb.toString()));
220 assertEquals(expected, actual);
221
222 sb.setLength(0);
223 formatter.printHelp("commandSyntax", null, options, "", false);
224 expected.remove(6);
225 expected.remove(5);
226 actual = IOUtils.readLines(new StringReader(sb.toString()));
227 assertEquals(expected, actual);
228
229 sb.setLength(0);
230 formatter.printHelp("commandSyntax", null, options, null, false);
231 actual = IOUtils.readLines(new StringReader(sb.toString()));
232 assertEquals(expected, actual);
233
234 sb.setLength(0);
235 final HelpFormatter fHelp = formatter;
236 assertThrows(IllegalArgumentException.class, () -> fHelp.printHelp("", "header", options, "footer", true));
237 assertEquals(0, sb.length(), "Should not write to output");
238 assertThrows(IllegalArgumentException.class, () -> fHelp.printHelp(null, "header", options, "footer", true));
239 assertEquals(0, sb.length(), "Should not write to output");
240 }
241
242 @Test
243 void testPrintHelpXML() throws IOException {
244 final StringBuilder sb = new StringBuilder();
245 final XhtmlHelpAppendable serializer = new XhtmlHelpAppendable(sb);
246 final HelpFormatter formatter = HelpFormatter.builder().setHelpAppendable(serializer).get();
247
248 final Options options = new Options().addOption("a", false, "aaaa aaaa aaaa aaaa aaaa");
249
250 final List<String> expected = new ArrayList<>();
251 expected.add("<p>usage: commandSyntax [-a]</p>");
252 expected.add("<p>header</p>");
253 expected.add("<table class='commons_cli_table'>");
254 expected.add(" <tr>");
255 expected.add(" <th>Options</th>");
256 expected.add(" <th>Since</th>");
257 expected.add(" <th>Description</th>");
258 expected.add(" </tr>");
259 expected.add(" <tr>");
260 expected.add(" <td>-a</td>");
261 expected.add(" <td>--</td>");
262 expected.add(" <td>aaaa aaaa aaaa aaaa aaaa</td>");
263 expected.add(" </tr>");
264 expected.add("</table>");
265 expected.add("<p>footer</p>");
266
267 formatter.printHelp("commandSyntax", "header", options, "footer", true);
268 final List<String> actual = IOUtils.readLines(new StringReader(sb.toString()));
269
270 assertEquals(expected, actual);
271 }
272
273 @Test
274 void testPrintOptions() throws IOException {
275 final StringBuilder sb = new StringBuilder();
276 final TextHelpAppendable serializer = new TextHelpAppendable(sb);
277 final HelpFormatter formatter = HelpFormatter.builder().setHelpAppendable(serializer).setShowSince(false).get();
278
279
280
281
282
283
284
285
286
287 Options options;
288 List<String> expected = new ArrayList<>();
289 expected.add(" Options Description ");
290 expected.add(" -a aaaa aaaa aaaa aaaa aaaa");
291 expected.add("");
292
293 options = new Options().addOption("a", false, "aaaa aaaa aaaa aaaa aaaa");
294
295 formatter.printOptions(options);
296 List<String> actual = IOUtils.readLines(new StringReader(sb.toString()));
297 assertEquals(expected, actual);
298
299 sb.setLength(0);
300 serializer.setMaxWidth(30);
301 expected = new ArrayList<>();
302 expected.add(" Options Description ");
303 expected.add(" -a aaaa aaaa aaaa ");
304 expected.add(" aaaa aaaa ");
305 expected.add("");
306 formatter.printOptions(options);
307 actual = IOUtils.readLines(new StringReader(sb.toString()));
308 assertEquals(31, actual.get(0).length());
309 assertEquals(expected, actual);
310
311 sb.setLength(0);
312 serializer.setLeftPad(5);
313 expected = new ArrayList<>();
314 expected.add(" Options Description ");
315 expected.add(" -a aaaa aaaa aaaa ");
316 expected.add(" aaaa aaaa ");
317 expected.add("");
318 formatter.printOptions(options);
319 actual = IOUtils.readLines(new StringReader(sb.toString()));
320 assertEquals(expected, actual);
321 }
322
323 @Test
324 void testSetOptionFormatBuilderTest() {
325 final HelpFormatter.Builder underTest = HelpFormatter.builder();
326 final OptionFormatter.Builder ofBuilder = OptionFormatter.builder().setOptPrefix("Just Another ");
327 underTest.setOptionFormatBuilder(ofBuilder);
328 final HelpFormatter formatter = underTest.get();
329 final OptionFormatter oFormatter = formatter.getOptionFormatter(Option.builder("thing").get());
330 assertEquals("Just Another thing", oFormatter.getOpt());
331
332 }
333
334 @Test
335 void testSetOptionGroupSeparatorTest() {
336 final HelpFormatter.Builder underTest = HelpFormatter.builder().setOptionGroupSeparator(" and ");
337 final HelpFormatter formatter = underTest.get();
338 final String result = formatter.toSyntaxOptions(new OptionGroup().addOption(Option.builder("this").get()).addOption(Option.builder("that").get()));
339 assertTrue(result.contains("-that and -this"));
340 }
341
342 @Test
343 void testSortOptionGroupsTest() {
344 final Options options = getTestGroups();
345 final List<Option> optList = new ArrayList<>(options.getOptions());
346 final HelpFormatter underTest = HelpFormatter.builder().get();
347 final List<Option> expected = new ArrayList<>();
348 expected.add(optList.get(0));
349 expected.add(optList.get(1));
350 expected.add(optList.get(5));
351 expected.add(optList.get(4));
352 expected.add(optList.get(6));
353 expected.add(optList.get(8));
354 expected.add(optList.get(7));
355 expected.add(optList.get(3));
356 expected.add(optList.get(2));
357 assertEquals(expected, underTest.sort(options));
358 }
359
360 @Test
361 void testSortOptionsTest() {
362
363 final Options options = new Options()
364 .addOption(Option.builder("a").longOpt("optA").hasArg().desc("The description of A").get())
365 .addOption(Option.builder("b").longOpt("BOpt").hasArg().desc("B description").get())
366 .addOption(Option.builder().longOpt("COpt").hasArg().desc("A COpt description").get());
367
368
369 HelpFormatter underTest = HelpFormatter.builder().get();
370 final List<Option> expected = new ArrayList<>();
371 expected.add(options.getOption("a"));
372 expected.add(options.getOption("b"));
373 expected.add(options.getOption("COpt"));
374
375 assertEquals(expected, underTest.sort(options));
376
377 expected.set(0, expected.get(2));
378 expected.set(2, options.getOption("a"));
379 underTest = HelpFormatter.builder().setComparator(AbstractHelpFormatter.DEFAULT_COMPARATOR.reversed()).get();
380 assertEquals(expected, underTest.sort(options));
381
382 assertEquals(0, underTest.sort(Collections.emptyList()).size(), "empty colleciton should return empty list");
383 assertEquals(0, underTest.sort((Iterable<Option>) null).size(), "null iterable should return empty list");
384 assertEquals(0, underTest.sort((Options) null).size(), "null Options should return empty list");
385 }
386
387 @Test
388 void testSyntaxPrefix() {
389 final StringBuilder sb = new StringBuilder();
390 final TextHelpAppendable serializer = new TextHelpAppendable(sb);
391 final HelpFormatter formatter = HelpFormatter.builder().setHelpAppendable(serializer).get();
392 formatter.setSyntaxPrefix("Something new");
393 assertEquals("Something new", formatter.getSyntaxPrefix());
394 assertEquals(0, sb.length(), "Should not write to output");
395 }
396
397 @Test
398 void testToArgNameTest() {
399 final StringBuilder sb = new StringBuilder();
400 final TextHelpAppendable serializer = new TextHelpAppendable(sb);
401 final HelpFormatter formatter = HelpFormatter.builder().setHelpAppendable(serializer).get();
402
403 assertEquals("<some Arg>", formatter.toArgName("some Arg"));
404 assertEquals("<>", formatter.toArgName(""));
405 assertEquals("<>", formatter.toArgName(null));
406 }
407
408 @Test
409 void testToSyntaxOptionGroupTest() {
410 final HelpFormatter underTest = HelpFormatter.builder().get();
411
412 final OptionGroup optionGroup = new OptionGroup()
413 .addOption(Option.builder().option("o").longOpt("one").hasArg().get())
414 .addOption(Option.builder().option("t").longOpt("two").hasArg().required().argName("other").get())
415 .addOption(Option.builder().option("th").longOpt("three").required().argName("other").get())
416 .addOption(Option.builder().option("f").argName("other").get())
417 .addOption(Option.builder().longOpt("five").hasArg().argName("other").get())
418 .addOption(Option.builder().longOpt("six").required().hasArg().argName("other").get())
419 .addOption(Option.builder().option("s").longOpt("sevem").hasArg().get());
420
421 assertEquals("[-f | --five <other> | -o <arg> | -s <arg> | --six <other> | -t <other> | -th]", underTest.toSyntaxOptions(optionGroup));
422
423 optionGroup.setRequired(true);
424 assertEquals("-f | --five <other> | -o <arg> | -s <arg> | --six <other> | -t <other> | -th", underTest.toSyntaxOptions(optionGroup));
425
426 assertEquals("", underTest.toSyntaxOptions(new OptionGroup()), "empty group should return empty string");
427 }
428
429 @Test
430 void testToSyntaxOptionIterableTest() {
431 final HelpFormatter underTest = HelpFormatter.builder().get();
432 final List<Option> options = new ArrayList<>();
433
434 options.add(Option.builder().option("o").longOpt("one").hasArg().get());
435 options.add(Option.builder().option("t").longOpt("two").hasArg().required().argName("other").get());
436 options.add(Option.builder().option("th").longOpt("three").required().argName("other").get());
437 options.add(Option.builder().option("f").argName("other").get());
438 options.add(Option.builder().longOpt("five").hasArg().argName("other").get());
439 options.add(Option.builder().longOpt("six").required().hasArg().argName("other").get());
440 options.add(Option.builder().option("s").longOpt("sevem").hasArg().get());
441 assertEquals("[-f] [--five <other>] [-o <arg>] [-s <arg>] --six <other> -t <other> -th", underTest.toSyntaxOptions(options));
442
443 }
444
445 @Test
446 void testToSyntaxOptionOptionsTest() {
447 final HelpFormatter underTest = HelpFormatter.builder().get();
448 Options options = getTestGroups();
449 assertEquals("[-1 <arg> | --aon <arg> | --uno <arg>] [--dos <arg> | --dó <arg> | --two <arg>] [--three <arg> | --tres <arg> | --trí <arg>]",
450 underTest.toSyntaxOptions(options), "getTestGroup options failed");
451
452
453 options = new Options()
454 .addOption(Option.builder().option("o").longOpt("one").hasArg().get())
455 .addOption(Option.builder().option("t").longOpt("two").hasArg().required().argName("other").get())
456 .addOption(Option.builder().option("th").longOpt("three").required().argName("other").get())
457 .addOption(Option.builder().option("f").argName("other").get())
458 .addOption(Option.builder().longOpt("five").hasArg().argName("other").get())
459 .addOption(Option.builder().longOpt("six").required().hasArg().argName("other").get())
460 .addOption(Option.builder().option("s").longOpt("seven").hasArg().get());
461
462 assertEquals("[-f] [--five <other>] [-o <arg>] [-s <arg>] --six <other> -t <other> -th", underTest.toSyntaxOptions(options), "assorted options failed");
463
464 options = new Options()
465 .addOption(Option.builder().option("o").longOpt("one").hasArg().get())
466 .addOptionGroup(
467 new OptionGroup()
468 .addOption(Option.builder().option("t").longOpt("two").hasArg().required().argName("other").get())
469 .addOption(Option.builder().option("th").longOpt("three").required().argName("other").get()))
470 .addOption(Option.builder().option("f").argName("other").get())
471 .addOption(Option.builder().longOpt("five").hasArg().argName("other").get())
472 .addOption(Option.builder().longOpt("six").required().hasArg().argName("other").get())
473 .addOption(Option.builder().option("s").longOpt("seven").hasArg().get());
474
475 assertEquals("[-f] [--five <other>] [-o <arg>] [-s <arg>] --six <other> [-t <other> | -th]", underTest.toSyntaxOptions(options),
476 "option with group failed");
477
478
479 final OptionGroup group1 = new OptionGroup()
480 .addOption(Option.builder().option("t").longOpt("two").hasArg().required().argName("other").get())
481 .addOption(Option.builder().option("th").longOpt("three").required().argName("other").get());
482
483 group1.setRequired(true);
484
485 options = new Options()
486 .addOption(Option.builder().option("o").longOpt("one").hasArg().get())
487 .addOptionGroup(group1)
488 .addOption(Option.builder().option("f").argName("other").get())
489 .addOption(Option.builder().longOpt("five").hasArg().argName("other").get())
490 .addOption(Option.builder().longOpt("six").required().hasArg().argName("other").get())
491 .addOption(Option.builder().option("s").longOpt("seven").hasArg().get());
492
493 assertEquals("[-f] [--five <other>] [-o <arg>] [-s <arg>] --six <other> -t <other> | -th", underTest.toSyntaxOptions(options),
494 "options with required group failed");
495 }
496
497 @Test
498 void verifyOptionGroupingOutput() throws IOException {
499
500 final Option o1 = new Option("o1", "Descr");
501 final Option o2 = new Option("o2", "Descr");
502
503 final Options options = new Options();
504 options.addOption(o1);
505 options.addOption(o2);
506
507 final OptionGroup group = new OptionGroup();
508 group.addOption(o1);
509 group.addOption(o2);
510 options.addOptionGroup(group);
511
512 final StringBuilder output = new StringBuilder();
513
514 final org.apache.commons.cli.help.HelpFormatter newFormatter =
515 org.apache.commons.cli.help.HelpFormatter.builder().setShowSince(false)
516 .setHelpAppendable(new TextHelpAppendable(output)).get();
517 newFormatter.printHelp("Command", null, options, null, true);
518 assertTrue(output.toString().contains("[-o1 | -o2]"));
519 }
520
521 }