Skip to content

Commit d0fb62e

Browse files
committed
Fix issue with groups not being reported in help output.
AbstractHelpFormatter: * Swapped printHelp using Iterable<Option> with printHelp using Options. Effectively switching to using Options class to print the help. * Removed public String toSyntaxOptions(final Iterable<Option> options) as it was added in this version and should have only been used internally. HelpFormatterTest: * removed toSyntaxOptions using Iterable<Option> test. * added a printHelpTest with Iterable<Option> as previously we depended on printHelpTest with Option to test the code branch. * added verifyOptionGroupingOutput as a test to show that reported issue is corrected.
1 parent 252a587 commit d0fb62e

2 files changed

Lines changed: 110 additions & 30 deletions

File tree

src/main/java/org/apache/commons/cli/help/AbstractHelpFormatter.java

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ public final String getSyntaxPrefix() {
293293
* @param autoUsage whether to print an automatically generated usage statement
294294
* @throws IOException If the output could not be written to the {@link HelpAppendable}
295295
*/
296-
public void printHelp(final String cmdLineSyntax, final String header, final Iterable<Option> options, final String footer, final boolean autoUsage)
296+
public void printHelp(final String cmdLineSyntax, final String header, final Options options, final String footer, final boolean autoUsage)
297297
throws IOException {
298298
if (Util.isEmpty(cmdLineSyntax)) {
299299
throw new IllegalArgumentException("cmdLineSyntax not provided");
@@ -306,7 +306,7 @@ public void printHelp(final String cmdLineSyntax, final String header, final Ite
306306
if (!Util.isEmpty(header)) {
307307
helpAppendable.appendParagraph(header);
308308
}
309-
helpAppendable.appendTable(getTableDefinition(options));
309+
helpAppendable.appendTable(getTableDefinition(options.getOptions()));
310310
if (!Util.isEmpty(footer)) {
311311
helpAppendable.appendParagraph(footer);
312312
}
@@ -317,14 +317,16 @@ public void printHelp(final String cmdLineSyntax, final String header, final Ite
317317
*
318318
* @param cmdLineSyntax the syntax for this application
319319
* @param header the banner to display at the beginning of the help
320-
* @param options the {@link Options} to print
320+
* @param options the collection of {@link Option} objects to print.
321321
* @param footer the banner to display at the end of the help
322322
* @param autoUsage whether to print an automatically generated usage statement
323323
* @throws IOException If the output could not be written to the {@link HelpAppendable}
324324
*/
325-
public final void printHelp(final String cmdLineSyntax, final String header, final Options options, final String footer, final boolean autoUsage)
325+
public final void printHelp(final String cmdLineSyntax, final String header, final Iterable<Option> options, final String footer, final boolean autoUsage)
326326
throws IOException {
327-
printHelp(cmdLineSyntax, header, options.getOptions(), footer, autoUsage);
327+
Options optionsObject = new Options();
328+
options.forEach(optionsObject::addOption);
329+
printHelp(cmdLineSyntax, header, optionsObject, footer, autoUsage);
328330
}
329331

330332
/**
@@ -401,16 +403,6 @@ public final String toArgName(final String argName) {
401403
return optionFormatBuilder.toArgName(argName);
402404
}
403405

404-
/**
405-
* Return the string representation of the options as used in the syntax display.
406-
*
407-
* @param options The collection of {@link Option} instances to create the string representation for.
408-
* @return the string representation of the options as used in the syntax display.
409-
*/
410-
public String toSyntaxOptions(final Iterable<Option> options) {
411-
return toSyntaxOptions(options, o -> null);
412-
}
413-
414406
/**
415407
* Return the string representation of the options as used in the syntax display.
416408
*

src/test/java/org/apache/commons/cli/help/HelpFormatterTest.java

Lines changed: 103 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,85 @@ public void testPrintHelp() throws IOException {
147147
assertEquals(0, sb.length(), "Should not write to output");
148148
}
149149

150+
@Test
151+
public void testPrintHelpWithIterableOptions() throws IOException {
152+
final StringBuilder sb = new StringBuilder();
153+
final TextHelpAppendable serializer = new TextHelpAppendable(sb);
154+
HelpFormatter formatter = HelpFormatter.builder().setHelpAppendable(serializer).get();
155+
156+
final List<Option> options = new ArrayList<>();
157+
options.add(Option.builder("a").since("1853").hasArg().desc("aaaa aaaa aaaa aaaa aaaa").build());
158+
159+
List<String> expected = new ArrayList<>();
160+
expected.add(" usage: commandSyntax [-a <arg>]");
161+
expected.add("");
162+
expected.add(" header");
163+
expected.add("");
164+
expected.add(" Options Since Description ");
165+
expected.add(" -a <arg> 1853 aaaa aaaa aaaa aaaa aaaa");
166+
expected.add("");
167+
expected.add(" footer");
168+
expected.add("");
169+
170+
formatter.printHelp("commandSyntax", "header", options, "footer", true);
171+
List<String> actual = IOUtils.readLines(new StringReader(sb.toString()));
172+
assertEquals(expected, actual);
173+
174+
formatter = HelpFormatter.builder().setShowSince(false).setHelpAppendable(serializer).get();
175+
expected = new ArrayList<>();
176+
expected.add(" usage: commandSyntax [-a <arg>]");
177+
expected.add("");
178+
expected.add(" header");
179+
expected.add("");
180+
expected.add(" Options Description ");
181+
expected.add(" -a <arg> aaaa aaaa aaaa aaaa aaaa");
182+
expected.add("");
183+
expected.add(" footer");
184+
expected.add("");
185+
186+
sb.setLength(0);
187+
formatter.printHelp("commandSyntax", "header", options, "footer", true);
188+
actual = IOUtils.readLines(new StringReader(sb.toString()));
189+
assertEquals(expected, actual);
190+
191+
sb.setLength(0);
192+
formatter.printHelp("commandSyntax", "header", options, "footer", false);
193+
expected.set(0, " usage: commandSyntax");
194+
actual = IOUtils.readLines(new StringReader(sb.toString()));
195+
assertEquals(expected, actual);
196+
197+
sb.setLength(0);
198+
formatter.printHelp("commandSyntax", "", options, "footer", false);
199+
expected.remove(3);
200+
expected.remove(2);
201+
actual = IOUtils.readLines(new StringReader(sb.toString()));
202+
assertEquals(expected, actual);
203+
204+
sb.setLength(0);
205+
formatter.printHelp("commandSyntax", null, options, "footer", false);
206+
actual = IOUtils.readLines(new StringReader(sb.toString()));
207+
assertEquals(expected, actual);
208+
209+
sb.setLength(0);
210+
formatter.printHelp("commandSyntax", null, options, "", false);
211+
expected.remove(6);
212+
expected.remove(5);
213+
actual = IOUtils.readLines(new StringReader(sb.toString()));
214+
assertEquals(expected, actual);
215+
216+
sb.setLength(0);
217+
formatter.printHelp("commandSyntax", null, options, null, false);
218+
actual = IOUtils.readLines(new StringReader(sb.toString()));
219+
assertEquals(expected, actual);
220+
221+
sb.setLength(0);
222+
final HelpFormatter fHelp = formatter;
223+
assertThrows(IllegalArgumentException.class, () -> fHelp.printHelp("", "header", options, "footer", true));
224+
assertEquals(0, sb.length(), "Should not write to output");
225+
assertThrows(IllegalArgumentException.class, () -> fHelp.printHelp(null, "header", options, "footer", true));
226+
assertEquals(0, sb.length(), "Should not write to output");
227+
}
228+
150229
@Test
151230
public void testPrintHelpXML() throws IOException {
152231
final StringBuilder sb = new StringBuilder();
@@ -334,21 +413,6 @@ public void testToSyntaxOptionGroupTest() {
334413
assertEquals("", underTest.toSyntaxOptions(new OptionGroup()), "empty group should return empty string");
335414
}
336415

337-
@Test
338-
public void testToSyntaxOptionIterableTest() {
339-
final HelpFormatter underTest = HelpFormatter.builder().get();
340-
final List<Option> options = new ArrayList<>();
341-
342-
options.add(Option.builder().option("o").longOpt("one").hasArg().build());
343-
options.add(Option.builder().option("t").longOpt("two").hasArg().required().argName("other").build());
344-
options.add(Option.builder().option("th").longOpt("three").required().argName("other").build());
345-
options.add(Option.builder().option("f").argName("other").build());
346-
options.add(Option.builder().longOpt("five").hasArg().argName("other").build());
347-
options.add(Option.builder().longOpt("six").required().hasArg().argName("other").build());
348-
options.add(Option.builder().option("s").longOpt("sevem").hasArg().build());
349-
assertEquals("[-f] [--five <other>] [-o <arg>] [-s <arg>] --six <other> -t <other> -th", underTest.toSyntaxOptions(options));
350-
351-
}
352416

353417
@Test
354418
public void testToSyntaxOptionOptionsTest() {
@@ -402,4 +466,28 @@ public void testToSyntaxOptionOptionsTest() {
402466
"options with required group failed");
403467
}
404468

469+
@Test
470+
void verifyOptionGroupingOutput() throws IOException {
471+
// create options and groups
472+
final Option o1 = new Option("o1", "Descr");
473+
final Option o2 = new Option("o2", "Descr");
474+
475+
final Options options = new Options();
476+
options.addOption(o1);
477+
options.addOption(o2);
478+
479+
final OptionGroup group = new OptionGroup();
480+
group.addOption(o1);
481+
group.addOption(o2);
482+
options.addOptionGroup(group);
483+
484+
final StringBuilder output = new StringBuilder();
485+
//format options with new formatter
486+
final org.apache.commons.cli.help.HelpFormatter newFormatter =
487+
org.apache.commons.cli.help.HelpFormatter.builder().setShowSince(false)
488+
.setHelpAppendable(new TextHelpAppendable(output)).get();
489+
newFormatter.printHelp("Command", null, options, null, true);
490+
assertTrue(output.toString().contains("[-o1 | -o2]"));
491+
}
492+
405493
}

0 commit comments

Comments
 (0)