|
|
@@ -1,423 +1,420 @@
|
|
|
-/*
|
|
|
- * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
- * @copyright Ranides Atterwim
|
|
|
- * @license WTFPL
|
|
|
- * @url http://ranides.net/projects/assira.junit
|
|
|
- */
|
|
|
-package net.ranides.assira.junit;
|
|
|
-
|
|
|
-import java.io.PrintStream;
|
|
|
-import java.lang.reflect.Field;
|
|
|
-import java.lang.reflect.InvocationTargetException;
|
|
|
-import java.lang.reflect.Method;
|
|
|
-import java.lang.reflect.Modifier;
|
|
|
-import java.lang.reflect.ParameterizedType;
|
|
|
-import java.lang.reflect.Type;
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.HashMap;
|
|
|
-import java.util.HashSet;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
-import java.util.Set;
|
|
|
-import java.util.SortedSet;
|
|
|
-import java.util.TreeSet;
|
|
|
-import java.util.function.Function;
|
|
|
-import java.util.function.Supplier;
|
|
|
-import javax.annotation.Resource;
|
|
|
-import org.junit.Assert;
|
|
|
-import org.junit.Ignore;
|
|
|
-
|
|
|
-/**
|
|
|
- * Inspects inheritance tree of objects passed to {@link #run} method and invokes
|
|
|
- * all compatible testers registered by {#append} method. For example, if you
|
|
|
- * register testers annotated as compatible with {@code Map} & {@code SortedMap},
|
|
|
- * then method {@code run} will invoke both of them for {@code TreeMap}, but only
|
|
|
- * the first one for {@codee HashMap}.
|
|
|
- * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
- */
|
|
|
-public class InterfaceSuite {
|
|
|
-
|
|
|
- private final SortedSet<MethodRunner> methods = new TreeSet<>();
|
|
|
-
|
|
|
- private final List<TesterWrapper> testers = new ArrayList<>();
|
|
|
-
|
|
|
- private final Set<String> ignored = new HashSet<>();
|
|
|
-
|
|
|
- private final PrintStream output;
|
|
|
-
|
|
|
- private boolean debug;
|
|
|
-
|
|
|
- public InterfaceSuite(PrintStream output) {
|
|
|
- this.output = output;
|
|
|
- }
|
|
|
-
|
|
|
- public InterfaceSuite debug(boolean value) {
|
|
|
- this.debug = value;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public InterfaceSuite ignore(String method) {
|
|
|
- this.ignored.add(method);
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public InterfaceSuite append(Object tester) {
|
|
|
- testers.add(new TesterWrapper(tester));
|
|
|
- for(Method m : tester.getClass().getMethods()) {
|
|
|
- if(Modifier.isStatic(m.getModifiers()) ) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- if(1 != m.getParameterCount()) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- if(null != m.getAnnotation(Ignore.class)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- if(null == m.getAnnotation(InterfaceTest.class)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- if(!m.getReturnType().equals(void.class)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- MethodRunner mr = runner(tester, m);
|
|
|
- if(null != mr) {
|
|
|
- methods.add(mr);
|
|
|
- }
|
|
|
- }
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public InterfaceSuite param(String name, Object value) {
|
|
|
- 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<String> failed = new ArrayList<>();
|
|
|
- methods.stream().filter((m)->m.accept(type)).forEach((m)->{
|
|
|
- try {
|
|
|
- m.run(counter, generator);
|
|
|
- } catch(Throwable cause) {
|
|
|
- failed.add(m.label);
|
|
|
- cause.printStackTrace(output);
|
|
|
- }
|
|
|
- });
|
|
|
- ireset();
|
|
|
- if(!failed.isEmpty()) {
|
|
|
- output.println();
|
|
|
- output.println("Failed interface tests: ");
|
|
|
- for(String f : failed) {
|
|
|
- output.print(f);
|
|
|
- }
|
|
|
- output.println();
|
|
|
- Assert.fail("Failed interface tests: " + failed.size() + "/"+counter.value);
|
|
|
- }
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- private void ireset() {
|
|
|
- debug = false;
|
|
|
- ignored.clear();
|
|
|
- for(TesterWrapper tester : testers) {
|
|
|
- tester.resetParams();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- 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 abstract class MethodRunner implements Comparable<MethodRunner> {
|
|
|
-
|
|
|
- protected final String uid;
|
|
|
-
|
|
|
- protected final String nid;
|
|
|
-
|
|
|
- protected final Class<?> type;
|
|
|
-
|
|
|
- protected final Object that;
|
|
|
-
|
|
|
- protected final Method method;
|
|
|
-
|
|
|
- protected String label;
|
|
|
-
|
|
|
- public MethodRunner(Object that, Method method) {
|
|
|
- this.type = typeof(method);
|
|
|
- this.that = that;
|
|
|
- this.method = method;
|
|
|
- this.uid = type.getName()+"#"+method.getDeclaringClass().getName() +"#" + method.getName();
|
|
|
- this.nid = method.getDeclaringClass().getSimpleName() + "." + method.getName();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public int compareTo(MethodRunner object) {
|
|
|
- return uid.compareTo(object.uid);
|
|
|
- }
|
|
|
-
|
|
|
- public boolean accept(Class<?> param) {
|
|
|
- return type.isAssignableFrom(param);
|
|
|
- }
|
|
|
-
|
|
|
- protected abstract void invoke(TGenerator argument) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
|
|
|
-
|
|
|
- public void run(Counter counter, TGenerator param) throws RuntimeException {
|
|
|
- if( ignored.contains(nid) ) {
|
|
|
- printf(type, counter.next(), method, "IGNORED");
|
|
|
- return;
|
|
|
- }
|
|
|
- try {
|
|
|
- invoke(param);
|
|
|
- printf(type, counter.next(), method, "");
|
|
|
- } catch (IllegalAccessException | IllegalArgumentException ex) {
|
|
|
- throw NewAssert.rethrow(ex);
|
|
|
- } catch (InvocationTargetException ex) {
|
|
|
- if(ex.getTargetException() instanceof UnsupportedOperationException) {
|
|
|
- UnsupportedOperationException uoe = (UnsupportedOperationException)ex.getTargetException();
|
|
|
-
|
|
|
- if( "NO TEST".equals(uoe.getMessage())) {
|
|
|
- printf(type, counter.next(), method, "UNSUPPORTED");
|
|
|
- if(debug) {
|
|
|
- ex.printStackTrace(output);
|
|
|
- }
|
|
|
- } else {
|
|
|
- printf(type, counter.next(), method, "FAILED");
|
|
|
- throw NewAssert.rethrow(ex.getTargetException());
|
|
|
- }
|
|
|
-
|
|
|
- } else {
|
|
|
- printf(type, counter.next(), method, "FAILED");
|
|
|
- throw NewAssert.rethrow(ex.getTargetException());
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void printf(Class<?> type, int index, Method method, String comment) {
|
|
|
- if(null == comment || comment.isEmpty()) {
|
|
|
- comment = "";
|
|
|
- } else {
|
|
|
- comment = " - " + comment;
|
|
|
- }
|
|
|
- label = String.format(" - SUITE: %s [%d] %s.%s%s%n",
|
|
|
- type.getName(),
|
|
|
- index,
|
|
|
- method.getDeclaringClass().getSimpleName(),
|
|
|
- method.getName(),
|
|
|
- comment
|
|
|
- );
|
|
|
- output.print(label);
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- static Class<?> typeof(Method method) {
|
|
|
- Class<?> cparam = method.getParameterTypes()[0];
|
|
|
- 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<?>) {
|
|
|
- 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 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)
|
|
|
- });
|
|
|
- return e;
|
|
|
- }
|
|
|
-
|
|
|
- private static final class Counter {
|
|
|
- private int value = 0;
|
|
|
-
|
|
|
- public int next() {
|
|
|
- return ++value;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static final class TesterWrapper {
|
|
|
-
|
|
|
- private final Object object;
|
|
|
- private final Map<String, IParam> resources = new HashMap<>();
|
|
|
-
|
|
|
- public TesterWrapper(Object object) {
|
|
|
- this.object = object;
|
|
|
- for(Class<?> c=object.getClass(); c!=null; c=c.getSuperclass()) {
|
|
|
- for(Field f : c.getDeclaredFields()) {
|
|
|
- Resource info = f.getAnnotation(Resource.class);
|
|
|
- if(null != info) {
|
|
|
- String name = info.name();
|
|
|
- if("".equals(name)) {
|
|
|
- name = f.getName();
|
|
|
- }
|
|
|
- f.setAccessible(true);
|
|
|
- resources.put(name, new IParam(object, f));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void param(String name, Object value) {
|
|
|
- if( resources.containsKey(name) ) {
|
|
|
- resources.get(name).set(value);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void resetParams() {
|
|
|
- for(IParam param : resources.values()) {
|
|
|
- param.reset();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- 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("Generator function not supplied.");
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- private static final class IParam {
|
|
|
- private final Object object;
|
|
|
- private final Field field;
|
|
|
- private final Object value;
|
|
|
-
|
|
|
- public IParam(Object object, Field field) {
|
|
|
- try {
|
|
|
- this.object = object;
|
|
|
- this.field = field;
|
|
|
- this.value = field.get(object);
|
|
|
- } catch(ReflectiveOperationException cause) {
|
|
|
- throw NewAssert.rethrow(cause);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void set(Object value) {
|
|
|
- try {
|
|
|
- field.set(object, value);
|
|
|
- } catch(ReflectiveOperationException cause) {
|
|
|
- throw NewAssert.rethrow(cause);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void reset() {
|
|
|
- try {
|
|
|
- field.set(object, value);
|
|
|
- } catch(ReflectiveOperationException cause) {
|
|
|
- throw NewAssert.rethrow(cause);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
+/*
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ * @copyright Ranides Atterwim
|
|
|
+ * @license WTFPL
|
|
|
+ * @url http://ranides.net/projects/assira.junit
|
|
|
+ */
|
|
|
+package net.ranides.assira.junit;
|
|
|
+
|
|
|
+import java.io.PrintStream;
|
|
|
+import java.lang.reflect.Field;
|
|
|
+import java.lang.reflect.InvocationTargetException;
|
|
|
+import java.lang.reflect.Method;
|
|
|
+import java.lang.reflect.Modifier;
|
|
|
+import java.lang.reflect.ParameterizedType;
|
|
|
+import java.lang.reflect.Type;
|
|
|
+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 java.util.regex.Pattern;
|
|
|
+import javax.annotation.Resource;
|
|
|
+import org.junit.Assert;
|
|
|
+import org.junit.Ignore;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Inspects inheritance tree of objects passed to {@link #run} method and invokes
|
|
|
+ * all compatible testers registered by {#append} method. For example, if you
|
|
|
+ * register testers annotated as compatible with {@code Map} & {@code SortedMap},
|
|
|
+ * then method {@code run} will invoke both of them for {@code TreeMap}, but only
|
|
|
+ * the first one for {@codee HashMap}.
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ */
|
|
|
+public class InterfaceSuite {
|
|
|
+
|
|
|
+ private final SortedSet<MethodRunner> methods = new TreeSet<>();
|
|
|
+
|
|
|
+ private final List<TesterWrapper> testers = new ArrayList<>();
|
|
|
+
|
|
|
+ private final List<Pattern> ignored = new ArrayList<>();
|
|
|
+
|
|
|
+ private final PrintStream output;
|
|
|
+
|
|
|
+ private static final boolean DEBUG = "true".equals(System.getProperty("assira.junit.debug"));
|
|
|
+
|
|
|
+ private boolean debug = DEBUG;
|
|
|
+
|
|
|
+ public InterfaceSuite(PrintStream output) {
|
|
|
+ this.output = output;
|
|
|
+ }
|
|
|
+
|
|
|
+ public InterfaceSuite debug(boolean value) {
|
|
|
+ this.debug = value;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public InterfaceSuite ignore(String method) {
|
|
|
+ this.ignored.add(Pattern.compile(Pattern.quote(method)));
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public InterfaceSuite ignore(Pattern method) {
|
|
|
+ this.ignored.add(method);
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public InterfaceSuite append(Object tester) {
|
|
|
+ testers.add(new TesterWrapper(tester));
|
|
|
+ for(Method m : tester.getClass().getMethods()) {
|
|
|
+ if(Modifier.isStatic(m.getModifiers()) ) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if(1 != m.getParameterCount()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if(null != m.getAnnotation(Ignore.class)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if(null == m.getAnnotation(InterfaceTest.class)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if(!m.getReturnType().equals(void.class)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ MethodRunner mr = runner(tester, m);
|
|
|
+ if(null != mr) {
|
|
|
+ methods.add(mr);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public InterfaceSuite param(String name, Object value) {
|
|
|
+ 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<String> failed = new ArrayList<>();
|
|
|
+ methods.stream().filter((m)->m.accept(type)).forEach((m)->{
|
|
|
+ try {
|
|
|
+ m.run(counter, generator);
|
|
|
+ } catch(Throwable cause) {
|
|
|
+ failed.add(m.label);
|
|
|
+ cause.printStackTrace(output);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ ireset();
|
|
|
+ if(!failed.isEmpty()) {
|
|
|
+ output.println();
|
|
|
+ output.println("Failed interface tests: ");
|
|
|
+ for(String f : failed) {
|
|
|
+ output.print(f);
|
|
|
+ }
|
|
|
+ output.println();
|
|
|
+ Assert.fail("Failed interface tests: " + failed.size() + "/"+counter.value);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void ireset() {
|
|
|
+ debug = DEBUG;
|
|
|
+ ignored.clear();
|
|
|
+ for(TesterWrapper tester : testers) {
|
|
|
+ tester.resetParams();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 abstract class MethodRunner implements Comparable<MethodRunner> {
|
|
|
+
|
|
|
+ protected final String uid;
|
|
|
+
|
|
|
+ protected final String nid;
|
|
|
+
|
|
|
+ protected final Class<?> type;
|
|
|
+
|
|
|
+ protected final Object that;
|
|
|
+
|
|
|
+ protected final Method method;
|
|
|
+
|
|
|
+ protected String label;
|
|
|
+
|
|
|
+ public MethodRunner(Object that, Method method) {
|
|
|
+ this.type = typeof(method);
|
|
|
+ this.that = that;
|
|
|
+ this.method = method;
|
|
|
+ this.uid = type.getName()+"#"+method.getDeclaringClass().getName() +"#" + method.getName();
|
|
|
+ this.nid = method.getDeclaringClass().getSimpleName() + "." + method.getName();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int compareTo(MethodRunner object) {
|
|
|
+ return uid.compareTo(object.uid);
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean accept(Class<?> param) {
|
|
|
+ return type.isAssignableFrom(param);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected abstract void invoke(TGenerator argument) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
|
|
|
+
|
|
|
+ public void run(Counter counter, TGenerator param) throws RuntimeException {
|
|
|
+ for(Pattern p : ignored) {
|
|
|
+ if(p.matcher(nid).matches()) {
|
|
|
+ if(debug) {
|
|
|
+ printf(type, counter.next(), method, "IGNORED");
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ invoke(param);
|
|
|
+ if(debug) {
|
|
|
+ printf(type, counter.next(), method, "");
|
|
|
+ }
|
|
|
+ } catch (IllegalAccessException | IllegalArgumentException ex) {
|
|
|
+ throw NewAssert.rethrow(ex);
|
|
|
+ } catch (InvocationTargetException ex) {
|
|
|
+ printf(type, counter.next(), method, "FAILED");
|
|
|
+ throw NewAssert.rethrow(ex.getTargetException());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void printf(Class<?> type, int index, Method method, String comment) {
|
|
|
+ if(null == comment || comment.isEmpty()) {
|
|
|
+ comment = "";
|
|
|
+ } else {
|
|
|
+ comment = " - " + comment;
|
|
|
+ }
|
|
|
+ label = String.format(" - SUITE: %s [%d] %s.%s%s%n",
|
|
|
+ type.getName(),
|
|
|
+ index,
|
|
|
+ method.getDeclaringClass().getSimpleName(),
|
|
|
+ method.getName(),
|
|
|
+ comment
|
|
|
+ );
|
|
|
+ output.print(label);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ static Class<?> typeof(Method method) {
|
|
|
+ Class<?> cparam = method.getParameterTypes()[0];
|
|
|
+ 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<?>) {
|
|
|
+ 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 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)
|
|
|
+ });
|
|
|
+ return e;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class Counter {
|
|
|
+ private int value = 0;
|
|
|
+
|
|
|
+ public int next() {
|
|
|
+ return ++value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class TesterWrapper {
|
|
|
+
|
|
|
+ private final Object object;
|
|
|
+ private final Map<String, IParam> resources = new HashMap<>();
|
|
|
+
|
|
|
+ public TesterWrapper(Object object) {
|
|
|
+ this.object = object;
|
|
|
+ for(Class<?> c=object.getClass(); c!=null; c=c.getSuperclass()) {
|
|
|
+ for(Field f : c.getDeclaredFields()) {
|
|
|
+ Resource info = f.getAnnotation(Resource.class);
|
|
|
+ if(null != info) {
|
|
|
+ String name = info.name();
|
|
|
+ if("".equals(name)) {
|
|
|
+ name = f.getName();
|
|
|
+ }
|
|
|
+ f.setAccessible(true);
|
|
|
+ resources.put(name, new IParam(object, f));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void param(String name, Object value) {
|
|
|
+ if( resources.containsKey(name) ) {
|
|
|
+ resources.get(name).set(value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void resetParams() {
|
|
|
+ for(IParam param : resources.values()) {
|
|
|
+ param.reset();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ 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("Generator function not supplied.");
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class IParam {
|
|
|
+ private final Object object;
|
|
|
+ private final Field field;
|
|
|
+ private final Object value;
|
|
|
+
|
|
|
+ public IParam(Object object, Field field) {
|
|
|
+ try {
|
|
|
+ this.object = object;
|
|
|
+ this.field = field;
|
|
|
+ this.value = field.get(object);
|
|
|
+ } catch(ReflectiveOperationException cause) {
|
|
|
+ throw NewAssert.rethrow(cause);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void set(Object value) {
|
|
|
+ try {
|
|
|
+ field.set(object, value);
|
|
|
+ } catch(ReflectiveOperationException cause) {
|
|
|
+ throw NewAssert.rethrow(cause);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void reset() {
|
|
|
+ try {
|
|
|
+ field.set(object, value);
|
|
|
+ } catch(ReflectiveOperationException cause) {
|
|
|
+ throw NewAssert.rethrow(cause);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|