Skip to content

Commit

Permalink
[#2058] Bugfix: superfluous defaultValue for options with IParameterC…
Browse files Browse the repository at this point in the history
…onsumer

* fix: `defaultValue` should not be applied in addition to user-specified value for options with a custom `IParameterConsumer`
* added test
* update RELEASE-NOTES.md

Closes #2058
  • Loading branch information
remkop committed Aug 27, 2023
1 parent 26be968 commit d3d82b6
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 0 deletions.
1 change: 1 addition & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Artifacts in this release are signed by Remko Popma (6601 E5C0 8DCC BB96).

## <a name="4.7.6-fixes"></a> Fixed issues

* [#2058] Bugfix: `defaultValue` should not be applied in addition to user-specified value for options with a custom `IParameterConsumer`. Thanks to [Staffan Arvidsson McShane](https://github.com/StaffanArvidsson) for raising this.
* [#2047] DEP: Bump andymckay/append-gist-action from 1fbfbbce708a39bd45846f0955ed5521f2099c6d to 6e8d64427fe47cbacf4ab6b890411f1d67c07f3e
* [#2091] DEP: Bump actions/checkout from 3.5.2 to 3.6.0
* [#2085] DEP: Bump github/codeql-action from 2.3.5 to 2.21.4
Expand Down
1 change: 1 addition & 0 deletions src/main/java/picocli/CommandLine.java
Original file line number Diff line number Diff line change
Expand Up @@ -14153,6 +14153,7 @@ private int applyOption(ArgSpec argSpec,

if (argSpec.parameterConsumer() != null) {
argSpec.parameterConsumer().consumeParameters(args, argSpec, commandSpec);
addToInitialized(argSpec, initialized);
return args.size() - originalSize;
}
boolean consumeOnlyOne = commandSpec.parser().aritySatisfiedByAttachedOptionParam() && lookBehind.isAttached();
Expand Down
106 changes: 106 additions & 0 deletions src/test/java/picocli/Issue2058DefaultValForParamConsumer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package picocli;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Stack;

import org.junit.Assert;
import org.junit.Test;

import picocli.CommandLine.IParameterConsumer;
import picocli.CommandLine.ITypeConverter;
import picocli.CommandLine.Model.ArgSpec;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Option;

public class Issue2058DefaultValForParamConsumer {

// Toy custom class
static class CustomType {
private final String lowerCase;

public CustomType(String value){
lowerCase = value.toLowerCase(Locale.ENGLISH);
}

// Check equals in the test-case
public String toString(){
return lowerCase;
}
}

// Custom converter version
static class MyTypeConverter implements ITypeConverter<CustomType> {
public CustomType convert(String value) {
return new CustomType(value);
}
}

static class CmdWithTypeConverter {

@Option(names = {"-f"},
defaultValue = "defaultArg",
converter = MyTypeConverter.class,
split = "\\s")
public List<CustomType> arguments = new ArrayList<CustomType>();
}

// Consumer version
static class MyParamConsumer implements IParameterConsumer {

public void consumeParameters(Stack<String> args, ArgSpec argSpec, CommandSpec commandSpec) {
List<CustomType> list = argSpec.getValue();

// Simple example, all remaining input are of this type
while (!args.empty()){
list.add(new CustomType(args.pop()));
}
}
}

static class CmdWithParamConsumer {

@Option(names = {"-f"},
defaultValue = "defaultArg",
parameterConsumer = MyParamConsumer.class,
split = "\\s")
public List<CustomType> arguments = new ArrayList<CustomType>();
}

@Test
public void testDefaultValueUsedInCmdWithTypeConverter() {
// Converter implementation - no argument given
CmdWithTypeConverter program = new CmdWithTypeConverter();
new CommandLine(program).parseArgs();
Assert.assertEquals(1, program.arguments.size());
Assert.assertEquals("defaultarg", program.arguments.get(0).toString());
}

@Test
public void testSpecifiedValueUsedInCmdWithTypeConverter() {
// Converter implementation - when giving an expecit argument (works as well)
CmdWithTypeConverter program = new CmdWithTypeConverter();
new CommandLine(program).parseArgs("-f", "explicitArg");
Assert.assertEquals(1, program.arguments.size());
Assert.assertEquals("explicitarg",program.arguments.get(0).toString());
}

@Test
public void testDefaultValueUsedInCmdWithParamConsumer() {
// Test consumer - no argument
CmdWithParamConsumer cmd = new CmdWithParamConsumer();
new CommandLine(cmd).parseArgs();
Assert.assertEquals(1, cmd.arguments.size());
Assert.assertEquals("defaultarg",cmd.arguments.get(0).toString());
}

@Test
public void testOnlySpecifiedValueUsedInCmdWithParamConsumer() {
// Consumer with explicit arg
CmdWithParamConsumer cmd = new CmdWithParamConsumer();
new CommandLine(cmd).parseArgs("-f", "explicitArgument");
Assert.assertEquals(1, cmd.arguments.size()); // fails, cmd.arguments.size == 2 (explicit argument and "defaultarg")
Assert.assertEquals("explicitargument", cmd.arguments.get(0).toString());
}
}

0 comments on commit d3d82b6

Please sign in to comment.