mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
jetCheck: avoid shrinking steps inapplicable to lists/choices
This commit is contained in:
@@ -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}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user