diff options
author | Carlo Zancanaro <carlo@zancanaro.id.au> | 2015-06-09 23:31:54 +1000 |
---|---|---|
committer | Carlo Zancanaro <carlo@zancanaro.id.au> | 2015-06-09 23:31:54 +1000 |
commit | 40961d4950c40643d5d71721a7e024e3951323ce (patch) | |
tree | 330af94a98468ab0c10cff76c2bb36eb28a64ef0 /src/main | |
parent | dd9f72b94eb7b2c37061c80457e74e8d7ac3e18f (diff) |
Generalise the ObjectGeneration stuff
The new ObjectGeneration stuff is now used to generate everything for a test
case, which means it's all unified and "nice" now.
Add a @UseGenerator annotation to be used to specify how to generate specific
field values.
Obviously, not everything can be generated magically, so if you specify a
@DataSource in your test then it will be used in preference to any magically
generated value.
Diffstat (limited to 'src/main')
10 files changed, 60 insertions, 78 deletions
diff --git a/src/main/java/au/id/zancanaro/javacheck/DataSourceHelper.java b/src/main/java/au/id/zancanaro/javacheck/DataSourceHelper.java index 522fd26..f118649 100644 --- a/src/main/java/au/id/zancanaro/javacheck/DataSourceHelper.java +++ b/src/main/java/au/id/zancanaro/javacheck/DataSourceHelper.java @@ -3,10 +3,11 @@ package au.id.zancanaro.javacheck; import au.id.zancanaro.javacheck.annotations.DataSource; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; public class DataSourceHelper { private final Class<?> classObject; @@ -17,47 +18,6 @@ public class DataSourceHelper { this.generators = new HashMap<>(); } - public static Set<Type> validateGeneratorFields(Class<?> classObject, List<Throwable> errors) { - Set<Type> result = new HashSet<>(); - - for (Field field : classObject.getDeclaredFields()) { - if (field.isAnnotationPresent(DataSource.class)) { - boolean error = false; - if (!Modifier.isStatic(field.getModifiers())) { - errors.add(new Error("@DataSource field " + field.getName() + " must be static")); - error = true; - } - if (!Modifier.isPublic(field.getModifiers())) { - errors.add(new Error("@DataSource field " + field.getName() + " must be public")); - error = true; - } - - Type type = field.getGenericType(); - ParameterizedType parameterizedType; - if (type instanceof ParameterizedType) { - parameterizedType = (ParameterizedType) type; - if (parameterizedType.getRawType() instanceof Class) { - Class<?> c = (Class) parameterizedType.getRawType(); - if (c == Generator.class) { - if (!error) { - result.add(parameterizedType.getActualTypeArguments()[0]); - } - } else { - errors.add(new Error("@DataSource fields must be of type Generator<T>")); - } - } else { - errors.add(new Error("@DataSource fields must be of type Generator<T>")); - } - } else { - errors.add(new Error("@DataSource fields must be of type Generator<T>")); - } - } - } - - return result; - } - - private static final Map<Type, Type> rawTypes; static { diff --git a/src/main/java/au/id/zancanaro/javacheck/junit/Properties.java b/src/main/java/au/id/zancanaro/javacheck/junit/Properties.java index 94032bc..a7bf19b 100644 --- a/src/main/java/au/id/zancanaro/javacheck/junit/Properties.java +++ b/src/main/java/au/id/zancanaro/javacheck/junit/Properties.java @@ -1,11 +1,11 @@ package au.id.zancanaro.javacheck.junit; -import au.id.zancanaro.javacheck.DataSourceHelper; import au.id.zancanaro.javacheck.Generator; import au.id.zancanaro.javacheck.ShrinkResult; import au.id.zancanaro.javacheck.ShrinkTree; import au.id.zancanaro.javacheck.annotations.Property; import au.id.zancanaro.javacheck.annotations.Seed; +import au.id.zancanaro.javacheck.object.GeneratorProvider; import org.junit.AssumptionViolatedException; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.FrameworkMethod; @@ -13,38 +13,24 @@ import org.junit.runners.model.InitializationError; import org.junit.runners.model.Statement; import org.junit.runners.model.TestClass; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import java.lang.reflect.Type; import java.util.*; @SuppressWarnings("WeakerAccess") public class Properties extends BlockJUnit4ClassRunner { - private final DataSourceHelper helper; + private final GeneratorProvider provider; public Properties(Class<?> classObject) throws InitializationError { super(classObject); - helper = new DataSourceHelper(classObject); + provider = GeneratorProvider.DEFAULT_PROVIDER.withDataSources(classObject); } @Override protected void collectInitializationErrors(List<Throwable> errors) { super.collectInitializationErrors(errors); - Set<Type> generated = DataSourceHelper.validateGeneratorFields(getTestClass().getJavaClass(), errors); - validateTestMethodParameters(errors, generated); } - private void validateTestMethodParameters(List<Throwable> errors, Set<Type> generated) { - for (FrameworkMethod each : computeTestMethods()) { - for (Type type : each.getMethod().getGenericParameterTypes()) { - if (!generated.contains(type)) { - errors.add(new Error("No @DataSource for type: " + type)); - generated.add(type); // ignore future errors on this type - } - } - } - } - - @Override protected void validateConstructor(List<Throwable> errors) { validateOnlyOneConstructor(errors); @@ -73,18 +59,18 @@ public class Properties extends BlockJUnit4ClassRunner { @Override public Statement methodBlock(final FrameworkMethod method) { - return new GenerativeTester(method, getTestClass(), helper.computeGenerators()); + return new GenerativeTester(method, getTestClass(), provider); } public static class GenerativeTester extends Statement { private final FrameworkMethod testMethod; private final TestClass testClass; - private final Map<Type, Generator<?>> generators; + private final GeneratorProvider provider; - public GenerativeTester(FrameworkMethod testMethod, TestClass testClass, Map<Type, Generator<?>> generators) { + public GenerativeTester(FrameworkMethod testMethod, TestClass testClass, GeneratorProvider provider) { this.testMethod = testMethod; this.testClass = testClass; - this.generators = generators; + this.provider = provider; } private static long getSeed(Method method) { @@ -103,7 +89,7 @@ public class Properties extends BlockJUnit4ClassRunner { runTest(new Object[0]); } else { Generator<?>[] generators = Arrays.stream(method.getGenericParameterTypes()) - .map(this.generators::get) + .map(param -> provider.getGenerator(param, new Annotation[0], provider)) .toArray(Generator[]::new); Generator<List<Object>> generator = Generator.tuple(generators); @@ -126,7 +112,6 @@ public class Properties extends BlockJUnit4ClassRunner { throw new Error("Violated 50 assumptions in a row: failing test"); } } catch (Throwable ex) { -// tree.print(new OutputStreamWriter(System.out), Arrays::toString); throw new PropertyError(method.getName(), seed, shrink(tree, ex)); } } @@ -178,8 +163,7 @@ public class Properties extends BlockJUnit4ClassRunner { public void runTest(final Object[] args) throws Throwable { new BlockJUnit4ClassRunner(testClass.getJavaClass()) { @Override - protected void collectInitializationErrors( - List<Throwable> errors) { + protected void collectInitializationErrors(List<Throwable> errors) { // do nothing } diff --git a/src/main/java/au/id/zancanaro/javacheck/junit/PropertyError.java b/src/main/java/au/id/zancanaro/javacheck/junit/PropertyError.java index d3747b9..5f89ca8 100644 --- a/src/main/java/au/id/zancanaro/javacheck/junit/PropertyError.java +++ b/src/main/java/au/id/zancanaro/javacheck/junit/PropertyError.java @@ -2,7 +2,6 @@ package au.id.zancanaro.javacheck.junit; import au.id.zancanaro.javacheck.ShrinkResult; -import java.util.Arrays; import java.util.Iterator; import java.util.List; diff --git a/src/main/java/au/id/zancanaro/javacheck/object/CharType.java b/src/main/java/au/id/zancanaro/javacheck/object/CharType.java index befbd04..aa3049e 100644 --- a/src/main/java/au/id/zancanaro/javacheck/object/CharType.java +++ b/src/main/java/au/id/zancanaro/javacheck/object/CharType.java @@ -5,7 +5,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Target(ElementType.FIELD) +@Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface CharType { public static enum TYPE { diff --git a/src/main/java/au/id/zancanaro/javacheck/object/DoubleRange.java b/src/main/java/au/id/zancanaro/javacheck/object/DoubleRange.java index ca7c9a9..08f8e3a 100644 --- a/src/main/java/au/id/zancanaro/javacheck/object/DoubleRange.java +++ b/src/main/java/au/id/zancanaro/javacheck/object/DoubleRange.java @@ -5,7 +5,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Target(ElementType.FIELD) +@Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface DoubleRange { double min(); diff --git a/src/main/java/au/id/zancanaro/javacheck/object/GeneratorProvider.java b/src/main/java/au/id/zancanaro/javacheck/object/GeneratorProvider.java index 7e5964f..67769b6 100644 --- a/src/main/java/au/id/zancanaro/javacheck/object/GeneratorProvider.java +++ b/src/main/java/au/id/zancanaro/javacheck/object/GeneratorProvider.java @@ -1,8 +1,10 @@ package au.id.zancanaro.javacheck.object; +import au.id.zancanaro.javacheck.DataSourceHelper; import au.id.zancanaro.javacheck.Generator; import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; @@ -28,7 +30,14 @@ public interface GeneratorProvider { @Override public Generator<?> getGenerator(Type type, Annotation[] annotations, GeneratorProvider provider) { - if (type == Integer.TYPE || type == Integer.class) { + UseGenerator generator = getAnnotation(annotations, UseGenerator.class); + if (generator != null) { + try { + return generator.value().getConstructor().newInstance(); + } catch (InvocationTargetException | NoSuchMethodException | InstantiationException | IllegalAccessException ex) { + throw new ObjectGenerationException("Could not construct generator of type " + generator.value(), ex); + } + } else if (type == Integer.TYPE || type == Integer.class) { IntRange range = getAnnotation(annotations, IntRange.class); if (range == null) { return integer(); @@ -50,11 +59,11 @@ public interface GeneratorProvider { return doublePrecision(range.min(), range.max()); } } else if (type == String.class) { - CharType range = getAnnotation(annotations, CharType.class); - if (range == null) { + CharType charType = getAnnotation(annotations, CharType.class); + if (charType == null) { return string(); } else { - switch (range.value()) { + switch (charType.value()) { case ALPHA: return stringOf(alphaCharacter()); case ALPHA_NUMERIC: @@ -114,4 +123,16 @@ public interface GeneratorProvider { } }; } + + default GeneratorProvider withDataSources(Class<?> wrappingType) { + DataSourceHelper helper = new DataSourceHelper(wrappingType); + Map<Type, Generator<?>> generators = helper.computeGenerators(); + return (type, annotations, provider) -> { + if (generators.containsKey(type)) { + return generators.get(type); + } else { + return this.getGenerator(type, annotations, provider); + } + }; + } } diff --git a/src/main/java/au/id/zancanaro/javacheck/object/IntRange.java b/src/main/java/au/id/zancanaro/javacheck/object/IntRange.java index 8dc3f28..5d2aa30 100644 --- a/src/main/java/au/id/zancanaro/javacheck/object/IntRange.java +++ b/src/main/java/au/id/zancanaro/javacheck/object/IntRange.java @@ -5,7 +5,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Target(ElementType.FIELD) +@Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface IntRange { int max(); diff --git a/src/main/java/au/id/zancanaro/javacheck/object/LongRange.java b/src/main/java/au/id/zancanaro/javacheck/object/LongRange.java index dd5a61f..dd5b8cf 100644 --- a/src/main/java/au/id/zancanaro/javacheck/object/LongRange.java +++ b/src/main/java/au/id/zancanaro/javacheck/object/LongRange.java @@ -5,7 +5,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Target(ElementType.FIELD) +@Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface LongRange { long max(); diff --git a/src/main/java/au/id/zancanaro/javacheck/object/ObjectGenerationException.java b/src/main/java/au/id/zancanaro/javacheck/object/ObjectGenerationException.java index d2147eb..444de32 100644 --- a/src/main/java/au/id/zancanaro/javacheck/object/ObjectGenerationException.java +++ b/src/main/java/au/id/zancanaro/javacheck/object/ObjectGenerationException.java @@ -8,4 +8,8 @@ public class ObjectGenerationException extends RuntimeException { public ObjectGenerationException(Throwable cause) { super(cause); } + + public ObjectGenerationException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/src/main/java/au/id/zancanaro/javacheck/object/UseGenerator.java b/src/main/java/au/id/zancanaro/javacheck/object/UseGenerator.java new file mode 100644 index 0000000..f62d978 --- /dev/null +++ b/src/main/java/au/id/zancanaro/javacheck/object/UseGenerator.java @@ -0,0 +1,14 @@ +package au.id.zancanaro.javacheck.object; + +import au.id.zancanaro.javacheck.Generator; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface UseGenerator { + Class<? extends Generator> value(); +} |