|
|
@@ -6,8 +6,6 @@
|
|
|
*/
|
|
|
package net.ranides.assira.junit;
|
|
|
|
|
|
-import java.io.IOException;
|
|
|
-import java.io.OutputStream;
|
|
|
import java.io.PrintStream;
|
|
|
import java.lang.reflect.Field;
|
|
|
import java.lang.reflect.InvocationTargetException;
|
|
|
@@ -15,16 +13,16 @@ import java.lang.reflect.Method;
|
|
|
import java.lang.reflect.Modifier;
|
|
|
import java.lang.reflect.ParameterizedType;
|
|
|
import java.lang.reflect.Type;
|
|
|
-import java.nio.charset.StandardCharsets;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.SortedSet;
|
|
|
import java.util.TreeSet;
|
|
|
+import java.util.function.Function;
|
|
|
import java.util.function.Supplier;
|
|
|
import javax.annotation.Resource;
|
|
|
-import junit.framework.Assert;
|
|
|
+import org.junit.Assert;
|
|
|
import org.junit.Ignore;
|
|
|
|
|
|
/**
|
|
|
@@ -44,11 +42,11 @@ public class InterfaceSuite {
|
|
|
|
|
|
private final List<TesterWrapper> testers = new ArrayList<>();
|
|
|
|
|
|
- private final OutputStream output;
|
|
|
+ private final PrintStream output;
|
|
|
|
|
|
private boolean debug;
|
|
|
|
|
|
- public InterfaceSuite(OutputStream output) {
|
|
|
+ public InterfaceSuite(PrintStream output) {
|
|
|
this.output = output;
|
|
|
}
|
|
|
|
|
|
@@ -75,54 +73,96 @@ public class InterfaceSuite {
|
|
|
if(!m.getReturnType().equals(void.class)) {
|
|
|
continue;
|
|
|
}
|
|
|
- methods.add(new MethodRunner(tester, m));
|
|
|
+ MethodRunner mr = runner(tester, m);
|
|
|
+ if(null != mr) {
|
|
|
+ methods.add(mr);
|
|
|
+ }
|
|
|
}
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
public InterfaceSuite param(String name, Object value) {
|
|
|
- for(TesterWrapper t : testers) {
|
|
|
+ testers.stream().forEach((t) -> {
|
|
|
t.param(name, value);
|
|
|
- }
|
|
|
+ });
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
+ public boolean run(Function<int[], ?> generator) {
|
|
|
+ return irun(new TGeneratorF(generator));
|
|
|
+ }
|
|
|
+
|
|
|
public boolean run(Supplier<?> generator) {
|
|
|
+ return irun(new TGeneratorS(generator));
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean irun(TGenerator generator) {
|
|
|
Class<?> type = generator.get().getClass();
|
|
|
Counter counter = new Counter();
|
|
|
List<Exception> errors = new ArrayList<>();
|
|
|
- for(MethodRunner m : methods) {
|
|
|
- if( m.accept(type)) {
|
|
|
- try {
|
|
|
- m.run(counter, generator);
|
|
|
- } catch(Exception cause) {
|
|
|
- errors.add(cause);
|
|
|
- }
|
|
|
+ methods.stream().filter((m)->m.accept(type)).forEach((m)->{
|
|
|
+ try {
|
|
|
+ m.run(counter, generator);
|
|
|
+ } catch(Exception cause) {
|
|
|
+ errors.add(cause);
|
|
|
}
|
|
|
- }
|
|
|
- for(Exception cause : errors) {
|
|
|
+ });
|
|
|
+ errors.stream().forEach((cause) -> {
|
|
|
cause.printStackTrace(new PrintStream(output));
|
|
|
- }
|
|
|
+ });
|
|
|
if(!errors.isEmpty()) {
|
|
|
Assert.fail("Failed interface tests: " + errors.size() + "/"+counter.value);
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- private final class MethodRunner implements Comparable<MethodRunner> {
|
|
|
+ private MethodRunner runner(Object that, Method method) {
|
|
|
+ if( method.getParameterTypes()[0].equals(Supplier.class) ) {
|
|
|
+ return new SMethodRunner(that, method);
|
|
|
+ }
|
|
|
+ if( method.getParameterTypes()[0].equals(Function.class) ) {
|
|
|
+ return new SMethodRunner(that, method);
|
|
|
+ }
|
|
|
+ return new CMethodRunner(that, method);
|
|
|
+ }
|
|
|
+
|
|
|
+ private class CMethodRunner extends MethodRunner {
|
|
|
+
|
|
|
+ public CMethodRunner(Object that, Method method) {
|
|
|
+ super(that, method);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected void invoke(TGenerator argument) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
|
|
+ method.invoke(that, argument.get());
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private class SMethodRunner extends MethodRunner {
|
|
|
+
|
|
|
+ public SMethodRunner(Object that, Method method) {
|
|
|
+ super(that, method);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected void invoke(TGenerator argument) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
|
|
+ method.invoke(that, argument);
|
|
|
+ }
|
|
|
|
|
|
- private final String uid;
|
|
|
+ }
|
|
|
+
|
|
|
+ private abstract class MethodRunner implements Comparable<MethodRunner> {
|
|
|
|
|
|
- private final boolean supp;
|
|
|
+ protected final String uid;
|
|
|
|
|
|
- private final Class<?> type;
|
|
|
+ protected final Class<?> type;
|
|
|
|
|
|
- private final Object that;
|
|
|
+ protected final Object that;
|
|
|
|
|
|
- private final Method method;
|
|
|
+ protected final Method method;
|
|
|
|
|
|
public MethodRunner(Object that, Method method) {
|
|
|
- this.supp = method.getParameterTypes()[0].equals(Supplier.class);
|
|
|
this.type = typeof(method);
|
|
|
this.that = that;
|
|
|
this.method = method;
|
|
|
@@ -138,15 +178,12 @@ public class InterfaceSuite {
|
|
|
return type.isAssignableFrom(param);
|
|
|
}
|
|
|
|
|
|
- public void run(Counter counter, Supplier<?> supplier) throws RuntimeException {
|
|
|
+ protected abstract void invoke(TGenerator argument) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
|
|
|
+
|
|
|
+ public void run(Counter counter, TGenerator param) throws RuntimeException {
|
|
|
try {
|
|
|
- method.invoke(that, supp ? supplier : supplier.get());
|
|
|
- printf(" - SUITE: %s [%d] %s.%s%n",
|
|
|
- type.getName(),
|
|
|
- counter.next(),
|
|
|
- method.getDeclaringClass().getSimpleName(),
|
|
|
- method.getName()
|
|
|
- );
|
|
|
+ invoke(param);
|
|
|
+ printf(type, counter.next(), method, "");
|
|
|
} catch (IllegalAccessException | IllegalArgumentException ex) {
|
|
|
throw NewAssert.rethrow(ex);
|
|
|
} catch (InvocationTargetException ex) {
|
|
|
@@ -155,45 +192,48 @@ public class InterfaceSuite {
|
|
|
|
|
|
if( !"TEST.SILENT".equals(uoe.getMessage())) {
|
|
|
if(debug) {
|
|
|
- ex.printStackTrace();
|
|
|
+ ex.printStackTrace(output);
|
|
|
}
|
|
|
String comment = "TEST.IGNORE".equals(uoe.getMessage()) ? "IGNORED" : "UNSUPPORTED";
|
|
|
- printf(" - SUITE: %s [%d] %s.%s - %s%n",
|
|
|
- type.getName(),
|
|
|
- counter.next(),
|
|
|
- method.getDeclaringClass().getSimpleName(),
|
|
|
- method.getName(),
|
|
|
- comment
|
|
|
- );
|
|
|
+ printf(type, counter.next(), method, comment);
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
- printf(" - SUITE: %s [%d] %s.%s - FAILED%n",
|
|
|
- type.getName(),
|
|
|
- counter.next(),
|
|
|
- method.getDeclaringClass().getSimpleName(),
|
|
|
- method.getName()
|
|
|
- );
|
|
|
+ printf(type, counter.next(), method, "FAILED");
|
|
|
throw NewAssert.rethrow(ex.getTargetException());
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void printf(String format, Object... values) {
|
|
|
- try {
|
|
|
- output.write(String.format(format, values).getBytes(StandardCharsets.UTF_8));
|
|
|
- } catch (IOException ex) {
|
|
|
- throw new Error(ex); // NOPMD
|
|
|
+ private void printf(Class<?> type, int index, Method method, String comment) {
|
|
|
+ if(null == comment || comment.isEmpty()) {
|
|
|
+ comment = "";
|
|
|
+ } else {
|
|
|
+ comment = " - " + comment;
|
|
|
}
|
|
|
+ output.printf(" - SUITE: %s [%d] %s.%s%s%n",
|
|
|
+ type.getName(),
|
|
|
+ index,
|
|
|
+ method.getDeclaringClass().getSimpleName(),
|
|
|
+ method.getName(),
|
|
|
+ comment
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
static Class<?> typeof(Method method) {
|
|
|
Class<?> cparam = method.getParameterTypes()[0];
|
|
|
- if(!Supplier.class.equals(cparam)) {
|
|
|
- return cparam;
|
|
|
+ if(Supplier.class.isAssignableFrom(cparam)) {
|
|
|
+ return typeofSupplier(method);
|
|
|
}
|
|
|
+ if(Function.class.isAssignableFrom(cparam)) {
|
|
|
+ return typeofFunction(method);
|
|
|
+ }
|
|
|
+ return cparam;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Class<?> typeofSupplier(Method method) throws AssertionError {
|
|
|
ParameterizedType gparam = (ParameterizedType)method.getGenericParameterTypes()[0];
|
|
|
Type tparam = gparam.getActualTypeArguments()[0];
|
|
|
if(tparam instanceof Class<?>) {
|
|
|
@@ -202,13 +242,32 @@ public class InterfaceSuite {
|
|
|
if(tparam instanceof ParameterizedType) {
|
|
|
return (Class<?>)(((ParameterizedType)tparam).getRawType());
|
|
|
}
|
|
|
+ throw new AssertionError("You can't use wildcard generic types in method: " + method, causeForMethod(method));
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Class<?> typeofFunction(Method method) throws AssertionError {
|
|
|
+ ParameterizedType gparam = (ParameterizedType)method.getGenericParameterTypes()[0];
|
|
|
+ if(!gparam.getActualTypeArguments()[0].equals(int[].class)) {
|
|
|
+ throw new AssertionError("Function must accept int[] in method: " + method, causeForMethod(method));
|
|
|
+ }
|
|
|
+ Type tparam = gparam.getActualTypeArguments()[1];
|
|
|
+ if(tparam instanceof Class<?>) {
|
|
|
+ return (Class<?>)tparam;
|
|
|
+ }
|
|
|
+ if(tparam instanceof ParameterizedType) {
|
|
|
+ return (Class<?>)(((ParameterizedType)tparam).getRawType());
|
|
|
+ }
|
|
|
+ throw new AssertionError("You can't use wildcard generic types in method: " + method, causeForMethod(method));
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Exception causeForMethod(Method method) {
|
|
|
Exception e = new Exception("Invalid declaration");
|
|
|
e.setStackTrace(new StackTraceElement[]{
|
|
|
new StackTraceElement(
|
|
|
- method.getDeclaringClass().getName(),
|
|
|
- method.getName(), "?", -1)
|
|
|
+ method.getDeclaringClass().getName(),
|
|
|
+ method.getName(), "?", -1)
|
|
|
});
|
|
|
- throw new AssertionError("You can't use wildcard generic types in method: " + method, e);
|
|
|
+ return e;
|
|
|
}
|
|
|
|
|
|
private static final class Counter {
|
|
|
@@ -253,5 +312,49 @@ public class InterfaceSuite {
|
|
|
}
|
|
|
|
|
|
}
|
|
|
+
|
|
|
+ private interface TGenerator extends Function<int[], Object>, Supplier<Object> {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class TGeneratorF implements TGenerator {
|
|
|
+
|
|
|
+ private final Function<int[], ?> function;
|
|
|
+
|
|
|
+ public TGeneratorF(Function<int[], ?> function) {
|
|
|
+ this.function = function;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Object get() {
|
|
|
+ return function.apply(new int[0]);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Object apply(int[] t) {
|
|
|
+ return function.apply(t);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class TGeneratorS implements TGenerator {
|
|
|
+
|
|
|
+ private final Supplier<?> function;
|
|
|
+
|
|
|
+ public TGeneratorS(Supplier<?> function) {
|
|
|
+ this.function = function;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Object get() {
|
|
|
+ return function.get();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Object apply(int[] t) {
|
|
|
+ throw new UnsupportedOperationException("Not supported yet.");
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
}
|