Skip to content

Commit

Permalink
[#182] annotated methods bugfix and initial tests
Browse files Browse the repository at this point in the history
  • Loading branch information
remkop committed May 18, 2018
1 parent 8860415 commit bd9c27c
Show file tree
Hide file tree
Showing 3 changed files with 338 additions and 11 deletions.
25 changes: 14 additions & 11 deletions src/main/java/picocli/CommandLine.java
Original file line number Diff line number Diff line change
Expand Up @@ -4118,6 +4118,9 @@ static TypedMember createIfAnnotated(Field field, Object scope) {
}
private TypedMember(Field field, Object scope) {
this(field);
if (Proxy.isProxyClass(scope.getClass())) {
throw new InitializationException("Invalid picocli annotation on interface field");
}
FieldBinding binding = new FieldBinding(scope, field);
getter = binding; setter = binding;
}
Expand All @@ -4143,15 +4146,15 @@ private TypedMember(Method method, Object scope) {

// initial value
try {
if (type.isPrimitive()) {
if ("boolean".equals(type.getSimpleName().toLowerCase())) {
setter.set(false);
} else {
setter.set((byte) 0);
}
}
if (type == Boolean.TYPE || type == Boolean.class) { setter.set(false); }
else if (type == Byte.TYPE || type == Byte.class) { setter.set(Byte.valueOf((byte) 0)); }
else if (type == Short.TYPE || type == Short.class) { setter.set(Short.valueOf((short) 0)); }
else if (type == Integer.TYPE || type == Integer.class) { setter.set(Integer.valueOf(0)); }
else if (type == Long.TYPE || type == Long.class) { setter.set(Long.valueOf(0L)); }
else if (type == Float.TYPE || type == Float.class) { setter.set(Float.valueOf(0f)); }
else if (type == Double.TYPE || type == Double.class) { setter.set(Double.valueOf(0d)); }
} catch (Exception ex) {
throw new PicocliException("Could not set initial value for " + method + ": " + ex.toString(), ex);
throw new InitializationException("Could not set initial value for " + method + ": " + ex.toString(), ex);
}
} else {
//throw new IllegalArgumentException("Getter method but not a proxy: " + scope + ": " + method);
Expand Down Expand Up @@ -4209,16 +4212,16 @@ private static class CommandReflection {
static CommandSpec extractCommandSpec(Object command, IFactory factory, boolean annotationsAreMandatory) {
if (command instanceof CommandSpec) { return (CommandSpec) command; }
Object instance = command;
String commandClassName = command.getClass().getName();
Class<?> cls = command.getClass();
String commandClassName = cls.getName();
if (command instanceof Class && ((Class) command).isInterface()) {
Class cls = (Class) command;
cls = (Class) command;
commandClassName = cls.getName();
instance = Proxy.newProxyInstance(cls.getClassLoader(), new Class[] {cls}, new PicocliInvocationHandler());
}

CommandSpec result = CommandSpec.wrapWithoutInspection(Assert.notNull(instance, "command"));

Class<?> cls = command.getClass();
boolean hasCommandAnnotation = false;
while (cls != null) {
boolean thisCommandHasAnnotation = updateCommandAttributes(cls, result, factory);
Expand Down
132 changes: 132 additions & 0 deletions src/test/java/picocli/CommandLineAnnotatedMethodImplTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package picocli;

import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.ProvideSystemProperty;

import java.math.BigInteger;
import java.util.*;

import static org.junit.Assert.*;
import static picocli.CommandLine.Option;

public class CommandLineAnnotatedMethodImplTest {
@Rule
public final ProvideSystemProperty ansiOFF = new ProvideSystemProperty("picocli.ansi", "false");

static class Primitives {
boolean aBoolean;
byte aByte;
short aShort;
int anInt;
long aLong;
float aFloat;
double aDouble;

@Option(names = "-b") void setBoolean(boolean val) { aBoolean = val; }
@Option(names = "-y") void setByte(byte val) { aByte = val; }
@Option(names = "-s") void setShort(short val) { aShort = val; }
@Option(names = "-i") void setInt(int val) { anInt = val; }
@Option(names = "-l") void setLong(long val) { aLong = val; }
@Option(names = "-f") void setFloat(float val) { aFloat = val; }
@Option(names = "-d") void setDouble(double val) { aDouble = val; }
}

@Test
public void testPrimitiveDefaultValues() {
Primitives primitives = CommandLine.populateCommand(new Primitives());
assertFalse(primitives.aBoolean);
assertEquals(0, primitives.aByte);
assertEquals((short) 0, primitives.aShort);
assertEquals(0, primitives.anInt);
assertEquals(0, primitives.aLong);
assertEquals(0, primitives.aFloat, 0.0001);
assertEquals(0, primitives.aDouble, 0.0001);
}

@Test
public void testPrimitives() {
String[] args = "-b -y1 -s2 -i3 -l4 -f5 -d6".split(" ");
Primitives primitives = CommandLine.populateCommand(new Primitives(), args);
assertTrue(primitives.aBoolean);
assertEquals(1, primitives.aByte);
assertEquals(2, primitives.aShort);
assertEquals(3, primitives.anInt);
assertEquals(4, primitives.aLong);
assertEquals(5, primitives.aFloat, 0.0001);
assertEquals(6, primitives.aDouble, 0.0001);
}

static class Objects {
Boolean aBoolean;
Byte aByte;
Short aShort;
Integer anInt;
Long aLong;
Float aFloat;
Double aDouble;
BigInteger aBigInteger;
String aString;
List<String> aList;
Map<Integer, Double> aMap;
SortedSet<Short> aSet;

@Option(names = "-b") void setBoolean(Boolean val) { aBoolean = val; }
@Option(names = "-y") void setByte(Byte val) { aByte = val; }
@Option(names = "-s") void setShort(Short val) { aShort = val; }
@Option(names = "-i") void setInt(Integer val) { anInt = val; }
@Option(names = "-l") void setLong(Long val) { aLong = val; }
@Option(names = "-f") void setFloat(Float val) { aFloat = val; }
@Option(names = "-d") void setDouble(Double val) { aDouble = val; }

@Option(names = "-bigint") void setBigInteger(BigInteger val) { aBigInteger = val; }
@Option(names = "-string") void setString(String val) { aString = val; }
@Option(names = "-list") void setList(List<String> val) { aList = val; }

@Option(names = "-map", type = {Integer.class, Double.class})
void setMap(Map<Integer, Double> val) { aMap = val; }

@Option(names = "-set", type = Short.class)
void setSortedSet(SortedSet<Short> val) { aSet = val; }
}

@Test
public void testObjectsDefaultValues() {
Objects objects = CommandLine.populateCommand(new Objects());
assertNull(objects.aBoolean);
assertNull(objects.aByte);
assertNull(objects.aShort);
assertNull(objects.anInt);
assertNull(objects.aLong);
assertNull(objects.aFloat);
assertNull(objects.aDouble);
assertNull(objects.aBigInteger);
assertNull(objects.aString);
assertNull(objects.aList);
assertNull(objects.aMap);
assertNull(objects.aSet);
}

@Test
public void testObjects() {
String[] args = "-b -y1 -s2 -i3 -l4 -f5 -d6 -bigint=7 -string abc -list a -list b -map 1=2.0 -set 33 -set 22".split(" ");
Objects objects = CommandLine.populateCommand(new Objects(), args);
assertTrue(objects.aBoolean);
assertEquals(Byte.valueOf((byte) 1), objects.aByte);
assertEquals(Short.valueOf((short) 2), objects.aShort);
assertEquals(Integer.valueOf(3), objects.anInt);
assertEquals(Long.valueOf(4), objects.aLong);
assertEquals(5f, objects.aFloat, 0.0001);
assertEquals(6d, objects.aDouble, 0.0001);
assertEquals(BigInteger.valueOf(7), objects.aBigInteger);
assertEquals("abc", objects.aString);
assertEquals(Arrays.asList("a", "b"), objects.aList);
Map<Integer, Double> map = new HashMap<Integer, Double>();
map.put(1, 2.0);
assertEquals(map, objects.aMap);
Set<Short> set = new TreeSet<Short>();
set.add((short) 22);
set.add((short) 33);
assertEquals(set, objects.aSet);
}
}
192 changes: 192 additions & 0 deletions src/test/java/picocli/CommandLineAnnotatedMethodSpecTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package picocli;

import org.junit.*;
import org.junit.contrib.java.lang.system.ProvideSystemProperty;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import static org.junit.Assert.*;
import static picocli.CommandLine.*;

public class CommandLineAnnotatedMethodSpecTest {
@Rule
public final ProvideSystemProperty ansiOFF = new ProvideSystemProperty("picocli.ansi", "false");

interface Primitives {
@Option(names = "-b")
boolean aBoolean();

@Option(names = "-y")
byte aByte();

@Option(names = "-s")
short aShort();

@Option(names = "-i")
int anInt();

@Option(names = "-l")
long aLong();

@Option(names = "-f")
float aFloat();

@Option(names = "-d")
double aDouble();
}

@Test
public void testInterfaceIsInstantiated() {
CommandLine cmd = new CommandLine(Primitives.class);
assertTrue(cmd.getCommand() instanceof Primitives);
}

@Test
public void testPrimitiveDefaultValues() {
CommandLine cmd = new CommandLine(Primitives.class);
Primitives primitives = cmd.getCommand();
assertFalse(primitives.aBoolean());
assertEquals(0, primitives.aByte());
assertEquals((short) 0, primitives.aShort());
assertEquals(0, primitives.anInt());
assertEquals(0, primitives.aLong());
assertEquals(0, primitives.aFloat(), 0.0001);
assertEquals(0, primitives.aDouble(), 0.0001);
}

@Test
public void testPrimitives() {
CommandLine cmd = new CommandLine(Primitives.class);
cmd.parse("-b -y1 -s2 -i3 -l4 -f5 -d6".split(" "));
Primitives primitives = cmd.getCommand();
assertTrue(primitives.aBoolean());
assertEquals(1, primitives.aByte());
assertEquals(2, primitives.aShort());
assertEquals(3, primitives.anInt());
assertEquals(4, primitives.aLong());
assertEquals(5, primitives.aFloat(), 0.0001);
assertEquals(6, primitives.aDouble(), 0.0001);
}

interface Objects {
@Option(names = "-b")
Boolean aBoolean();

@Option(names = "-y")
Byte aByte();

@Option(names = "-s")
Short aShort();

@Option(names = "-i")
Integer anInt();

@Option(names = "-l")
Long aLong();

@Option(names = "-f")
Float aFloat();

@Option(names = "-d")
Double aDouble();

@Option(names = "-bigint")
BigInteger aBigInteger();

@Option(names = "-string")
String aString();

@Option(names = "-list")
List<String> getList();

@Option(names = "-map", type = {Integer.class, Double.class})
Map<Integer, Double> getMap();

@Option(names = "-set", type = Short.class)
SortedSet<Short> getSortedSet();
}

@Test
public void testObjectsDefaultValues() {
CommandLine cmd = new CommandLine(Objects.class);
Objects objects = cmd.getCommand();
assertFalse(objects.aBoolean());
assertEquals(Byte.valueOf((byte) 0), objects.aByte());
assertEquals(Short.valueOf((short) 0), objects.aShort());
assertEquals(Integer.valueOf(0), objects.anInt());
assertEquals(Long.valueOf(0), objects.aLong());
assertEquals(0f, objects.aFloat(), 0.0001);
assertEquals(0d, objects.aDouble(), 0.0001);
assertNull(objects.aBigInteger());
assertNull(objects.aString());
assertNull(objects.getList());
assertNull(objects.getMap());
assertNull(objects.getSortedSet());
}

@Test
public void testObjects() {
CommandLine cmd = new CommandLine(Objects.class);
cmd.parse("-b -y1 -s2 -i3 -l4 -f5 -d6 -bigint=7 -string abc -list a -list b -map 1=2.0 -set 33 -set 22".split(" "));
Objects objects = cmd.getCommand();
assertTrue(objects.aBoolean());
assertEquals(Byte.valueOf((byte) 1), objects.aByte());
assertEquals(Short.valueOf((short) 2), objects.aShort());
assertEquals(Integer.valueOf(3), objects.anInt());
assertEquals(Long.valueOf(4), objects.aLong());
assertEquals(5f, objects.aFloat(), 0.0001);
assertEquals(6d, objects.aDouble(), 0.0001);
assertEquals(BigInteger.valueOf(7), objects.aBigInteger());
assertEquals("abc", objects.aString());
assertEquals(Arrays.asList("a", "b"), objects.getList());
Map<Integer, Double> map = new HashMap<Integer, Double>();
map.put(1, 2.0);
assertEquals(map, objects.getMap());
Set<Short> set = new TreeSet<Short>();
set.add((short) 22);
set.add((short) 33);
assertEquals(set, objects.getSortedSet());
}

interface InvalidAnnotatedStringOrPrimitiveFields {
@Option(names = "-i")
int anInt = 0;

@Option(names = "-s")
String aString = null;
}

@Test
public void testInvalidAnnotatedFieldsOnInterface() {
try {
new CommandLine(InvalidAnnotatedStringOrPrimitiveFields.class);
fail("Expected exception");
} catch (InitializationException ok) {
assertEquals("Invalid picocli annotation on interface field", ok.getMessage());
}
}

interface InvalidAnnotatedMutableFields {
@Option(names = "-s")
final List<String> aList = new ArrayList<String>();
}

@Test
public void testAnnotatedMutableFieldsOnInterfaceAreValid() {
try {
CommandLine cmd = new CommandLine(InvalidAnnotatedMutableFields.class);
cmd.parse("-s a -s b -s c".split(" "));
fail("Expected exception");
} catch (InitializationException ok) {
assertEquals("Invalid picocli annotation on interface field", ok.getMessage());
}
}
}

0 comments on commit bd9c27c

Please sign in to comment.