mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
slowCheck: print iteration seed, customize size hint, add "rechecking"
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
package slowCheck;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
class CounterExampleImpl<T> implements PropertyFailure.CounterExample<T> {
|
||||
final StructureNode data;
|
||||
private final T value;
|
||||
@Nullable private final Throwable exception;
|
||||
|
||||
private CounterExampleImpl(StructureNode data, T value, @Nullable Throwable exception) {
|
||||
this.data = data;
|
||||
this.value = value;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getExampleValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Throwable getExceptionCause() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
static <T> CounterExampleImpl<T> checkProperty(Predicate<T> property, T value, StructureNode node) {
|
||||
try {
|
||||
if (!property.test(value)) {
|
||||
return new CounterExampleImpl<>(node, value, null);
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
return new CounterExampleImpl<>(node, value, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,12 @@ public interface DataStructure {
|
||||
}
|
||||
|
||||
int drawInt(@NotNull IntDistribution distribution);
|
||||
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
int getSizeHint();
|
||||
|
||||
default int suggestCollectionSize() {
|
||||
|
||||
@@ -5,7 +5,7 @@ package slowCheck;
|
||||
*/
|
||||
public class GeneratorException extends RuntimeException {
|
||||
|
||||
GeneratorException(long seed, Throwable cause) {
|
||||
super("Exception while generating data, seed=" + seed, cause);
|
||||
GeneratorException(Iteration<?> iteration, Throwable cause) {
|
||||
super("Exception while generating data, " + iteration.printSeeds(), cause);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
package slowCheck;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
class Iteration<T> {
|
||||
|
||||
private static final Predicate<Object> DATA_IS_DIFFERENT = new Predicate<Object>() {
|
||||
@Override
|
||||
public boolean test(Object o) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ": cannot generate enough sufficiently different values";
|
||||
}
|
||||
};
|
||||
|
||||
final CheckSession<T> session;
|
||||
final long iterationSeed;
|
||||
final int sizeHint;
|
||||
final int iterationNumber;
|
||||
|
||||
Iteration(CheckSession<T> session, long iterationSeed, int iterationNumber) {
|
||||
this.session = session;
|
||||
this.iterationSeed = iterationSeed;
|
||||
this.sizeHint = session.sizeHintFun.applyAsInt(iterationNumber);
|
||||
this.iterationNumber = iterationNumber;
|
||||
if (sizeHint < 0) {
|
||||
throw new IllegalArgumentException("Size hint should be non-negative, found " + sizeHint);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private CounterExampleImpl<T> findCounterExample(Random random) {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
StructureNode node = new StructureNode();
|
||||
T value;
|
||||
try {
|
||||
value = session.generator.getGeneratorFunction().apply(new GenerativeDataStructure(random, node, sizeHint));
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throw new GeneratorException(this, e);
|
||||
}
|
||||
if (!session.generatedHashes.add(node.hashCode())) continue;
|
||||
|
||||
return CounterExampleImpl.checkProperty(session.property, value, node);
|
||||
}
|
||||
throw new CannotSatisfyCondition(DATA_IS_DIFFERENT);
|
||||
}
|
||||
|
||||
String printToReproduce() {
|
||||
return "To reproduce the last iteration, run PropertyChecker.forAll(...).rechecking(" + iterationSeed + "L, " + sizeHint + ").shouldHold(...)\n" +
|
||||
"Global seed: " + session.globalSeed + "L";
|
||||
}
|
||||
|
||||
String printSeeds() {
|
||||
return "iteration seed=" + iterationSeed + "L, " +
|
||||
"size hint=" + sizeHint + ", " +
|
||||
"global seed=" + session.globalSeed + "L";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Iteration<T> performIteration() {
|
||||
session.notifier.iterationStarted(iterationNumber);
|
||||
|
||||
Random random = new Random(iterationSeed);
|
||||
CounterExampleImpl<T> example = findCounterExample(random);
|
||||
if (example != null) {
|
||||
session.notifier.counterExampleFound(this);
|
||||
PropertyFailureImpl<T> failure = new PropertyFailureImpl<>(example, this);
|
||||
throw new PropertyFalsified(failure, () -> new ReplayDataStructure(failure.getMinimalCounterexample().data, sizeHint));
|
||||
}
|
||||
|
||||
if (iterationNumber >= session.iterationCount) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Iteration<>(session, random.nextLong(), iterationNumber + 1);
|
||||
}
|
||||
}
|
||||
|
||||
class CheckSession<T> {
|
||||
final Generator<T> generator;
|
||||
final Predicate<T> property;
|
||||
final long globalSeed;
|
||||
final Set<Integer> generatedHashes = new HashSet<>();
|
||||
final StatusNotifier notifier;
|
||||
final int iterationCount;
|
||||
final IntUnaryOperator sizeHintFun;
|
||||
|
||||
CheckSession(Generator<T> generator, Predicate<T> property, long globalSeed, int iterationCount, IntUnaryOperator sizeHintFun) {
|
||||
this.generator = generator;
|
||||
this.property = property;
|
||||
this.globalSeed = globalSeed;
|
||||
this.iterationCount = iterationCount;
|
||||
this.sizeHintFun = sizeHintFun;
|
||||
notifier = new StatusNotifier(iterationCount);
|
||||
}
|
||||
|
||||
Iteration<T> firstIteration() {
|
||||
return new Iteration<>(this, globalSeed, 1);
|
||||
}
|
||||
}
|
||||
@@ -1,201 +1,81 @@
|
||||
package slowCheck;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
* An entry point to property-based testing. The main usage pattern: {@code PropertyChecker.forAll(generator).shouldHold(property)}.
|
||||
*/
|
||||
public class PropertyChecker<T> {
|
||||
private static final Predicate<Object> DATA_IS_DIFFERENT = new Predicate<Object>() {
|
||||
@Override
|
||||
public boolean test(Object o) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ": cannot generate enough sufficiently different values";
|
||||
}
|
||||
};
|
||||
private final Generator<T> generator;
|
||||
private Predicate<T> property;
|
||||
private final Set<Integer> generatedHashes = new HashSet<>();
|
||||
private long seed = new Random().nextLong();
|
||||
private long globalSeed = new Random().nextLong();
|
||||
private IntUnaryOperator sizeHintFun = iteration -> (iteration - 1) % 100 + 1;
|
||||
private int iterationCount = 100;
|
||||
private StatusNotifier notifier;
|
||||
|
||||
private PropertyChecker(Generator<T> generator) {
|
||||
this.generator = generator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a property checker for the given generator. It can be further customized using {@code with*}-methods,
|
||||
* and should finally used for property to check via {@link #shouldHold(Predicate)} call.
|
||||
*/
|
||||
public static <T> PropertyChecker<T> forAll(Generator<T> generator) {
|
||||
return new PropertyChecker<>(generator);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function allows to start the test with a fixed random seed. It's useful to reproduce some previous test run and debug it.
|
||||
* @param seed A random seed to use for the first iteration.
|
||||
* The following iterations will use other, pseudo-random seeds, but still derived from this one.
|
||||
* @return this PropertyChecker
|
||||
*/
|
||||
public PropertyChecker<T> withSeed(long seed) {
|
||||
this.seed = seed;
|
||||
globalSeed = seed;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param iterationCount the number of iterations to try. By default it's 100.
|
||||
* @return this PropertyChecker
|
||||
*/
|
||||
public PropertyChecker<T> withIterationCount(int iterationCount) {
|
||||
this.iterationCount = iterationCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sizeHintFun a function determining how size hint should be distributed depending on the iteration number.
|
||||
* By default the size hint will be 1 in the first iteration, 2 in the second one, and so on until 100,
|
||||
* then again 1,...,100,1,...,100, etc.
|
||||
* @return this PropertyChecker
|
||||
* @see DataStructure#getSizeHint()
|
||||
*/
|
||||
public PropertyChecker<T> withSizeHint(@NotNull IntUnaryOperator sizeHintFun) {
|
||||
this.sizeHintFun = sizeHintFun;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the property within a single iteration by using specified seed and size hint. Useful to debug the test after it's failed.
|
||||
*/
|
||||
public PropertyChecker<T> rechecking(long seed, int sizeHint) {
|
||||
return withSeed(seed).withSizeHint(whatever -> sizeHint).withIterationCount(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the given property returns {@code true} and doesn't throw exceptions by running the generator and the property
|
||||
* given number of times (see {@link #withIterationCount(int)}).
|
||||
*/
|
||||
public void shouldHold(@NotNull Predicate<T> property) {
|
||||
if (this.property != null) throw new IllegalArgumentException("Property " + property + " already checked");
|
||||
this.property = property;
|
||||
notifier = new StatusNotifier(iterationCount, this.seed);
|
||||
|
||||
Random random = new Random(seed);
|
||||
|
||||
for (int i = 1; i <= iterationCount; i++) {
|
||||
notifier.iterationStarted(i);
|
||||
|
||||
CounterExampleImpl<T> example = findCounterExample(i, random);
|
||||
if (example != null) {
|
||||
notifier.counterExampleFound();
|
||||
PropertyFailureImpl failure = new PropertyFailureImpl(example, i);
|
||||
throw new PropertyFalsified(seed, failure, () -> new ReplayDataStructure(failure.getMinimalCounterexample().data, failure.sizeHint));
|
||||
}
|
||||
Iteration<T> iteration = new CheckSession<>(generator, property, globalSeed, iterationCount, sizeHintFun).firstIteration();
|
||||
while (iteration != null) {
|
||||
iteration = iteration.performIteration();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private CounterExampleImpl<T> findCounterExample(int sizeHint, Random random) {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
StructureNode node = new StructureNode();
|
||||
T value;
|
||||
try {
|
||||
value = generator.getGeneratorFunction().apply(new GenerativeDataStructure(random, node, sizeHint));
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throw new GeneratorException(seed, e);
|
||||
}
|
||||
if (!generatedHashes.add(node.hashCode())) continue;
|
||||
|
||||
return CounterExampleImpl.checkProperty(property, value, node);
|
||||
}
|
||||
throw new CannotSatisfyCondition(DATA_IS_DIFFERENT);
|
||||
}
|
||||
|
||||
private class PropertyFailureImpl implements PropertyFailure<T> {
|
||||
private final CounterExampleImpl<T> initial;
|
||||
private CounterExampleImpl<T> minimized;
|
||||
private int totalSteps;
|
||||
private int successfulSteps;
|
||||
private int sizeHint;
|
||||
private Throwable stoppingReason;
|
||||
|
||||
PropertyFailureImpl(@NotNull CounterExampleImpl<T> initial, int sizeHint) {
|
||||
this.initial = initial;
|
||||
this.minimized = initial;
|
||||
this.sizeHint = sizeHint;
|
||||
try {
|
||||
shrink();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
stoppingReason = e;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CounterExampleImpl<T> getFirstCounterExample() {
|
||||
return initial;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CounterExampleImpl<T> getMinimalCounterexample() {
|
||||
return minimized;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Throwable getStoppingReason() {
|
||||
return stoppingReason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTotalMinimizationExampleCount() {
|
||||
return totalSteps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinimizationStageCount() {
|
||||
return successfulSteps;
|
||||
}
|
||||
|
||||
private void shrink() {
|
||||
ShrinkRunner shrinkRunner = new ShrinkRunner();
|
||||
while (true) {
|
||||
CounterExampleImpl<T> shrank = shrinkRunner.findShrink(minimized.data, node -> {
|
||||
if (!generatedHashes.add(node.hashCode())) return null;
|
||||
|
||||
notifier.shrinkAttempt(this);
|
||||
|
||||
try {
|
||||
T value = generator.getGeneratorFunction().apply(new ReplayDataStructure(node, sizeHint));
|
||||
totalSteps++;
|
||||
return CounterExampleImpl.checkProperty(property, value, node);
|
||||
}
|
||||
catch (CannotRestoreValue e) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
if (shrank != null) {
|
||||
minimized = shrank;
|
||||
successfulSteps++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class CounterExampleImpl<T> implements PropertyFailure.CounterExample<T> {
|
||||
final StructureNode data;
|
||||
private final T value;
|
||||
@Nullable private final Throwable exception;
|
||||
|
||||
private CounterExampleImpl(StructureNode data, T value, @Nullable Throwable exception) {
|
||||
this.data = data;
|
||||
this.value = value;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getExampleValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Throwable getExceptionCause() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
static <T> CounterExampleImpl<T> checkProperty(Predicate<T> property, T value, StructureNode node) {
|
||||
try {
|
||||
if (!property.test(value)) {
|
||||
return new CounterExampleImpl<>(node, value, null);
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
return new CounterExampleImpl<>(node, value, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,14 @@ public interface PropertyFailure<T> {
|
||||
int getTotalMinimizationExampleCount();
|
||||
|
||||
int getMinimizationStageCount();
|
||||
|
||||
int getIterationNumber();
|
||||
|
||||
long getIterationSeed();
|
||||
|
||||
long getGlobalSeed();
|
||||
|
||||
int getSizeHint();
|
||||
|
||||
interface CounterExample<T> {
|
||||
T getExampleValue();
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package slowCheck;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
class PropertyFailureImpl<T> implements PropertyFailure<T> {
|
||||
private final CounterExampleImpl<T> initial;
|
||||
private CounterExampleImpl<T> minimized;
|
||||
private int totalSteps;
|
||||
private int successfulSteps;
|
||||
final Iteration<T> iteration;
|
||||
private Throwable stoppingReason;
|
||||
|
||||
PropertyFailureImpl(@NotNull CounterExampleImpl<T> initial, Iteration<T> iteration) {
|
||||
this.initial = initial;
|
||||
this.minimized = initial;
|
||||
this.iteration = iteration;
|
||||
try {
|
||||
shrink();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
stoppingReason = e;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CounterExampleImpl<T> getFirstCounterExample() {
|
||||
return initial;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CounterExampleImpl<T> getMinimalCounterexample() {
|
||||
return minimized;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Throwable getStoppingReason() {
|
||||
return stoppingReason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTotalMinimizationExampleCount() {
|
||||
return totalSteps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinimizationStageCount() {
|
||||
return successfulSteps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIterationNumber() {
|
||||
return iteration.iterationNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getIterationSeed() {
|
||||
return iteration.iterationSeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getGlobalSeed() {
|
||||
return iteration.session.globalSeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSizeHint() {
|
||||
return iteration.sizeHint;
|
||||
}
|
||||
|
||||
private void shrink() {
|
||||
ShrinkRunner shrinkRunner = new ShrinkRunner();
|
||||
while (true) {
|
||||
CounterExampleImpl<T> shrank = shrinkRunner.findShrink(minimized.data, node -> {
|
||||
if (!iteration.session.generatedHashes.add(node.hashCode())) return null;
|
||||
|
||||
iteration.session.notifier.shrinkAttempt(this, iteration);
|
||||
|
||||
try {
|
||||
T value = iteration.session.generator.getGeneratorFunction().apply(new ReplayDataStructure(node, iteration.sizeHint));
|
||||
totalSteps++;
|
||||
return CounterExampleImpl.checkProperty(iteration.session.property, value, node);
|
||||
}
|
||||
catch (CannotRestoreValue e) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
if (shrank != null) {
|
||||
minimized = shrank;
|
||||
successfulSteps++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,13 +11,11 @@ import java.util.function.Supplier;
|
||||
public class PropertyFalsified extends RuntimeException {
|
||||
static final String FAILURE_REASON_HAS_CHANGED_DURING_MINIMIZATION = "!!! FAILURE REASON HAS CHANGED DURING MINIMIZATION !!!";
|
||||
private static final String SEPARATOR = "\n==========================\n";
|
||||
private final long seed;
|
||||
private final PropertyFailure<?> failure;
|
||||
private final PropertyFailureImpl<?> failure;
|
||||
private final Supplier<DataStructure> data;
|
||||
|
||||
PropertyFalsified(long seed, PropertyFailure<?> failure, Supplier<DataStructure> data) {
|
||||
PropertyFalsified(PropertyFailureImpl<?> failure, Supplier<DataStructure> data) {
|
||||
super(failure.getMinimalCounterexample().getExceptionCause());
|
||||
this.seed = seed;
|
||||
this.failure = failure;
|
||||
this.data = data;
|
||||
}
|
||||
@@ -26,7 +24,7 @@ public class PropertyFalsified extends RuntimeException {
|
||||
public String getMessage() {
|
||||
String msg = "Falsified on " + failure.getMinimalCounterexample().getExampleValue() + "\n" +
|
||||
getMinimizationStats() +
|
||||
"Seed=" + seed;
|
||||
failure.iteration.printToReproduce();
|
||||
|
||||
if (failure.getStoppingReason() != null) {
|
||||
msg += "\n Shrinking stopped because of " + StatusNotifier.printStackTrace(failure.getStoppingReason());
|
||||
|
||||
@@ -15,13 +15,11 @@ import java.util.Locale;
|
||||
@SuppressWarnings("UseOfSystemOutOrSystemErr")
|
||||
class StatusNotifier {
|
||||
private final int iterationCount;
|
||||
private final long seed;
|
||||
private int currentIteration;
|
||||
private long lastPrinted = System.currentTimeMillis();
|
||||
|
||||
StatusNotifier(int iterationCount, long seed) {
|
||||
StatusNotifier(int iterationCount) {
|
||||
this.iterationCount = iterationCount;
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
void iterationStarted(int iteration) {
|
||||
@@ -31,9 +29,9 @@ class StatusNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
void counterExampleFound() {
|
||||
void counterExampleFound(Iteration<?> iteration) {
|
||||
lastPrinted = System.currentTimeMillis();
|
||||
System.err.println(formatCurrentTime() + ": failed on iteration " + currentIteration + " (seed=" + seed + "), shrinking...");
|
||||
System.err.println(formatCurrentTime() + ": failed on iteration " + currentIteration + " (" + iteration.printSeeds() + "), shrinking...");
|
||||
}
|
||||
|
||||
private boolean shouldPrint() {
|
||||
@@ -46,11 +44,11 @@ class StatusNotifier {
|
||||
|
||||
private int lastReportedStage = -1;
|
||||
private String lastReportedTrace = null;
|
||||
void shrinkAttempt(PropertyFailure<?> failure) {
|
||||
<T> void shrinkAttempt(PropertyFailure<T> failure, Iteration<T> iteration) {
|
||||
if (shouldPrint()) {
|
||||
int stage = failure.getMinimizationStageCount();
|
||||
System.err.println(formatCurrentTime() + ": still shrinking (seed=" + seed + "). " +
|
||||
"Examples tried: " + failure.getTotalMinimizationExampleCount() +
|
||||
System.out.println(formatCurrentTime() + ": still shrinking (" + iteration.printSeeds() + "). " +
|
||||
"Examples tried: " + failure.getTotalMinimizationExampleCount() +
|
||||
", successful minimizations: " + stage);
|
||||
if (lastReportedStage != stage) {
|
||||
lastReportedStage = stage;
|
||||
|
||||
@@ -8,44 +8,30 @@ import static slowCheck.Generator.*;
|
||||
public class ExceptionTest extends PropertyCheckerTestCase {
|
||||
|
||||
public void testFailureReasonUnchanged() {
|
||||
try {
|
||||
forAllStable(integers()).shouldHold(i -> {
|
||||
throw new AssertionError("fail");
|
||||
});
|
||||
fail();
|
||||
}
|
||||
catch (PropertyFalsified e) {
|
||||
assertFalse(e.getMessage().contains(PropertyFalsified.FAILURE_REASON_HAS_CHANGED_DURING_MINIMIZATION));
|
||||
}
|
||||
PropertyFalsified e = checkFails(forAllStable(integers()), i -> {
|
||||
throw new AssertionError("fail");
|
||||
});
|
||||
|
||||
assertFalse(e.getMessage().contains(PropertyFalsified.FAILURE_REASON_HAS_CHANGED_DURING_MINIMIZATION));
|
||||
}
|
||||
|
||||
public void testFailureReasonChangedExceptionClass() {
|
||||
try {
|
||||
forAllStable(integers()).shouldHold(i -> {
|
||||
throw (i == 0 ? new RuntimeException("fail") : new IllegalArgumentException("fail"));
|
||||
});
|
||||
fail();
|
||||
}
|
||||
catch (PropertyFalsified e) {
|
||||
assertTrue(e.getMessage().contains(PropertyFalsified.FAILURE_REASON_HAS_CHANGED_DURING_MINIMIZATION));
|
||||
}
|
||||
PropertyFalsified e = checkFails(forAllStable(integers()), i -> {
|
||||
throw (i == 0 ? new RuntimeException("fail") : new IllegalArgumentException("fail"));
|
||||
});
|
||||
assertTrue(e.getMessage().contains(PropertyFalsified.FAILURE_REASON_HAS_CHANGED_DURING_MINIMIZATION));
|
||||
}
|
||||
|
||||
public void testFailureReasonChangedExceptionTrace() {
|
||||
try {
|
||||
forAllStable(integers()).shouldHold(i -> {
|
||||
if (i == 0) {
|
||||
throw new AssertionError("fail");
|
||||
}
|
||||
else {
|
||||
throw new AssertionError("fail2");
|
||||
}
|
||||
});
|
||||
fail();
|
||||
}
|
||||
catch (PropertyFalsified e) {
|
||||
assertTrue(e.getMessage().contains(PropertyFalsified.FAILURE_REASON_HAS_CHANGED_DURING_MINIMIZATION));
|
||||
}
|
||||
PropertyFalsified e = checkFails(forAllStable(integers()), i -> {
|
||||
if (i == 0) {
|
||||
throw new AssertionError("fail");
|
||||
}
|
||||
else {
|
||||
throw new AssertionError("fail2");
|
||||
}
|
||||
});
|
||||
assertTrue(e.getMessage().contains(PropertyFalsified.FAILURE_REASON_HAS_CHANGED_DURING_MINIMIZATION));
|
||||
}
|
||||
|
||||
public void testExceptionWhileGeneratingValue() {
|
||||
@@ -60,17 +46,13 @@ public class ExceptionTest extends PropertyCheckerTestCase {
|
||||
}
|
||||
|
||||
public void testExceptionWhileShrinkingValue() {
|
||||
try {
|
||||
forAllStable(listsOf(integers()).suchThat(l -> {
|
||||
if (l.size() == 1 && l.get(0) == 0) throw new RuntimeException("my exception");
|
||||
return true;
|
||||
})).shouldHold(l -> l.stream().allMatch(i -> i > 0));
|
||||
fail();
|
||||
}
|
||||
catch (PropertyFalsified e) {
|
||||
assertEquals("my exception", e.getFailure().getStoppingReason().getMessage());
|
||||
assertTrue(StatusNotifier.printStackTrace(e).contains("my exception"));
|
||||
}
|
||||
PropertyFalsified e = checkFails(PropertyChecker.forAll(listsOf(integers()).suchThat(l -> {
|
||||
if (l.size() == 1 && l.get(0) == 0) throw new RuntimeException("my exception");
|
||||
return true;
|
||||
})), l -> l.stream().allMatch(i -> i > 0));
|
||||
|
||||
assertEquals("my exception", e.getFailure().getStoppingReason().getMessage());
|
||||
assertTrue(StatusNotifier.printStackTrace(e).contains("my exception"));
|
||||
}
|
||||
|
||||
public void testUnsatisfiableSuchThat() {
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static slowCheck.Generator.*;
|
||||
@@ -28,25 +29,25 @@ public class GeneratorTest extends PropertyCheckerTestCase {
|
||||
public void testListContainsDivisible() {
|
||||
checkFalsified(nonEmptyLists(integers()),
|
||||
l -> l.stream().allMatch(i -> i % 10 != 0),
|
||||
9);
|
||||
3);
|
||||
}
|
||||
|
||||
public void testStringContains() {
|
||||
checkFalsified(stringsOf(asciiPrintableChars()),
|
||||
s -> !s.contains("a"),
|
||||
10);
|
||||
7);
|
||||
}
|
||||
|
||||
public void testLetterStringContains() {
|
||||
checkFalsified(stringsOf(asciiLetters()),
|
||||
s -> !s.contains("a"),
|
||||
5);
|
||||
3);
|
||||
}
|
||||
|
||||
public void testIsSorted() {
|
||||
PropertyFailure<List<Integer>> failure = checkFalsified(nonEmptyLists(integers()),
|
||||
l -> l.stream().sorted().collect(Collectors.toList()).equals(l),
|
||||
69);
|
||||
67);
|
||||
assertEquals(2, failure.getMinimalCounterexample().getExampleValue().size());
|
||||
}
|
||||
|
||||
@@ -57,7 +58,7 @@ public class GeneratorTest extends PropertyCheckerTestCase {
|
||||
public void testSortedDoublesNonDescending() {
|
||||
PropertyFailure<List<Double>> failure = checkFalsified(listsOf(doubles()),
|
||||
l -> isSorted(l.stream().sorted().collect(Collectors.toList())),
|
||||
141);
|
||||
76);
|
||||
assertEquals(2, failure.getMinimalCounterexample().getExampleValue().size());
|
||||
}
|
||||
|
||||
@@ -77,13 +78,13 @@ public class GeneratorTest extends PropertyCheckerTestCase {
|
||||
public void testStringOfStringChecksAllChars() {
|
||||
checkFalsified(stringsOf("abc "),
|
||||
s -> !s.contains(" "),
|
||||
3);
|
||||
4);
|
||||
}
|
||||
|
||||
public void testLongListsHappen() {
|
||||
PropertyFailure<List<Integer>> failure = checkFalsified(listsOf(integers()),
|
||||
l -> l.size() < 200,
|
||||
631);
|
||||
504);
|
||||
assertEquals(200, failure.getMinimalCounterexample().getExampleValue().size());
|
||||
}
|
||||
|
||||
@@ -114,15 +115,31 @@ public class GeneratorTest extends PropertyCheckerTestCase {
|
||||
public void testBoolean() {
|
||||
PropertyFailure<List<Boolean>> failure = checkFalsified(listsOf(booleans()),
|
||||
l -> !l.contains(true) || !l.contains(false),
|
||||
4);
|
||||
3);
|
||||
assertEquals(2, failure.getMinimalCounterexample().getExampleValue().size());
|
||||
}
|
||||
|
||||
public void testShrinkingNonEmptyList() {
|
||||
PropertyFailure<List<Integer>> failure = checkFalsified(nonEmptyLists(integers(0, 100)),
|
||||
l -> !l.contains(42),
|
||||
10);
|
||||
4);
|
||||
assertEquals(1, failure.getMinimalCounterexample().getExampleValue().size());
|
||||
}
|
||||
|
||||
public void testRecheckWithGivenSeeds() {
|
||||
Generator<List<Integer>> gen = nonEmptyLists(integers(0, 100));
|
||||
Predicate<List<Integer>> property = l -> !l.contains(42);
|
||||
|
||||
PropertyFailure<?> failure = checkFails(PropertyChecker.forAll(gen), property).getFailure();
|
||||
assertTrue(failure.getIterationNumber() > 1);
|
||||
|
||||
PropertyFalsified e;
|
||||
|
||||
e = checkFails(PropertyChecker.forAll(gen).rechecking(failure.getIterationSeed(), failure.getSizeHint()), property);
|
||||
assertEquals(1, e.getFailure().getIterationNumber());
|
||||
|
||||
e = checkFails(PropertyChecker.forAll(gen).withSeed(failure.getGlobalSeed()), property);
|
||||
assertEquals(failure.getIterationNumber(), e.getFailure().getIterationNumber());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,25 +9,30 @@ import java.util.function.Predicate;
|
||||
*/
|
||||
abstract class PropertyCheckerTestCase extends TestCase {
|
||||
|
||||
protected <T> PropertyFailure<T> checkFalsified(Generator<T> generator, Predicate<T> predicate, int minimizationSteps) {
|
||||
protected <T> PropertyFalsified checkFails(PropertyChecker<T> checker, Predicate<T> predicate) {
|
||||
try {
|
||||
forAllStable(generator).shouldHold(predicate);
|
||||
checker.shouldHold(predicate);
|
||||
throw new AssertionError("Can't falsify " + getName());
|
||||
}
|
||||
catch (PropertyFalsified e) {
|
||||
//noinspection unchecked
|
||||
PropertyFailure<T> failure = (PropertyFailure<T>)e.getFailure();
|
||||
|
||||
System.out.println(" " + getName());
|
||||
System.out.println("Value: " + e.getBreakingValue());
|
||||
System.out.println("Data: " + e.getData());
|
||||
assertEquals(minimizationSteps, failure.getTotalMinimizationExampleCount());
|
||||
assertEquals(e.getBreakingValue(), generator.getGeneratorFunction().apply(e.getData()));
|
||||
|
||||
return failure;
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> PropertyFailure<T> checkFalsified(Generator<T> generator, Predicate<T> predicate, int minimizationSteps) {
|
||||
PropertyFalsified e = checkFails(forAllStable(generator), predicate);
|
||||
//noinspection unchecked
|
||||
PropertyFailure<T> failure = (PropertyFailure<T>)e.getFailure();
|
||||
|
||||
System.out.println(" " + getName());
|
||||
System.out.println("Value: " + e.getBreakingValue());
|
||||
System.out.println("Data: " + e.getData());
|
||||
assertEquals(minimizationSteps, failure.getTotalMinimizationExampleCount());
|
||||
assertEquals(e.getBreakingValue(), generator.getGeneratorFunction().apply(e.getData()));
|
||||
|
||||
return failure;
|
||||
}
|
||||
|
||||
protected static <T> PropertyChecker<T> forAllStable(Generator<T> generator) {
|
||||
return PropertyChecker.forAll(generator).withSeed(0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user