From 0aa99a5f941b886597e7366be763dadc8c6db6dd Mon Sep 17 00:00:00 2001 From: Carlo Zancanaro Date: Thu, 4 Jun 2015 11:41:19 +1000 Subject: Clean up a bit of the ShrinkTree stuff, particularly for shrinking --- .../java/au/id/zancanaro/javacheck/Generator.java | 25 +++++++-- .../java/au/id/zancanaro/javacheck/Generators.java | 3 +- .../java/au/id/zancanaro/javacheck/ShrinkTree.java | 65 ++++++++++------------ 3 files changed, 49 insertions(+), 44 deletions(-) diff --git a/src/main/java/au/id/zancanaro/javacheck/Generator.java b/src/main/java/au/id/zancanaro/javacheck/Generator.java index 103bc69..0679f10 100644 --- a/src/main/java/au/id/zancanaro/javacheck/Generator.java +++ b/src/main/java/au/id/zancanaro/javacheck/Generator.java @@ -72,7 +72,7 @@ public interface Generator { for (Generator generator : generators) { result[index++] = generator.generate(random, size).map(x -> (T) x); } - return ShrinkTree.zip(Function.identity(), result); + return ShrinkTree.combine(result, ShrinkTree::promoteChildren); }; } @@ -81,9 +81,7 @@ public interface Generator { * element taken from the provided generator. * * Shrinking for this type involves attempting to remove terms and shrink - * each subtree in turn, recursively. (If the length must remain fixed then - * the {@link ShrinkTree} produced by this generator should be filtered with - * {@link ShrinkTree#filter(java.util.function.Predicate)}. + * each subtree in turn, recursively. * * @param count The length of the list to generate * @param generator The generator to use for each term in the generated @@ -98,7 +96,7 @@ public interface Generator { for (int i = 0; i < count; ++i) { result[i] = generator.generate(random, size); } - return ShrinkTree.shrink(Function.identity(), result); + return ShrinkTree.combine(result, ShrinkTree::removeAndPromoteChildren); }; } @@ -128,7 +126,10 @@ public interface Generator { * result of this */ default Generator flatMap(Function> action) { - return (random, size) -> ShrinkTree.join(this.generate(random, size).map(action).map(g -> g.generate(random, size))); + return (random, size) -> ShrinkTree.join( + this.generate(random, size) + .map(action + .andThen(g -> g.generate(random, size)))); } /** @@ -156,6 +157,18 @@ public interface Generator { }; } + /** + * Create a new generator which generates values with a shrink tree + * determined by the provided {@link ShrinkStrategy}. + * + * @param strategy The shrink strategy by which to shrink generated values + * @return A new generator which will shrink values from this according to + * the provided strategy + */ + default Generator withShrinkStrategy(ShrinkStrategy strategy) { + return (random, size) -> this.generate(random, size).withShrinkStrategy(strategy); + } + default Iterator sample(Random random, int maxSize) { return new GeneratorSampleIterator<>(this, random, maxSize); } diff --git a/src/main/java/au/id/zancanaro/javacheck/Generators.java b/src/main/java/au/id/zancanaro/javacheck/Generators.java index e5699ac..9730bac 100644 --- a/src/main/java/au/id/zancanaro/javacheck/Generators.java +++ b/src/main/java/au/id/zancanaro/javacheck/Generators.java @@ -29,7 +29,7 @@ public final class Generators { * removed */ public static Generator noShrink(Generator gen) { - return (random, size) -> ShrinkTree.pure(gen.generate(random, size).getValue()); + return gen.withShrinkStrategy(value -> Iterators.emptyIterator()); } @SafeVarargs @@ -230,7 +230,6 @@ public final class Generators { chars[i++] = c; } return String.valueOf(chars); -// return new String(chars); }); } } diff --git a/src/main/java/au/id/zancanaro/javacheck/ShrinkTree.java b/src/main/java/au/id/zancanaro/javacheck/ShrinkTree.java index 7dff917..bdc7604 100644 --- a/src/main/java/au/id/zancanaro/javacheck/ShrinkTree.java +++ b/src/main/java/au/id/zancanaro/javacheck/ShrinkTree.java @@ -41,55 +41,50 @@ public class ShrinkTree { tree.getValue().children.iterator())); } - private static Iterator[]> permutations(ShrinkTree[] trees) { + private static List makeHeadList(ShrinkTree[] trees) { + List heads = new ArrayList<>(trees.length); + for (ShrinkTree tree : trees) { + heads.add(tree.getValue()); + } + return heads; + } + + public static Iterator[]> promoteChildren(ShrinkTree[] trees) { return flatten( - rangeIterator(trees.length, - index -> mappingIterator(child -> { + rangeIterator(trees.length, index -> + mappingIterator(child -> { @SuppressWarnings("unchecked") ShrinkTree[] result = (ShrinkTree[]) new ShrinkTree[trees.length]; for (int i = 0; i < trees.length; ++i) { result[i] = (i == index ? child : trees[i]); } return result; - }, trees[index].getChildren()) - )); - } - - private static List makeHeadList(ShrinkTree[] trees) { - List heads = new ArrayList<>(trees.length); - for (ShrinkTree tree : trees) { - heads.add(tree.getValue()); - } - return heads; + }, trees[index].getChildren()))); } - public static ShrinkTree zip(Function, R> fn, ShrinkTree[] trees) { - return new ShrinkTree<>( - fn.apply(makeHeadList(trees)), - () -> mappingIterator( - shrinks -> ShrinkTree.zip(fn, shrinks), - ShrinkTree.permutations(trees))); + public static Iterator[]> removeChildren(ShrinkTree[] trees) { + return rangeIterator(trees.length, index -> { + @SuppressWarnings("unchecked") + ShrinkTree[] result = (ShrinkTree[]) new ShrinkTree[trees.length - 1]; + for (int i = 0; i < trees.length - 1; ++i) { + result[i] = trees[(i >= index ? i + 1 : i)]; + } + return result; + }); } - private static Iterator[]> removeEach(ShrinkTree[] trees) { - return concat( - rangeIterator(trees.length, index -> { - @SuppressWarnings("unchecked") - ShrinkTree[] result = (ShrinkTree[]) new ShrinkTree[trees.length - 1]; - for (int i = 0; i < trees.length - 1; ++i) { - result[i] = trees[(i >= index ? i + 1 : i)]; - } - return result; - }), - permutations(trees)); + public static Iterator[]> removeAndPromoteChildren(ShrinkTree[] trees) { + return concat(removeChildren(trees), promoteChildren(trees)); } - public static ShrinkTree shrink(Function, R> fn, ShrinkTree[] trees) { + public static ShrinkTree> combine( + ShrinkTree[] trees, + Function[], Iterator[]>> processChildren) { return new ShrinkTree<>( - fn.apply(makeHeadList(trees)), + makeHeadList(trees), () -> mappingIterator( - shrinks -> ShrinkTree.shrink(fn, shrinks), - ShrinkTree.removeEach(trees))); + shrinks -> ShrinkTree.combine(shrinks, processChildren), + processChildren.apply(trees))); } public ShrinkTree map(Function f) { @@ -126,12 +121,10 @@ public class ShrinkTree { strategy.shrink(value)); } - @SuppressWarnings("unused") public void print(Writer output) throws IOException { print(output, Object::toString); } - @SuppressWarnings("unused") public void print(Writer output, Function toString) throws IOException { output.write(toString.apply(this.getValue())); output.write('['); -- cgit v1.2.3