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 | |
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.
15 files changed, 82 insertions, 100 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(); +} diff --git a/src/test/java/au/id/zancanaro/javacheck/ListFunctorRulesTest.java b/src/test/java/au/id/zancanaro/javacheck/ListFunctorRulesTest.java index b757bcd..4100378 100644 --- a/src/test/java/au/id/zancanaro/javacheck/ListFunctorRulesTest.java +++ b/src/test/java/au/id/zancanaro/javacheck/ListFunctorRulesTest.java @@ -9,7 +9,8 @@ import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; -import static au.id.zancanaro.javacheck.Generators.*; +import static au.id.zancanaro.javacheck.Generators.longInteger; +import static au.id.zancanaro.javacheck.Generators.oneOf; import static org.junit.Assert.assertEquals; @RunWith(Properties.class) @@ -19,9 +20,6 @@ public class ListFunctorRulesTest { private final static int maxSize = 1000; @DataSource - public static Generator<List<Long>> listOfIntegers = listOf(longInteger()); - - @DataSource public static Generator<Function<Long, Long>> integerFunction = oneOf( longInteger().map(ListFunctorRulesTest::plusI), diff --git a/src/test/java/au/id/zancanaro/javacheck/SimpleListOperationsTest.java b/src/test/java/au/id/zancanaro/javacheck/SimpleListOperationsTest.java index fcc8baf..3f4d58b 100644 --- a/src/test/java/au/id/zancanaro/javacheck/SimpleListOperationsTest.java +++ b/src/test/java/au/id/zancanaro/javacheck/SimpleListOperationsTest.java @@ -1,6 +1,5 @@ package au.id.zancanaro.javacheck; -import au.id.zancanaro.javacheck.annotations.DataSource; import au.id.zancanaro.javacheck.annotations.Property; import au.id.zancanaro.javacheck.junit.Properties; import org.junit.runner.RunWith; @@ -8,7 +7,6 @@ import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.List; -import static au.id.zancanaro.javacheck.Generators.*; import static java.util.Collections.reverse; import static java.util.Collections.sort; import static org.junit.Assert.assertEquals; @@ -19,12 +17,6 @@ public class SimpleListOperationsTest { private final static int runs = 1000; private final static int maxSize = 1000; - @DataSource - public static Generator<List<Integer>> listOfIntegers = listOf(integer()); - - @DataSource - public static Generator<List<String>> listOfStrings = listOf(stringOf(alphaNumericCharacter())); - @Property(maxSize = maxSize, runs = runs) public void sortingIsIdempotent(List<Integer> list) { List<Integer> left = new ArrayList<>(list); diff --git a/src/test/java/au/id/zancanaro/javacheck/object/MyObject.java b/src/test/java/au/id/zancanaro/javacheck/object/MyObject.java index ac0c370..824fd8d 100644 --- a/src/test/java/au/id/zancanaro/javacheck/object/MyObject.java +++ b/src/test/java/au/id/zancanaro/javacheck/object/MyObject.java @@ -1,5 +1,13 @@ package au.id.zancanaro.javacheck.object; +import au.id.zancanaro.javacheck.Generator; +import au.id.zancanaro.javacheck.ShrinkTree; + +import java.util.Random; + +import static au.id.zancanaro.javacheck.Generators.asciiCharacter; +import static au.id.zancanaro.javacheck.Generators.stringOf; + public class MyObject { public final String string; public final int value; @@ -12,10 +20,21 @@ public class MyObject { } @UseForGeneration - public MyObject(String string, SubObject<Integer> subObject) { + public MyObject( + @UseGenerator(StringGenerator.class) String string, + SubObject<Integer> subObject) { this(string, string.length(), subObject); } + public static class StringGenerator implements Generator<String> { + public final Generator<String> gen = stringOf(asciiCharacter()); + + @Override + public ShrinkTree<String> generate(Random random, int size) { + return gen.generate(random, size); + } + } + public MyObject add(MyObject other) { return new MyObject( this.string + other.string, diff --git a/src/test/java/au/id/zancanaro/javacheck/object/MyObjectAddTest.java b/src/test/java/au/id/zancanaro/javacheck/object/MyObjectAddTest.java index 7965449..9321acb 100644 --- a/src/test/java/au/id/zancanaro/javacheck/object/MyObjectAddTest.java +++ b/src/test/java/au/id/zancanaro/javacheck/object/MyObjectAddTest.java @@ -1,23 +1,15 @@ package au.id.zancanaro.javacheck.object; -import au.id.zancanaro.javacheck.Generator; -import au.id.zancanaro.javacheck.annotations.DataSource; import au.id.zancanaro.javacheck.annotations.Property; import au.id.zancanaro.javacheck.junit.Properties; import org.junit.runner.RunWith; -import java.util.Collections; - -import static au.id.zancanaro.javacheck.Generators.ofType; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @RunWith(Properties.class) public class MyObjectAddTest { - @DataSource - public static Generator<MyObject> source = ofType(MyObject.class); - @Property public void testAdd(MyObject a, MyObject b) { MyObject added = a.add(b); diff --git a/src/test/java/au/id/zancanaro/javacheck/state/queue/commands/Offer.java b/src/test/java/au/id/zancanaro/javacheck/state/queue/commands/Offer.java index 301efeb..366e974 100644 --- a/src/test/java/au/id/zancanaro/javacheck/state/queue/commands/Offer.java +++ b/src/test/java/au/id/zancanaro/javacheck/state/queue/commands/Offer.java @@ -7,7 +7,6 @@ import au.id.zancanaro.javacheck.state.queue.QueueState; import java.util.ArrayList; import java.util.List; -import java.util.Queue; public class Offer<T> extends Command<QueueState<T>, T, Void> { public final Generator<T> generator; |