|
|
@@ -11,17 +11,14 @@ import java.io.OutputStream;
|
|
|
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.nio.charset.StandardCharsets;
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.Arrays;
|
|
|
-import java.util.IdentityHashMap;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
-import java.util.Set;
|
|
|
+import java.util.SortedSet;
|
|
|
import java.util.TreeSet;
|
|
|
import java.util.function.Supplier;
|
|
|
-import org.junit.Assert;
|
|
|
import org.junit.Ignore;
|
|
|
+import org.junit.Test;
|
|
|
|
|
|
/**
|
|
|
* Inspects inheritance tree of objects passed to {@link #run} method and invokes
|
|
|
@@ -33,7 +30,7 @@ import org.junit.Ignore;
|
|
|
*/
|
|
|
public class QTesterSuite {
|
|
|
|
|
|
- private final Map<Class<?>, List<TesterRunner>> testers = new IdentityHashMap<>();
|
|
|
+ private final SortedSet<MethodRunner> methods = new TreeSet<>();
|
|
|
|
|
|
private final OutputStream output;
|
|
|
|
|
|
@@ -41,82 +38,69 @@ public class QTesterSuite {
|
|
|
this.output = output;
|
|
|
}
|
|
|
|
|
|
- public QTesterSuite append(Class<?> tester) {
|
|
|
- Class<?> target = tester.getAnnotation(QTester.class).value();
|
|
|
- if(!testers.containsKey(target)) {
|
|
|
- testers.put(target, new ArrayList<>());
|
|
|
+ public QTesterSuite append(Object 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(Test.class)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if(!m.getReturnType().equals(void.class)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ methods.add(new MethodRunner(tester, m));
|
|
|
}
|
|
|
- testers.get(target).add(new TesterRunner(tester, target));
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
public boolean run(Supplier<?> generator) {
|
|
|
Class<?> type = generator.get().getClass();
|
|
|
Counter counter = new Counter();
|
|
|
- match(type).stream().forEach((runner) -> {
|
|
|
- runner.run(counter, type, generator);
|
|
|
- });
|
|
|
+ for(MethodRunner m : methods) {
|
|
|
+ if( m.accept(type)) {
|
|
|
+ m.run(counter, generator);
|
|
|
+ }
|
|
|
+ }
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- private Set<TesterRunner> match(Class<?> type) {
|
|
|
- Set<TesterRunner> result = new TreeSet<>();
|
|
|
- for(Class<?> clazz = type; clazz!=Object.class; clazz=clazz.getSuperclass()) {
|
|
|
- matchInsert(result, clazz);
|
|
|
- matchInsert(result, clazz.getInterfaces());
|
|
|
- }
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- private void matchInsert(Set<TesterRunner> collection, Class[] list) {
|
|
|
- for(Class<?> clazz : list) {
|
|
|
- matchInsert(collection, clazz);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void matchInsert(Set<TesterRunner> collection, Class<?> clazz) {
|
|
|
- if( testers.containsKey(clazz)) {
|
|
|
- collection.addAll(testers.get(clazz));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private final class TesterRunner implements Comparable<TesterRunner> {
|
|
|
+ private final class MethodRunner implements Comparable<MethodRunner> {
|
|
|
|
|
|
- private final Class<?> tester;
|
|
|
+ private final String uid;
|
|
|
|
|
|
- private final List<Method> methods = new ArrayList<>();
|
|
|
-
|
|
|
- public TesterRunner(Class<?> tester, Class<?> target) {
|
|
|
- this.tester = tester;
|
|
|
+ private final boolean supp;
|
|
|
+
|
|
|
+ private final Class<?> type;
|
|
|
+
|
|
|
+ private final Object that;
|
|
|
+
|
|
|
+ private final Method method;
|
|
|
|
|
|
- Method[] array = tester.getMethods();
|
|
|
- Arrays.sort(array,
|
|
|
- (method1, method2) -> method1.getName().compareTo(method2.getName())
|
|
|
- );
|
|
|
- for(Method method : array) {
|
|
|
- if(method.getDeclaringClass().equals(Object.class)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- if(!accept(method, target)) {
|
|
|
- Assert.assertNotNull("public method should have compatible signature: "+method, method.getAnnotation(Ignore.class));
|
|
|
- continue;
|
|
|
- }
|
|
|
- methods.add(method);
|
|
|
- }
|
|
|
+ public MethodRunner(Object that, Method method) {
|
|
|
+ this.supp = method.getParameterTypes()[0].equals(Supplier.class);
|
|
|
+ this.type = param(method);
|
|
|
+ this.that = that;
|
|
|
+ this.method = method;
|
|
|
+ this.uid = type.getName()+"#"+method.getDeclaringClass().getName() +"#" + method.getName();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public int compareTo(TesterRunner other) {
|
|
|
- return tester.getSimpleName().compareTo(other.tester.getSimpleName());
|
|
|
+ public int compareTo(MethodRunner object) {
|
|
|
+ return uid.compareTo(object.uid);
|
|
|
}
|
|
|
|
|
|
- public void run(Counter counter, Class<?> type, Supplier<?> generator) {
|
|
|
- for(Method method : methods) {
|
|
|
- run(counter, method, type, param(method, generator));
|
|
|
- }
|
|
|
+ public boolean accept(Class<?> param) {
|
|
|
+ return type.isAssignableFrom(param);
|
|
|
}
|
|
|
|
|
|
- private void run(Counter counter, Method method, Class<?> type, Object object) throws RuntimeException {
|
|
|
+ public void run(Counter counter, Supplier<?> supplier) throws RuntimeException {
|
|
|
try {
|
|
|
printf(" - SUITE: %s [%d] %s.%s%n",
|
|
|
type.getName(),
|
|
|
@@ -124,7 +108,7 @@ public class QTesterSuite {
|
|
|
method.getDeclaringClass().getSimpleName(),
|
|
|
method.getName()
|
|
|
);
|
|
|
- method.invoke(null, object);
|
|
|
+ method.invoke(that, supp ? supplier : supplier.get());
|
|
|
} catch (IllegalAccessException | IllegalArgumentException ex) {
|
|
|
throw QAssert.rethrow(ex);
|
|
|
} catch (InvocationTargetException ex) {
|
|
|
@@ -141,25 +125,6 @@ public class QTesterSuite {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private boolean accept(Method method, Class<?> target) {
|
|
|
- return
|
|
|
- Modifier.isStatic(method.getModifiers()) &&
|
|
|
- 1 == method.getParameterCount() &&
|
|
|
- accept(method.getParameterTypes()[0], target);
|
|
|
- }
|
|
|
-
|
|
|
- private boolean accept(Class<?> param, Class<?> target) {
|
|
|
- return param.equals(target) || param.equals(Supplier.class);
|
|
|
- }
|
|
|
-
|
|
|
- private Object param(Method method, Supplier<?> generator) {
|
|
|
- if(method.getParameterTypes()[0].equals(Supplier.class)) {
|
|
|
- return generator;
|
|
|
- } else {
|
|
|
- return generator.get();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
private void printf(String format, Object... values) {
|
|
|
try {
|
|
|
output.write(String.format(format, values).getBytes(StandardCharsets.UTF_8));
|
|
|
@@ -170,6 +135,28 @@ public class QTesterSuite {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ static Class<?> param(Method method) {
|
|
|
+ Class<?> cparam = method.getParameterTypes()[0];
|
|
|
+ if(!Supplier.class.equals(cparam)) {
|
|
|
+ return cparam;
|
|
|
+ }
|
|
|
+ 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());
|
|
|
+ }
|
|
|
+ Exception e = new Exception("Invalid declaration");
|
|
|
+ e.setStackTrace(new StackTraceElement[]{
|
|
|
+ new StackTraceElement(
|
|
|
+ method.getDeclaringClass().getName(),
|
|
|
+ method.getName(), "?", -1)
|
|
|
+ });
|
|
|
+ throw new AssertionError("You can't use wildcard generic types in method: " + method, e);
|
|
|
+ }
|
|
|
+
|
|
|
private static final class Counter {
|
|
|
private int value = 0;
|
|
|
|