From 737b66217990d4b59d4b61a491bc04be243bea19 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 16 Aug 2017 09:23:35 +0200 Subject: [PATCH] jetCheck: when property fails, report useful things closer to the top of the stack trace --- jetCheck/src/jetCheck/PropertyFalsified.java | 58 +++++++++++++++++--- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/jetCheck/src/jetCheck/PropertyFalsified.java b/jetCheck/src/jetCheck/PropertyFalsified.java index abb0edd35518..034a1221ab66 100644 --- a/jetCheck/src/jetCheck/PropertyFalsified.java +++ b/jetCheck/src/jetCheck/PropertyFalsified.java @@ -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 data; + private final String message; PropertyFalsified(PropertyFailureImpl failure, Supplier 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 ""; + } + } + private String getMinimizationStats() { int exampleCount = failure.getTotalMinimizationExampleCount(); if (exampleCount == 0) return "";