package picocli; 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.Help.Visibility; import picocli.CommandLine.ITypeConverter; import picocli.CommandLine.InitializationException; import picocli.CommandLine.Model.CommandSpec; import picocli.CommandLine.Model.IGetter; import picocli.CommandLine.Model.ISetter; import picocli.CommandLine.Model.OptionSpec; import picocli.CommandLine.Option; import picocli.CommandLine.Range; import java.io.StringWriter; import java.util.List; import java.util.Map; import static org.junit.Assert.*; import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; public class ModelOptionSpecTest { // 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"); @Test public void testOptionIsOption() { assertTrue(OptionSpec.builder("-x").build().isOption()); } @Test public void testOptionIsNotPositional() { assertFalse(OptionSpec.builder("-x").build().isPositional()); } @Test public void testOptionDefaultOrderIsMinusOne() { assertEquals(OptionSpec.DEFAULT_ORDER, OptionSpec.builder("-x").build().order()); } @Test public void testOptionDefaultUsageHelpIsFalse() { assertFalse(OptionSpec.builder("-x").build().usageHelp()); } @Test public void testOptionDefaultVersionHelpIsFalse() { assertFalse(OptionSpec.builder("-x").build().versionHelp()); } @Deprecated @Test public void testOptionDefaultHelpIsFalse() { assertFalse(OptionSpec.builder("-x").build().help()); } @Test public void testOptionDefaultHiddenIsFalse() { assertFalse(OptionSpec.builder("-x").build().hidden()); } @Test public void testPositionalDefaultHiddenIsFalse() { assertFalse(CommandLine.Model.PositionalParamSpec.builder().build().hidden()); } @Test public void testOptionDefaultRequiredIsFalse() { assertFalse(OptionSpec.builder("-x").build().required()); } @Test public void testOptionDefaultTypeIsBoolean_withDefaultArity() { assertEquals(boolean.class, OptionSpec.builder("-x").build().type()); } @Test public void testOptionDefaultTypeIsBoolean_withArityZero() { assertEquals(boolean.class, OptionSpec.builder("-x").arity("0").build().type()); } @Test public void testOptionDefaultTypeIsString_withArityOne() { assertEquals(String.class, OptionSpec.builder("-x").arity("1").build().type()); } @Test public void testOptionDefaultTypeIsStringArray_withArityTwo() { assertEquals(String[].class, OptionSpec.builder("-x").arity("2").build().type()); } @Test public void testOptionDefaultAuxiliaryTypesIsDerivedFromType() { assertArrayEquals(new Class[] {boolean.class}, OptionSpec.builder("-x").build().auxiliaryTypes()); assertArrayEquals(new Class[] {int.class}, OptionSpec.builder("-x").type(int.class).build().auxiliaryTypes()); } @Test public void testOptionDefaultTypDependsOnArity() { assertEquals(boolean.class, OptionSpec.builder("-x").arity("0").build().type()); assertEquals(String.class, OptionSpec.builder("-x").arity("1").build().type()); assertEquals(String.class, OptionSpec.builder("-x").arity("0..1").build().type()); assertEquals(String[].class, OptionSpec.builder("-x").arity("2").build().type()); assertEquals(String[].class, OptionSpec.builder("-x").arity("0..2").build().type()); assertEquals(String[].class, OptionSpec.builder("-x").arity("*").build().type()); } @Test public void testOptionAuxiliaryTypeOverridesDefaultType() { assertEquals(int.class, OptionSpec.builder("-x").auxiliaryTypes(int.class).build().type()); assertEquals(int.class, OptionSpec.builder("-x").arity("0").auxiliaryTypes(int.class).build().type()); assertEquals(int.class, OptionSpec.builder("-x").arity("1").auxiliaryTypes(int.class).build().type()); assertEquals(int.class, OptionSpec.builder("-x").arity("0..1").auxiliaryTypes(int.class).build().type()); assertEquals(int.class, OptionSpec.builder("-x").arity("2").auxiliaryTypes(int.class).build().type()); assertEquals(int.class, OptionSpec.builder("-x").arity("0..2").auxiliaryTypes(int.class).build().type()); assertEquals(int.class, OptionSpec.builder("-x").arity("*").auxiliaryTypes(int.class).build().type()); } @Test public void testOptionDefaultArityIsZeroIfUntyped() { assertEquals(Range.valueOf("0"), OptionSpec.builder("-x").build().arity()); } @Test public void testOptionDefaultArityIsZeroIfTypeBoolean() { assertEquals(Range.valueOf("0"), OptionSpec.builder("-x").type(boolean.class).build().arity()); assertEquals(Range.valueOf("0"), OptionSpec.builder("-x").type(Boolean.class).build().arity()); } @Test public void testOptionDefaultArityIsOneIfTypeNonBoolean() { assertEquals(Range.valueOf("1"), OptionSpec.builder("-x").type(int.class).build().arity()); assertEquals(Range.valueOf("1"), OptionSpec.builder("-x").type(Integer.class).build().arity()); assertEquals(Range.valueOf("1"), OptionSpec.builder("-x").type(Byte.class).build().arity()); assertEquals(Range.valueOf("1"), OptionSpec.builder("-x").type(String.class).build().arity()); } @Test public void testOptionDefaultSplitRegexIsEmptyString() { assertEquals("", OptionSpec.builder("-x").build().splitRegex()); } @Test public void testOptionDefaultDescriptionIsEmptyArray() { assertArrayEquals(new String[0], OptionSpec.builder("-x").build().description()); } @Test public void testOptionDefaultParamLabel() { assertEquals("PARAM", OptionSpec.builder("-x").build().paramLabel()); } @Test public void testOptionDefaultConvertersIsEmpty() { assertArrayEquals(new ITypeConverter[0], OptionSpec.builder("-x").build().converters()); } @Test public void testOptionSpecRequiresNonNullName() { try { OptionSpec.builder(null, "-s").build(); fail("Expected exception"); } catch (NullPointerException ex) { assertEquals("name", ex.getMessage()); } } @Test() public void testOptionSpecRequiresNonNullNameArray() { try { OptionSpec.builder((String[]) null).build(); fail("Expected exception"); } catch (InitializationException ex) { assertEquals("OptionSpec names cannot be null. Specify at least one option name.", ex.getMessage()); } } @Test public void testOptionSpecRequiresAtLeastOneName() { try { OptionSpec.builder(new String[0]).build(); fail("Expected exception"); } catch (InitializationException ex) { assertEquals("Invalid names: []", ex.getMessage()); } } @Test public void testOptionSpecRequiresNonEmptyName() { try { OptionSpec.builder("").build(); fail("Expected exception"); } catch (InitializationException ex) { assertEquals("Invalid names: []", ex.getMessage()); } } @Test public void testOptionBuilderNamesOverwriteInitialValue() { OptionSpec option = OptionSpec.builder("-a", "--aaa").names("-b", "--bbb").build(); assertArrayEquals(new String[] {"-b", "--bbb"}, option.names()); } @Test public void testOptionCopyBuilder() { OptionSpec option = OptionSpec.builder("-a", "--aaa").arity("1").type(int.class).description("abc").paramLabel("ABC").build(); OptionSpec copy = option.toBuilder().build(); assertEquals(option, copy); assertNotSame(option, copy); } @Test @SuppressWarnings("deprecation") public void testGettersOnOptionBuilder() { ISetter setter = new ISetter() { public T set(T value) { return null; } }; IGetter getter = new IGetter() { public T get() { return null; } }; ITypeConverter converter = new ITypeConverter() { public Integer convert(String value) { return null; } }; OptionSpec.Builder builder = OptionSpec.builder("-x"); builder.auxiliaryTypes(Integer.class, Integer.TYPE) .type(Double.TYPE) .splitRegex(",,,") .required(true) .defaultValue("DEF") .description("Description") .paramLabel("param") .arity("1") .help(true) .versionHelp(true) .usageHelp(true) .order(123) .hidden(true) .setter(setter) .getter(getter) .converters(converter) .initialValue("ABC") .showDefaultValue(Visibility.NEVER) .withToString("TOSTRING"); assertArrayEquals(new Class[]{Integer.class, Integer.TYPE}, builder.auxiliaryTypes()); assertEquals(Double.TYPE, builder.type()); assertEquals(",,,", builder.splitRegex()); assertTrue(builder.required()); assertEquals("DEF", builder.defaultValue()); assertArrayEquals(new String[]{"Description"}, builder.description()); assertEquals("param", builder.paramLabel()); assertEquals(Range.valueOf("1"), builder.arity()); assertTrue(builder.help()); assertTrue(builder.versionHelp()); assertTrue(builder.usageHelp()); assertEquals(123, builder.order()); assertTrue(builder.hidden()); assertSame(getter, builder.getter()); assertSame(setter, builder.setter()); assertSame(converter, builder.converters()[0]); assertEquals("ABC", builder.initialValue()); assertEquals(Visibility.NEVER, builder.showDefaultValue()); assertEquals("TOSTRING", builder.toString()); builder.names("a", "b", "c") .type(String.class) .auxiliaryTypes(StringWriter.class); assertArrayEquals(new String[]{"a", "b", "c"}, builder.names()); assertArrayEquals(new Class[]{StringWriter.class}, builder.auxiliaryTypes()); assertEquals(String.class, builder.type()); } @Test public void testOptionInteractiveFalseByDefault() { assertFalse(OptionSpec.builder("-x").interactive()); assertFalse(OptionSpec.builder("-x").build().interactive()); } @Test public void testOptionInteractiveIfSet() { assertTrue(OptionSpec.builder("-x").interactive(true).interactive()); assertTrue(OptionSpec.builder("-x").arity("0").interactive(true).build().interactive()); } @Test public void testOptionInteractiveReadFromAnnotation() { class App { @Option(names = "-x", interactive = true) int x; @Option(names = "-y", interactive = false) int y; @Option(names = "-z") int z; } CommandLine cmd = new CommandLine(new App()); assertTrue(cmd.getCommandSpec().findOption("x").interactive()); assertFalse(cmd.getCommandSpec().findOption("y").interactive()); assertFalse(cmd.getCommandSpec().findOption("z").interactive()); } @Test public void testOptionInteractiveNotSupportedForMultiValue() { OptionSpec.Builder[] options = new OptionSpec.Builder[]{ OptionSpec.builder("-x").arity("1").interactive(true), OptionSpec.builder("-x").arity("2").interactive(true), OptionSpec.builder("-x").arity("3").interactive(true), OptionSpec.builder("-x").arity("1..2").interactive(true), OptionSpec.builder("-x").arity("1..*").interactive(true), OptionSpec.builder("-x").arity("0..*").interactive(true), }; for (OptionSpec.Builder opt : options) { try { opt.build(); fail("Expected exception"); } catch (InitializationException ex) { assertEquals("Interactive options and positional parameters are only supported for arity=0 and arity=0..1; not for arity=" + opt.arity(), ex.getMessage()); } } // no errors OptionSpec.builder("-x").arity("0").interactive(true).build(); OptionSpec.builder("-x").arity("0..1").interactive(true).build(); } @Test public void testOptionHasCommand() { class App { @Option(names = "-x") int x; } CommandSpec cmd = new CommandLine(new App()).getCommandSpec(); CommandSpec cmd2 = new CommandLine(new App()).getCommandSpec(); OptionSpec optx = cmd.findOption('x'); assertEquals(cmd, optx.command()); OptionSpec opty = OptionSpec.builder("-y").arity("1").build(); assertEquals(null, opty.command()); cmd.add(opty); assertEquals(cmd, opty.command()); cmd2.add(opty); assertEquals(cmd2, opty.command()); assertEquals(opty, cmd.findOption('y')); } @Test public void testOptionSpecEquals() { OptionSpec.Builder option = OptionSpec.builder("-x") .arity("1") .hideParamSyntax(true) .required(true) .splitRegex(";") .description("desc") .descriptionKey("key") .type(Map.class) .auxiliaryTypes(Integer.class, Double.class) .help(true) .usageHelp(true) .versionHelp(true) .order(123); OptionSpec p1 = option.build(); assertEquals(p1, p1); assertEquals(p1, option.build()); assertNotEquals(p1, option.arity("2").build()); assertNotEquals(p1, option.arity("1").hideParamSyntax(false).build()); assertNotEquals(p1, option.hideParamSyntax(true).required(false).build()); assertNotEquals(p1, option.required(true).splitRegex(",").build()); assertNotEquals(p1, option.splitRegex(";").description("xyz").build()); assertNotEquals(p1, option.description("desc").descriptionKey("XX").build()); assertNotEquals(p1, option.descriptionKey("key").type(List.class).build()); assertNotEquals(p1, option.type(Map.class).auxiliaryTypes(Short.class).build()); assertEquals(p1, option.auxiliaryTypes(Integer.class, Double.class).build()); assertNotEquals(p1, option.help(false).build()); assertNotEquals(p1, option.help(true).usageHelp(false).build()); assertNotEquals(p1, option.usageHelp(true).versionHelp(false).build()); assertNotEquals(p1, option.versionHelp(true).order(999).build()); assertNotEquals(p1, option.order(123).names("-a", "-b", "-c").build()); assertEquals(p1, option.names("-x").build()); } @Test public void testOptionSpecBuilder_negatableGetter() { OptionSpec.Builder builder = OptionSpec.builder("-x"); assertFalse(builder.negatable()); } @Test public void testOptionSpec_negatableSetter() { OptionSpec.Builder builder = OptionSpec.builder("-x").negatable(true); assertTrue(builder.negatable()); } @Test public void testOptionSpec_fallbackValueGetter() { OptionSpec.Builder builder = OptionSpec.builder("-x"); assertEquals("", builder.fallbackValue()); } @Test public void testOptionSpec_fallbackValueSetter() { OptionSpec.Builder builder = OptionSpec.builder("-x").fallbackValue("fallback"); assertEquals("fallback", builder.fallbackValue()); } @Test public void testEmptyUsageSplit() { assertEquals("", OptionSpec.builder("-x").build().splitRegexSynopsisLabel()); } @Test public void testGetterAndSetterOfUsageSplit() { OptionSpec.Builder builder = OptionSpec.builder("-x"); builder.auxiliaryTypes(Integer.class, Integer.TYPE) .splitRegex("\\|") .splitRegexSynopsisLabel("|"); assertEquals("\\|", builder.splitRegex()); assertEquals("|", builder.splitRegexSynopsisLabel()); } @Test public void testUsageSplitEquals() { OptionSpec.Builder option = OptionSpec.builder("-x") .arity("1") .hideParamSyntax(true) .required(true) .splitRegex("\\|") .splitRegexSynopsisLabel("|") .description("desc") .descriptionKey("key") .type(Map.class) .auxiliaryTypes(Integer.class, Double.class) .help(true) .usageHelp(true) .versionHelp(true) .order(123); OptionSpec p1 = option.build(); assertEquals(p1, p1); assertEquals(p1, option.build()); assertNotEquals(p1, option.splitRegexSynopsisLabel("\\\\?").build()); } }