jetCheck: avoid shrinking steps inapplicable to lists/choices

This commit is contained in:
peter
2018-06-21 17:45:43 +02:00
parent 1c9a61eda8
commit 2376b367fe
11 changed files with 60 additions and 30 deletions
@@ -35,4 +35,5 @@ abstract class AbstractDataStructure implements DataStructure {
abstract <T> T generateConditional(@NotNull Generator<T> generator, @NotNull Predicate<? super T> condition);
abstract void changeKind(StructureKind kind);
}
@@ -33,7 +33,10 @@ public class FrequencyGenerator<T> extends Generator<T> {
private static <T> Function<DataStructure, T> frequencyFunction(List<WeightedGenerator<T>> alternatives) {
List<Integer> weights = alternatives.stream().map(w -> w.weight).collect(Collectors.toList());
IntDistribution distribution = IntDistribution.frequencyDistribution(weights);
return data -> data.generate(alternatives.get(((AbstractDataStructure)data).drawInt(distribution)).generator);
return data -> {
((AbstractDataStructure) data).changeKind(StructureKind.CHOICE);
return data.generate(alternatives.get(((AbstractDataStructure)data).drawInt(distribution)).generator);
};
}
@NotNull
@@ -60,6 +60,11 @@ class GenerativeDataStructure extends AbstractDataStructure {
throw new CannotSatisfyCondition(condition);
}
@Override
void changeKind(StructureKind kind) {
node.kind = kind;
}
private class CurrentData {
DataStructure current = GenerativeDataStructure.this;
@@ -133,7 +133,8 @@ public class Generator<T> {
public static <T> Generator<T> anyOf(List<? extends Generator<? extends T>> alternatives) {
if (alternatives.isEmpty()) throw new IllegalArgumentException("No alternatives to choose from");
return from(data -> {
int index = ((AbstractDataStructure)data).generateNonShrinkable(integers(0, alternatives.size() - 1));
((AbstractDataStructure) data).changeKind(StructureKind.CHOICE);
int index = ((AbstractDataStructure)data).drawInt(IntDistribution.uniform(0, alternatives.size() - 1));
return data.generate(alternatives.get(index));
});
}
@@ -296,6 +297,7 @@ public class Generator<T> {
}
private static <T> List<T> generateList(Generator<T> itemGenerator, DataStructure data, int size) {
((AbstractDataStructure) data).changeKind(StructureKind.LIST);
List<T> list = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
list.add(data.generate(itemGenerator));
@@ -19,8 +19,11 @@ class RemoveListRange extends ShrinkStep {
private final int start;
private final int length;
RemoveListRange(StructureNode node) {
this(node, node.children.size(), node.children.size() - 1, 1);
static RemoveListRange fromEnd(StructureNode node) {
int likelyFailingSuffix = node.isIncompleteList() && node.children.size() > 2 ? 1 : 0;
return new RemoveListRange(node,
node.children.size() - likelyFailingSuffix,
node.children.size() - likelyFailingSuffix - 1, 1);
}
private RemoveListRange(StructureNode node, int lastSuccessfulRemove, int start, int length) {
@@ -47,10 +50,12 @@ class RemoveListRange extends ShrinkStep {
if (!lengthDistribution.isValidValue(newSize)) return null;
List<StructureElement> lessItems = new ArrayList<>(newSize + 1);
lessItems.add(new IntData(node.children.get(0).id, newSize, lengthDistribution));
lessItems.add(node.isIncompleteList() ? node.children.get(0) : new IntData(node.children.get(0).id, newSize, lengthDistribution));
lessItems.addAll(node.children.subList(1, start));
lessItems.addAll(node.children.subList(start + length, node.children.size()));
return root.replace(node.id, new StructureNode(node.id, lessItems));
StructureNode replacement = new StructureNode(node.id, lessItems);
replacement.kind = StructureKind.LIST;
return root.replace(node.id, replacement);
}
@Override
@@ -71,7 +76,7 @@ class RemoveListRange extends ShrinkStep {
if (length == node.children.size() - 1) return null;
StructureNode inheritor = (StructureNode)Objects.requireNonNull(smallerRoot.findChildById(node.id));
if (start == 1) return new RemoveListRange(inheritor);
if (start == 1) return fromEnd(inheritor);
int newLength = Math.min(length * 2, start - 1);
return new RemoveListRange(inheritor, start, start - newLength, newLength);
@@ -46,6 +46,13 @@ class ReplayDataStructure extends AbstractDataStructure {
return value;
}
@Override
void changeKind(StructureKind kind) {
if (node.kind != kind) {
throw new CannotRestoreValue();
}
}
@Override
public String toString() {
return node.toString();
@@ -33,6 +33,7 @@ abstract class StructureElement {
class StructureNode extends StructureElement {
final List<StructureElement> children;
@NotNull StructureKind kind = StructureKind.GENERIC;
boolean shrinkProhibited;
StructureNode(NodeId id) {
@@ -70,12 +71,12 @@ class StructureNode extends StructureElement {
ShrinkStep shrink() {
if (shrinkProhibited) return null;
return isList() ? new RemoveListRange(this) : shrinkChild(children.size() - 1);
return kind == StructureKind.LIST && children.size() > 1 ? RemoveListRange.fromEnd(this) : shrinkChild(children.size() - 1);
}
@Nullable
ShrinkStep shrinkChild(int index) {
int minIndex = isList() ? 1 : 0;
int minIndex = kind == StructureKind.GENERIC ? 0 : 1;
for (; index >= minIndex; index--) {
ShrinkStep childShrink = children.get(index).shrink();
if (childShrink != null) return wrapChildShrink(index, childShrink);
@@ -126,15 +127,8 @@ class StructureNode extends StructureElement {
};
}
private boolean isList() {
if (children.size() > 1 &&
children.get(0) instanceof IntData && ((IntData)children.get(0)).value >= children.size() - 1) {
for (int i = 1; i < children.size(); i++) {
if (!(children.get(i) instanceof StructureNode)) return false;
}
return true;
}
return false;
boolean isIncompleteList() {
return ((IntData)children.get(0)).value > children.size() - 1;
}
private void findChildrenWithGenerator(int generatorHash, List<StructureNode> result) {
@@ -188,6 +182,7 @@ class StructureNode extends StructureElement {
newChildren.set(index, newChild);
StructureNode copy = new StructureNode(this.id, newChildren);
copy.shrinkProhibited = this.shrinkProhibited;
copy.kind = this.kind;
return copy;
}
@@ -225,7 +220,11 @@ class StructureNode extends StructureElement {
@Override
public String toString() {
String inner = children.stream().map(Object::toString).collect(Collectors.joining(", "));
return isList() ? "[" + inner + "]" : "(" + inner + ")";
switch (kind) {
case LIST: return "[" + inner + "]";
case CHOICE: return "?(" + inner + ")";
default: return "(" + inner + ")";
}
}
}
@@ -300,4 +299,8 @@ class IntData extends StructureElement {
public int hashCode() {
return value;
}
}
enum StructureKind {
GENERIC, LIST, CHOICE
}
@@ -117,7 +117,7 @@ public class GeneratorTest extends PropertyCheckerTestCase {
s -> Character.isJavaIdentifierStart(s.charAt(0)) && s.chars().allMatch(Character::isJavaIdentifierPart));
checkGeneratesExample(asciiIdentifiers(),
s -> s.contains("_"),
10);
9);
}
public void testBoolean() {
@@ -147,11 +147,11 @@ public class GeneratorTest extends PropertyCheckerTestCase {
public void testSameFrequency() {
checkFalsified(listsOf(frequency(1, constant(1), 1, constant(2))),
l -> !l.contains(1) || !l.contains(2),
3);
2);
checkFalsified(listsOf(frequency(1, constant(1), 1, constant(2)).with(1, constant(3))),
l -> !l.contains(1) || !l.contains(2) || !l.contains(3),
7);
5);
}
public void testReplay() {
@@ -33,6 +33,10 @@ abstract class PropertyCheckerTestCase extends TestCase {
//noinspection unchecked
PropertyFailure<T> failure = (PropertyFailure<T>)e.getFailure();
if (failure.getStoppingReason() != null) {
throw new RuntimeException(failure.getStoppingReason());
}
/*
System.out.println(" " + getName());
System.out.println("Value: " + e.getBreakingValue());
@@ -40,7 +40,7 @@ public class StatefulGeneratorTest extends PropertyCheckerTestCase {
Scenario minHistory = checkFalsified(Scenario.scenarios(() -> env -> {
StringBuilder sb = new StringBuilder();
env.executeCommands(withRecursion(insertStringCmd(sb), deleteStringCmd(sb), checkDoesNotContain(sb, "A")));
}), Scenario::ensureSuccessful, 33).getMinimalCounterexample().getExampleValue();
}), Scenario::ensureSuccessful, 29).getMinimalCounterexample().getExampleValue();
assertEquals("commands:\n" +
" insert A at 0\n" +
@@ -59,7 +59,7 @@ public class StatefulGeneratorTest extends PropertyCheckerTestCase {
};
env.executeCommands(withRecursion(insertStringCmd(sb), replace, deleteStringCmd(sb), checkDoesNotContain(sb, "A")));
}), Scenario::ensureSuccessful, 58).getMinimalCounterexample().getExampleValue();
}), Scenario::ensureSuccessful, 52).getMinimalCounterexample().getExampleValue();
assertEquals("commands:\n" +
" insert A at 0\n" +
@@ -27,12 +27,12 @@ public class SubSequenceTest extends PropertyCheckerTestCase{
@Parameterized.Parameters(name = "{0}")
public static Collection data() {
return Arrays.asList(
new Object[]{"abcde", 448},
new Object[]{"abcdef", 463},
new Object[]{"sadf", 117},
new Object[]{"asdf", 132},
new Object[]{"xxx", 96},
new Object[]{"AA", 60}
new Object[]{"abcde", 399},
new Object[]{"abcdef", 420},
new Object[]{"sadf", 107},
new Object[]{"asdf", 118},
new Object[]{"xxx", 81},
new Object[]{"AA", 47}
);
}