/*
* Copyright (c) 2014 Google, Inc.
*
* 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.
*/
package com.google.common.truth;
import javax.annotation.Nullable;
/**
* Propositions for {@link Throwable} subjects.
*
* @author Kurt Alfred Kluever
*/
public final class ThrowableSubject extends Subject<ThrowableSubject, Throwable> {
static ThrowableSubject create(FailureStrategy failureStrategy, @Nullable Throwable throwable) {
return new ThrowableSubject(causeInsertingStrategy(failureStrategy, throwable), throwable);
}
private ThrowableSubject(FailureStrategy failureStrategy, @Nullable Throwable throwable) {
super(failureStrategy, throwable);
}
/*
* TODO(cpovirk): consider a special case for isEqualTo and isSameAs that adds |expected| as a
* suppressed exception
*/
/**
* Fails if the subject does not have the given message.
*
* @deprecated Use {@code hasMessageThat().isEqualTo(expected)} instead. You may also consider
* using inexact matching of the message (e.g. {@code hasMessageThat().contains(substring)})
* for less brittle tests.
*/
@Deprecated
public void hasMessage(@Nullable String expected) {
hasMessageThat().isEqualTo(expected);
}
/** Returns a {@code StringSubject} to make assertions about the throwable's message. */
public StringSubject hasMessageThat() {
return new StringSubject(badMessageStrategy(failureStrategy), actual().getMessage());
}
/**
* Returns a new {@code ThrowableSubject} that supports assertions on this throwable's direct
* cause. This method can be invoked repeatedly (e.g. {@code
* assertThat(e).hasCauseThat().hasCauseThat()....} to assert on a particular indirect cause.
*/
public ThrowableSubject hasCauseThat() {
// provides a more helpful error message if hasCauseThat() methods are chained too deep
// e.g. assertThat(new Exception()).hCT().hCT()....
// TODO(diamondm) in keeping with other subjects' behavior this should still NPE if the subject
// *itself* is null, since there's no context to lose. See also b/37645583
if (actual() == null) {
failWithRawMessage("Causal chain is not deep enough - add a .isNotNull() check?");
}
return new ThrowableSubject(badCauseStrategy(failureStrategy), actual().getCause());
}
private static FailureStrategy causeInsertingStrategy(
final FailureStrategy delegate, final Throwable defaultCause) {
return new FailureStrategy() {
@Override
public void fail(String message) {
delegate.fail(message, defaultCause);
}
@Override
public void fail(String message, Throwable cause) {
delegate.fail(message, cause);
// TODO(cpovirk): add defaultCause as a suppressed exception? If fail() throws...
}
@Override
public void failComparing(String message, CharSequence expected, CharSequence actual) {
delegate.failComparing(message, expected, actual, defaultCause);
}
@Override
public void failComparing(
String message, CharSequence expected, CharSequence actual, Throwable cause) {
delegate.failComparing(message, expected, actual, cause);
// TODO(cpovirk): add defaultCause as a suppressed exception? If failComparing() throws...
}
};
}
private FailureStrategy badMessageStrategy(final FailureStrategy delegate) {
return new FailureStrategy() {
private String prependMessage(String message) {
String name = actual().getClass().getName();
if (internalCustomName() != null) {
name = internalCustomName() + "(" + name + ")";
}
return "Unexpected message for " + name + ":" + (message.isEmpty() ? "" : " " + message);
}
@Override
public void fail(String message) {
delegate.fail(prependMessage(message));
}
@Override
public void fail(String message, Throwable cause) {
delegate.fail(prependMessage(message), cause);
}
@Override
public void failComparing(String message, CharSequence expected, CharSequence actual) {
delegate.failComparing(prependMessage(message), expected, actual);
}
@Override
public void failComparing(
String message, CharSequence expected, CharSequence actual, Throwable cause) {
delegate.failComparing(prependMessage(message), expected, actual, cause);
}
};
}
private FailureStrategy badCauseStrategy(final FailureStrategy delegate) {
return new FailureStrategy() {
private String prependMessage(String message) {
String name = actual().getClass().getName();
if (internalCustomName() != null) {
name = internalCustomName() + "(" + name + ")";
}
return "Unexpected cause for " + name + ":" + (message.isEmpty() ? "" : " " + message);
}
@Override
public void fail(String message) {
delegate.fail(prependMessage(message));
}
@Override
public void fail(String message, Throwable cause) {
delegate.fail(prependMessage(message), cause);
}
@Override
public void failComparing(String message, CharSequence expected, CharSequence actual) {
delegate.failComparing(prependMessage(message), expected, actual);
}
@Override
public void failComparing(
String message, CharSequence expected, CharSequence actual, Throwable cause) {
delegate.failComparing(prependMessage(message), expected, actual, cause);
}
};
}
}