com.fitbur.assertj.internal.Failures Maven / Gradle / Ivy
/**
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* Copyright 2012-2016 the original author or authors.
*/
package com.fitbur.assertj.internal;
import static java.lang.String.format;
import static com.fitbur.assertj.util.Strings.isNullOrEmpty;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import com.fitbur.assertj.api.AssertionInfo;
import com.fitbur.assertj.description.Description;
import com.fitbur.assertj.error.AssertionErrorFactory;
import com.fitbur.assertj.error.ErrorMessageFactory;
import com.fitbur.assertj.error.MessageFormatter;
import com.fitbur.assertj.error.ShouldBeEqual;
import com.fitbur.assertj.util.Throwables;
import com.fitbur.assertj.util.VisibleForTesting;
/**
* Failure actions.
*
* @author Yvonne Wang
* @author Alex Ruiz
*/
public class Failures {
private static final String LINE_SEPARATOR = com.fitbur.assertj.util.Compatibility.System.lineSeparator();
private static final Failures INSTANCE = new Failures();
/**
* flag indicating that in case of a failure a threaddump is printed out.
*/
private boolean printThreadDump = false;
/**
* Returns the singleton instance of this class.
*
* @return the singleton instance of this class.
*/
public static Failures instance() {
return INSTANCE;
}
/**
* flag indicating whether or not we remove elements related to AssertJ from assertion error stack trace.
*/
private boolean removeAssertJRelatedElementsFromStackTrace = true;
/**
* Sets whether we remove elements related to AssertJ from assertion error stack trace.
*
* @param removeAssertJRelatedElementsFromStackTrace flag
*/
public void setRemoveAssertJRelatedElementsFromStackTrace(boolean removeAssertJRelatedElementsFromStackTrace) {
this.removeAssertJRelatedElementsFromStackTrace = removeAssertJRelatedElementsFromStackTrace;
}
@VisibleForTesting
Failures() {
}
/**
* Creates a {@link AssertionError}
following this pattern:
*
* - creates a
{@link AssertionError}
using {@link AssertionInfo#overridingErrorMessage()}
* as the error message if such value is not {@code null}, or
* - uses the given
{@link AssertionErrorFactory}
to create an {@link AssertionError}
,
* prepending the value of {@link AssertionInfo#description()}
to the error message
*
*
* @param info contains information about the failed assertion.
* @param factory knows how to create {@code AssertionError}s.
* @return the created {@link AssertionError}
.
*/
public AssertionError failure(AssertionInfo info, AssertionErrorFactory factory) {
AssertionError error = failureIfErrorMessageIsOverridden(info);
if (error != null) return error;
printThreadDumpIfNeeded();
return factory.newAssertionError(info.description(), info.representation());
}
/**
* Creates a {@link AssertionError}
following this pattern:
*
* - creates a
{@link AssertionError}
using {@link AssertionInfo#overridingErrorMessage()}
* as the error message if such value is not {@code null}, or
* - uses the given
{@link ErrorMessageFactory}
to create the detail message of the
* {@link AssertionError}
, prepending the value of {@link AssertionInfo#description()}
to
* the error message
*
*
* @param info contains information about the failed assertion.
* @param message knows how to create detail messages for {@code AssertionError}s.
* @return the created {@link AssertionError}
.
*/
public AssertionError failure(AssertionInfo info, ErrorMessageFactory message) {
AssertionError error = failureIfErrorMessageIsOverridden(info);
if (error != null) return error;
AssertionError assertionError = new AssertionError(message.create(info.description(), info.representation()));
removeAssertJRelatedElementsFromStackTraceIfNeeded(assertionError);
printThreadDumpIfNeeded();
return assertionError;
}
public AssertionError failureIfErrorMessageIsOverridden(AssertionInfo info) {
String overridingErrorMessage = info.overridingErrorMessage();
return isNullOrEmpty(overridingErrorMessage) ? null :
failure(MessageFormatter.instance().format(info.description(), info.representation(), overridingErrorMessage));
}
/**
* Creates a {@link AssertionError}
using the given {@code String} as message.
*
* It filters the AssertionError stack trace be default, to have full stack trace use
* {@link #setRemoveAssertJRelatedElementsFromStackTrace(boolean)}.
*
* @param message the message of the {@code AssertionError} to create.
* @return the created {@link AssertionError}
.
*/
public AssertionError failure(String message) {
AssertionError assertionError = new AssertionError(message);
removeAssertJRelatedElementsFromStackTraceIfNeeded(assertionError);
printThreadDumpIfNeeded();
return assertionError;
}
private void printThreadDumpIfNeeded() {
if (printThreadDump) System.err.println(threadDumpDescription());
}
/**
* If is {@link #removeAssertJRelatedElementsFromStackTrace} is true, it filters the stack trace of the given {@link AssertionError}
* by removing stack trace elements related to AssertJ in order to get a more readable stack trace.
*
* See example below :
*
--------------- stack trace not filtered -----------------
org.junit.ComparisonFailure: expected:<'[Ronaldo]'> but was:<'[Messi]'>
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:501)
at com.fitbur.assertj.error.ConstructorInvoker.newInstance(ConstructorInvoker.java:34)
at com.fitbur.assertj.error.ShouldBeEqual.newComparisonFailure(ShouldBeEqual.java:111)
at com.fitbur.assertj.error.ShouldBeEqual.comparisonFailure(ShouldBeEqual.java:103)
at com.fitbur.assertj.error.ShouldBeEqual.newAssertionError(ShouldBeEqual.java:81)
at com.fitbur.assertj.internal.Failures.failure(Failures.java:76)
at com.fitbur.assertj.internal.Objects.assertEqual(Objects.java:116)
at com.fitbur.assertj.api.AbstractAssert.isEqualTo(AbstractAssert.java:74)
at examples.StackTraceFilterExample.main(StackTraceFilterExample.java:13)
--------------- stack trace filtered -----------------
org.junit.ComparisonFailure: expected:<'[Ronaldo]'> but was:<'[Messi]'>
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at examples.StackTraceFilterExample.main(StackTraceFilterExample.java:20)
*
* Method is public because we need to call it from {@link ShouldBeEqual#newAssertionError(Description, com.fitbur.assertj.presentation.Representation)} that is building a junit ComparisonFailure by reflection.
*
* @param assertionError the {@code AssertionError} to filter stack trace if option is set.
*/
public void removeAssertJRelatedElementsFromStackTraceIfNeeded(AssertionError assertionError) {
if (removeAssertJRelatedElementsFromStackTrace) {
Throwables.removeAssertJRelatedElementsFromStackTrace(assertionError);
}
}
/**
* Set the flag indicating that in case of a failure a threaddump is printed out.
*/
public void enablePrintThreadDump() {
printThreadDump = true;
}
private String threadDumpDescription() {
StringBuilder threadDumpDescription = new StringBuilder();
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(true, true);
for (ThreadInfo threadInfo : threadInfos) {
threadDumpDescription.append(format("\"%s\"%n\tjava.lang.Thread.State: %s",
threadInfo.getThreadName(), threadInfo.getThreadState()));
for (StackTraceElement stackTraceElement : threadInfo.getStackTrace()) {
threadDumpDescription.append(LINE_SEPARATOR + "\t\tat " + stackTraceElement);
}
threadDumpDescription.append(LINE_SEPARATOR + LINE_SEPARATOR);
}
return threadDumpDescription.toString();
}
}