jetCheck: when property fails, report useful things closer to the top of the stack trace

This commit is contained in:
peter
2017-08-16 09:23:35 +02:00
parent ef9f5c4740
commit 737b662179
+49 -9
View File
@@ -9,41 +9,81 @@ import java.util.function.Supplier;
*/
@SuppressWarnings("ExceptionClassNameDoesntEndWithException")
public class PropertyFalsified extends RuntimeException {
static final String FAILURE_REASON_HAS_CHANGED_DURING_MINIMIZATION = "!!! FAILURE REASON HAS CHANGED DURING MINIMIZATION !!!";
static final String FAILURE_REASON_HAS_CHANGED_DURING_MINIMIZATION = "Failure reason has changed during minimization, see initial failing example below";
private static final String SEPARATOR = "\n==========================\n";
private final PropertyFailureImpl<?> failure;
private final Supplier<DataStructure> data;
private final String message;
PropertyFalsified(PropertyFailureImpl<?> failure, Supplier<DataStructure> data) {
super(failure.getMinimalCounterexample().getExceptionCause());
this.failure = failure;
this.data = data;
this.message = calcMessage();
}
@Override
public String getMessage() {
String msg = "Falsified on " + failure.getMinimalCounterexample().getExampleValue() + "\n" +
return message;
}
private String calcMessage() {
StringBuilder traceBuilder = new StringBuilder();
String msg = "Falsified on " + valueToString(failure.getMinimalCounterexample(), traceBuilder) + "\n" +
getMinimizationStats() +
failure.iteration.printToReproduce();
Throwable failureReason = failure.getMinimalCounterexample().getExceptionCause();
if (failureReason != null) {
Throwable rootCause = getRootCause(failureReason);
appendTrace(traceBuilder,
rootCause == failureReason ? "Property failure reason: " : "Property failure reason, innermost exception (see full trace below): ",
rootCause);
}
if (failure.getStoppingReason() != null) {
msg += "\n Shrinking stopped because of " + StatusNotifier.printStackTrace(failure.getStoppingReason());
msg += SEPARATOR;
msg += "\n Minimization stopped prematurely, see the reason below.";
appendTrace(traceBuilder, "An unexpected exception happened during minimization: ", failure.getStoppingReason());
}
Throwable first = failure.getFirstCounterExample().getExceptionCause();
if (exceptionsDiffer(first, failure.getMinimalCounterexample().getExceptionCause())) {
msg += "\n " + FAILURE_REASON_HAS_CHANGED_DURING_MINIMIZATION;
msg += "\n Initial value: " + failure.getFirstCounterExample().getExampleValue() + "\n";
if (first != null) {
msg += "\n Initial exception: " + StatusNotifier.printStackTrace(first) + SEPARATOR;
StringBuilder secondaryTrace = new StringBuilder();
traceBuilder.append("\n Initial value: ").append(valueToString(failure.getFirstCounterExample(), secondaryTrace));
if (first == null) {
traceBuilder.append("\n Initially property was falsified without exceptions");
traceBuilder.append(secondaryTrace);
} else {
msg += "\n Initially property was falsified without exceptions\n";
traceBuilder.append(secondaryTrace);
appendTrace(traceBuilder, "Initially failed because of ", first);
}
}
return msg;
return msg + traceBuilder;
}
private static Throwable getRootCause(Throwable t) {
while (t.getCause() != null) {
t = t.getCause();
}
return t;
}
private static void appendTrace(StringBuilder traceBuilder, String prefix, Throwable e) {
traceBuilder.append("\n ").append(prefix).append(StatusNotifier.printStackTrace(e)).append(SEPARATOR);
}
private static String valueToString(CounterExampleImpl<?> example, StringBuilder traceBuilder) {
try {
return String.valueOf(example.getExampleValue());
}
catch (Throwable e) {
appendTrace(traceBuilder, "Exception during toString evaluation: ", e);
return "<can't evaluate toString(), see exception below>";
}
}
private String getMinimizationStats() {
int exampleCount = failure.getTotalMinimizationExampleCount();
if (exampleCount == 0) return "";