mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
jetCheck: improve shrinking
after recursive substitution, re-run shrinking for parent in case it can now optimize more allow recursive shrinking for ImperativeCommand lists
This commit is contained in:
@@ -50,17 +50,26 @@ class ScenarioImpl implements Scenario {
|
||||
|
||||
@Override
|
||||
public void executeCommands(IntDistribution count, Generator<? extends ImperativeCommand> cmdGen) {
|
||||
data.generate(Generator.listsOf(count, innerCommands(cmdGen)));
|
||||
innerCommandLists(Generator.listsOf(count, innerCommands(cmdGen)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeCommands(Generator<? extends ImperativeCommand> cmdGen) {
|
||||
data.generate(Generator.nonEmptyLists(innerCommands(cmdGen)));
|
||||
innerCommandLists(Generator.nonEmptyLists(innerCommands(cmdGen)));
|
||||
}
|
||||
|
||||
private void innerCommandLists(final Generator<List<Object>> listGen) {
|
||||
data.generate(Generator.from(new EquivalentGenerator<List<Object>>() {
|
||||
@Override
|
||||
public List<Object> apply(DataStructure data) {
|
||||
return listGen.getGeneratorFunction().apply(data);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Generator<Object> innerCommands(Generator<? extends ImperativeCommand> cmdGen) {
|
||||
return Generator.from(new Function<DataStructure, Object>() {
|
||||
return Generator.from(new EquivalentGenerator<Object>() {
|
||||
@Override
|
||||
public Object apply(DataStructure cmdData) {
|
||||
List<Object> localLog = new ArrayList<>();
|
||||
@@ -68,16 +77,6 @@ class ScenarioImpl implements Scenario {
|
||||
performCommand(safeGenerate(cmdData, cmdGen), cmdData, localLog);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return getClass() == obj.getClass(); // for recursive shrinking to work
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -129,5 +128,18 @@ class ScenarioImpl implements Scenario {
|
||||
if (failure != null) throw new RuntimeException(failure);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static abstract class EquivalentGenerator<T> implements Function<DataStructure, T> {
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return getClass() == obj.getClass(); // for recursive shrinking to work
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -76,16 +76,18 @@ class StructureNode extends StructureElement {
|
||||
ShrinkStep shrinkChild(int index) {
|
||||
for (; index < children.size(); index++) {
|
||||
ShrinkStep childShrink = children.get(index).shrink();
|
||||
if (childShrink != null) return shrinkNextChildAfter(index, childShrink);
|
||||
if (childShrink != null) return wrapChildShrink(index, childShrink);
|
||||
}
|
||||
|
||||
return shrinkRecursion();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ShrinkStep shrinkNextChildAfter(int index, @Nullable ShrinkStep step) {
|
||||
private ShrinkStep wrapChildShrink(int index, @Nullable ShrinkStep step) {
|
||||
if (step == null) return shrinkChild(index + 1);
|
||||
|
||||
|
||||
NodeId oldChild = children.get(index).id;
|
||||
|
||||
return new ShrinkStep() {
|
||||
@Nullable
|
||||
@Override
|
||||
@@ -97,12 +99,16 @@ class StructureNode extends StructureElement {
|
||||
ShrinkStep onSuccess(StructureNode smallerRoot) {
|
||||
StructureNode inheritor = (StructureNode)Objects.requireNonNull(smallerRoot.findChildById(id));
|
||||
assert inheritor.children.size() == children.size();
|
||||
return inheritor.shrinkNextChildAfter(index, step.onSuccess(smallerRoot));
|
||||
if (inheritor.children.get(index).id != oldChild) {
|
||||
return inheritor.shrink();
|
||||
}
|
||||
|
||||
return inheritor.wrapChildShrink(index, step.onSuccess(smallerRoot));
|
||||
}
|
||||
|
||||
@Override
|
||||
ShrinkStep onFailure() {
|
||||
return shrinkNextChildAfter(index, step.onFailure());
|
||||
return wrapChildShrink(index, step.onFailure());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -33,27 +33,54 @@ public class StatefulGeneratorTest extends PropertyCheckerTestCase {
|
||||
public void testImperativeInsertDeleteCheckCommands() {
|
||||
Scenario minHistory = checkFalsified(ImperativeCommand.scenarios(() -> env -> {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
ImperativeCommand check = env1 -> {
|
||||
env1.logMessage("check");
|
||||
if (sb.indexOf("A") >= 0) throw new AssertionError();
|
||||
};
|
||||
env.executeCommands(recursive(rec -> {
|
||||
ImperativeCommand group = env1 -> {
|
||||
env1.logMessage("Group");
|
||||
env1.executeCommands(rec);
|
||||
};
|
||||
return frequency(2, constant(group),
|
||||
3, sampledFrom(insertStringCmd(sb), deleteStringCmd(sb), check));
|
||||
}));
|
||||
}), Scenario::ensureSuccessful, 36).getMinimalCounterexample().getExampleValue();
|
||||
env.executeCommands(withRecursion(insertStringCmd(sb), deleteStringCmd(sb), checkDoesNotContain(sb, "A")));
|
||||
}), Scenario::ensureSuccessful, 42).getMinimalCounterexample().getExampleValue();
|
||||
|
||||
assertEquals("commands:\n" +
|
||||
" Group\n" +
|
||||
" insert A at 0\n" +
|
||||
" check",
|
||||
" insert A at 0\n" +
|
||||
" check",
|
||||
minHistory.toString());
|
||||
}
|
||||
|
||||
public void testImperativeInsertReplaceDeleteCommands() {
|
||||
Scenario minHistory = checkFalsified(ImperativeCommand.scenarios(() -> env -> {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
ImperativeCommand replace = env1 -> {
|
||||
if (sb.length() == 0) return;
|
||||
int index = env1.generateValue(integers(0, sb.length() - 1), null);
|
||||
char toReplace = env1.generateValue(asciiLetters().suchThat(c -> c != 'A'), "replace " + sb.charAt(index) + " with %s at " + index);
|
||||
sb.setCharAt(index, toReplace);
|
||||
};
|
||||
|
||||
env.executeCommands(withRecursion(insertStringCmd(sb), replace, deleteStringCmd(sb), checkDoesNotContain(sb, "A")));
|
||||
}), Scenario::ensureSuccessful, 76).getMinimalCounterexample().getExampleValue();
|
||||
|
||||
assertEquals("commands:\n" +
|
||||
" insert A at 0\n" +
|
||||
" check",
|
||||
minHistory.toString());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Generator<ImperativeCommand> withRecursion(ImperativeCommand... commands) {
|
||||
return recursive(rec -> {
|
||||
ImperativeCommand group = env -> {
|
||||
env.logMessage("Group");
|
||||
env.executeCommands(rec);
|
||||
};
|
||||
return frequency(2, constant(group), 3, sampledFrom(commands));
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
@NotNull
|
||||
private static ImperativeCommand checkDoesNotContain(StringBuilder sb, String infix) {
|
||||
return env -> {
|
||||
env.logMessage("check");
|
||||
if (sb.indexOf(infix) >= 0) throw new AssertionError();
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static ImperativeCommand insertStringCmd(StringBuilder sb) {
|
||||
return env -> {
|
||||
|
||||
Reference in New Issue
Block a user