/*
* Copyright (c) 2011-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.vertx.core.cli.impl;
import io.vertx.core.cli.*;
import io.vertx.core.cli.Option;
import io.vertx.core.cli.annotations.Argument;
import io.vertx.core.cli.annotations.*;
import io.vertx.core.cli.converters.*;
import org.junit.Test;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Fail.fail;
public class CLIConfiguratorTest {
@Test
public void testHelloCLIFromClass() {
CLI command = CLIConfigurator.define(HelloClI.class);
assertThat(command.getOptions()).hasSize(1);
TypedOption option = (TypedOption) find(command.getOptions(), "name");
assertThat(option.getLongName()).isEqualToIgnoringCase("name");
assertThat(option.getShortName()).isEqualToIgnoringCase("n");
assertThat(option.getType()).isEqualTo(String.class);
assertThat(option.getArgName()).isEqualTo("name");
assertThat(option.getDescription()).isEqualToIgnoringCase("your name");
assertThat(option.getDefaultValue()).isNull();
assertThat(option.acceptValue()).isTrue();
assertThat(option.isMultiValued()).isFalse();
assertThat(option.isRequired()).isTrue();
}
@Test
public void testUsage() {
CLI command = CLIConfigurator.define(HelloClI.class);
StringBuilder builder = new StringBuilder();
command.usage(builder);
assertThat(builder)
.containsIgnoringCase("Usage: hello -n <name>")
.containsIgnoringCase("A command saying hello.")
.containsIgnoringCase("A simple cli to wish you a good day. Pass your name with `--name`")
.containsIgnoringCase(" -n,--name <name> your name");
}
@Name("test")
public static class CommandForDefaultValueTest {
@io.vertx.core.cli.annotations.Option(longName = "option", shortName = "o")
@DefaultValue("bar")
public void setFoo(String foo) {
}
}
@Test
public void testOptionsWithDefaultValue() {
CLI cli = CLIConfigurator.define(CommandForDefaultValueTest.class);
assertThat(cli.getOptions()).hasSize(1);
assertThat(find(cli.getOptions(), "option").getDefaultValue()).isEqualTo("bar");
assertThat(find(cli.getOptions(), "option").getName()).isEqualTo("option");
}
@Name("test")
public static class CommandForDescriptionTest {
@io.vertx.core.cli.annotations.Option(longName = "option", shortName = "o")
@Description("This option is awesome")
public void setFoo(String foo) {
}
}
@Test
public void testOptionsWithDescription() {
CLI cli = CLIConfigurator.define(CommandForDescriptionTest.class);
assertThat(cli.getOptions()).hasSize(1);
assertThat(find(cli.getOptions(), "option").getDescription())
.isEqualTo("This option is awesome");
}
@Name("test")
public static class CommandForParsedAsList {
@io.vertx.core.cli.annotations.Option(longName = "option", shortName = "o")
@ParsedAsList(separator = ":")
public void setFoo(List<String> foo) {
}
}
@Test
public void testOptionsParsedAsList() {
CLI command = CLIConfigurator.define(CommandForParsedAsList.class);
assertThat(command.getOptions()).hasSize(1);
assertThat(((TypedOption) find(command.getOptions(), "option"))
.getListSeparator()).isEqualTo(":");
assertThat(find(command.getOptions(), "option").isMultiValued()).isTrue();
assertThat(((TypedOption) find(command.getOptions(), "option")).getType())
.isEqualTo(String.class);
}
@Name("test")
public static class CommandForTypeExtractTest {
@io.vertx.core.cli.annotations.Option(longName = "list", shortName = "l")
public void setFoo(List<String> list) {
}
@io.vertx.core.cli.annotations.Option(longName = "set", shortName = "s")
public void setFoo(Set<Character> set) {
}
@io.vertx.core.cli.annotations.Option(longName = "collection", shortName = "c")
public void setFoo(Collection<Integer> collection) {
}
@io.vertx.core.cli.annotations.Option(longName = "tree", shortName = "t")
public void setFoo(TreeSet<String> list) {
}
@io.vertx.core.cli.annotations.Option(longName = "al", shortName = "al")
public void setFoo(ArrayList<String> list) {
}
@io.vertx.core.cli.annotations.Option(longName = "array", shortName = "a")
public void setFoo(int[] list) {
}
}
@Test
public void testTypeExtraction() {
CLI command = CLIConfigurator.define(CommandForTypeExtractTest.class);
assertThat(command.getOptions()).hasSize(6);
TypedOption model = (TypedOption) find(command.getOptions(), "list");
assertThat(model.getType()).isEqualTo(String.class);
assertThat(model.isMultiValued()).isTrue();
model = (TypedOption) find(command.getOptions(), "set");
assertThat(model.getType()).isEqualTo(Character.class);
assertThat(model.isMultiValued()).isTrue();
model = (TypedOption) find(command.getOptions(), "collection");
assertThat(model.getType()).isEqualTo(Integer.class);
assertThat(model.isMultiValued()).isTrue();
model = (TypedOption) find(command.getOptions(), "tree");
assertThat(model.getType()).isEqualTo(String.class);
assertThat(model.isMultiValued()).isTrue();
model = (TypedOption) find(command.getOptions(), "al");
assertThat(model.getType()).isEqualTo(String.class);
assertThat(model.isMultiValued()).isTrue();
model = (TypedOption) find(command.getOptions(), "array");
assertThat(model.getType()).isEqualTo(Integer.TYPE);
assertThat(model.isMultiValued()).isTrue();
}
@Test
public void testInjectionOfString() throws CLIException {
HelloClI command = new HelloClI();
CLI cli = CLIConfigurator.define(HelloClI.class);
CommandLine evaluatedCLI = cli.parse(Arrays.asList("--name", "vert.x"));
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.run()).isEqualToIgnoringCase("Hello vert.x");
assertThat(command.name).isEqualToIgnoringCase("vert.x");
}
private CommandLine parse(CLI cli, String... args) throws CLIException {
return cli.parse(Arrays.asList(args));
}
@Test
public void testSingleValueInjection() throws CLIException {
CLIWithSingleValue command = new CLIWithSingleValue();
CLI cli = CLIConfigurator.define(command.getClass());
CommandLine evaluatedCLI = parse(cli, "--boolean", "--short=1", "--byte=1", "--int=1", "--long=1",
"--double=1.1", "--float=1.1", "--char=c", "--string=hello");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.aBoolean).isTrue();
assertThat(command.aShort).isEqualTo((short) 1);
assertThat(command.aByte).isEqualTo((byte) 1);
assertThat(command.anInt).isEqualTo(1);
assertThat(command.aLong).isEqualTo(1l);
assertThat(command.aDouble).isEqualTo(1.1d);
assertThat(command.aFloat).isEqualTo(1.1f);
assertThat(command.aChar).isEqualTo('c');
assertThat(command.aString).isEqualTo("hello");
evaluatedCLI = parse(cli, "--boolean2", "--short2=1", "--byte2=1", "--int2=1", "--long2=1",
"--double2=1.1", "--float2=1.1", "--char2=c", "--string=hello");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.anotherBoolean).isTrue();
assertThat(command.anotherShort).isEqualTo((short) 1);
assertThat(command.anotherByte).isEqualTo((byte) 1);
assertThat(command.anotherInt).isEqualTo(1);
assertThat(command.anotherLong).isEqualTo(1l);
assertThat(command.anotherDouble).isEqualTo(1.1d);
assertThat(command.anotherFloat).isEqualTo(1.1f);
assertThat(command.anotherChar).isEqualTo('c');
assertThat(command.aString).isEqualTo("hello");
evaluatedCLI = parse(cli, "--state=NEW");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.aState).isEqualTo(Thread.State.NEW);
evaluatedCLI = parse(cli, "--person=vert.x");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.aPerson.name).isEqualTo("vert.x");
evaluatedCLI = parse(cli, "--person2=vert.x");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.anotherPerson.name).isEqualTo("vert.x");
evaluatedCLI = parse(cli, "--person3=vert.x");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.aThirdPerson.name).isEqualTo("vert.x");
evaluatedCLI = parse(cli, "--person4=bob,morane");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.aFourthPerson.first).isEqualTo("bob");
assertThat(command.aFourthPerson.last).isEqualTo("morane");
}
@Test
public void testMultiValuesInjection() throws CLIException {
CLIWithMultipleValues command = new CLIWithMultipleValues();
CLI cli = CLIConfigurator.define(command.getClass());
CommandLine evaluatedCLI = parse(cli, "--persons=x", "--persons", "y", "z");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.persons).hasSize(3);
evaluatedCLI = parse(cli, "--persons2=x", "--persons2", "y", "z");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.persons2).hasSize(3);
evaluatedCLI = parse(cli, "--persons3=x", "--persons3", "y", "z");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.persons3).hasSize(3);
evaluatedCLI = parse(cli, "--persons4=x:y:z");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.persons4).hasSize(3);
evaluatedCLI = parse(cli, "--states=NEW", "--states", "BLOCKED", "RUNNABLE");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.states).hasSize(3).containsExactly(Thread.State.NEW, Thread.State.BLOCKED,
Thread.State.RUNNABLE);
evaluatedCLI = parse(cli, "--ints=1", "--ints", "2", "3");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.ints).hasSize(3).containsExactly(1, 2, 3);
evaluatedCLI = parse(cli, "--shorts=1", "--shorts", "2", "3");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.shorts).hasSize(3).containsExactly((short) 1, (short) 2, (short) 3);
evaluatedCLI = parse(cli, "--strings=a");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.strings).hasSize(1).containsExactly("a");
evaluatedCLI = parse(cli, "--doubles=1", "--doubles", "2.2", "3.3");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.doubles).hasSize(3).containsExactly(1.0, 2.2, 3.3);
}
@Name("test")
public static class CommandForArgumentInjectionTest {
AtomicReference<String> reference = new AtomicReference<>();
@Argument(index = 0)
public void setX(String s) {
reference.set(s);
}
}
@Test
public void testArgumentInjection() throws CLIException {
CommandForArgumentInjectionTest command = new CommandForArgumentInjectionTest();
CLI cli = CLIConfigurator.define(command.getClass()).setName("test");
CommandLine evaluatedCLI = parse(cli, "foo");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.reference.get()).isEqualTo("foo");
}
@Name("test")
public class CommandForConvertedValueTest {
AtomicReference<Person4> reference = new AtomicReference<>();
@Argument(index = 0, required = false)
@DefaultValue("Bill,Balantine")
@ConvertedBy(Person4Converter.class)
public void setX(Person4 s) {
reference.set(s);
}
}
@Test
public void testArgumentInjectionWithConvertedByAndDefaultValue() throws CLIException {
CommandForConvertedValueTest command = new CommandForConvertedValueTest();
CLI cli = CLIConfigurator.define(command.getClass()).setName("test");
CommandLine evaluatedCLI = parse(cli, "Bob,Morane");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.reference.get().first).isEqualTo("Bob");
assertThat(command.reference.get().last).isEqualTo("Morane");
evaluatedCLI = parse(cli);
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.reference.get().first).isEqualTo("Bill");
assertThat(command.reference.get().last).isEqualTo("Balantine");
}
@Name("test")
public static class CommandForMultipleArgumentTest {
AtomicReference<String> x = new AtomicReference<>();
AtomicReference<Integer> y = new AtomicReference<>();
@Argument(index = 0)
public void setX(String s) {
x.set(s);
}
@Argument(index = 1)
public void setY(int s) {
y.set(s);
}
}
@Test
public void testArgumentInjectionWithSeveralArguments() throws CLIException {
CommandForMultipleArgumentTest command = new CommandForMultipleArgumentTest();
CLI cli = CLIConfigurator.define(command.getClass()).setName("test");
CommandLine evaluatedCLI = parse(cli, "foo", "1");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.x.get()).isEqualTo("foo");
assertThat(command.y.get()).isEqualTo(1);
}
@Name("test")
public static class CommandWithDefaultValueOnArgument {
AtomicReference<String> x = new AtomicReference<>();
AtomicReference<Integer> y = new AtomicReference<>();
@Argument(index = 0)
public void setX(String s) {
x.set(s);
}
@Argument(index = 1, required = false)
@DefaultValue("25")
public void setY(int s) {
y.set(s);
}
}
@Test
public void testArgumentWithDefaultValue() throws CLIException {
CommandWithDefaultValueOnArgument command = new CommandWithDefaultValueOnArgument();
CLI cli = CLIConfigurator.define(command.getClass()).setName("test");
CommandLine evaluatedCLI = parse(cli, "foo");
CLIConfigurator.inject(evaluatedCLI, command);
assertThat(command.x.get()).isEqualTo("foo");
assertThat(command.y.get()).isEqualTo(25);
}
private Option find(List<Option> options, String name) {
final List<Option> match = options.stream().filter(c -> c.getLongName().equalsIgnoreCase(name))
.collect(Collectors.toList());
if (match.isEmpty()) {
fail("Cannot find option '" + name + "' in " + options.stream().map(Option::getLongName)
.collect(Collectors.toList()));
}
return match.get(0);
}
@Name("single")
private class CLIWithSingleValue {
String aString;
Thread.State aState;
boolean aBoolean;
Boolean anotherBoolean;
byte aByte;
Byte anotherByte;
char aChar;
Character anotherChar;
double aDouble;
Double anotherDouble;
float aFloat;
Float anotherFloat;
int anInt;
Integer anotherInt;
long aLong;
Long anotherLong;
short aShort;
Short anotherShort;
Person aPerson;
Person2 anotherPerson;
Person3 aThirdPerson;
Person4 aFourthPerson;
@io.vertx.core.cli.annotations.Option(longName = "boolean", shortName = "Z", flag = true)
public void setaBoolean(boolean aBoolean) {
this.aBoolean = aBoolean;
}
@io.vertx.core.cli.annotations.Option(longName = "byte", shortName = "B")
public void setaByte(byte aByte) {
this.aByte = aByte;
}
@io.vertx.core.cli.annotations.Option(longName = "char", shortName = "C")
public void setaChar(char aChar) {
this.aChar = aChar;
}
@io.vertx.core.cli.annotations.Option(longName = "double", shortName = "D")
public void setaDouble(double aDouble) {
this.aDouble = aDouble;
}
@io.vertx.core.cli.annotations.Option(longName = "float", shortName = "F")
public void setaFloat(float aFloat) {
this.aFloat = aFloat;
}
@io.vertx.core.cli.annotations.Option(longName = "long", shortName = "J")
public void setaLong(long aLong) {
this.aLong = aLong;
}
@io.vertx.core.cli.annotations.Option(longName = "int", shortName = "I")
public void setAnInt(int anInt) {
this.anInt = anInt;
}
@io.vertx.core.cli.annotations.Option(longName = "boolean2", shortName = "AZ", flag = true)
public void setAnotherBoolean(Boolean anotherBoolean) {
this.anotherBoolean = anotherBoolean;
}
@io.vertx.core.cli.annotations.Option(longName = "byte2", shortName = "AB")
public void setAnotherByte(Byte anotherByte) {
this.anotherByte = anotherByte;
}
@io.vertx.core.cli.annotations.Option(longName = "char2", shortName = "AC")
public void setAnotherChar(Character anotherChar) {
this.anotherChar = anotherChar;
}
@io.vertx.core.cli.annotations.Option(longName = "double2", shortName = "AD")
public void setAnotherDouble(Double anotherDouble) {
this.anotherDouble = anotherDouble;
}
@io.vertx.core.cli.annotations.Option(longName = "float2", shortName = "AF")
public void setAnotherFloat(Float anotherFloat) {
this.anotherFloat = anotherFloat;
}
@io.vertx.core.cli.annotations.Option(longName = "int2", shortName = "AI")
public void setAnotherInt(Integer anotherInt) {
this.anotherInt = anotherInt;
}
@io.vertx.core.cli.annotations.Option(longName = "long2", shortName = "AJ")
public void setAnotherLong(Long anotherLong) {
this.anotherLong = anotherLong;
}
@io.vertx.core.cli.annotations.Option(longName = "person2", shortName = "p2")
public void setAnotherPerson(Person2 anotherPerson) {
this.anotherPerson = anotherPerson;
}
@io.vertx.core.cli.annotations.Option(longName = "short2", shortName = "AS")
public void setAnotherShort(Short anotherShort) {
this.anotherShort = anotherShort;
}
@io.vertx.core.cli.annotations.Option(longName = "person", shortName = "p")
public void setaPerson(Person aPerson) {
this.aPerson = aPerson;
}
@io.vertx.core.cli.annotations.Option(longName = "person4", shortName = "p4")
@ConvertedBy(Person4Converter.class)
public void setAFourthPerson(Person4 aPerson) {
this.aFourthPerson = aPerson;
}
@io.vertx.core.cli.annotations.Option(longName = "short", shortName = "s")
public void setaShort(short aShort) {
this.aShort = aShort;
}
@io.vertx.core.cli.annotations.Option(longName = "state", shortName = "st")
public void setaState(Thread.State aState) {
this.aState = aState;
}
@io.vertx.core.cli.annotations.Option(longName = "string", shortName = "str")
public void setaString(String aString) {
this.aString = aString;
}
@io.vertx.core.cli.annotations.Option(longName = "person3", shortName = "p3")
public void setaThirdPerson(Person3 aThirdPerson) {
this.aThirdPerson = aThirdPerson;
}
}
@Name("multi")
private class CLIWithMultipleValues {
List<Person> persons;
List<Person> persons2;
List<Person> persons3;
Set<Thread.State> states;
Collection<Integer> ints;
Set<String> strings;
List<Short> shorts;
double[] doubles;
List<Person> persons4;
@io.vertx.core.cli.annotations.Option(longName = "doubles", shortName = "ds")
public void setDoubles(double[] doubles) {
this.doubles = doubles;
}
@io.vertx.core.cli.annotations.Option(longName = "ints", shortName = "is")
public void setInts(Collection<Integer> ints) {
this.ints = ints;
}
@io.vertx.core.cli.annotations.Option(longName = "persons2", shortName = "ps2")
public void setPersons2(List<Person> persons2) {
this.persons2 = persons2;
}
@io.vertx.core.cli.annotations.Option(longName = "persons3", shortName = "ps3")
public void setPersons3(List<Person> persons3) {
this.persons3 = persons3;
}
@io.vertx.core.cli.annotations.Option(longName = "persons4", shortName = "ps4")
@ParsedAsList(separator = ":")
public void setPersons4(List<Person> persons4) {
this.persons4 = persons4;
}
@io.vertx.core.cli.annotations.Option(longName = "persons", shortName = "ps")
public void setPersons(List<Person> persons) {
this.persons = persons;
}
@io.vertx.core.cli.annotations.Option(longName = "shorts", shortName = "ss")
public void setShorts(List<Short> shorts) {
this.shorts = shorts;
}
@io.vertx.core.cli.annotations.Option(longName = "states", shortName = "sts")
public void setStates(Set<Thread.State> states) {
this.states = states;
}
@io.vertx.core.cli.annotations.Option(longName = "strings", shortName = "str")
public void setStrings(Set<String> strings) {
this.strings = strings;
}
}
}