From 39f19448fa529714796e2e8bfcda979913caf413 Mon Sep 17 00:00:00 2001 From: rpopma Date: Mon, 2 Oct 2017 00:06:51 +0900 Subject: [PATCH] #185 Missing option exception text should not use field names but be more descriptive and consistent with usage help. Thanks to [AlexFalappa](https://github.com/AlexFalappa). Closes #185 --- RELEASE-NOTES.md | 1 + src/main/java/picocli/CommandLine.java | 29 ++- src/test/java/picocli/AutoCompleteTest.java | 2 +- .../java/picocli/CommandLineHelpTest.java | 188 +++++++++++++++--- src/test/java/picocli/CommandLineTest.java | 124 ++++++++---- 5 files changed, 267 insertions(+), 77 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index e659248c8..f77349462 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -55,6 +55,7 @@ class Args { - #193 Splitting an argument should not cause max arity to be exceeded - #191 Arity should not limit the total number of values put in an array or collection - #195 Usage help should show Map types if paramLabel not specified +- #185 Missing option exception text should not use field names but be more descriptive and consistent with usage help. Thanks to [AlexFalappa](https://github.com/AlexFalappa). - #186 Confusing usage message for collection options - #181 Fixed bug where incorrect help message is displayed for short options with paramLabel when arity > 1 - #184 Improved CommandLine.setSeparator javadoc to clarify that this affects parsing only and link to the `@Command` `separator` annotation attribute. diff --git a/src/main/java/picocli/CommandLine.java b/src/main/java/picocli/CommandLine.java index aa61c3ba3..af7d9ca53 100644 --- a/src/main/java/picocli/CommandLine.java +++ b/src/main/java/picocli/CommandLine.java @@ -1528,7 +1528,7 @@ private void parse(List parsedCommands, Stack argumentStack } if (!isAnyHelpRequested() && !required.isEmpty()) { if (required.get(0).isAnnotationPresent(Option.class)) { - throw MissingParameterException.create(required); + throw MissingParameterException.create(required, separator); } else { try { processPositionalParameters0(required, true, new Stack()); @@ -1567,7 +1567,7 @@ private void processArguments(List parsedCommands, // if we find another command, we are done with the current command if (commands.containsKey(arg)) { if (!isHelpRequested && !required.isEmpty()) { // ensure current command portion is valid - throw MissingParameterException.create(required); + throw MissingParameterException.create(required, separator); } if (tracer.isDebug()) {tracer.debug("Found subcommand '%s' (%s)%n", arg, commands.get(arg).interpreter.command.getClass().getName());} commands.get(arg).interpreter.parse(parsedCommands, args, originalArgs); @@ -2908,14 +2908,7 @@ public static IParameterRenderer createMinimalParameterRenderer() { public static IParamLabelRenderer createMinimalParamLabelRenderer() { return new IParamLabelRenderer() { public Text renderParameterLabel(Field field, Ansi ansi, List styles) { - String paramLabel = null; - Parameters parameters = field.getAnnotation(Parameters.class); - if (parameters != null) { - paramLabel = parameters.paramLabel(); - } else { - paramLabel = field.isAnnotationPresent(Option.class) ? field.getAnnotation(Option.class).paramLabel() : null; - } - String text = paramLabel == null || paramLabel.length() == 0 ? field.getName() : paramLabel; + String text = DefaultParamLabelRenderer.renderParameterName(field); return ansi.apply(text, styles); } public String separator() { return ""; } @@ -3158,7 +3151,7 @@ public Text renderParameterLabel(Field field, Ansi ansi, List styles) { } return result; } - private String renderParameterName(Field field) { + private static String renderParameterName(Field field) { String result = null; if (field.isAnnotationPresent(Option.class)) { result = field.getAnnotation(Option.class).paramLabel(); @@ -3172,7 +3165,7 @@ private String renderParameterName(Field field) { if (Map.class.isAssignableFrom(field.getType())) { // #195 better param labels for map fields Class[] paramTypes = getTypeAttribute(field); if (paramTypes.length < 2 || paramTypes[0] == null || paramTypes[1] == null) { - name = "Object=Object"; + name = "String=String"; } else { name = paramTypes[0].getSimpleName() + "=" + paramTypes[1].getSimpleName(); } } return "<" + name + ">"; @@ -4094,17 +4087,23 @@ public MissingParameterException(String msg) { super(msg); } - private static MissingParameterException create(Collection missing) { + private static MissingParameterException create(Collection missing, String separator) { if (missing.size() == 1) { return new MissingParameterException("Missing required option '" - + missing.iterator().next().getName() + "'"); + + describe(missing.iterator().next(), separator) + "'"); } List names = new ArrayList(missing.size()); for (Field field : missing) { - names.add(field.getName()); + names.add(describe(field, separator)); } return new MissingParameterException("Missing required options " + names.toString()); } + private static String describe(Field field, String separator) { + String prefix = (field.isAnnotationPresent(Option.class)) + ? field.getAnnotation(Option.class).names()[0] + separator + : "params[" + field.getAnnotation(Parameters.class).index() + "]" + separator; + return prefix + Help.DefaultParamLabelRenderer.renderParameterName(field); + } } /** diff --git a/src/test/java/picocli/AutoCompleteTest.java b/src/test/java/picocli/AutoCompleteTest.java index 73a059ef4..cbac9c200 100644 --- a/src/test/java/picocli/AutoCompleteTest.java +++ b/src/test/java/picocli/AutoCompleteTest.java @@ -150,7 +150,7 @@ private static String toString(Object obj) { "Usage: picocli.AutoComplete [-fhw] [-n=] [-o=]%n" + " %n" + "Generates a bash completion script for the specified command class.%n" + - " commandLineFQCN Fully qualified class name of the annotated%n" + + " Fully qualified class name of the annotated%n" + " @Command class to generate a completion script%n" + " for.%n" + " -n, --name= Name of the command to create a completion script%n" + diff --git a/src/test/java/picocli/CommandLineHelpTest.java b/src/test/java/picocli/CommandLineHelpTest.java index 5c5b1e35d..e4c1900c9 100644 --- a/src/test/java/picocli/CommandLineHelpTest.java +++ b/src/test/java/picocli/CommandLineHelpTest.java @@ -174,7 +174,7 @@ class ParamLabels { assertEquals(format("" + "Usage:
[-n=] [-f=FILE]... [-P=KEY=VALUE]... NUM %n" + " NUM number param%n" + - " host the host parameter%n" + + " the host parameter%n" + " -f= FILE files%n" + " -n= a number option%n" + " -P= KEY=VALUE Project properties (key-value pairs)%n", @@ -197,7 +197,7 @@ class ParamLabels { assertEquals(format("" + "Usage:
[-f=FILE] [-n=] [-P=KEY=VALUE]... NUM %n" + " NUM number param%n" + - " host the host parameter%n" + + " the host parameter%n" + " -f= FILE a file%n" + " -n= a number option%n" + " -P, --properties=KEY=VALUE Project properties (key-value pairs)%n", @@ -513,12 +513,12 @@ class Args { Map d; } String expected = String.format("" + - "Usage:
-a=KEY=VAL [-a=KEY=VAL]... -b=[]... [-b=%n" + - " []...]... -c=...%n" + + "Usage:
-a=KEY=VAL [-a=KEY=VAL]... -b=[]... [-b=%n" + + " []...]... -c=...%n" + " [-c=...]... -d=%n" + " ... [-d= ...]...%n" + " -a= KEY=VAL%n" + - " -b= []...%n" + + " -b= []...%n" + " -c= ...%n" + " -d= ...%n" + " description%n"); @@ -539,9 +539,9 @@ class Args { Map d; } String expected = String.format("" + - "Usage:
[-a=]... [-b=[]...]...%n" + + "Usage:
[-a=]... [-b=[]...]...%n" + " [-c=KEY=VALUE...]... [-d= ...]...%n" + - " -a= %n" + + " -a= %n" + " -b= []...%n" + " -c= KEY=VALUE...%n" + " -d= ...%n" + @@ -565,13 +565,13 @@ class Args { Map d; } String expected = String.format("" + - "Usage:
-a[=] [-a[=]]...%n" + + "Usage:
-a[=] [-a[=]]...%n" + " -b= []%n" + " [-b= []]...%n" + " -c= [ []]%n" + " [-c= [ []]]... -d=K=URL%n" + " K=URL [K=URL [K=URL]] [-d=K=URL K=URL [K=URL [K=URL]]]...%n" + - " -a= [] a description%n" + + " -a= [] a description%n" + " -b= []%n" + " b description%n" + " -c= [ []]%n" + @@ -595,13 +595,13 @@ class Args { Map d; } String expected = String.format("" + - "Usage:
[-a[=]]... [-b= []]...%n" + - " [-c= [ []]]...%n" + + "Usage:
[-a[=]]... [-b= []]...%n" + + " [-c= [ []]]...%n" + " [-d=K=V K=V [K=V [K=V]]]...%n" + - " -a= [] a description%n" + + " -a= [] a description%n" + " -b= []%n" + " b description%n" + - " -c= [ []]%n" + + " -c= [ []]%n" + " c description%n" + " -d= K=V K=V [K=V [K=V]] d description%n"); //CommandLine.usage(new Args(), System.out); @@ -624,10 +624,10 @@ class Args { Map d; } String expected = String.format("" + - "Usage:
-b [-b]... -a= [-a=]...%n" + + "Usage:
-b [-b]... -a= [-a=]...%n" + " -c= [-c=]... -d= %n" + " [-d= ]...%n" + - " -a= a description%n" + + " -a= a description%n" + " -b b description%n" + " -c= c description%n" + " -d= d description%n"); @@ -649,17 +649,155 @@ class Args { Map d; } String expected = String.format("" + - "Usage:
[-b]... [-a=]... [-c=]...%n" + + "Usage:
[-b]... [-a=]... [-c=]...%n" + " [-d= ]...%n" + " -a= a description%n" + " -b b description%n" + - " -c= c description%n" + + " -c= c description%n" + " -d= d description%n"); //CommandLine.usage(new Args(), System.out); assertEquals(expected, usageString(new Args(), Help.Ansi.OFF)); } //-------------- @Test + public void testUsageVariableArityParametersArray() throws UnsupportedEncodingException { + // if option is required at least once and can be specified multiple times: + // -f=ARG [-f=ARG]... + class Args { + @Parameters(paramLabel = "APARAM") + String[] a; + @Parameters(arity = "0..*") + List b; + @Parameters(arity = "1..*") + String[] c; + @Parameters(arity = "2..*") + List d; + } + String expected = String.format("" + + "Usage:
[APARAM]... []... ... ...%n" + + " APARAM%n" + + " %n" + + " %n" + + " %n"); + //CommandLine.usage(new Args(), System.out); + assertEquals(expected, usageString(new Args(), Help.Ansi.OFF)); + } + + @Test + public void testUsageRangeArityParameterArray() throws UnsupportedEncodingException { + class Args { + @Parameters(paramLabel = "PARAMA", arity = "0..1") + List a; + @Parameters(paramLabel = "PARAMB", arity = "1..2") + String[] b; + @Parameters(paramLabel = "PARAMC", arity = "1..3") + String[] c; + @Parameters(paramLabel = "PARAMD", arity = "2..4") + String[] d; + } + String expected = String.format("" + + "Usage:
[PARAMA] PARAMB [PARAMB] PARAMC [PARAMC [PARAMC]] PARAMD%n" + + " PARAMD [PARAMD [PARAMD]]%n" + + " PARAMA%n" + + " PARAMB%n" + + " PARAMC%n" + + " PARAMD%n"); + //CommandLine.usage(new Args(), System.out); + assertEquals(expected, usageString(new Args(), Help.Ansi.OFF)); + } + + @Test + public void testUsageFixedArityParametersArray() throws UnsupportedEncodingException { + class Args { + @Parameters() + String[] a; + @Parameters(arity = "0") + String[] b; + @Parameters(arity = "1") + String[] c; + @Parameters(arity = "2") + String[] d; + } + String expected = String.format("" + + "Usage:
[]... %n" + + " %n" + + " %n" + + " %n" + + " %n"); + //CommandLine.usage(new Args(), System.out); + assertEquals(expected, usageString(new Args(), Help.Ansi.OFF)); + } + + @Test + public void testUsageVariableArityParametersMap() throws UnsupportedEncodingException { + class Args { + @Parameters() + Map a; + @Parameters(arity = "0..*", type = {Integer.class, Integer.class}) + Map b; + @Parameters(paramLabel = "KEY=VALUE", arity = "1..*", type = {String.class, TimeUnit.class}) + Map c; + @Parameters(arity = "2..*", type = {String.class, URL.class}, description = "description") + Map d; + } + String expected = String.format("" + + "Usage:
[]... []... KEY=VALUE...%n" + + " ...%n" + + " %n" + + " %n" + + " KEY=VALUE%n" + + " description%n"); + //CommandLine.usage(new Args(), System.out); + assertEquals(expected, usageString(new Args(), Help.Ansi.OFF)); + } + + @Test + public void testUsageRangeArityParametersMap() throws UnsupportedEncodingException { + class Args { + @Parameters(arity = "0..1"/*, type = {UUID.class, URL.class}*/, description = "a description") + Map a; + @Parameters(arity = "1..2", type = {Long.class, UUID.class}, description = "b description") + Map b; + @Parameters(arity = "1..3", type = {Long.class}, description = "c description") + Map c; + @Parameters(paramLabel = "K=V", arity = "2..4", description = "d description") + Map d; + } + String expected = String.format("" + + "Usage:
[] [] %n" + + " [ []] K=V K=V [K=V [K=V]]%n" + + " a description%n" + + " b description%n" + + " c description%n" + + " K=V d description%n"); + //CommandLine.usage(new Args(), System.out); + assertEquals(expected, usageString(new Args(), Help.Ansi.OFF)); + } + + @Test + public void testUsageFixedArityParametersMap() throws UnsupportedEncodingException { + class Args { + @Parameters(type = {Short.class, Field.class}, description = "a description") + Map a; + @Parameters(arity = "0", type = {UUID.class, Long.class}, description = "b description") + @SuppressWarnings("unchecked") + Map b; + @Parameters(arity = "1", description = "c description") + Map c; + @Parameters(arity = "2", type = {URI.class, URL.class}, description = "d description") + Map d; + } + String expected = String.format("" + + "Usage:
[]... %n" + + " b description%n" + + " a description%n" + + " c description%n" + + " d description%n"); + //CommandLine.usage(new Args(), System.out); + assertEquals(expected, usageString(new Args(), Help.Ansi.OFF)); + } + //---------- + @Test public void testShortestFirstComparator_sortsShortestFirst() { String[] values = {"12345", "12", "123", "123456", "1", "", "1234"}; Arrays.sort(values, new Help.ShortestFirst()); @@ -858,7 +996,7 @@ class Required { Field field = help.positionalParametersFields.get(0); Text[][] row1 = renderer.render(field.getAnnotation(Parameters.class), field, parameterRenderer, help.colorScheme); assertEquals(1, row1.length); - assertArrayEquals(Arrays.toString(row1[0]), textArray(help, " ", "", "", "required", "required"), row1[0]); + assertArrayEquals(Arrays.toString(row1[0]), textArray(help, " ", "", "", "", "required"), row1[0]); } @Test @@ -873,7 +1011,7 @@ class Required { Field field = help.positionalParametersFields.get(0); Text[][] row1 = renderer.render(field.getAnnotation(Parameters.class), field, parameterRenderer, help.colorScheme); assertEquals(1, row1.length); - assertArrayEquals(Arrays.toString(row1[0]), textArray(help, "*", "", "", "required", "required"), row1[0]); + assertArrayEquals(Arrays.toString(row1[0]), textArray(help, "*", "", "", "", "required"), row1[0]); } @Test @@ -888,7 +1026,7 @@ class Optional { Field field = help.positionalParametersFields.get(0); Text[][] row1 = renderer.render(field.getAnnotation(Parameters.class), field, parameterRenderer, help.colorScheme); assertEquals(1, row1.length); - assertArrayEquals(Arrays.toString(row1[0]), textArray(help, "", "", "", "optional", "optional"), row1[0]); + assertArrayEquals(Arrays.toString(row1[0]), textArray(help, "", "", "", "", "optional"), row1[0]); } @Test @@ -1600,11 +1738,11 @@ class App { String expected = String.format( "Usage:
[]%n" + " []...%n" + - " host1 source host%n" + - " port1 source port%n" + - " host2 destination host%n" + - " port2range destination port range%n" + - " files files to transfer%n" + " source host%n" + + " source port%n" + + " destination host%n" + + " destination port range%n" + + " files to transfer%n" ); assertEquals(expected, actual); } diff --git a/src/test/java/picocli/CommandLineTest.java b/src/test/java/picocli/CommandLineTest.java index 47f25e674..858de7515 100644 --- a/src/test/java/picocli/CommandLineTest.java +++ b/src/test/java/picocli/CommandLineTest.java @@ -574,7 +574,7 @@ public void testEnumArrayTypeConversionFailsForInvalidInput() { CommandLine.populateCommand(new EnumParams(), "-timeUnitArray", "a", "b"); fail("Accepted invalid timeunit"); } catch (Exception ex) { - assertEquals("Could not convert 'a' to TimeUnit for option '-timeUnitArray' at index 0 (timeUnitArray)" + + assertEquals("Could not convert 'a' to TimeUnit for option '-timeUnitArray' at index 0 ()" + ": java.lang.IllegalArgumentException: No enum constant java.util.concurrent.TimeUnit.a", ex.getMessage()); } } @@ -584,7 +584,7 @@ public void testEnumListTypeConversionFailsForInvalidInput() { CommandLine.populateCommand(new EnumParams(), "-timeUnitList", "DAYS", "b", "c"); fail("Accepted invalid timeunit"); } catch (Exception ex) { - assertEquals("Could not convert 'b' to TimeUnit for option '-timeUnitList' at index 1 (timeUnitList)" + + assertEquals("Could not convert 'b' to TimeUnit for option '-timeUnitList' at index 1 ()" + ": java.lang.IllegalArgumentException: No enum constant java.util.concurrent.TimeUnit.b", ex.getMessage()); } @@ -850,7 +850,7 @@ public void testErrorIfRequiredOptionNotSpecified() { CommandLine.populateCommand(new RequiredField(), "arg1", "arg2"); fail("Missing required field should have thrown exception"); } catch (MissingParameterException ex) { - assertEquals("Missing required option 'required'", ex.getMessage()); + assertEquals("Missing required option '--required='", ex.getMessage()); } } @Test @@ -905,7 +905,7 @@ class Example { CommandLine.populateCommand(new Example(), new String[0]); fail("Should not accept missing mandatory parameter"); } catch (MissingParameterException ex) { - assertEquals("Missing required parameter: mandatory", ex.getMessage()); + assertEquals("Missing required parameter: ", ex.getMessage()); } } @Test @@ -919,13 +919,13 @@ class Tricky1 { CommandLine.populateCommand(new Tricky1(), new String[0]); fail("Should not accept missing mandatory parameter"); } catch (MissingParameterException ex) { - assertEquals("Missing required parameters: mandatory, anotherMandatory", ex.getMessage()); + assertEquals("Missing required parameters: , ", ex.getMessage()); } try { CommandLine.populateCommand(new Tricky1(), new String[] {"firstonly"}); fail("Should not accept missing mandatory parameter"); } catch (MissingParameterException ex) { - assertEquals("Missing required parameter: anotherMandatory", ex.getMessage()); + assertEquals("Missing required parameter: ", ex.getMessage()); } } @Test @@ -942,7 +942,7 @@ class Tricky2 { CommandLine.populateCommand(new Tricky2(), new String[0]); fail("Should not accept missing mandatory parameter"); } catch (MissingParameterException ex) { - assertEquals("Missing required parameter: mandatory", ex.getMessage()); + assertEquals("Missing required parameter: ", ex.getMessage()); } } @Test @@ -957,14 +957,14 @@ class Tricky3 { CommandLine.populateCommand(new Tricky3(), new String[] {"-t", "-v", "mandatory"}); fail("Should not accept missing mandatory parameter"); } catch (MissingParameterException ex) { - assertEquals("Missing required parameter: alsoMandatory", ex.getMessage()); + assertEquals("Missing required parameter: ", ex.getMessage()); } try { CommandLine.populateCommand(new Tricky3(), new String[] { "-t", "-v"}); fail("Should not accept missing two mandatory parameters"); } catch (MissingParameterException ex) { - assertEquals("Missing required parameters: mandatory, alsoMandatory", ex.getMessage()); + assertEquals("Missing required parameters: , ", ex.getMessage()); } } @Test @@ -977,7 +977,7 @@ class Tricky3 { CommandLine.populateCommand(new Tricky3(), new String[] {"-t"}); fail("Should not accept missing mandatory parameter"); } catch (MissingParameterException ex) { - assertEquals("Missing required parameter: mandatory", ex.getMessage()); + assertEquals("Missing required parameter: ", ex.getMessage()); } } @Test @@ -997,7 +997,27 @@ class App { CommandLine.populateCommand(new App(), new String[0]); fail("Should not accept missing mandatory parameter"); } catch (MissingParameterException ex) { - assertEquals("Missing required parameters: host, port", ex.getMessage()); + assertEquals("Missing required parameters: , ", ex.getMessage()); + } + } + @Test + public void testNoMissingRequiredParamErrorWithLabelIfHelpOptionSpecified() { + class App { + @Parameters(hidden = true) // "hidden": don't show this parameter in usage help message + List allParameters; // no "index" attribute: captures _all_ arguments (as Strings) + + @Parameters(index = "0", paramLabel = "HOST") InetAddress host; + @Parameters(index = "1", paramLabel = "PORT") int port; + @Parameters(index = "2..*", paramLabel = "FILES") File[] files; + + @Option(names = "-?", help = true) boolean help; + } + CommandLine.populateCommand(new App(), new String[] {"-?"}); + try { + CommandLine.populateCommand(new App(), new String[0]); + fail("Should not accept missing mandatory parameter"); + } catch (MissingParameterException ex) { + assertEquals("Missing required parameters: HOST, PORT", ex.getMessage()); } } @Test @@ -1012,7 +1032,7 @@ public void testHelpRequestedFlagResetWhenParsing_staticMethod() { CommandLine.populateCommand(requiredField, "arg1", "arg2"); fail("Missing required field should have thrown exception"); } catch (MissingParameterException ex) { - assertEquals("Missing required option 'required'", ex.getMessage()); + assertEquals("Missing required option '--required='", ex.getMessage()); } } @Test @@ -1029,7 +1049,7 @@ public void testHelpRequestedFlagResetWhenParsing_instanceMethod() { commandLine.parse("arg1", "arg2"); fail("Missing required field should have thrown exception"); } catch (MissingParameterException ex) { - assertEquals("Missing required option 'required'", ex.getMessage()); + assertEquals("Missing required option '--required='", ex.getMessage()); } } @@ -1512,7 +1532,7 @@ public void testBooleanOptionsArity1_nErrorIfValueMissing() { CommandLine.populateCommand(new BooleanOptionsArity1_nAndParameters(), "-bool".split(" ")); fail("Missing param was accepted for boolean with arity=1"); } catch (ParameterException expected) { - assertEquals("Missing required parameter for option '-bool' at index 0 (aBoolean)", expected.getMessage()); + assertEquals("Missing required parameter for option '-bool' at index 0 ()", expected.getMessage()); } } @@ -1690,7 +1710,7 @@ class ArrayParamsArity1_n { params = CommandLine.populateCommand(new ArrayParamsArity1_n()); fail("Should not accept input with missing parameter"); } catch (MissingParameterException ex) { - assertEquals("Missing required parameters at positions 0..*: params", ex.getMessage()); + assertEquals("Missing required parameters at positions 0..*: ", ex.getMessage()); } } @@ -1707,14 +1727,14 @@ class ArrayParamsArity2_n { params = CommandLine.populateCommand(new ArrayParamsArity2_n(), "a"); fail("Should not accept input with missing parameter"); } catch (MissingParameterException ex) { - assertEquals("positional parameter at index 0..* (params) requires at least 2 values, but only 1 were specified.", ex.getMessage()); + assertEquals("positional parameter at index 0..* () requires at least 2 values, but only 1 were specified.", ex.getMessage()); } try { params = CommandLine.populateCommand(new ArrayParamsArity2_n()); fail("Should not accept input with missing parameter"); } catch (MissingParameterException ex) { - assertEquals("positional parameter at index 0..* (params) requires at least 2 values, but only 0 were specified.", ex.getMessage()); + assertEquals("positional parameter at index 0..* () requires at least 2 values, but only 0 were specified.", ex.getMessage()); } } @@ -1781,7 +1801,7 @@ class NonVarArgArrayParamsArity1 { params = CommandLine.populateCommand(new NonVarArgArrayParamsArity1()); fail("Should not accept input with missing parameter"); } catch (MissingParameterException ex) { - assertEquals("Missing required parameter: params", ex.getMessage()); + assertEquals("Missing required parameter: ", ex.getMessage()); } } @@ -1806,14 +1826,14 @@ class NonVarArgArrayParamsArity2 { params = CommandLine.populateCommand(new NonVarArgArrayParamsArity2(), "a"); fail("Should not accept input with missing parameter"); } catch (MissingParameterException ex) { - assertEquals("positional parameter at index 0..* (params) requires at least 2 values, but only 1 were specified.", ex.getMessage()); + assertEquals("positional parameter at index 0..* () requires at least 2 values, but only 1 were specified.", ex.getMessage()); } try { params = CommandLine.populateCommand(new NonVarArgArrayParamsArity2()); fail("Should not accept input with missing parameter"); } catch (MissingParameterException ex) { - assertEquals("positional parameter at index 0..* (params) requires at least 2 values, but only 0 were specified.", ex.getMessage()); + assertEquals("positional parameter at index 0..* () requires at least 2 values, but only 0 were specified.", ex.getMessage()); } } @@ -1826,7 +1846,7 @@ class WithParams { try { CommandLine.populateCommand(new WithParams(), new String[0]); } catch (MissingParameterException ex) { - assertEquals("Missing required parameters: param0, param1", ex.getMessage()); + assertEquals("Missing required parameters: , ", ex.getMessage()); } } @@ -1913,7 +1933,7 @@ class App { cmd.parse("--opt=abc"); fail("Expected MissingParameterException"); } catch (MissingParameterException ok) { - assertEquals("Missing required option 'opt'", ok.getMessage()); + assertEquals("Missing required option '--opt:'", ok.getMessage()); assertEquals(Arrays.asList("--opt=abc"), cmd.getUnmatchedArguments()); } } @@ -1924,13 +1944,13 @@ public void testGnuLongOptionsWithVariousSeparatorsOnlyAndNoValue() { params = CommandLine.populateCommand(new VariousPrefixCharacters(), "--dash".split(" ")); fail("int option needs arg"); } catch (ParameterException ex) { - assertEquals("Missing required parameter for option '-d' (dash)", ex.getMessage()); + assertEquals("Missing required parameter for option '-d' ()", ex.getMessage()); } try { params = CommandLine.populateCommand(new VariousPrefixCharacters(), "--owner".split(" ")); } catch (ParameterException ex) { - assertEquals("Missing required parameter for option '/Owner' (owner)", ex.getMessage()); + assertEquals("Missing required parameter for option '/Owner' ()", ex.getMessage()); } params = CommandLine.populateCommand(new VariousPrefixCharacters(), "--owner=".split(" ")); @@ -2174,7 +2194,7 @@ class App { CommandLine.populateCommand(new App(), "000"); fail("Should fail with missingParamException"); } catch (MissingParameterException ex) { - assertEquals("Missing required parameter: file1", ex.getMessage()); + assertEquals("Missing required parameter: ", ex.getMessage()); } } @@ -2220,7 +2240,7 @@ class App { CommandLine.populateCommand(new App()); fail("Should fail with missingParamException"); } catch (MissingParameterException ex) { - assertEquals("Missing required parameter: file0_1", ex.getMessage()); + assertEquals("Missing required parameter: ", ex.getMessage()); } } @@ -2521,13 +2541,13 @@ class Args { CommandLine.populateCommand(new Args(), "a,b,c,d,e"); // 1 arg: should fail fail("MissingParameterException expected"); } catch (MissingParameterException ex) { - assertEquals("positional parameter at index 0..* (values) requires at least 2 values, but only 1 were specified.", ex.getMessage()); + assertEquals("positional parameter at index 0..* () requires at least 2 values, but only 1 were specified.", ex.getMessage()); } try { CommandLine.populateCommand(new Args()); // 0 arg: should fail fail("MissingParameterException expected"); } catch (MissingParameterException ex) { - assertEquals("positional parameter at index 0..* (values) requires at least 2 values, but only 0 were specified.", ex.getMessage()); + assertEquals("positional parameter at index 0..* () requires at least 2 values, but only 0 were specified.", ex.getMessage()); } try { CommandLine.populateCommand(new Args(), "a,b,c", "B,C", "d", "e", "f,g"); // 5 args @@ -3027,13 +3047,13 @@ class App { CommandLine.populateCommand(new App(), "-s", "1", "-s", "2"); fail("expected exception"); } catch (OverwrittenOptionException ex) { - assertEquals("option '-s' (string) should be specified only once", ex.getMessage()); + assertEquals("option '-s' () should be specified only once", ex.getMessage()); } try { CommandLine.populateCommand(new App(), "-v", "-v"); fail("expected exception"); } catch (OverwrittenOptionException ex) { - assertEquals("option '-v' (bool) should be specified only once", ex.getMessage()); + assertEquals("option '-v' () should be specified only once", ex.getMessage()); } } @@ -3047,13 +3067,13 @@ class App { CommandLine.populateCommand(new App(), "-s", "1", "--str", "2"); fail("expected exception"); } catch (OverwrittenOptionException ex) { - assertEquals("option '-s' (string) should be specified only once", ex.getMessage()); + assertEquals("option '-s' () should be specified only once", ex.getMessage()); } try { CommandLine.populateCommand(new App(), "-v", "--verbose"); fail("expected exception"); } catch (OverwrittenOptionException ex) { - assertEquals("option '-v' (bool) should be specified only once", ex.getMessage()); + assertEquals("option '-v' () should be specified only once", ex.getMessage()); } } @@ -3088,7 +3108,7 @@ class A { commandLine.parse("-u", "foo"); fail("expected exception"); } catch (MissingParameterException ex) { - assertEquals("Missing required option 'password'", ex.getLocalizedMessage()); + assertEquals("Missing required option '-p='", ex.getLocalizedMessage()); } commandLine.parse("-u", "foo", "-p", "abc"); } @@ -3357,7 +3377,7 @@ private void validate() { CommandLine.populateCommand(new App(), "-fix", "1=a|2=b|3=c|4=d"); // 1 arg fail("MissingParameterException expected"); } catch (MissingParameterException ex) { - assertEquals("option '-fix' at index 0 (message) requires at least 2 values, but only 1 were specified.", ex.getMessage()); + assertEquals("option '-fix' at index 0 () requires at least 2 values, but only 1 were specified.", ex.getMessage()); } try { CommandLine.populateCommand(new App(), "-fix", "1=a", "2=b", "3=c|4=d"); // 3 args @@ -3384,7 +3404,7 @@ class App { assertEquals(Arrays.asList("3=c", "4=d"), cmd.getUnmatchedArguments()); } @Test - public void testMultipleMissingParams() { + public void testMultipleMissingOptions() { class App { @Option(names = "-a", required = true) String first; @Option(names = "-b", required = true) String second; @@ -3394,7 +3414,39 @@ class App { CommandLine.populateCommand(new App()); fail("MissingParameterException expected"); } catch (MissingParameterException ex) { - assertEquals("Missing required options [first, second, third]", ex.getMessage()); + assertEquals("Missing required options [-a=, -b=, -c=]", ex.getMessage()); + } + } + @Test + public void test185MissingOptionsShouldUseLabel() { + class App { + @Parameters(arity = "1", paramLabel = "IN_FILE", description = "The input file") + File foo; + @Option(names = "-o", paramLabel = "OUT_FILE", description = "The output file", required = true) + File bar; + } + try { + CommandLine.populateCommand(new App()); + fail("MissingParameterException expected"); + } catch (MissingParameterException ex) { + assertEquals("Missing required options [-o=OUT_FILE, params[*]=IN_FILE]", ex.getMessage()); + } + } + @Test + public void test185MissingMapOptionsShouldUseLabel() { + class App { + @Parameters(arity = "1", type = {Long.class, File.class}, description = "The input file mapping") + Map foo; + @Option(names = "-o", description = "The output file mapping", required = true) + Map bar; + @Option(names = "-x", paramLabel = "KEY=VAL", description = "Some other mapping", required = true) + Map xxx; + } + try { + CommandLine.populateCommand(new App()); + fail("MissingParameterException expected"); + } catch (MissingParameterException ex) { + assertEquals("Missing required options [-o=, -x=KEY=VAL, params[*]=]", ex.getMessage()); } } @Test