// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.testutils; import org.hamcrest.Description; import org.hamcrest.Factory; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; /** * Matches the root cause of a {@code Throwable}. * This has been rejected from JUnit developers (see https://github.com/junit-team/junit4/pull/778), * But this is really useful, thanks to pimterry for implementing it. * * @param <T> Throwable type * @see <a href="https://github.com/junit-team/junit4/pull/778">Github pull request</a> */ public class ThrowableRootCauseMatcher<T extends Throwable> extends TypeSafeMatcher<T> { private final Matcher<T> fMatcher; /** * Constructs a new {@code ThrowableRootCauseMatcher}. * @param matcher matcher */ public ThrowableRootCauseMatcher(Matcher<T> matcher) { fMatcher = matcher; } @Override public void describeTo(Description description) { description.appendText("exception with cause "); description.appendDescriptionOf(fMatcher); } @Override protected boolean matchesSafely(T item) { Throwable exception = item; while (exception.getCause() != null) { exception = exception.getCause(); } return fMatcher.matches(exception); } @Override protected void describeMismatchSafely(T item, Description description) { description.appendText("cause "); fMatcher.describeMismatch(item.getCause(), description); } /** * Returns a new {@code ThrowableRootCauseMatcher} instance. * @param <T> Throwable type * @param matcher matcher * @return new {@code ThrowableRootCauseMatcher} instance */ @Factory public static <T extends Throwable> Matcher<T> hasRootCause(final Matcher<T> matcher) { return new ThrowableRootCauseMatcher<>(matcher); } }