package picocli; import static java.lang.String.format; import static org.junit.Assert.*; import static picocli.CommandLine.Option.NULL_VALUE; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.ProvideSystemProperty; import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.junit.rules.TestRule; import picocli.CommandLine.Command; import picocli.CommandLine.IDefaultValueProvider; import picocli.CommandLine.Model.ArgSpec; import picocli.CommandLine.Option; import picocli.CommandLine.Parameters; import picocli.CommandLine.PropertiesDefaultProvider; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Properties; public class DefaultProviderTest { // allows tests to set any kind of properties they like, without having to individually roll them back @Rule public final TestRule restoreSystemProperties = new RestoreSystemProperties(); @Rule public final ProvideSystemProperty ansiOFF = new ProvideSystemProperty("picocli.ansi", "false"); static class TestDefaultProvider implements IDefaultValueProvider { public String defaultValue(ArgSpec argSpec) { return "Default provider string value"; } } static class TestNullDefaultProvider implements IDefaultValueProvider { public String defaultValue(ArgSpec argSpec) { return null; } } @Command(defaultValueProvider = TestDefaultProvider.class, abbreviateSynopsis = true) static class App { @Option(names = "-a", description = "Default: ${DEFAULT-VALUE}") private String optionStringFieldWithoutDefaultNorInitialValue; @Option(names = "-b", description = "Default: ${DEFAULT-VALUE}", defaultValue = "Annotated default value") private String optionStringFieldWithAnnotatedDefault; @Option(names = "-c", description = "Default: ${DEFAULT-VALUE}", showDefaultValue = CommandLine.Help.Visibility.ALWAYS) private String optionStringFieldWithInitDefault = "Initial default value"; @Parameters(arity = "0..1", description = "Default: ${DEFAULT-VALUE}", showDefaultValue = CommandLine.Help.Visibility.ALWAYS) private String paramStringFieldWithoutDefaultNorInitialValue; @Parameters(arity = "0..1", description = "Default: ${DEFAULT-VALUE}", defaultValue = "Annotated default value") private String paramStringFieldWithAnnotatedDefault; @Parameters(arity = "0..1", description = "Default: ${DEFAULT-VALUE}") private String paramStringFieldWithInitDefault = "Initial default value"; private String stringForSetterDefault; @Option(names = "-d", description = "Default: ${DEFAULT-VALUE}", defaultValue = "Annotated setter default value") void setString(String val) { stringForSetterDefault = val; } } @Command(name = "sub") static class Sub { @Option(names = "-a") private String optionStringFieldWithoutDefaultNorInitialValue; @Option(names = "-b", defaultValue = "Annotated default value") private String optionStringFieldWithAnnotatedDefault; @Option(names = "-c") private String optionStringFieldWithInitDefault = "Initial default value"; @Parameters(arity = "0..1") private String paramStringFieldWithoutDefaultNorInitialValue; @Parameters(arity = "0..1", defaultValue = "Annotated default value") private String paramStringFieldWithAnnotatedDefault; @Parameters(arity = "0..1") private String paramStringFieldWithInitDefault = "Initial default value"; private String stringForSetterDefault; @Option(names = "-d", defaultValue = "Annotated setter default value") void setString(String val) { stringForSetterDefault = val; } } @Test public void testCommandDefaultProviderByAnnotationOverridesValues() { CommandLine cmd = new CommandLine(App.class); cmd.parseArgs(); App app = cmd.getCommand(); // if no default defined on the option, command default provider should be used assertEquals("Default provider string value", app.optionStringFieldWithoutDefaultNorInitialValue); assertEquals("Default provider string value", app.paramStringFieldWithoutDefaultNorInitialValue); // if a default is defined on the option either by annotation or by initial value, it must // override the default provider. assertEquals("Default provider string value", app.optionStringFieldWithAnnotatedDefault); assertEquals("Default provider string value", app.paramStringFieldWithAnnotatedDefault); assertEquals("Default provider string value", app.optionStringFieldWithInitDefault); assertEquals("Default provider string value", app.paramStringFieldWithInitDefault); assertEquals("Default provider string value", app.stringForSetterDefault); } @Test public void testCommandDefaultProviderDoesntOverridesDefaultsIfValueIsNull() { CommandLine cmd = new CommandLine(App.class); cmd.setDefaultValueProvider(new TestNullDefaultProvider()); cmd.parseArgs(); App app = cmd.getCommand(); // if no default defined on the option, command default provider should be used assertNull(app.optionStringFieldWithoutDefaultNorInitialValue); assertNull(app.paramStringFieldWithoutDefaultNorInitialValue); // if a default is defined on the option either by annotation or by initial value, it must // override the default provider. assertEquals("Annotated default value", app.optionStringFieldWithAnnotatedDefault); assertEquals("Annotated default value", app.paramStringFieldWithAnnotatedDefault); assertEquals("Initial default value", app.optionStringFieldWithInitDefault); assertEquals("Initial default value", app.paramStringFieldWithInitDefault); assertEquals("Annotated setter default value", app.stringForSetterDefault); } @Test public void testDefaultProviderNullByDefault() { CommandLine cmd = new CommandLine(Sub.class); assertNull(cmd.getDefaultValueProvider()); } @SuppressWarnings("unchecked") @Test(expected = UnsupportedOperationException.class) public void testNoDefaultProviderThrowsUnsupportedOperation() throws Exception { Class c = (Class) Class.forName("picocli.CommandLine$NoDefaultProvider"); IDefaultValueProvider provider = CommandLine.defaultFactory().create(c); assertNotNull(provider); provider.defaultValue(CommandLine.Model.PositionalParamSpec.builder().build()); } @Test public void testDefaultProviderReturnsSetValue() { CommandLine cmd = new CommandLine(Sub.class); TestDefaultProvider provider = new TestDefaultProvider(); cmd.setDefaultValueProvider(provider); assertSame(provider, cmd.getDefaultValueProvider()); } @Test public void testDefaultProviderPropagatedToSubCommand() { CommandLine cmd = new CommandLine(App.class); cmd.addSubcommand("sub", new CommandLine(Sub.class)); CommandLine subCommandLine = cmd.getSubcommands().get("sub"); cmd.setDefaultValueProvider(new TestDefaultProvider()); assertNotNull(subCommandLine.getCommandSpec().defaultValueProvider()); assertEquals(TestDefaultProvider.class, subCommandLine.getCommandSpec().defaultValueProvider().getClass()); } @Test public void testCommandDefaultProviderSetting() { CommandLine cmd = new CommandLine(App.class); cmd.setDefaultValueProvider(new TestDefaultProvider()); cmd.parseArgs(); App app = cmd.getCommand(); // if no default defined on the option, command default provider should be used assertEquals("Default provider string value", app.optionStringFieldWithoutDefaultNorInitialValue); } @Test public void testDefaultValueInDescription() { String expected = String.format("" + "Usage:
[OPTIONS] [] [] []%n" + " []%n" + " Default: Default provider string value%n" + " Default: Default provider string value%n" + " []%n" + " Default: Default provider string value%n" + " []%n" + " Default: Default provider string value%n" + " -a=%n" + " Default: Default provider string value%n" + " -b=%n" + " Default: Default provider string value%n" + " -c=%n" + " Default: Default provider string value%n" + " Default: Default provider string value%n" + " -d= Default: Default provider string value%n"); CommandLine cmd = new CommandLine(App.class); assertEquals(expected, cmd.getUsageMessage(CommandLine.Help.Ansi.OFF)); } @Test public void testDefaultValueInDescriptionAfterSetProvider() { String expected2 = String.format("" + "Usage:
[OPTIONS] [] [] []%n" + " []%n" + " Default: XYZ%n" + " Default: XYZ%n" + " []%n" + " Default: XYZ%n" + " []%n" + " Default: XYZ%n" + " -a=%n" + " Default: XYZ%n" + " -b=%n" + " Default: XYZ%n" + " -c=%n" + " Default: XYZ%n" + " Default: XYZ%n" + " -d= Default: XYZ%n"); CommandLine cmd = new CommandLine(App.class); cmd.setDefaultValueProvider(new IDefaultValueProvider() { public String defaultValue(ArgSpec argSpec) throws Exception { return "XYZ"; } }); assertEquals(expected2, cmd.getUsageMessage(CommandLine.Help.Ansi.OFF)); } @Test public void testDefaultValueInDescriptionWithErrorProvider() { String expected2 = String.format("" + "Usage:
[OPTIONS] [] [] []%n" + " []%n" + " Default: null%n" + " Default: null%n" + " []%n" + " Default: Annotated default value%n" + " []%n" + " Default: Initial default value%n" + " -a=%n" + " Default: null%n" + " -b=%n" + " Default: Annotated default value%n" + " -c=%n" + " Default: Initial default value%n" + " Default: Initial default value%n" + " -d= Default: Annotated setter default value%n"); CommandLine cmd = new CommandLine(App.class); cmd.setDefaultValueProvider(new IDefaultValueProvider() { public String defaultValue(ArgSpec argSpec) throws Exception { throw new IllegalStateException("abc"); } }); assertEquals(expected2, cmd.getUsageMessage(CommandLine.Help.Ansi.OFF)); } static class FooDefaultProvider implements IDefaultValueProvider { public String defaultValue(ArgSpec argSpec) throws Exception { return "DURATION".equals(argSpec.paramLabel()) ? "1200" : null; } } @Test public void testIssue616DefaultProviderWithShowDefaultValues() { @Command(name = "foo", mixinStandardHelpOptions = true, defaultValueProvider = FooDefaultProvider.class, showDefaultValues = true) class FooCommand implements Runnable { @Option(names = {"-d", "--duration"}, paramLabel = "DURATION", arity = "1", description = "The duration, in seconds.") Integer duration; public void run() { System.out.printf("duration=%s%n", duration); } } String expected = String.format("" + "Usage: foo [-hV] [-d=DURATION]%n" + " -d, --duration=DURATION The duration, in seconds.%n" + " Default: 1200%n" + " -h, --help Show this help message and exit.%n" + " -V, --version Print version information and exit.%n"); String actual = new CommandLine(new FooCommand()).getUsageMessage(); assertEquals(expected, actual); } @Test public void testDefaultProvider_PropertiesConstructor() throws Exception { Properties properties = new Properties(); properties.setProperty("x", "xvalue"); PropertiesDefaultProvider defaultProvider = new PropertiesDefaultProvider(properties); String value = defaultProvider.defaultValue(CommandLine.Model.OptionSpec.builder("-x").build()); assertEquals("xvalue", value); } @Test public void testIssue962DefaultNotUsedIfArgumentSpecifiedOnCommandLine() { class App { List specified = new ArrayList(); @Option(names = "--port", defaultValue = "${sys:TEST_PORT_962}", required = true) void setPort(Integer port) { specified.add(port); } @Option(names = "--field", defaultValue = "${sys:TEST_A_962}", required = true) Integer a; } System.setProperty("TEST_PORT_962", "xxx"); System.setProperty("TEST_A_962", "xxx"); App app1 = CommandLine.populateCommand(new App(), "--port=123", "--field=987"); assertEquals((Integer) 987, app1.a); assertEquals(Arrays.asList(123), app1.specified); } @Test public void testIssue961DefaultNotUsedIfArgumentSpecifiedOnCommandLine() { class App { List specified = new ArrayList(); @Option(names = "--option", defaultValue = "DEFAULT", required = true) void setValue(String value) { specified.add(value); } @Option(names = "--num", defaultValue = "INVALID", required = true) Integer a; } App app1 = CommandLine.populateCommand(new App(), "--option=123", "--num=987"); assertEquals((Integer) 987, app1.a); assertEquals(Arrays.asList("123"), app1.specified); } static class NullDefaultProvider implements IDefaultValueProvider { public String defaultValue(ArgSpec argSpec) throws Exception { return ArgSpec.NULL_VALUE; } } @Test public void testNullDefault() { @Command(defaultValueProvider = NullDefaultProvider.class) class App { @Option(names = "-a") Integer a; } App app1 = CommandLine.populateCommand(new App()); assertEquals(null, app1.a); } }