001/* 002 * SonarQube 003 * Copyright (C) 2009-2016 SonarSource SA 004 * mailto:contact AT sonarsource DOT com 005 * 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public License 017 * along with this program; if not, write to the Free Software Foundation, 018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 019 */ 020package org.sonar.test; 021 022import java.util.Objects; 023import javax.annotation.CheckForNull; 024import javax.annotation.Nullable; 025import javax.annotation.concurrent.Immutable; 026import org.hamcrest.Description; 027import org.hamcrest.Matcher; 028import org.hamcrest.TypeSafeMatcher; 029 030/** 031 * Matchers designed to be used as an argument of {@link org.junit.rules.ExpectedException#expectCause(Matcher)} such as: 032 * 033 * <pre> 034 * expectedException.expect(VisitException.class); 035 * expectedException.expectCause(hasType(IllegalArgumentException.class).andMessage("file and otherFile Components can not be the same")); 036 * </pre> 037 * 038 * Class strongly inspired from {@code CauseMatcher} class from {@code http://blog.codeleak.pl/2014/03/junit-expectedexception-rule-beyond.html} 039 */ 040@Immutable 041public class ExceptionCauseMatcher extends TypeSafeMatcher<Throwable> { 042 private static final String EXPECT_NO_MESSAGE_CONSTANT = "RQXG8QTUCXOT7HZ3APPKBKUE5"; 043 044 private final Class<? extends Throwable> type; 045 @CheckForNull 046 private final String expectedMessage; 047 048 private ExceptionCauseMatcher(Class<? extends Throwable> type, @Nullable String expectedMessage) { 049 this.type = type; 050 this.expectedMessage = expectedMessage; 051 } 052 053 public static ExceptionCauseMatcher hasType(Class<? extends Throwable> type) { 054 return new ExceptionCauseMatcher(type, null); 055 } 056 057 public ExceptionCauseMatcher andMessage(String expectedMessage) { 058 return new ExceptionCauseMatcher(type, Objects.requireNonNull(expectedMessage)); 059 } 060 061 public ExceptionCauseMatcher andNoMessage() { 062 return new ExceptionCauseMatcher(type, EXPECT_NO_MESSAGE_CONSTANT); 063 } 064 065 @Override 066 protected boolean matchesSafely(Throwable item) { 067 if (!item.getClass().isAssignableFrom(type)) { 068 return false; 069 } 070 if (expectedMessage == null) { 071 return true; 072 } 073 if (EXPECT_NO_MESSAGE_CONSTANT.equals(expectedMessage)) { 074 return item.getMessage() == null; 075 } 076 return item.getMessage().contains(expectedMessage); 077 } 078 079 @Override 080 public void describeTo(Description description) { 081 description.appendText("of type ") 082 .appendValue(type); 083 if (EXPECT_NO_MESSAGE_CONSTANT.equals(expectedMessage)) { 084 description.appendText(" and no message"); 085 } else if (expectedMessage != null) { 086 description.appendText(" and message ") 087 .appendValue(expectedMessage); 088 } 089 description.appendText(" but"); 090 } 091}