View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.commons.utils;
18  
19  import static org.apache.juneau.commons.utils.ThrowableUtils.*;
20  import static org.junit.jupiter.api.Assertions.*;
21  
22  import java.io.*;
23  import java.sql.*;
24  import java.util.Optional;
25  
26  import org.apache.juneau.*;
27  import org.apache.juneau.commons.reflect.*;
28  import org.junit.jupiter.api.*;
29  
30  class ThrowableUtils_Test extends TestBase {
31  
32  	//====================================================================================================
33  	// Constructor (line 29)
34  	//====================================================================================================
35  	@Test
36  	void a00_constructor() {
37  		// Test line 29: class instantiation
38  		// ThrowableUtils has an implicit public no-arg constructor
39  		var instance = new ThrowableUtils();
40  		assertNotNull(instance);
41  	}
42  
43  	//====================================================================================================
44  	// bex(Class<?>, String, Object...)
45  	//====================================================================================================
46  	@Test
47  	void a001_bex_withClass() {
48  		BeanRuntimeException ex = bex(String.class, "Error in class {0}", "TestClass");
49  		assertNotNull(ex);
50  		assertTrue(ex.getMessage().contains("Error in class TestClass"));
51  		assertTrue(ex.getMessage().contains("java.lang.String")); // Class name is in message
52  	}
53  
54  	//====================================================================================================
55  	// bex(String, Object...)
56  	//====================================================================================================
57  	@Test
58  	void a002_bex_withMessage() {
59  		BeanRuntimeException ex = bex("Error message {0}", "test");
60  		assertNotNull(ex);
61  		assertTrue(ex.getMessage().contains("Error message test"));
62  	}
63  
64  	//====================================================================================================
65  	// bex(Throwable)
66  	//====================================================================================================
67  	@Test
68  	void a003_bex_withCause() {
69  		var cause = new IOException("root cause");
70  		BeanRuntimeException ex = bex(cause);
71  		assertNotNull(ex);
72  		assertSame(cause, ex.getCause());
73  	}
74  
75  	//====================================================================================================
76  	// bex(Throwable, Class<?>, String, Object...)
77  	//====================================================================================================
78  	@Test
79  	void a004_bex_withCauseAndClass() {
80  		var cause = new IOException("root cause");
81  		BeanRuntimeException ex = bex(cause, String.class, "Error in {0}", "TestClass");
82  		assertNotNull(ex);
83  		assertSame(cause, ex.getCause());
84  		assertTrue(ex.getMessage().contains("Error in TestClass"));
85  		assertTrue(ex.getMessage().contains("java.lang.String")); // Class name is in message
86  	}
87  
88  	//====================================================================================================
89  	// bex(Throwable, String, Object...)
90  	//====================================================================================================
91  	@Test
92  	void a005_bex_withCauseAndMessage() {
93  		var cause = new IOException("root cause");
94  		// Note: bex(Throwable, String, ...) calls new BeanRuntimeException(f(msg, args), cause)
95  		// but BeanRuntimeException doesn't have a (String, Throwable) constructor,
96  		// so it matches BeanRuntimeException(String) and the cause is lost
97  		BeanRuntimeException ex = bex(cause, "Error message {0}", "test");
98  		assertNotNull(ex);
99  		// The cause is not preserved because BeanRuntimeException(String) constructor is used
100 		assertTrue(ex.getMessage().contains("Error message test"));
101 	}
102 
103 	//====================================================================================================
104 	// castException(Class<T>, Throwable)
105 	//====================================================================================================
106 	@Test
107 	void a006_castException() {
108 		// Test casting to same type
109 		var original = new IllegalArgumentException("test");
110 		IllegalArgumentException result = castException(IllegalArgumentException.class, original);
111 		assertSame(original, result);
112 
113 		// Test wrapping to different type
114 		var ioException = new IOException("io error");
115 		RuntimeException wrapped = castException(RuntimeException.class, ioException);
116 		assertNotNull(wrapped);
117 		assertSame(ioException, wrapped.getCause());
118 		assertInstanceOf(RuntimeException.class, wrapped);
119 
120 		// Test with exception that doesn't have Throwable constructor
121 		var npe = new NullPointerException("npe");
122 		// IllegalArgumentException has a Throwable constructor, so this should work
123 		IllegalArgumentException wrapped2 = castException(IllegalArgumentException.class, npe);
124 		assertNotNull(wrapped2);
125 		assertSame(npe, wrapped2.getCause());
126 	}
127 
128 	//====================================================================================================
129 	// findCause(Throwable, Class<T>)
130 	//====================================================================================================
131 	@Test
132 	void a007_findCause() {
133 		var rootCause = new IOException("root");
134 		var middleCause = new RuntimeException("middle", rootCause);
135 		var topException = new Exception("top", middleCause);
136 
137 		// Find IOException
138 		Optional<IOException> ioOpt = findCause(topException, IOException.class);
139 		assertTrue(ioOpt.isPresent());
140 		assertEquals("root", ioOpt.get().getMessage());
141 
142 		// Find RuntimeException
143 		Optional<RuntimeException> reOpt = findCause(topException, RuntimeException.class);
144 		assertTrue(reOpt.isPresent());
145 		assertEquals("middle", reOpt.get().getMessage());
146 
147 		// Find Exception (returns itself)
148 		Optional<Exception> exOpt = findCause(topException, Exception.class);
149 		assertTrue(exOpt.isPresent());
150 		assertEquals("top", exOpt.get().getMessage());
151 
152 		// Not found
153 		Optional<IllegalArgumentException> iaeOpt = findCause(topException, IllegalArgumentException.class);
154 		assertFalse(iaeOpt.isPresent());
155 
156 		// Null exception
157 		Optional<IOException> nullOpt = findCause(null, IOException.class);
158 		assertFalse(nullOpt.isPresent());
159 
160 		// Test with exception that has no cause
161 		var ex = new Exception("test");
162 		Optional<Exception> selfOpt = findCause(ex, Exception.class);
163 		assertTrue(selfOpt.isPresent());
164 		assertEquals("test", selfOpt.get().getMessage());
165 
166 		// Test long chain
167 		Throwable cause = new IllegalStateException("root");
168 		for (var i = 0; i < 10; i++) {
169 			cause = new RuntimeException("level" + i, cause);
170 		}
171 		Optional<IllegalStateException> rootOpt = findCause(cause, IllegalStateException.class);
172 		assertTrue(rootOpt.isPresent());
173 		assertEquals("root", rootOpt.get().getMessage());
174 	}
175 
176 	//====================================================================================================
177 	// getStackTrace(Throwable)
178 	//====================================================================================================
179 	@Test
180 	void a008_getStackTrace() {
181 		var ex = new RuntimeException("test exception");
182 		String stackTrace = getStackTrace(ex);
183 		assertNotNull(stackTrace);
184 		assertTrue(stackTrace.contains("RuntimeException"));
185 		assertTrue(stackTrace.contains("test exception"));
186 		assertTrue(stackTrace.contains("ThrowableUtils_Test") || stackTrace.contains("getStackTrace"));
187 	}
188 
189 	//====================================================================================================
190 	// getThrowableCause(Class<T>, Throwable)
191 	//====================================================================================================
192 	@Test
193 	void a009_getThrowableCause() {
194 		var rootCause = new IOException("root");
195 		var middleCause = new RuntimeException("middle", rootCause);
196 		var topException = new Exception("top", middleCause);
197 
198 		// Find IOException in cause chain
199 		IOException ioCause = getThrowableCause(IOException.class, topException);
200 		assertNotNull(ioCause);
201 		assertEquals("root", ioCause.getMessage());
202 
203 		// Find RuntimeException in cause chain
204 		RuntimeException reCause = getThrowableCause(RuntimeException.class, topException);
205 		assertNotNull(reCause);
206 		assertEquals("middle", reCause.getMessage());
207 
208 		// Not found
209 		IllegalArgumentException iaeCause = getThrowableCause(IllegalArgumentException.class, topException);
210 		assertNull(iaeCause);
211 
212 		// Null exception
213 		IOException nullCause = getThrowableCause(IOException.class, null);
214 		assertNull(nullCause);
215 
216 		// Exception with no cause
217 		var ex = new Exception("test");
218 		IOException noCause = getThrowableCause(IOException.class, ex);
219 		assertNull(noCause);
220 	}
221 
222 	//====================================================================================================
223 	// hash(Throwable, String)
224 	//====================================================================================================
225 	@Test
226 	void a010_hash() {
227 		var ex1 = new RuntimeException("test");
228 		var ex2 = new RuntimeException("test");
229 
230 		// Same exception type and stack trace should produce same hash
231 		int hash1 = hash(ex1, null);
232 		int hash2 = hash(ex2, null);
233 		// Note: hash might be different due to different stack traces, but should be consistent
234 		assertNotNull(hash1);
235 		assertNotNull(hash2);
236 
237 		// Test with stop class - covers line 180 (break when stopClass matches)
238 		// Use a class name that will be in the stack trace
239 		String testClassName = ThrowableUtils_Test.class.getName();
240 		int hash3 = hash(ex1, testClassName);
241 		assertNotNull(hash3);
242 
243 		// Test with stop class that doesn't match (should process all stack frames)
244 		int hash4 = hash(ex1, "java.lang.Object");
245 		assertNotNull(hash4);
246 
247 		// Test with nested exception
248 		var cause = new IOException("cause");
249 		var wrapped = new RuntimeException("wrapped", cause);
250 		int hash5 = hash(wrapped, null);
251 		assertNotNull(hash5);
252 
253 		// Test with null exception
254 		int hash6 = hash(null, null);
255 		assertEquals(0, hash6);
256 
257 		// Test with stop class matching a class in the cause chain - covers line 180
258 		int hash7 = hash(wrapped, "java.io.IOException");
259 		assertNotNull(hash7);
260 	}
261 
262 	//====================================================================================================
263 	// illegalArg(String, Object...)
264 	//====================================================================================================
265 	@Test
266 	void a011_illegalArg_withMessage() {
267 		IllegalArgumentException ex = illegalArg("Invalid parameter {0}", "userId");
268 		assertNotNull(ex);
269 		assertTrue(ex.getMessage().contains("Invalid parameter userId"));
270 		assertNull(ex.getCause());
271 	}
272 
273 	//====================================================================================================
274 	// illegalArg(Throwable)
275 	//====================================================================================================
276 	@Test
277 	void a012_illegalArg_withCause() {
278 		var cause = new IOException("root cause");
279 		IllegalArgumentException ex = illegalArg(cause);
280 		assertNotNull(ex);
281 		assertSame(cause, ex.getCause());
282 	}
283 
284 	//====================================================================================================
285 	// illegalArg(Throwable, String, Object...)
286 	//====================================================================================================
287 	@Test
288 	void a013_illegalArg_withCauseAndMessage() {
289 		var cause = new IOException("root cause");
290 		IllegalArgumentException ex = illegalArg(cause, "Invalid parameter {0}", "userId");
291 		assertNotNull(ex);
292 		assertSame(cause, ex.getCause());
293 		assertTrue(ex.getMessage().contains("Invalid parameter userId"));
294 	}
295 
296 	//====================================================================================================
297 	// ioex(String, Object...)
298 	//====================================================================================================
299 	@Test
300 	void a014_ioex_withMessage() {
301 		IOException ex = ioex("File not found: {0}", "/tmp/test.txt");
302 		assertNotNull(ex);
303 		assertTrue(ex.getMessage().contains("File not found: /tmp/test.txt"));
304 		assertNull(ex.getCause());
305 	}
306 
307 	//====================================================================================================
308 	// ioex(Throwable)
309 	//====================================================================================================
310 	@Test
311 	void a015_ioex_withCause() {
312 		var cause = new FileNotFoundException("config.xml");
313 		IOException ex = ioex(cause);
314 		assertNotNull(ex);
315 		assertSame(cause, ex.getCause());
316 	}
317 
318 	//====================================================================================================
319 	// ioex(Throwable, String, Object...)
320 	//====================================================================================================
321 	@Test
322 	void a016_ioex_withCauseAndMessage() {
323 		var cause = new FileNotFoundException("config.xml");
324 		IOException ex = ioex(cause, "Failed to load {0}", "configuration");
325 		assertNotNull(ex);
326 		assertSame(cause, ex.getCause());
327 		assertTrue(ex.getMessage().contains("Failed to load configuration"));
328 	}
329 
330 	//====================================================================================================
331 	// lm(Throwable)
332 	//====================================================================================================
333 	@Test
334 	void a017_lm() {
335 		var ex = new RuntimeException("test message");
336 		String localized = lm(ex);
337 		assertEquals("test message", localized);
338 		assertEquals(ex.getLocalizedMessage(), localized);
339 	}
340 
341 	//====================================================================================================
342 	// rex(String, Object...)
343 	//====================================================================================================
344 	@Test
345 	void a018_rex_withMessage() {
346 		RuntimeException ex = rex("Error message {0}", "test");
347 		assertNotNull(ex);
348 		assertTrue(ex.getMessage().contains("Error message test"));
349 		assertNull(ex.getCause());
350 	}
351 
352 	//====================================================================================================
353 	// rex(Throwable)
354 	//====================================================================================================
355 	@Test
356 	void a019_rex_withCause() {
357 		var cause = new SQLException("Database error");
358 		RuntimeException ex = rex(cause);
359 		assertNotNull(ex);
360 		assertSame(cause, ex.getCause());
361 	}
362 
363 	//====================================================================================================
364 	// rex(Throwable, String, Object...)
365 	//====================================================================================================
366 	@Test
367 	void a020_rex_withCauseAndMessage() {
368 		var cause = new SQLException("Database error");
369 		RuntimeException ex = rex(cause, "Failed to process {0} at {1}", "user", "login");
370 		assertNotNull(ex);
371 		assertSame(cause, ex.getCause());
372 		assertTrue(ex.getMessage().contains("Failed to process user at login"));
373 	}
374 
375 	//====================================================================================================
376 	// toRex(Throwable)
377 	//====================================================================================================
378 	@Test
379 	void a021_toRex() {
380 		// Test with RuntimeException (should return same)
381 		var re = new RuntimeException("test");
382 		RuntimeException result1 = toRex(re);
383 		assertSame(re, result1);
384 
385 		// Test with IOException (should wrap)
386 		var io = new IOException("io error");
387 		RuntimeException result2 = toRex(io);
388 		assertNotNull(result2);
389 		assertSame(io, result2.getCause());
390 		assertInstanceOf(RuntimeException.class, result2);
391 
392 		// Test with Exception (should wrap)
393 		var ex = new Exception("exception");
394 		RuntimeException result3 = toRex(ex);
395 		assertNotNull(result3);
396 		assertSame(ex, result3.getCause());
397 		assertInstanceOf(RuntimeException.class, result3);
398 	}
399 
400 	//====================================================================================================
401 	// unsupportedOp()
402 	//====================================================================================================
403 	@Test
404 	void a022_unsupportedOp() {
405 		UnsupportedOperationException ex = unsupportedOp();
406 		assertNotNull(ex);
407 		assertEquals("Not supported.", ex.getMessage());
408 		assertNull(ex.getCause());
409 	}
410 
411 	//====================================================================================================
412 	// unsupportedOp(String, Object...)
413 	//====================================================================================================
414 	@Test
415 	void a023_unsupportedOp_withMessage() {
416 		UnsupportedOperationException ex = unsupportedOp("Operation {0} is not supported for type {1}", "delete", "User");
417 		assertNotNull(ex);
418 		assertTrue(ex.getMessage().contains("Operation delete is not supported for type User"));
419 		assertNull(ex.getCause());
420 	}
421 
422 	//====================================================================================================
423 	// unsupportedOp(Throwable)
424 	//====================================================================================================
425 	@Test
426 	void a024_unsupportedOp_withCause() {
427 		var cause = new IllegalStateException("Locked");
428 		UnsupportedOperationException ex = unsupportedOp(cause);
429 		assertNotNull(ex);
430 		assertSame(cause, ex.getCause());
431 	}
432 
433 	//====================================================================================================
434 	// unsupportedOp(Throwable, String, Object...)
435 	//====================================================================================================
436 	@Test
437 	void a025_unsupportedOp_withCauseAndMessage() {
438 		var cause = new IllegalStateException("Locked");
439 		UnsupportedOperationException ex = unsupportedOp(cause, "Cannot {0} on {1}", "delete", "immutable collection");
440 		assertNotNull(ex);
441 		assertSame(cause, ex.getCause());
442 		assertTrue(ex.getMessage().contains("Cannot delete on immutable collection"));
443 	}
444 
445 	//====================================================================================================
446 	// unsupportedOpReadOnly()
447 	//====================================================================================================
448 	@Test
449 	void a026_unsupportedOpReadOnly() {
450 		UnsupportedOperationException ex = unsupportedOpReadOnly();
451 		assertNotNull(ex);
452 		assertEquals("Object is read only.", ex.getMessage());
453 		assertNull(ex.getCause());
454 	}
455 
456 	//====================================================================================================
457 	// exex(String, Object...)
458 	//====================================================================================================
459 	@Test
460 	void a027_exex_withMessage() {
461 		ExecutableException ex = exex("Error message {0}", "test");
462 		assertNotNull(ex);
463 		assertTrue(ex.getMessage().contains("Error message test"));
464 		assertNull(ex.getCause());
465 	}
466 
467 	//====================================================================================================
468 	// exex(Throwable)
469 	//====================================================================================================
470 	@Test
471 	void a028_exex_withCause() {
472 		var cause = new IOException("root cause");
473 		ExecutableException ex = exex(cause);
474 		assertNotNull(ex);
475 		assertSame(cause, ex.getCause());
476 	}
477 
478 	//====================================================================================================
479 	// exex(Throwable, String, Object...)
480 	//====================================================================================================
481 	@Test
482 	void a029_exex_withCauseAndMessage() {
483 		var cause = new IOException("root cause");
484 		ExecutableException ex = exex(cause, "Error message {0}", "test");
485 		assertNotNull(ex);
486 		assertSame(cause, ex.getCause());
487 		assertTrue(ex.getMessage().contains("Error message test"));
488 	}
489 }
490