From e221748257cde8f18cfdd883167e650b2422976c Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 16 Jun 2017 20:48:26 +0200 Subject: [PATCH] slowCheck: shrink faster by remembering previous unsuccessful attempts --- slowCheck/src/slowCheck/PropertyChecker.java | 3 ++- slowCheck/src/slowCheck/RemoveListRange.java | 7 ++++++- slowCheck/src/slowCheck/ShrinkAction.java | 6 +++--- slowCheck/src/slowCheck/ShrinkRunner.java | 20 ++++++++++++++++--- slowCheck/src/slowCheck/StructureNode.java | 21 ++++++-------------- slowCheck/src/slowCheck/StructurePath.java | 2 +- slowCheck/test/slowCheck/GeneratorTest.java | 8 ++++---- 7 files changed, 39 insertions(+), 28 deletions(-) diff --git a/slowCheck/src/slowCheck/PropertyChecker.java b/slowCheck/src/slowCheck/PropertyChecker.java index d62803ca990a..2dd1117c962b 100644 --- a/slowCheck/src/slowCheck/PropertyChecker.java +++ b/slowCheck/src/slowCheck/PropertyChecker.java @@ -106,8 +106,9 @@ class PropertyFailureImpl implements PropertyFailure { } private void shrink(Generator gen, Predicate property) { + ShrinkRunner shrinkRunner = new ShrinkRunner(); while (true) { - CounterExampleImpl shrank = ShrinkRunner.findShrink(minimized.data, node -> { + CounterExampleImpl shrank = shrinkRunner.findShrink(minimized.data, node -> { try { T value = gen.generateUnstructured(new ReplayDataStructure(node)); totalSteps++; diff --git a/slowCheck/src/slowCheck/RemoveListRange.java b/slowCheck/src/slowCheck/RemoveListRange.java index 36d819536f9e..7ad5d219fa94 100644 --- a/slowCheck/src/slowCheck/RemoveListRange.java +++ b/slowCheck/src/slowCheck/RemoveListRange.java @@ -27,7 +27,7 @@ class RemoveListRange implements Shrink.ElementaryShrink { list.addAll(original.subList(1, start + 1)); list.addAll(original.subList(end + 1, original.size())); - return new StructureNode(list, start == end - 1 ? start: 0); + return new StructureNode(list); } @Override @@ -42,4 +42,9 @@ class RemoveListRange implements Shrink.ElementaryShrink { public int hashCode() { return Objects.hash(start, end); } + + @Override + public String toString() { + return "RemoveListRange{" + + start + ", " + end + '}'; + } } diff --git a/slowCheck/src/slowCheck/ShrinkAction.java b/slowCheck/src/slowCheck/ShrinkAction.java index 3b593b12fafd..d486304395c5 100644 --- a/slowCheck/src/slowCheck/ShrinkAction.java +++ b/slowCheck/src/slowCheck/ShrinkAction.java @@ -61,14 +61,14 @@ interface Shrink { @NotNull public List expandShrinks(@NotNull StructureNode source) { List result = new ArrayList<>(); - StructureNode.indices(source.children.size(), source.lastModifiedChild).forEach(i -> { + for (int i = 0; i < source.children.size(); i++) { StructureElement child = source.children.get(i); - if (child instanceof IntData && !myShrinkInts) return; + if (child instanceof IntData && !myShrinkInts) continue; for (Shrink shrink : child.shrink()) { result.add(new ShrinkAction(new StructurePath.ChildPath(StructurePath.EMPTY, i), shrink)); } - }); + } return result; } diff --git a/slowCheck/src/slowCheck/ShrinkRunner.java b/slowCheck/src/slowCheck/ShrinkRunner.java index aa7d294ba524..b9a3621313e9 100644 --- a/slowCheck/src/slowCheck/ShrinkRunner.java +++ b/slowCheck/src/slowCheck/ShrinkRunner.java @@ -3,29 +3,43 @@ package slowCheck; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.LinkedList; +import java.util.*; import java.util.function.Function; /** * @author peter */ class ShrinkRunner { + private final Set unsuccessful = new HashSet<>(); @Nullable - static T findShrink(@NotNull StructureNode element, Function shrinkProcessor) { + T findShrink(@NotNull StructureNode element, Function shrinkProcessor) { + List postponed = new ArrayList<>(); + LinkedList queue = new LinkedList<>(); for (Shrink shrink : element.shrink()) { queue.add(new ShrinkAction(StructurePath.EMPTY, shrink)); } while (true) { + if (queue.isEmpty() && postponed != null) { + queue.addAll(postponed); + postponed = null; + } + ShrinkAction action = queue.poll(); if (action == null) return null; - + if (action.shrink instanceof Shrink.ElementaryShrink) { + if (postponed != null && !unsuccessful.add(action)) { + postponed.add(action); + continue; + } + StructureNode shrank = action.path.applyShrink(element, (Shrink.ElementaryShrink)action.shrink); T result = shrinkProcessor.apply(shrank); if (result != null) { + unsuccessful.remove(action); return result; } } else { diff --git a/slowCheck/src/slowCheck/StructureNode.java b/slowCheck/src/slowCheck/StructureNode.java index f30e85f05c0f..1d2f03544867 100644 --- a/slowCheck/src/slowCheck/StructureNode.java +++ b/slowCheck/src/slowCheck/StructureNode.java @@ -4,7 +4,6 @@ import org.jetbrains.annotations.NotNull; import java.util.*; import java.util.stream.Collectors; -import java.util.stream.IntStream; /** * @author peter @@ -15,16 +14,14 @@ interface StructureElement { class StructureNode implements StructureElement { final List children; - final int lastModifiedChild; boolean shrinkProhibited; StructureNode() { - this(new ArrayList<>(), -1); + this(new ArrayList<>()); } - StructureNode(List children, int lastModifiedChild) { + StructureNode(List children) { this.children = children; - this.lastModifiedChild = lastModifiedChild; } Iterator childrenIterator() { @@ -48,14 +45,6 @@ class StructureNode implements StructureElement { children.remove(children.size() - 1); } - static IntStream indices(int size, int lastModified) { - if (lastModified > 0) { - return IntStream.concat(IntStream.range(lastModified, size), IntStream.range(0, lastModified)); - } - - return IntStream.range(0, size); - } - @Override public List shrink() { if (shrinkProhibited) return Collections.emptyList(); @@ -63,13 +52,15 @@ class StructureNode implements StructureElement { return isList() ? shrinkList(children.size() - 1) : Collections.singletonList(Shrink.SHRINK_ALL_CHILDREN); } - private List shrinkList(int size) { + private static List shrinkList(int size) { List result = new ArrayList<>(); if (size > 4) { result.add(new RemoveListRange(size / 2, size)); result.add(new RemoveListRange(0, size / 2)); } - indices(size, lastModifiedChild - 1).forEach(i -> result.add(new RemoveListRange(i, i + 1))); + for (int i = 0; i < size; i++) { + result.add(new RemoveListRange(i, i + 1)); + } result.add(Shrink.SHRINK_LIST_ELEMENTS); return result; } diff --git a/slowCheck/src/slowCheck/StructurePath.java b/slowCheck/src/slowCheck/StructurePath.java index a9f3dec901ef..ce493bb54a13 100644 --- a/slowCheck/src/slowCheck/StructurePath.java +++ b/slowCheck/src/slowCheck/StructurePath.java @@ -58,7 +58,7 @@ abstract class StructurePath { int index = indicesFromRoot.get(depth); List children = new ArrayList<>(((StructureNode)source).children); children.set(index, expandShrinks(children.get(index), depth + 1)); - return new StructureNode(children, index); + return new StructureNode(children); } }.expandShrinks(source, 0); } diff --git a/slowCheck/test/slowCheck/GeneratorTest.java b/slowCheck/test/slowCheck/GeneratorTest.java index 463bc8e83075..87cd5d5349fa 100644 --- a/slowCheck/test/slowCheck/GeneratorTest.java +++ b/slowCheck/test/slowCheck/GeneratorTest.java @@ -50,7 +50,7 @@ public class GeneratorTest extends TestCase { public void testIsSorted() { checkFalsified(nonEmptyListOf(integers()), l -> l.stream().sorted().collect(Collectors.toList()).equals(l), - 218); + 99); } public void testSuccess() { @@ -61,7 +61,7 @@ public class GeneratorTest extends TestCase { public void testSortedDoublesNonDescending() { checkFalsified(listOf(doubles()), l -> isSorted(l.stream().sorted().collect(Collectors.toList())), - 399); + 299); } private static boolean isSorted(List list) { @@ -99,9 +99,9 @@ public class GeneratorTest extends TestCase { } public void testLongListsHappen() { - checkFalsified(listOf(integers()).noShrink(), + checkFalsified(listOf(integers()), l -> l.size() < 200, - 0); + 6714); } public void testNonEmptyList() {