jetCheck: remove subStructure from API, invoke generators in more completion-friendly way

This commit is contained in:
peter
2017-12-02 08:47:00 +01:00
parent fa5f4ed2a9
commit 409da53e9f
11 changed files with 36 additions and 35 deletions
@@ -47,9 +47,9 @@ class JavaPsiIndexConsistencyTest : LightCodeInsightFixtureTestCase() {
PsiIndexConsistencyTester.commonActions(PsiIndexConsistencyTester.commonRefs + listOf(ClassRef)),
Generator.sampledFrom(AddImport, AddEnum, InvisiblePsiChange),
Generator.booleans().map { ChangeLanguageLevel(if (it) LanguageLevel.HIGHEST else LanguageLevel.JDK_1_3) },
Generator.from { data -> TextChange(Generator.asciiIdentifiers().suchThat { !JavaLexer.isKeyword(it, LanguageLevel.HIGHEST) }.generateValue(data),
Generator.booleans().generateValue(data),
Generator.booleans().generateValue(data)) }
Generator.from { data -> TextChange(data.generateConditional(Generator.asciiIdentifiers()) { !JavaLexer.isKeyword(it, LanguageLevel.HIGHEST) },
data.generate(Generator.booleans()),
data.generate(Generator.booleans())) }
)
PropertyChecker.forAll(Generator.listsOf(genAction)).withIterationCount(20).shouldHold { actions ->
val prevLevel = LanguageLevelModuleExtensionImpl.getInstance(myFixture.module).languageLevel
@@ -1,5 +1,7 @@
package jetCheck;
import org.jetbrains.annotations.NotNull;
/**
* @author peter
*/
@@ -21,4 +23,11 @@ abstract class AbstractDataStructure implements DataStructure {
return sizeHint;
}
@Override
public <T> T generate(@NotNull Generator<T> generator) {
return generator.getGeneratorFunction().apply(subStructure());
}
@NotNull
abstract DataStructure subStructure();
}
+3 -4
View File
@@ -17,8 +17,8 @@ public interface DataStructure {
/**
* @return a non-negative number used by various generators to guide the sizes of structures (e.g. collections) they create.
* The sizes need not be exactly equal to this hint, but in average bigger hints should in average correspond to bigger structures. When generators invoke other generators using {@link #subStructure}, the size hint of the sub-structure is
* generally less than the parent's one.
* The sizes need not be exactly equal to this hint, but in average bigger hints should in average correspond to bigger structures. When generators invoke other generators, the size hint of the structure used by called generators is
* generally less than the original one's.
*/
int getSizeHint();
@@ -26,8 +26,7 @@ public interface DataStructure {
return drawInt(IntDistribution.uniform(0, getSizeHint()));
}
@NotNull
DataStructure subStructure();
<T> T generate(@NotNull Generator<T> generator);
<T> T generateNonShrinkable(@NotNull Generator<T> generator);
@@ -33,12 +33,12 @@ 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 -> alternatives.get(data.drawInt(distribution)).generator.generateValue(data);
return data -> data.generate(alternatives.get(data.drawInt(distribution)).generator);
}
@NotNull
private static <T> List<WeightedGenerator<T>> weightedGenerators(int weight1, Generator<? extends T> alternative1,
int weight2, Generator<? extends T> alternative2) {
private static <T> List<WeightedGenerator<T>> weightedGenerators(int weight1, Generator<? extends T> alternative1,
int weight2, Generator<? extends T> alternative2) {
List<WeightedGenerator<T>> alternatives = new ArrayList<>();
alternatives.add(new WeightedGenerator<>(weight1, alternative1));
alternatives.add(new WeightedGenerator<>(weight2, alternative2));
@@ -26,7 +26,7 @@ class GenerativeDataStructure extends AbstractDataStructure {
@NotNull
@Override
public GenerativeDataStructure subStructure() {
GenerativeDataStructure subStructure() {
return new GenerativeDataStructure(random, node.subStructure(), childSizeHint());
}
+6 -13
View File
@@ -26,7 +26,7 @@ public class Generator<T> {
/**
* Creates a generator from a custom function, that creates objects of the given type based on the data from {@link DataStructure}.
* The generator may call {@link DataStructure#drawInt} methods directly (and interpret those ints in any way it wishes),
* or invoke other generators using {@link #generateValue(DataStructure)}.<p/>
* or invoke other generators using {@link DataStructure#generate(Generator)}.<p/>
*
* When a property is falsified, the DataStructure is attempted to be minimized, and the generator will be run on
* ever "smaller" versions of it, this enables automatic minimization on all kinds of generated types.<p/>
@@ -39,13 +39,6 @@ public class Generator<T> {
return new Generator<>(function);
}
/**
* Generates a value inside the given data structure.
*/
public T generateValue(@NotNull DataStructure data) {
return myFunction.apply(data.subStructure());
}
Function<DataStructure, T> getGeneratorFunction() {
return myFunction;
}
@@ -65,10 +58,10 @@ public class Generator<T> {
*/
public <V> Generator<V> flatMap(@NotNull Function<T,Generator<V>> fun) {
return from(data -> {
T value = generateValue(data);
T value = data.generate(this);
Generator<V> result = fun.apply(value);
if (result == null) throw new NullPointerException(fun + " returned null on " + value);
return result.generateValue(data);
return data.generate(result);
});
}
@@ -121,7 +114,7 @@ public class Generator<T> {
if (alternatives.isEmpty()) throw new IllegalArgumentException("No alternatives to choose from");
return from(data -> {
int index = data.generateNonShrinkable(integers(0, alternatives.size() - 1));
return alternatives.get(index).generateValue(data);
return data.generate(alternatives.get(index));
});
}
@@ -140,7 +133,7 @@ public class Generator<T> {
/** Gets the data from two generators and invokes the given function to produce a result based on the two generated values. */
public static <A,B,C> Generator<C> zipWith(Generator<A> gen1, Generator<B> gen2, BiFunction<A,B,C> zip) {
return from(data -> zip.apply(gen1.generateValue(data), gen2.generateValue(data)));
return from(data -> zip.apply(data.generate(gen1), data.generate(gen2)));
}
/**
@@ -269,7 +262,7 @@ public class Generator<T> {
private static <T> List<T> generateList(Generator<T> itemGenerator, DataStructure data, int size) {
List<T> list = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
list.add(itemGenerator.generateValue(data));
list.add(data.generate(itemGenerator));
}
return Collections.unmodifiableList(list);
}
@@ -31,18 +31,18 @@ class ReplayDataStructure extends AbstractDataStructure {
@NotNull
@Override
public DataStructure subStructure() {
DataStructure subStructure() {
return new ReplayDataStructure(nextChild(StructureNode.class), childSizeHint(), customizer);
}
@Override
public <T> T generateNonShrinkable(@NotNull Generator<T> generator) {
return generator.generateValue(this);
return generate(generator);
}
@Override
public <T> T generateConditional(@NotNull Generator<T> generator, @NotNull Predicate<T> condition) {
T value = generator.generateValue(this);
T value = generate(generator);
if (!condition.test(value)) throw new CannotRestoreValue();
return value;
}
@@ -26,7 +26,7 @@ public class RecursiveGeneratorTest extends PropertyCheckerTestCase {
public void testShrinksToLeafDespiteWrapping() {
checkShrinksToLeaf(Generator.recursive(nodes -> Generator.frequency(2, leaves,
1, Generator.from(data -> Generator.listsOf(nodes).map(Composite::new).generateValue(data)))));
1, Generator.from(data -> data.generate(Generator.listsOf(nodes).map(Composite::new))))));
}
private interface Node {}
@@ -15,10 +15,10 @@ public class StatefulGeneratorTest extends PropertyCheckerTestCase {
AtomicInteger modelLength = new AtomicInteger(0);
Generator<List<InsertChar>> cmds = Generator.listsOf(Generator.from(cmdData -> {
int index = cmdData.drawInt(IntDistribution.uniform(0, modelLength.getAndIncrement()));
char c = Generator.asciiLetters().generateValue(cmdData);
char c = cmdData.generate(Generator.asciiLetters());
return new InsertChar(c, index);
}));
return cmds.generateValue(data);
return data.generate(cmds);
});
List<InsertChar> minCmds = checkGeneratesExample(gen,
cmds -> InsertChar.performOperations(cmds).contains("ab"),
@@ -23,7 +23,7 @@ import one.util.streamex.IntStreamEx
class PsiEventConsistencyTest : LightPlatformCodeInsightFixtureTestCase() {
fun testPsiDocSynchronization() {
PropertyChecker.forAll(commands()).shouldHold { cmd ->
PropertyChecker.forAll(commands).shouldHold { cmd ->
runCommand(cmd)
true
}
@@ -91,14 +91,14 @@ class PsiEventConsistencyTest : LightPlatformCodeInsightFixtureTestCase() {
}
private val genCoords = Generator.zipWith(Generator.naturals(), Generator.integers(0, 5), ::NodeCoordinates)
private fun commands(): Generator<AstCommand> = Generator.frequency(
1, Generator.from { CommandGroup(Generator.listsOf(IntDistribution.uniform(1, 5), commands()).generateValue(it)) },
private val commands: Generator<AstCommand> = Generator.recursive { rec -> Generator.frequency(
1, Generator.listsOf(IntDistribution.uniform(1, 5), rec).map(::CommandGroup),
5, genCoords.flatMap { coords ->
Generator.anyOf(
Generator.constant(DeleteElement(coords)),
nodes.map { ReplaceElement(coords, it) },
Generator.zipWith(nodes, Generator.naturals()) { n, i -> AddElement(coords, i, n) }
) })
) }) }
private val leafTypes = IntStreamEx.range(1, 5).mapToObj { i -> IElementType("Leaf" + i, null) }.toList()
private val compositeTypes = IntStreamEx.range(1, 5).mapToObj { i -> IElementType("Composite" + i, null) }.toList()
@@ -36,7 +36,7 @@ public class DeleteRange extends ActionOnRange {
return Generator.from(data -> {
if (psiFile.getTextLength() == 0) return new DeleteRange(psiFile, 0, 0);
int startOffset = Generator.integers(0, psiFile.getTextLength() - 1).generateValue(data);
int startOffset = data.generate(Generator.integers(0, psiFile.getTextLength() - 1));
PsiElement start = psiFile.findElementAt(startOffset);
PsiElement end = psiFile.findElementAt(startOffset + data.drawInt(IntDistribution.geometric(10)));
if (start == null || end == null) return null;