001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.assertions; 014 015import java.util.function.*; 016 017import org.apache.juneau.internal.*; 018 019/** 020 * Used for fluent assertion calls against throwables. 021 * 022 * @param <R> The return type. 023 */ 024@FluentSetters(returns="FluentThrowableAssertion<R>") 025public class FluentThrowableAssertion<R> extends FluentAssertion<R> { 026 027 private final Throwable value; 028 029 /** 030 * Constructor. 031 * 032 * @param value The throwable being tested. 033 * @param returns The object to return after the test. 034 */ 035 public FluentThrowableAssertion(Throwable value, R returns) { 036 this(null, value, returns); 037 } 038 039 /** 040 * Constructor. 041 * 042 * @param creator The assertion that created this assertion. 043 * @param value The throwable being tested. 044 * @param returns The object to return after the test. 045 */ 046 public FluentThrowableAssertion(Assertion creator, Throwable value, R returns) { 047 super(creator, returns); 048 this.value = value; 049 } 050 051 /** 052 * Asserts that this throwable is of the specified type. 053 * 054 * <h5 class='section'>Example:</h5> 055 * <p class='bcode w800'> 056 * <jc>// Asserts that the specified method throws a RuntimeException. </jc> 057 * ThrowableAssertion.<jsm>assertThrown</jsm>(() -> {foo.getBar();}) 058 * .isType(RuntimeException.<jk>class</jk>); 059 * </p> 060 * 061 * @param type The type. 062 * @return This object (for method chaining). 063 */ 064 public R isType(Class<?> type) { 065 assertNotNull("type", type); 066 if (! type.isInstance(value)) 067 throw error("Exception was not expected type.\n\tExpected=[{0}]\n\tActual=[{1}]", className(type), className(value)); 068 return returns(); 069 } 070 071 /** 072 * Asserts that this throwable or any parent throwables contains all of the specified substrings. 073 * 074 * <h5 class='section'>Example:</h5> 075 * <p class='bcode w800'> 076 * <jc>// Asserts that the specified method throws an exception with 'foobar' somewhere in the messages. </jc> 077 * ThrowableAssertion.<jsm>assertThrown</jsm>(() -> {foo.getBar();}).contains(<js>"foobar"</js>); 078 * </p> 079 * 080 * @param substrings The substrings to check for. 081 * @return This object (for method chaining). 082 */ 083 public R contains(String...substrings) { 084 assertNotNull("substrings", substrings); 085 exists(); 086 for (String substring : substrings) { 087 if (substring != null) { 088 Throwable e2 = value; 089 boolean found = false; 090 while (e2 != null && ! found) { 091 found |= StringUtils.contains(e2.getMessage(), substring); 092 e2 = e2.getCause(); 093 } 094 if (! found) { 095 throw error("Exception message did not contain expected substring.\n\tSubstring=[{0}]\n\tText=[{1}]", substring, value.getMessage()); 096 } 097 } 098 } 099 return returns(); 100 } 101 102 /** 103 * Asserts that this throwable has the specified message. 104 * 105 * <h5 class='section'>Example:</h5> 106 * <p class='bcode w800'> 107 * <jc>// Asserts that the specified method throws an exception with the message 'foobar'.</jc> 108 * ThrowableAssertion.<jsm>assertThrown</jsm>(() -> {foo.getBar();}).is(<js>"foobar"</js>); 109 * </p> 110 * 111 * @param msg The message to check for. 112 * @return This object (for method chaining). 113 */ 114 public R is(String msg) { 115 return message().is(msg); 116 } 117 118 /** 119 * Asserts that this throwable exists. 120 * 121 * <h5 class='section'>Example:</h5> 122 * <p class='bcode w800'> 123 * <jc>// Asserts that the specified method throws any exception.</jc> 124 * ThrowableAssertion.<jsm>assertThrown</jsm>(() -> {foo.getBar();}).exists(); 125 * </p> 126 * 127 * @return This object (for method chaining). 128 */ 129 public R exists() { 130 if (value == null) 131 throw error("Exception was not thrown."); 132 return returns(); 133 } 134 135 /** 136 * Asserts that this throwable doesn't exist. 137 * 138 * <h5 class='section'>Example:</h5> 139 * <p class='bcode w800'> 140 * <jc>// Asserts that the specified method doesn't throw any exception.</jc> 141 * ThrowableAssertion.<jsm>assertThrown</jsm>(() -> {foo.getBar();}).notExists(); 142 * </p> 143 * 144 * @return This object (for method chaining). 145 */ 146 public R doesNotExist() { 147 if (value != null) 148 throw error("Exception was thrown."); 149 return returns(); 150 } 151 152 /** 153 * Asserts that the value passes the specified predicate test. 154 * 155 * @param test The predicate to use to test the value. 156 * @return The response object (for method chaining). 157 * @throws AssertionError If assertion failed. 158 */ 159 public R passes(Predicate<Throwable> test) throws AssertionError { 160 if (! test.test(value)) 161 throw error("Value did not pass predicate test.\n\tValue=[{0}]", value); 162 return returns(); 163 } 164 165 /** 166 * Asserts that the value passes the specified predicate test. 167 * 168 * @param c The class to cast to for the predicate. 169 * @param <T> The class to cast to for the predicate. 170 * @param test The predicate to use to test the value. 171 * @return The response object (for method chaining). 172 * @throws AssertionError If assertion failed. 173 */ 174 @SuppressWarnings("unchecked") 175 public <T extends Throwable> R passes(Class<T> c, Predicate<T> test) throws AssertionError { 176 isType(c); 177 if (! test.test((T) value)) 178 throw error("Value did not pass predicate test.\n\tValue=[{0}]", value); 179 return returns(); 180 } 181 182 /** 183 * Returns an assertion against the throwable message. 184 * 185 * <h5 class='section'>Example:</h5> 186 * <p class='bcode w800'> 187 * <jc>// Asserts that the specified method throws an exception with 'foobar' somewhere in the messages. </jc> 188 * ThrowableAssertion.<jsm>assertThrown</jsm>(() -> {foo.getBar();}).message().matches(<js>".*foobar.*"</js>); 189 * </p> 190 * 191 * @return An assertion against the throwable message. Never <jk>null</jk>. 192 */ 193 public FluentStringAssertion<R> message() { 194 return new FluentStringAssertion<>(this, value == null ? null : value.getMessage(), returns()); 195 } 196 197 /** 198 * Returns an assertion against the throwable localized message. 199 * 200 * <h5 class='section'>Example:</h5> 201 * <p class='bcode w800'> 202 * <jc>// Asserts that the specified method throws an exception with 'foobar' somewhere in the localized messages. </jc> 203 * ThrowableAssertion.<jsm>assertThrown</jsm>(() -> {foo.getBar();}).localizedMessage().matches(<js>".*foobar.*"</js>); 204 * </p> 205 * 206 * @return An assertion against the throwable localized message. Never <jk>null</jk>. 207 */ 208 public FluentStringAssertion<R> localizedMessage() { 209 return new FluentStringAssertion<>(this, value == null ? null : value.getLocalizedMessage(), returns()); 210 } 211 212 /** 213 * Returns an assertion against the throwable localized message. 214 * 215 * <h5 class='section'>Example:</h5> 216 * <p class='bcode w800'> 217 * <jc>// Asserts that the specified method throws an exception with 'foobar' somewhere in the stack trace. </jc> 218 * ThrowableAssertion.<jsm>assertThrown</jsm>(() -> {foo.getBar();}).stackTrace().contains(<js>"foobar"</js>); 219 * </p> 220 * 221 * @return An assertion against the throwable stacktrace. Never <jk>null</jk>. 222 */ 223 public FluentStringAssertion<R> stackTrace() { 224 return new FluentStringAssertion<>(this, value == null ? null : StringUtils.getStackTrace(value), returns()); 225 } 226 227 /** 228 * Returns an assertion against the caused-by throwable. 229 * 230 * <h5 class='section'>Example:</h5> 231 * <p class='bcode w800'> 232 * <jc>// Asserts that the specified method throws an exception whose caused-by message contains 'foobar'. </jc> 233 * ThrowableAssertion.<jsm>assertThrown</jsm>(() -> {foo.getBar();}).causedBy().message().contains(<js>"foobar"</js>); 234 * </p> 235 * 236 * @return An assertion against the caused-by. Never <jk>null</jk>. 237 */ 238 public FluentThrowableAssertion<R> causedBy() { 239 return new FluentThrowableAssertion<>(this, value == null ? null : value.getCause(), returns()); 240 } 241 242 /** 243 * Returns an assertion against the throwable localized message. 244 * 245 * <h5 class='section'>Example:</h5> 246 * <p class='bcode w800'> 247 * <jc>// Asserts that the specified method throws an exception with a caused-by RuntimeException containing 'foobar'</jc> 248 * ThrowableAssertion.<jsm>assertThrown</jsm>(() -> {foo.getBar();}).causedBy(RuntimeException.<jk>class</jk>).exists().contains(<js>"foobar"</js>); 249 * </p> 250 * 251 * @param throwableClass The class type to search for in the caused-by chain. 252 * @return An assertion against the caused-by throwable. Never <jk>null</jk>. 253 */ 254 public FluentThrowableAssertion<R> find(Class<?> throwableClass) { 255 Throwable t = value; 256 while (t != null) { 257 if (throwableClass.isInstance(t)) 258 return new FluentThrowableAssertion<>(this, t, returns()); 259 t = t.getCause(); 260 } 261 return new FluentThrowableAssertion<>(this, null, returns()); 262 } 263 264 // <FluentSetters> 265 266 @Override /* GENERATED - Assertion */ 267 public FluentThrowableAssertion<R> msg(String msg, Object...args) { 268 super.msg(msg, args); 269 return this; 270 } 271 272 @Override /* GENERATED - Assertion */ 273 public FluentThrowableAssertion<R> stderr() { 274 super.stderr(); 275 return this; 276 } 277 278 @Override /* GENERATED - Assertion */ 279 public FluentThrowableAssertion<R> stdout() { 280 super.stdout(); 281 return this; 282 } 283 284 // </FluentSetters> 285}