diff --git a/jetCheck/src/org/jetbrains/jetCheck/AbstractDataStructure.java b/jetCheck/src/org/jetbrains/jetCheck/AbstractDataStructure.java index da1589117e30..437f96e2d5ea 100644 --- a/jetCheck/src/org/jetbrains/jetCheck/AbstractDataStructure.java +++ b/jetCheck/src/org/jetbrains/jetCheck/AbstractDataStructure.java @@ -35,4 +35,5 @@ abstract class AbstractDataStructure implements DataStructure { abstract T generateConditional(@NotNull Generator generator, @NotNull Predicate condition); + abstract void changeKind(StructureKind kind); } diff --git a/jetCheck/src/org/jetbrains/jetCheck/FrequencyGenerator.java b/jetCheck/src/org/jetbrains/jetCheck/FrequencyGenerator.java index 1990f63df2a2..74f8608a07de 100644 --- a/jetCheck/src/org/jetbrains/jetCheck/FrequencyGenerator.java +++ b/jetCheck/src/org/jetbrains/jetCheck/FrequencyGenerator.java @@ -33,7 +33,10 @@ public class FrequencyGenerator extends Generator { private static Function frequencyFunction(List> alternatives) { List 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 diff --git a/jetCheck/src/org/jetbrains/jetCheck/GenerativeDataStructure.java b/jetCheck/src/org/jetbrains/jetCheck/GenerativeDataStructure.java index 9bd42438fddb..b176874b4293 100644 --- a/jetCheck/src/org/jetbrains/jetCheck/GenerativeDataStructure.java +++ b/jetCheck/src/org/jetbrains/jetCheck/GenerativeDataStructure.java @@ -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; diff --git a/jetCheck/src/org/jetbrains/jetCheck/Generator.java b/jetCheck/src/org/jetbrains/jetCheck/Generator.java index b6f6eb96c153..3876fe2e6c19 100644 --- a/jetCheck/src/org/jetbrains/jetCheck/Generator.java +++ b/jetCheck/src/org/jetbrains/jetCheck/Generator.java @@ -133,7 +133,8 @@ public class Generator { public static Generator anyOf(List> 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 { } private static List generateList(Generator itemGenerator, DataStructure data, int size) { + ((AbstractDataStructure) data).changeKind(StructureKind.LIST); List list = new ArrayList<>(size); for (int i = 0; i < size; i++) { list.add(data.generate(itemGenerator)); diff --git a/jetCheck/src/org/jetbrains/jetCheck/RemoveListRange.java b/jetCheck/src/org/jetbrains/jetCheck/RemoveListRange.java index 8d8c5357d362..13923a07bb4a 100644 --- a/jetCheck/src/org/jetbrains/jetCheck/RemoveListRange.java +++ b/jetCheck/src/org/jetbrains/jetCheck/RemoveListRange.java @@ -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 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); diff --git a/jetCheck/src/org/jetbrains/jetCheck/ReplayDataStructure.java b/jetCheck/src/org/jetbrains/jetCheck/ReplayDataStructure.java index 337b6c3cef31..00b73b4678d7 100644 --- a/jetCheck/src/org/jetbrains/jetCheck/ReplayDataStructure.java +++ b/jetCheck/src/org/jetbrains/jetCheck/ReplayDataStructure.java @@ -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(); diff --git a/jetCheck/src/org/jetbrains/jetCheck/StructureNode.java b/jetCheck/src/org/jetbrains/jetCheck/StructureNode.java index 13787836caf3..baa7ecaf8539 100644 --- a/jetCheck/src/org/jetbrains/jetCheck/StructureNode.java +++ b/jetCheck/src/org/jetbrains/jetCheck/StructureNode.java @@ -33,6 +33,7 @@ abstract class StructureElement { class StructureNode extends StructureElement { final List 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 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 } \ No newline at end of file diff --git a/jetCheck/test/org/jetbrains/jetCheck/GeneratorTest.java b/jetCheck/test/org/jetbrains/jetCheck/GeneratorTest.java index 03a408caf28e..961006d47128 100644 --- a/jetCheck/test/org/jetbrains/jetCheck/GeneratorTest.java +++ b/jetCheck/test/org/jetbrains/jetCheck/GeneratorTest.java @@ -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() { diff --git a/jetCheck/test/org/jetbrains/jetCheck/PropertyCheckerTestCase.java b/jetCheck/test/org/jetbrains/jetCheck/PropertyCheckerTestCase.java index ea5b89b538ca..74b4406886ab 100644 --- a/jetCheck/test/org/jetbrains/jetCheck/PropertyCheckerTestCase.java +++ b/jetCheck/test/org/jetbrains/jetCheck/PropertyCheckerTestCase.java @@ -33,6 +33,10 @@ abstract class PropertyCheckerTestCase extends TestCase { //noinspection unchecked PropertyFailure failure = (PropertyFailure)e.getFailure(); + if (failure.getStoppingReason() != null) { + throw new RuntimeException(failure.getStoppingReason()); + } + /* System.out.println(" " + getName()); System.out.println("Value: " + e.getBreakingValue()); diff --git a/jetCheck/test/org/jetbrains/jetCheck/StatefulGeneratorTest.java b/jetCheck/test/org/jetbrains/jetCheck/StatefulGeneratorTest.java index ca72782076c6..8404789f9a3c 100644 --- a/jetCheck/test/org/jetbrains/jetCheck/StatefulGeneratorTest.java +++ b/jetCheck/test/org/jetbrains/jetCheck/StatefulGeneratorTest.java @@ -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" + diff --git a/jetCheck/test/org/jetbrains/jetCheck/SubSequenceTest.java b/jetCheck/test/org/jetbrains/jetCheck/SubSequenceTest.java index fc2976f66081..fa5c08b0eccf 100644 --- a/jetCheck/test/org/jetbrains/jetCheck/SubSequenceTest.java +++ b/jetCheck/test/org/jetbrains/jetCheck/SubSequenceTest.java @@ -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} ); }