summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlo Zancanaro <carlo@zancanaro.id.au>2015-06-09 23:31:54 +1000
committerCarlo Zancanaro <carlo@zancanaro.id.au>2015-06-09 23:31:54 +1000
commit40961d4950c40643d5d71721a7e024e3951323ce (patch)
tree330af94a98468ab0c10cff76c2bb36eb28a64ef0
parentdd9f72b94eb7b2c37061c80457e74e8d7ac3e18f (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.
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/DataSourceHelper.java46
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/junit/Properties.java36
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/junit/PropertyError.java1
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/object/CharType.java2
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/object/DoubleRange.java2
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/object/GeneratorProvider.java29
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/object/IntRange.java2
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/object/LongRange.java2
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/object/ObjectGenerationException.java4
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/object/UseGenerator.java14
-rw-r--r--src/test/java/au/id/zancanaro/javacheck/ListFunctorRulesTest.java6
-rw-r--r--src/test/java/au/id/zancanaro/javacheck/SimpleListOperationsTest.java8
-rw-r--r--src/test/java/au/id/zancanaro/javacheck/object/MyObject.java21
-rw-r--r--src/test/java/au/id/zancanaro/javacheck/object/MyObjectAddTest.java8
-rw-r--r--src/test/java/au/id/zancanaro/javacheck/state/queue/commands/Offer.java1
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;