summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlo Zancanaro <carlo@zancanaro.id.au>2015-06-04 16:46:31 +1000
committerCarlo Zancanaro <carlo@zancanaro.id.au>2015-06-04 16:46:31 +1000
commitb435b8659eef0e8bc2910966d87b5b74b4cddbe2 (patch)
tree22194a911cf071766d99d7f9c1600889a6147ebb
parent6781ae52a41188e82a0354d4725a7c2718830e45 (diff)
Move stuff over to using streams instead of iterators: much nicer!
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/Generator.java9
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/GeneratorSampleIterator.java30
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/Generators.java91
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/Iterators.java179
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/ShrinkResult.java1
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/ShrinkStrategy.java4
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/ShrinkTree.java101
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/junit/Properties.java4
8 files changed, 97 insertions, 322 deletions
diff --git a/src/main/java/au/id/zancanaro/javacheck/Generator.java b/src/main/java/au/id/zancanaro/javacheck/Generator.java
index ac731de..5a8fec3 100644
--- a/src/main/java/au/id/zancanaro/javacheck/Generator.java
+++ b/src/main/java/au/id/zancanaro/javacheck/Generator.java
@@ -5,6 +5,7 @@ import java.util.List;
import java.util.Random;
import java.util.function.Function;
import java.util.function.Predicate;
+import java.util.stream.Stream;
/**
* Generators are a way of producing random objects and their associated shrink
@@ -170,7 +171,11 @@ public interface Generator<T> {
return (random, size) -> this.generate(random, size).withShrinkStrategy(strategy);
}
- default Iterator<T> sample(Random random, int maxSize) {
- return new GeneratorSampleIterator<>(this, random, maxSize);
+ default Stream<T> sample(Random random, int maxSize) {
+ return Stream.generate(() -> this.generate(random, maxSize).getValue());
+ }
+
+ default Stream<T> sample() {
+ return sample(new Random(), 100);
}
}
diff --git a/src/main/java/au/id/zancanaro/javacheck/GeneratorSampleIterator.java b/src/main/java/au/id/zancanaro/javacheck/GeneratorSampleIterator.java
deleted file mode 100644
index 6101d4a..0000000
--- a/src/main/java/au/id/zancanaro/javacheck/GeneratorSampleIterator.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package au.id.zancanaro.javacheck;
-
-import java.util.Iterator;
-import java.util.Random;
-
-class GeneratorSampleIterator<T> implements Iterator<T> {
- private final Generator<T> generator;
- private final Random random;
- private final int maxSize;
- private int size;
-
- public GeneratorSampleIterator(Generator<T> generator, Random random, int maxSize) {
- this.generator = generator;
- this.random = random;
- this.maxSize = maxSize;
- this.size = 0;
- }
-
- @Override
- public boolean hasNext() {
- return true;
- }
-
- @Override
- public T next() {
- return generator
- .generate(random, size = Math.min(size + 1, maxSize))
- .getValue();
- }
-}
diff --git a/src/main/java/au/id/zancanaro/javacheck/Generators.java b/src/main/java/au/id/zancanaro/javacheck/Generators.java
index 9730bac..619fe2d 100644
--- a/src/main/java/au/id/zancanaro/javacheck/Generators.java
+++ b/src/main/java/au/id/zancanaro/javacheck/Generators.java
@@ -1,7 +1,10 @@
package au.id.zancanaro.javacheck;
import java.util.*;
+import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
@SuppressWarnings({"unused", "WeakerAccess"})
public final class Generators {
@@ -29,7 +32,7 @@ public final class Generators {
* removed
*/
public static <T> Generator<T> noShrink(Generator<T> gen) {
- return gen.withShrinkStrategy(value -> Iterators.emptyIterator());
+ return gen.withShrinkStrategy(value -> Stream.empty());
}
@SafeVarargs
@@ -52,23 +55,7 @@ public final class Generators {
}
private static ShrinkStrategy<Boolean> boolShrinkStrategy() {
- return value -> new Iterator<Boolean>() {
- boolean hasMore = value;
-
- @Override
- public boolean hasNext() {
- return hasMore;
- }
-
- @Override
- public Boolean next() {
- if (hasMore) {
- return (hasMore = false);
- } else {
- throw new NoSuchElementException("Boolean shrink tree exhausted");
- }
- }
- };
+ return value -> (value ? Stream.of(false) : Stream.empty());
}
public static Generator<Long> longInteger(long lower, long upper) {
@@ -85,21 +72,19 @@ public final class Generators {
}
private static ShrinkStrategy<Long> longShrinkStrategy(final long bound) {
- return value -> new Iterator<Long>() {
+ return value -> StreamSupport.stream(new Spliterators.AbstractSpliterator<Long>(Long.MAX_VALUE, Spliterator.ORDERED) {
long curr = value - bound;
-
@Override
- public boolean hasNext() {
- return curr != 0;
- }
-
- @Override
- public Long next() {
- long prevCurr = curr;
- curr = curr / 2;
- return value - prevCurr;
+ public boolean tryAdvance(Consumer<? super Long> action) {
+ if (curr == 0) {
+ return false;
+ } else {
+ action.accept(value - curr);
+ curr /= 2;
+ return true;
+ }
}
- };
+ }, false);
}
public static Generator<Integer> integer(int lower, int upper) {
@@ -119,22 +104,21 @@ public final class Generators {
return sized(size -> integer(0, size));
}
- public static ShrinkStrategy<Integer> intShrinkStrategy(int bound) {
- return value -> new Iterator<Integer>() {
+ private static ShrinkStrategy<Integer> intShrinkStrategy(final int bound) {
+ return value -> StreamSupport.stream(new Spliterators.AbstractSpliterator<Integer>(Long.MAX_VALUE, Spliterator.ORDERED) {
int curr = value - bound;
@Override
- public boolean hasNext() {
- return curr != 0;
- }
-
- @Override
- public Integer next() {
- int prevCurr = curr;
- curr = curr / 2;
- return value - prevCurr;
+ public boolean tryAdvance(Consumer<? super Integer> action) {
+ if (curr == 0) {
+ return false;
+ } else {
+ action.accept(value - curr);
+ curr /= 2;
+ return true;
+ }
}
- };
+ }, false);
}
public static Generator<Double> doublePrecision(double lower, double upper) {
@@ -150,22 +134,21 @@ public final class Generators {
return sized(size -> doublePrecision(-size, size));
}
- public static ShrinkStrategy<Double> doubleShrinkStrategy(double bound, double epsilon) {
- return value -> new Iterator<Double>() {
+ private static ShrinkStrategy<Double> doubleShrinkStrategy(final double bound, double epsilon) {
+ return value -> StreamSupport.stream(new Spliterators.AbstractSpliterator<Double>(Long.MAX_VALUE, Spliterator.ORDERED) {
double curr = value - bound;
@Override
- public boolean hasNext() {
- return Math.abs(curr) > epsilon;
- }
-
- @Override
- public Double next() {
- double prevCurr = curr;
- curr = curr / 2;
- return value - prevCurr;
+ public boolean tryAdvance(Consumer<? super Double> action) {
+ if (Math.abs(curr) < epsilon) {
+ return false;
+ } else {
+ action.accept(value - curr);
+ curr /= 2;
+ return true;
+ }
}
- };
+ }, false);
}
public static <T> Generator<List<T>> listOf(Generator<T> gen, int minElements, int maxElements) {
diff --git a/src/main/java/au/id/zancanaro/javacheck/Iterators.java b/src/main/java/au/id/zancanaro/javacheck/Iterators.java
deleted file mode 100644
index 3f365eb..0000000
--- a/src/main/java/au/id/zancanaro/javacheck/Iterators.java
+++ /dev/null
@@ -1,179 +0,0 @@
-package au.id.zancanaro.javacheck;
-
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-public final class Iterators {
- private Iterators() {
- }
-
- public static <T> RangeIterator<T> rangeIterator(int countTo, Function<Integer,T> fn) {
- return new RangeIterator<>(countTo, fn);
- }
-
- private static class RangeIterator<T> implements Iterator<T> {
- private final Function<Integer, T> action;
- private final int countTo;
- private int index = 0;
-
- public RangeIterator(int countTo, Function<Integer, T> action) {
- this.countTo = countTo;
- this.action = action;
- }
-
- @Override
- public boolean hasNext() {
- return index < countTo;
- }
-
- @Override
- public T next() {
- return action.apply(index++);
- }
- }
-
- public static <T> FlattenIterator<T> flatten(Iterator<Iterator<T>> iterators) {
- return new FlattenIterator<>(iterators);
- }
- public static class FlattenIterator<T> implements Iterator<T> {
- private final Iterator<Iterator<T>> iterators;
-
- private Iterator<T> current;
-
- public FlattenIterator(Iterator<Iterator<T>> iterators) {
- this.current = Iterators.emptyIterator();
- this.iterators = iterators;
- }
-
- private Iterator<T> getCurrent() {
- while (!current.hasNext() && iterators.hasNext()) {
- current = iterators.next();
- }
- return current;
- }
-
- @Override
- public boolean hasNext() {
- return getCurrent().hasNext();
- }
- @Override
- public T next() {
- return getCurrent().next();
- }
-
- }
-
- public static <T> ConcatIterator<T> concat(Iterator<T> left, Iterator<T> right) {
- return new ConcatIterator<>(left, right);
- }
- public static class ConcatIterator<T> implements Iterator<T> {
- private final Iterator<T> left;
-
- private final Iterator<T> right;
-
- public ConcatIterator(Iterator<T> left, Iterator<T> right) {
- this.left = left;
- this.right = right;
- }
-
- @Override
- public boolean hasNext() {
- return left.hasNext() || right.hasNext();
- }
- @Override
- public T next() {
- if (left.hasNext()) {
- return left.next();
- } else {
- return right.next();
- }
- }
-
- }
-
- @SuppressWarnings("WeakerAccess")
- public static <T> EmptyIterator<T> emptyIterator() {
- return new EmptyIterator<>();
- }
- public static class EmptyIterator<T> implements Iterator<T> {
-
- @Override
- public boolean hasNext() {
- return false;
- }
- @Override
- public T next() {
- throw new NoSuchElementException("Empty iterators contain no elements");
- }
-
- }
-
- public static <T,R> MappingIterator<T,R> mappingIterator(Function<T,R> f, Iterator<T> iterator) {
- return new MappingIterator<>(f, iterator);
- }
- private static class MappingIterator<T, R> implements Iterator<R> {
- private final Function<T, R> mapping;
-
- private final Iterator<T> iterator;
-
- public MappingIterator(Function<T, R> mapping, Iterator<T> iterator) {
- this.mapping = mapping;
- this.iterator = iterator;
- }
-
- @Override
- public boolean hasNext() {
- return iterator.hasNext();
- }
- @Override
- public R next() {
- return mapping.apply(iterator.next());
- }
-
- }
-
- public static <T> FilteringIterator<T> filteringIterator(Predicate<T> predicate, Iterator<T> iterator) {
- return new FilteringIterator<>(predicate, iterator);
- }
-
- private static class FilteringIterator<T> implements Iterator<T> {
- private final Predicate<T> predicate;
- private final Iterator<T> iterator;
- private List<T> nextValue;
-
- public FilteringIterator(Predicate<T> predicate, Iterator<T> iterator) {
- this.predicate = predicate;
- this.iterator = iterator;
- this.nextValue = null;
- }
-
- private void populateNext() {
- while (nextValue == null && iterator.hasNext()) {
- T value = iterator.next();
- if (predicate.test(value)) {
- nextValue = Collections.singletonList(value);
- } else {
- nextValue = null;
- }
- }
- }
-
- @Override
- public boolean hasNext() {
- populateNext();
- return nextValue != null;
- }
-
- @Override
- public T next() {
- populateNext();
- T result = nextValue.get(0);
- nextValue = null;
- return result;
- }
- }
-}
diff --git a/src/main/java/au/id/zancanaro/javacheck/ShrinkResult.java b/src/main/java/au/id/zancanaro/javacheck/ShrinkResult.java
index a28c2a5..6bf3882 100644
--- a/src/main/java/au/id/zancanaro/javacheck/ShrinkResult.java
+++ b/src/main/java/au/id/zancanaro/javacheck/ShrinkResult.java
@@ -1,6 +1,5 @@
package au.id.zancanaro.javacheck;
-import java.util.Arrays;
import java.util.List;
public class ShrinkResult {
diff --git a/src/main/java/au/id/zancanaro/javacheck/ShrinkStrategy.java b/src/main/java/au/id/zancanaro/javacheck/ShrinkStrategy.java
index 0018309..cf74020 100644
--- a/src/main/java/au/id/zancanaro/javacheck/ShrinkStrategy.java
+++ b/src/main/java/au/id/zancanaro/javacheck/ShrinkStrategy.java
@@ -1,8 +1,8 @@
package au.id.zancanaro.javacheck;
-import java.util.Iterator;
+import java.util.stream.Stream;
@FunctionalInterface
interface ShrinkStrategy<T> {
- Iterator<T> shrink(T obj);
+ Stream<T> shrink(T obj);
}
diff --git a/src/main/java/au/id/zancanaro/javacheck/ShrinkTree.java b/src/main/java/au/id/zancanaro/javacheck/ShrinkTree.java
index bdc7604..4337c14 100644
--- a/src/main/java/au/id/zancanaro/javacheck/ShrinkTree.java
+++ b/src/main/java/au/id/zancanaro/javacheck/ShrinkTree.java
@@ -3,20 +3,20 @@ package au.id.zancanaro.javacheck;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
-
-import static au.id.zancanaro.javacheck.Iterators.*;
+import java.util.function.Supplier;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
@SuppressWarnings("unused")
public class ShrinkTree<T> {
private final T value;
- private final Iterable<ShrinkTree<T>> children;
+ private final Supplier<Stream<ShrinkTree<T>>> children;
- private ShrinkTree(T value, Iterable<ShrinkTree<T>> children) {
+ private ShrinkTree(T value, Supplier<Stream<ShrinkTree<T>>> children) {
this.value = value;
this.children = children;
}
@@ -25,20 +25,23 @@ public class ShrinkTree<T> {
return value;
}
- public Iterator<ShrinkTree<T>> getChildren() {
- return children.iterator();
+ public Stream<ShrinkTree<T>> getChildren() {
+ return children.get();
}
+ @SuppressWarnings("Convert2MethodRef")
public static <T> ShrinkTree<T> pure(T value) {
- return new ShrinkTree<>(value, Collections.emptyList());
+ // Converting the () -> Stream.empty() into Stream::empty actually
+ // breaks Java's generic type inference. Who would have thought?
+ return new ShrinkTree<>(value, () -> Stream.empty());
}
public static <T> ShrinkTree<T> join(ShrinkTree<ShrinkTree<T>> tree) {
return new ShrinkTree<>(
tree.getValue().getValue(),
- () -> concat(
- mappingIterator(ShrinkTree::join, tree.children.iterator()),
- tree.getValue().children.iterator()));
+ () -> Stream.concat(
+ tree.getChildren().map(ShrinkTree::join),
+ tree.getValue().getChildren()));
}
private static <T> List<T> makeHeadList(ShrinkTree<T>[] trees) {
@@ -49,48 +52,42 @@ public class ShrinkTree<T> {
return heads;
}
- public static <T> Iterator<ShrinkTree<T>[]> promoteChildren(ShrinkTree<T>[] trees) {
- return flatten(
- rangeIterator(trees.length, index ->
- mappingIterator(child -> {
- @SuppressWarnings("unchecked")
- ShrinkTree<T>[] result = (ShrinkTree<T>[]) new ShrinkTree[trees.length];
- for (int i = 0; i < trees.length; ++i) {
- result[i] = (i == index ? child : trees[i]);
- }
- return result;
- }, trees[index].getChildren())));
- }
-
- public static <T> Iterator<ShrinkTree<T>[]> removeChildren(ShrinkTree<T>[] trees) {
- return rangeIterator(trees.length, index -> {
- @SuppressWarnings("unchecked")
- ShrinkTree<T>[] result = (ShrinkTree<T>[]) new ShrinkTree[trees.length - 1];
- for (int i = 0; i < trees.length - 1; ++i) {
- result[i] = trees[(i >= index ? i + 1 : i)];
- }
- return result;
- });
+ @SuppressWarnings("unchecked")
+ public static <T> Stream<ShrinkTree<T>[]> promoteChildren(ShrinkTree<T>[] trees) {
+ return IntStream.range(0, trees.length)
+ .mapToObj(index -> trees[index].getChildren().map(child ->
+ IntStream.range(0, trees.length)
+ .mapToObj(i -> (i == index ? child : trees[i]))
+ .toArray(ShrinkTree[]::new)))
+ .flatMap(x -> x)
+ .map(x -> (ShrinkTree<T>[]) x);
}
- public static <T> Iterator<ShrinkTree<T>[]> removeAndPromoteChildren(ShrinkTree<T>[] trees) {
- return concat(removeChildren(trees), promoteChildren(trees));
+ public static <T> Stream<ShrinkTree<T>[]> removeChildren(ShrinkTree<T>[] trees) {
+ return IntStream.range(0, trees.length)
+ .mapToObj(index -> IntStream.range(0, trees.length)
+ .filter(i -> i != index)
+ .mapToObj(i -> trees[i])
+ .toArray(ShrinkTree[]::new));
+ }
+
+ public static <T> Stream<ShrinkTree<T>[]> removeAndPromoteChildren(ShrinkTree<T>[] trees) {
+ return Stream.concat(removeChildren(trees), promoteChildren(trees));
}
public static <T> ShrinkTree<List<T>> combine(
ShrinkTree<T>[] trees,
- Function<ShrinkTree<T>[], Iterator<ShrinkTree<T>[]>> processChildren) {
+ Function<ShrinkTree<T>[], Stream<ShrinkTree<T>[]>> processChildren) {
return new ShrinkTree<>(
makeHeadList(trees),
- () -> mappingIterator(
- shrinks -> ShrinkTree.combine(shrinks, processChildren),
- processChildren.apply(trees)));
+ () -> processChildren.apply(trees)
+ .map(shrinks -> combine(shrinks, processChildren)));
}
public <R> ShrinkTree<R> map(Function<T, R> f) {
return new ShrinkTree<>(
f.apply(this.value),
- () -> mappingIterator(tree -> tree.map(f), this.children.iterator()));
+ () -> this.getChildren().map(tree -> tree.map(f)));
}
public <R> ShrinkTree<R> flatMap(Function<T, ShrinkTree<R>> f) {
@@ -101,24 +98,20 @@ public class ShrinkTree<T> {
if (predicate.test(this.getValue())) {
return new ShrinkTree<>(
this.getValue(),
- () -> mappingIterator(tree -> tree.filter(predicate),
- filteringIterator(
- tree -> predicate.test(tree.getValue()),
- this.getChildren())));
+ () -> this.getChildren()
+ .filter(tree -> predicate.test(tree.getValue()))
+ .map(tree -> tree.filter(predicate)));
} else {
throw new IllegalArgumentException("Current value doesn't match predicate: whoops!");
}
}
public ShrinkTree<T> withShrinkStrategy(ShrinkStrategy<T> strategy) {
- return new ShrinkTree<>(this.getValue(), strategyIterable(this.getValue(), strategy));
+ return new ShrinkTree<>(this.getValue(), () -> strategyStream(this.getValue(), strategy));
}
- private static <T> Iterable<ShrinkTree<T>> strategyIterable(final T value, final ShrinkStrategy<T> strategy) {
- return () ->
- mappingIterator(
- v -> new ShrinkTree<>(v, strategyIterable(v, strategy)),
- strategy.shrink(value));
+ private static <T> Stream<ShrinkTree<T>> strategyStream(final T value, final ShrinkStrategy<T> strategy) {
+ return strategy.shrink(value).map(v -> new ShrinkTree<>(v, () -> strategyStream(v, strategy)));
}
public void print(Writer output) throws IOException {
@@ -128,8 +121,12 @@ public class ShrinkTree<T> {
public void print(Writer output, Function<T, String> toString) throws IOException {
output.write(toString.apply(this.getValue()));
output.write('[');
- for (ShrinkTree<T> child : children) {
- child.print(output, toString);
+ Iterator<ShrinkTree<T>> iterator = children.get().iterator();
+ while (iterator.hasNext()) {
+ iterator.next().print(output, toString);
+ if (iterator.hasNext()) {
+ output.write(' ');
+ }
}
output.write(']');
output.flush();
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 b0dd2cd..6bba786 100644
--- a/src/main/java/au/id/zancanaro/javacheck/junit/Properties.java
+++ b/src/main/java/au/id/zancanaro/javacheck/junit/Properties.java
@@ -229,8 +229,8 @@ public class Properties extends BlockJUnit4ClassRunner {
Thread shutdownHandler = makeShutdownHandler(smallest, originalEx);
Runtime.getRuntime().addShutdownHook(shutdownHandler);
- Iterator<ShrinkTree<List<Object>>> trees = failed.getChildren();
Set<List<Object>> seenArgs = new HashSet<>();
+ Iterator<ShrinkTree<List<Object>>> trees = failed.getChildren().iterator();
while (trees.hasNext()) {
ShrinkTree<List<Object>> tree = trees.next();
if (seenArgs.add(Arrays.asList(tree.getValue()))) {
@@ -240,7 +240,7 @@ public class Properties extends BlockJUnit4ClassRunner {
// ignore, because it's not useful
} catch (Throwable ex) {
smallest[0] = new ShrinkResult(tree.getValue(), ex);
- Iterator<ShrinkTree<List<Object>>> children = tree.getChildren();
+ Iterator<ShrinkTree<List<Object>>> children = tree.getChildren().iterator();
if (children.hasNext()) {
trees = children;
} else {