diff options
Diffstat (limited to 'src/main/java/au/id/zancanaro/PropertyTestRunner.java')
-rw-r--r-- | src/main/java/au/id/zancanaro/PropertyTestRunner.java | 249 |
1 files changed, 0 insertions, 249 deletions
diff --git a/src/main/java/au/id/zancanaro/PropertyTestRunner.java b/src/main/java/au/id/zancanaro/PropertyTestRunner.java deleted file mode 100644 index ea36341..0000000 --- a/src/main/java/au/id/zancanaro/PropertyTestRunner.java +++ /dev/null @@ -1,249 +0,0 @@ -package au.id.zancanaro; - -import au.id.zancanaro.annotations.Property; -import au.id.zancanaro.annotations.Seed; -import org.junit.AssumptionViolatedException; -import org.junit.runners.BlockJUnit4ClassRunner; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.InitializationError; -import org.junit.runners.model.Statement; -import org.junit.runners.model.TestClass; - -import java.lang.reflect.*; -import java.util.*; - - -public class PropertyTestRunner extends BlockJUnit4ClassRunner { - private final Map<Type, Generator<Object>> generators = new HashMap<>(); - - public PropertyTestRunner(Class<?> klass) throws InitializationError { - super(klass); - } - - @Override - protected void collectInitializationErrors(List<Throwable> errors) { - super.collectInitializationErrors(errors); - validateGeneratorFields(errors); - } - - private void validateGeneratorFields(List<Throwable> errors) { - Field[] fields = getTestClass().getJavaClass().getDeclaredFields(); - - for (Field field : fields) { - Type type = field.getGenericType(); - if (!(type instanceof ParameterizedType)) { - continue; - } - ParameterizedType ptype = (ParameterizedType) type; - if (!(ptype.getRawType() instanceof Class)) { - continue; - } - Class<?> c = (Class) ptype.getRawType(); - if (c != Generator.class) { - continue; - } - if (!Modifier.isStatic(field.getModifiers())) { - errors.add(new Error("Generator field " + field.getName() + " must be static")); - } - if (!Modifier.isPublic(field.getModifiers())) { - errors.add(new Error("Generator field " + field.getName() + " must be public")); - } - } - } - - private static final Map<Type, Type> rawTypes; - - static { - Map<Type, Type> types = new HashMap<>(); - types.put(Double.class, Double.TYPE); - types.put(Float.class, Float.TYPE); - types.put(Long.class, Long.TYPE); - types.put(Integer.class, Integer.TYPE); - types.put(Short.class, Short.TYPE); - types.put(Byte.class, Byte.TYPE); - types.put(Character.class, Character.TYPE); - types.put(Boolean.class, Boolean.TYPE); - rawTypes = Collections.unmodifiableMap(types); - } - - private Map<Type, Generator<Object>> computeGenerators() { - if (generators.isEmpty()) { - Field[] fields = getTestClass().getJavaClass().getDeclaredFields(); - - for (Field field : fields) { - Type type = field.getGenericType(); - if (!(type instanceof ParameterizedType)) { - continue; - } - ParameterizedType ptype = (ParameterizedType) type; - if (!(ptype.getRawType() instanceof Class)) { - continue; - } - Class<?> c = (Class) ptype.getRawType(); - if (c != Generator.class) { - continue; - } - try { - Type target = ptype.getActualTypeArguments()[0]; - @SuppressWarnings("unchecked") - Generator<Object> generator = (Generator<Object>) field.get(null); - generators.put(target, generator); - if (rawTypes.containsKey(target)) { - generators.put(rawTypes.get(target), generator); - } - } catch (IllegalAccessException ex) { - - } - } - } - return generators; - } - - @Override - protected void validateConstructor(List<Throwable> errors) { - validateOnlyOneConstructor(errors); - } - - @Override - protected void validateTestMethods(List<Throwable> errors) { - for (FrameworkMethod each : computeTestMethods()) { - if (each.getAnnotation(Property.class) != null) { - each.validatePublicVoid(false, errors); - each.validateNoTypeParametersOnArgs(errors); - } else { - each.validatePublicVoidNoArg(false, errors); - } - } - } - - @Override - protected List<FrameworkMethod> computeTestMethods() { - List<FrameworkMethod> testMethods = new ArrayList<>(super.computeTestMethods()); - List<FrameworkMethod> theoryMethods = getTestClass().getAnnotatedMethods(Property.class); - testMethods.removeAll(theoryMethods); - testMethods.addAll(theoryMethods); - return testMethods; - } - - @Override - public Statement methodBlock(final FrameworkMethod method) { - return new GenerativeTester(method, getTestClass(), computeGenerators()); - } - - public static class GenerativeTester extends Statement { - private final FrameworkMethod testMethod; - private final TestClass testClass; - private final Map<Type, Generator<Object>> generators; - - public GenerativeTester(FrameworkMethod testMethod, TestClass testClass, Map<Type, Generator<Object>> generators) { - this.testMethod = testMethod; - this.testClass = testClass; - this.generators = generators; - } - - private long getSeed(Method method) { - Seed seed = method.getAnnotation(Seed.class); - if (seed == null) { - return System.currentTimeMillis(); - } else { - return seed.value(); - } - } - - @Override - public void evaluate() throws Throwable { - Method method = testMethod.getMethod(); - if (method.getParameterCount() == 0) { - runTest(new Object[0]); - } else { - @SuppressWarnings("unchecked") - Generator<Object>[] generators = (Generator<Object>[]) new Generator[method.getParameterCount()]; - int index = 0; - for (Type type : method.getGenericParameterTypes()) { - generators[index++] = this.generators.get(type); - } - Generator<Object[]> generator = Generator.tuple(generators); - - long seed = getSeed(method); - Random random = new Random(seed); - - Property property = testMethod.getAnnotation(Property.class); - int assumptionsViolated = 0; - int maxSize = property.maxSize(); - int numTests = property.runs(); - for (int i = 0; i < numTests; ++i) { - int size = Math.min(i + 1, maxSize); - RoseTree<Object[]> tree = generator.generate(random, size); - try { - runTest(tree.getValue()); - assumptionsViolated = 0; - } catch (AssumptionViolatedException ex) { - numTests++; - if (assumptionsViolated++ == 50) { - throw new Error("Violated 50 assumptions in a row: failing test"); - } - ; - } catch (Throwable ex) { - throw new PropertyTestError(method.getName(), seed, shrink(tree, ex)); - } - } - } - } - - private ShrinkResult shrink(RoseTree<Object[]> failed, Throwable originalEx) { - ShrinkResult smallest = new ShrinkResult(failed.getValue(), originalEx); - Iterator<RoseTree<Object[]>> trees = failed.getChildren(); - Set<List<Object>> seenArgs = new HashSet<>(); - while (trees.hasNext()) { - RoseTree<Object[]> tree = trees.next(); - if (seenArgs.add(Arrays.asList(tree.getValue()))) { - try { - runTest(tree.getValue()); - } catch (AssumptionViolatedException ex) { - // ignore, because it's not useful - } catch (Throwable ex) { - smallest = new ShrinkResult(tree.getValue(), ex); - Iterator<RoseTree<Object[]>> children = tree.getChildren(); - if (children.hasNext()) { - trees = children; - } else { - break; - } - } - } - } - return smallest; - } - - public void runTest(final Object[] args) throws Throwable { - new BlockJUnit4ClassRunner(testClass.getJavaClass()) { - @Override - protected void collectInitializationErrors( - List<Throwable> errors) { - // do nothing - } - - @Override - public Statement methodBlock(FrameworkMethod method) { - return super.methodBlock(method); - } - - @Override - protected Statement methodInvoker(FrameworkMethod method, Object test) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - method.invokeExplosively(test, args); - } - }; - } - - @Override - public Object createTest() throws Exception { - return getTestClass().getOnlyConstructor().newInstance(); - } - }.methodBlock(testMethod).evaluate(); - } - } - -}
\ No newline at end of file |