slowCheck: shrink faster by remembering previous unsuccessful attempts

This commit is contained in:
peter
2017-06-16 21:21:56 +02:00
parent 99198b5e58
commit e221748257
7 changed files with 39 additions and 28 deletions
+2 -1
View File
@@ -106,8 +106,9 @@ class PropertyFailureImpl<T> implements PropertyFailure<T> {
}
private void shrink(Generator<T> gen, Predicate<T> property) {
ShrinkRunner shrinkRunner = new ShrinkRunner();
while (true) {
CounterExampleImpl<T> shrank = ShrinkRunner.findShrink(minimized.data, node -> {
CounterExampleImpl<T> shrank = shrinkRunner.findShrink(minimized.data, node -> {
try {
T value = gen.generateUnstructured(new ReplayDataStructure(node));
totalSteps++;
+6 -1
View File
@@ -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 + '}';
}
}
+3 -3
View File
@@ -61,14 +61,14 @@ interface Shrink {
@NotNull
public List<ShrinkAction> expandShrinks(@NotNull StructureNode source) {
List<ShrinkAction> 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;
}
+17 -3
View File
@@ -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<ShrinkAction> unsuccessful = new HashSet<>();
@Nullable
static <T> T findShrink(@NotNull StructureNode element, Function<StructureNode, T> shrinkProcessor) {
<T> T findShrink(@NotNull StructureNode element, Function<StructureNode, T> shrinkProcessor) {
List<ShrinkAction> postponed = new ArrayList<>();
LinkedList<ShrinkAction> 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 {
+6 -15
View File
@@ -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<StructureElement> children;
final int lastModifiedChild;
boolean shrinkProhibited;
StructureNode() {
this(new ArrayList<>(), -1);
this(new ArrayList<>());
}
StructureNode(List<StructureElement> children, int lastModifiedChild) {
StructureNode(List<StructureElement> children) {
this.children = children;
this.lastModifiedChild = lastModifiedChild;
}
Iterator<StructureElement> 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> 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<Shrink> shrinkList(int size) {
private static List<Shrink> shrinkList(int size) {
List<Shrink> 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;
}
+1 -1
View File
@@ -58,7 +58,7 @@ abstract class StructurePath {
int index = indicesFromRoot.get(depth);
List<StructureElement> 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);
}
+4 -4
View File
@@ -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<Double> 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() {