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    *      https://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.commons.lang3.exception;
18  
19  import static org.apache.commons.lang3.LangAssertions.assertNullPointerException;
20  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertFalse;
23  import static org.junit.jupiter.api.Assertions.assertNotNull;
24  import static org.junit.jupiter.api.Assertions.assertNotSame;
25  import static org.junit.jupiter.api.Assertions.assertNull;
26  import static org.junit.jupiter.api.Assertions.assertSame;
27  import static org.junit.jupiter.api.Assertions.assertThrows;
28  import static org.junit.jupiter.api.Assertions.assertTrue;
29  
30  import java.io.ByteArrayOutputStream;
31  import java.io.IOException;
32  import java.io.PrintStream;
33  import java.io.PrintWriter;
34  import java.io.StringWriter;
35  import java.lang.reflect.Constructor;
36  import java.lang.reflect.Modifier;
37  import java.util.ArrayList;
38  import java.util.List;
39  import java.util.stream.Collectors;
40  
41  import org.apache.commons.lang3.AbstractLangTest;
42  import org.apache.commons.lang3.test.NotVisibleExceptionFactory;
43  import org.junit.jupiter.api.AfterEach;
44  import org.junit.jupiter.api.BeforeEach;
45  import org.junit.jupiter.api.Test;
46  
47  /**
48   * Tests {@link org.apache.commons.lang3.exception.ExceptionUtils}.
49   */
50  class ExceptionUtilsTest extends AbstractLangTest {
51  
52      /**
53       * Provides a method with a well known chained/nested exception
54       * name which matches the full signature (e.g. has a return value
55       * of {@code Throwable}).
56       */
57      private static final class ExceptionWithCause extends Exception {
58          private static final long serialVersionUID = 1L;
59  
60          private Throwable cause;
61  
62          ExceptionWithCause(final String str, final Throwable cause) {
63              super(str);
64              setCause(cause);
65          }
66  
67          ExceptionWithCause(final Throwable cause) {
68              setCause(cause);
69          }
70  
71          @Override
72          public synchronized Throwable getCause() {
73              return cause;
74          }
75  
76          public void setCause(final Throwable cause) {
77              this.cause = cause;
78          }
79      }
80  
81      /**
82       * Provides a method with a well known chained/nested exception
83       * name which does not match the full signature (e.g. lacks a
84       * return value of {@code Throwable}).
85       */
86      private static final class ExceptionWithoutCause extends Exception {
87          private static final long serialVersionUID = 1L;
88  
89          @SuppressWarnings("unused")
90          public void getTargetException() {
91              // noop
92          }
93      }
94  
95      // Temporary classes to allow the nested exception code to be removed
96      // prior to a rewrite of this test class.
97      private static final class NestableException extends Exception {
98          private static final long serialVersionUID = 1L;
99  
100         @SuppressWarnings("unused")
101         NestableException() {
102         }
103 
104         NestableException(final Throwable t) {
105             super(t);
106         }
107     }
108 
109     public static class TestThrowable extends Throwable {
110         private static final long serialVersionUID = 1L;
111     }
112 
113     private static int redeclareCheckedException() {
114         return throwsCheckedException();
115     }
116 
117     private static int throwsCheckedException() {
118         try {
119             throw new IOException();
120         } catch (final Exception e) {
121             ExceptionUtils.asRuntimeException(e);
122             return -1;
123         }
124     }
125 
126     private ExceptionWithCause cyclicCause;
127 
128     private Throwable jdkNoCause;
129 
130     private NestableException nested;
131 
132     private Throwable notVisibleException;
133 
134     private Throwable withCause;
135 
136     private Throwable withoutCause;
137 
138     private Throwable createExceptionWithCause() {
139         try {
140             try {
141                 throw new ExceptionWithCause(createExceptionWithoutCause());
142             } catch (final Throwable t) {
143                 throw new ExceptionWithCause(t);
144             }
145         } catch (final Throwable t) {
146             return t;
147         }
148     }
149 
150     private Throwable createExceptionWithoutCause() {
151         try {
152             throw new ExceptionWithoutCause();
153         } catch (final Throwable t) {
154             return t;
155         }
156     }
157 
158     @BeforeEach
159     public void setUp() {
160         withoutCause = createExceptionWithoutCause();
161         nested = new NestableException(withoutCause);
162         withCause = new ExceptionWithCause(nested);
163         jdkNoCause = new NullPointerException();
164         final ExceptionWithCause a = new ExceptionWithCause(null);
165         final ExceptionWithCause b = new ExceptionWithCause(a);
166         a.setCause(b);
167         cyclicCause = new ExceptionWithCause(a);
168         notVisibleException = NotVisibleExceptionFactory.createException(withoutCause);
169     }
170 
171     @AfterEach
172     public void tearDown() {
173         withoutCause = null;
174         nested = null;
175         withCause = null;
176         jdkNoCause = null;
177         cyclicCause = null;
178         notVisibleException = null;
179     }
180 
181     @Test
182     void test_getMessage_Throwable() {
183         Throwable th = null;
184         assertEquals("", ExceptionUtils.getMessage(th));
185 
186         th = new IllegalArgumentException("Base");
187         assertEquals("IllegalArgumentException: Base", ExceptionUtils.getMessage(th));
188 
189         th = new ExceptionWithCause("Wrapper", th);
190         assertEquals("ExceptionUtilsTest.ExceptionWithCause: Wrapper", ExceptionUtils.getMessage(th));
191     }
192 
193     @Test
194     void test_getRootCauseMessage_Throwable() {
195         Throwable th = null;
196         assertEquals("", ExceptionUtils.getRootCauseMessage(th));
197 
198         th = new IllegalArgumentException("Base");
199         assertEquals("IllegalArgumentException: Base", ExceptionUtils.getRootCauseMessage(th));
200 
201         th = new ExceptionWithCause("Wrapper", th);
202         assertEquals("IllegalArgumentException: Base", ExceptionUtils.getRootCauseMessage(th));
203     }
204 
205     @Test
206     void testAsRuntimeException() {
207         final Exception expected = new InterruptedException();
208         assertSame(expected, assertThrows(Exception.class, () -> ExceptionUtils.asRuntimeException(expected)));
209         assertNotSame(expected, assertThrows(Exception.class, () -> ExceptionUtils.asRuntimeException(new InterruptedException())));
210         // API return typed to compile to Object
211         assertThrows(expected.getClass(), () -> {
212             @SuppressWarnings("unused")
213             final Object retVal = ExceptionUtils.asRuntimeException(expected);
214         });
215         // API return typed to compile to RuntimeException
216         assertThrows(expected.getClass(), () -> {
217             @SuppressWarnings("unused")
218             final RuntimeException retVal = ExceptionUtils.asRuntimeException(expected);
219         });
220         // API return typed to compile to RuntimeException subclass
221         assertThrows(expected.getClass(), () -> {
222             @SuppressWarnings("unused")
223             final IllegalStateException retVal = ExceptionUtils.asRuntimeException(expected);
224         });
225     }
226 
227     @Test
228     void testCatchTechniques() {
229         IOException ioe = assertThrows(IOException.class, ExceptionUtilsTest::throwsCheckedException);
230         assertEquals(1, ExceptionUtils.getThrowableCount(ioe));
231 
232         ioe = assertThrows(IOException.class, ExceptionUtilsTest::redeclareCheckedException);
233         assertEquals(1, ExceptionUtils.getThrowableCount(ioe));
234     }
235 
236     @Test
237     void testConstructor() {
238         assertNotNull(new ExceptionUtils());
239         final Constructor<?>[] cons = ExceptionUtils.class.getDeclaredConstructors();
240         assertEquals(1, cons.length);
241         assertTrue(Modifier.isPublic(cons[0].getModifiers()));
242         assertTrue(Modifier.isPublic(ExceptionUtils.class.getModifiers()));
243         assertFalse(Modifier.isFinal(ExceptionUtils.class.getModifiers()));
244     }
245 
246     @Test
247     void testForEach_jdkNoCause() {
248         final List<Throwable> throwables = new ArrayList<>();
249         ExceptionUtils.forEach(jdkNoCause, throwables::add);
250         assertEquals(1, throwables.size());
251         assertSame(jdkNoCause, throwables.get(0));
252     }
253 
254     @Test
255     void testForEach_nested() {
256         final List<Throwable> throwables = new ArrayList<>();
257         ExceptionUtils.forEach(nested, throwables::add);
258         assertEquals(2, throwables.size());
259         assertSame(nested, throwables.get(0));
260         assertSame(withoutCause, throwables.get(1));
261     }
262 
263     @Test
264     void testForEach_null() {
265         final List<Throwable> throwables = new ArrayList<>();
266         ExceptionUtils.forEach(null, throwables::add);
267         assertEquals(0, throwables.size());
268     }
269 
270     @Test
271     void testForEach_recursiveCause() {
272         final List<Throwable> throwables = new ArrayList<>();
273         ExceptionUtils.forEach(cyclicCause, throwables::add);
274         assertEquals(3, throwables.size());
275         assertSame(cyclicCause, throwables.get(0));
276         assertSame(cyclicCause.getCause(), throwables.get(1));
277         assertSame(cyclicCause.getCause().getCause(), throwables.get(2));
278     }
279 
280     @Test
281     void testForEach_withCause() {
282         final List<Throwable> throwables = new ArrayList<>();
283         ExceptionUtils.forEach(withCause, throwables::add);
284         assertEquals(3, throwables.size());
285         assertSame(withCause, throwables.get(0));
286         assertSame(nested, throwables.get(1));
287         assertSame(withoutCause, throwables.get(2));
288     }
289 
290     @Test
291     void testForEach_withoutCause() {
292         final List<Throwable> throwables = new ArrayList<>();
293         ExceptionUtils.forEach(withoutCause, throwables::add);
294         assertEquals(1, throwables.size());
295         assertSame(withoutCause, throwables.get(0));
296     }
297 
298     @SuppressWarnings("deprecation") // Specifically tests the deprecated methods
299     @Test
300     void testGetCause_Throwable() {
301         assertSame(null, ExceptionUtils.getCause(null));
302         assertSame(null, ExceptionUtils.getCause(withoutCause));
303         assertSame(withoutCause, ExceptionUtils.getCause(nested));
304         assertSame(nested, ExceptionUtils.getCause(withCause));
305         assertSame(null, ExceptionUtils.getCause(jdkNoCause));
306         assertSame(cyclicCause.getCause(), ExceptionUtils.getCause(cyclicCause));
307         assertSame(cyclicCause.getCause().getCause(), ExceptionUtils.getCause(cyclicCause.getCause()));
308         assertSame(cyclicCause.getCause(), ExceptionUtils.getCause(cyclicCause.getCause().getCause()));
309         assertSame(withoutCause, ExceptionUtils.getCause(notVisibleException));
310     }
311 
312     @SuppressWarnings("deprecation") // Specifically tests the deprecated methods
313     @Test
314     void testGetCause_ThrowableArray() {
315         assertSame(null, ExceptionUtils.getCause(null, null));
316         assertSame(null, ExceptionUtils.getCause(null, new String[0]));
317 
318         // not known type, so match on supplied method names
319         assertSame(nested, ExceptionUtils.getCause(withCause, null));  // default names
320         assertSame(null, ExceptionUtils.getCause(withCause, new String[0]));
321         assertSame(null, ExceptionUtils.getCause(withCause, new String[]{null}));
322         assertSame(nested, ExceptionUtils.getCause(withCause, new String[]{"getCause"}));
323 
324         // not known type, so match on supplied method names
325         assertSame(null, ExceptionUtils.getCause(withoutCause, null));
326         assertSame(null, ExceptionUtils.getCause(withoutCause, new String[0]));
327         assertSame(null, ExceptionUtils.getCause(withoutCause, new String[]{null}));
328         assertSame(null, ExceptionUtils.getCause(withoutCause, new String[]{"getCause"}));
329         assertSame(null, ExceptionUtils.getCause(withoutCause, new String[]{"getTargetException"}));
330     }
331 
332     @Test
333     void testGetRootCause_Throwable() {
334         assertSame(null, ExceptionUtils.getRootCause(null));
335         assertSame(withoutCause, ExceptionUtils.getRootCause(withoutCause));
336         assertSame(withoutCause, ExceptionUtils.getRootCause(nested));
337         assertSame(withoutCause, ExceptionUtils.getRootCause(withCause));
338         assertSame(jdkNoCause, ExceptionUtils.getRootCause(jdkNoCause));
339         assertSame(cyclicCause.getCause().getCause(), ExceptionUtils.getRootCause(cyclicCause));
340     }
341 
342     @Test
343     void testGetRootCauseStackTrace_Throwable() {
344         assertEquals(0, ExceptionUtils.getRootCauseStackTrace(null).length);
345 
346         final Throwable cause = createExceptionWithCause();
347         String[] stackTrace = ExceptionUtils.getRootCauseStackTrace(cause);
348         boolean match = false;
349         for (final String element : stackTrace) {
350             if (element.startsWith(ExceptionUtils.WRAPPED_MARKER)) {
351                 match = true;
352                 break;
353             }
354         }
355         assertTrue(match);
356 
357         stackTrace = ExceptionUtils.getRootCauseStackTrace(withoutCause);
358         match = false;
359         for (final String element : stackTrace) {
360             if (element.startsWith(ExceptionUtils.WRAPPED_MARKER)) {
361                 match = true;
362                 break;
363             }
364         }
365         assertFalse(match);
366     }
367 
368     @Test
369     void testGetRootCauseStackTraceList_Throwable() {
370         assertEquals(0, ExceptionUtils.getRootCauseStackTraceList(null).size());
371 
372         final Throwable cause = createExceptionWithCause();
373         List<String> stackTrace = ExceptionUtils.getRootCauseStackTraceList(cause);
374         boolean match = false;
375         for (final String element : stackTrace) {
376             if (element.startsWith(ExceptionUtils.WRAPPED_MARKER)) {
377                 match = true;
378                 break;
379             }
380         }
381         assertTrue(match);
382 
383         stackTrace = ExceptionUtils.getRootCauseStackTraceList(withoutCause);
384         match = false;
385         for (final String element : stackTrace) {
386             if (element.startsWith(ExceptionUtils.WRAPPED_MARKER)) {
387                 match = true;
388                 break;
389             }
390         }
391         assertFalse(match);
392     }
393 
394     @Test
395     /** getStackFrames returns empty string array when the argument is null */
396     void testgetStackFramesHappyPath() {
397         final String[] actual = ExceptionUtils.getStackFrames(new Throwable() {
398             private static final long serialVersionUID = 1L;
399 
400             // provide static stack trace to make test stable
401             @Override
402             public void printStackTrace(final PrintWriter s) {
403                 s.write("org.apache.commons.lang3.exception.ExceptionUtilsTest$1\n" +
404                     "\tat org.apache.commons.lang3.exception.ExceptionUtilsTest.testgetStackFramesGappyPath(ExceptionUtilsTest.java:706)\n" +
405                     "\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n" +
406                     "\tat com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)\n" +
407                     "\tat com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)\n");
408             }
409         });
410 
411         assertArrayEquals(new String[]{
412             "org.apache.commons.lang3.exception.ExceptionUtilsTest$1",
413             "\tat org.apache.commons.lang3.exception.ExceptionUtilsTest.testgetStackFramesGappyPath(ExceptionUtilsTest.java:706)",
414             "\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)",
415             "\tat com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)",
416             "\tat com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)"
417         }, actual);
418     }
419 
420     @Test
421     /** getStackFrames returns the string array of the stack frames when there is a real exception */
422     void testgetStackFramesNullArg() {
423         final String[] actual = ExceptionUtils.getStackFrames((Throwable) null);
424         assertEquals(0, actual.length);
425     }
426 
427     @Test
428     void testGetThrowableCount_Throwable() {
429         assertEquals(0, ExceptionUtils.getThrowableCount(null));
430         assertEquals(1, ExceptionUtils.getThrowableCount(withoutCause));
431         assertEquals(2, ExceptionUtils.getThrowableCount(nested));
432         assertEquals(3, ExceptionUtils.getThrowableCount(withCause));
433         assertEquals(1, ExceptionUtils.getThrowableCount(jdkNoCause));
434         assertEquals(3, ExceptionUtils.getThrowableCount(cyclicCause));
435     }
436 
437     @Test
438     void testGetThrowableList_Throwable_jdkNoCause() {
439         final List<?> throwables = ExceptionUtils.getThrowableList(jdkNoCause);
440         assertEquals(1, throwables.size());
441         assertSame(jdkNoCause, throwables.get(0));
442     }
443 
444     @Test
445     void testGetThrowableList_Throwable_nested() {
446         final List<?> throwables = ExceptionUtils.getThrowableList(nested);
447         assertEquals(2, throwables.size());
448         assertSame(nested, throwables.get(0));
449         assertSame(withoutCause, throwables.get(1));
450     }
451 
452     @Test
453     void testGetThrowableList_Throwable_null() {
454         final List<?> throwables = ExceptionUtils.getThrowableList(null);
455         assertEquals(0, throwables.size());
456     }
457 
458     @Test
459     void testGetThrowableList_Throwable_recursiveCause() {
460         final List<?> throwables = ExceptionUtils.getThrowableList(cyclicCause);
461         assertEquals(3, throwables.size());
462         assertSame(cyclicCause, throwables.get(0));
463         assertSame(cyclicCause.getCause(), throwables.get(1));
464         assertSame(cyclicCause.getCause().getCause(), throwables.get(2));
465     }
466 
467     @Test
468     void testGetThrowableList_Throwable_withCause() {
469         final List<?> throwables = ExceptionUtils.getThrowableList(withCause);
470         assertEquals(3, throwables.size());
471         assertSame(withCause, throwables.get(0));
472         assertSame(nested, throwables.get(1));
473         assertSame(withoutCause, throwables.get(2));
474     }
475 
476     @Test
477     void testGetThrowableList_Throwable_withoutCause() {
478         final List<?> throwables = ExceptionUtils.getThrowableList(withoutCause);
479         assertEquals(1, throwables.size());
480         assertSame(withoutCause, throwables.get(0));
481     }
482 
483     @Test
484     void testGetThrowables_Throwable_jdkNoCause() {
485         final Throwable[] throwables = ExceptionUtils.getThrowables(jdkNoCause);
486         assertEquals(1, throwables.length);
487         assertSame(jdkNoCause, throwables[0]);
488     }
489 
490     @Test
491     void testGetThrowables_Throwable_nested() {
492         final Throwable[] throwables = ExceptionUtils.getThrowables(nested);
493         assertEquals(2, throwables.length);
494         assertSame(nested, throwables[0]);
495         assertSame(withoutCause, throwables[1]);
496     }
497 
498     @Test
499     void testGetThrowables_Throwable_null() {
500         assertEquals(0, ExceptionUtils.getThrowables(null).length);
501     }
502 
503     @Test
504     void testGetThrowables_Throwable_recursiveCause() {
505         final Throwable[] throwables = ExceptionUtils.getThrowables(cyclicCause);
506         assertEquals(3, throwables.length);
507         assertSame(cyclicCause, throwables[0]);
508         assertSame(cyclicCause.getCause(), throwables[1]);
509         assertSame(cyclicCause.getCause().getCause(), throwables[2]);
510     }
511 
512     @Test
513     void testGetThrowables_Throwable_withCause() {
514         final Throwable[] throwables = ExceptionUtils.getThrowables(withCause);
515         assertEquals(3, throwables.length);
516         assertSame(withCause, throwables[0]);
517         assertSame(nested, throwables[1]);
518         assertSame(withoutCause, throwables[2]);
519     }
520 
521     @Test
522     void testGetThrowables_Throwable_withoutCause() {
523         final Throwable[] throwables = ExceptionUtils.getThrowables(withoutCause);
524         assertEquals(1, throwables.length);
525         assertSame(withoutCause, throwables[0]);
526     }
527 
528     @Test
529     void testIndexOf_ThrowableClass() {
530         assertEquals(-1, ExceptionUtils.indexOfThrowable(null, null));
531         assertEquals(-1, ExceptionUtils.indexOfThrowable(null, NestableException.class));
532 
533         assertEquals(-1, ExceptionUtils.indexOfThrowable(withoutCause, null));
534         assertEquals(-1, ExceptionUtils.indexOfThrowable(withoutCause, ExceptionWithCause.class));
535         assertEquals(-1, ExceptionUtils.indexOfThrowable(withoutCause, NestableException.class));
536         assertEquals(0, ExceptionUtils.indexOfThrowable(withoutCause, ExceptionWithoutCause.class));
537 
538         assertEquals(-1, ExceptionUtils.indexOfThrowable(nested, null));
539         assertEquals(-1, ExceptionUtils.indexOfThrowable(nested, ExceptionWithCause.class));
540         assertEquals(0, ExceptionUtils.indexOfThrowable(nested, NestableException.class));
541         assertEquals(1, ExceptionUtils.indexOfThrowable(nested, ExceptionWithoutCause.class));
542 
543         assertEquals(-1, ExceptionUtils.indexOfThrowable(withCause, null));
544         assertEquals(0, ExceptionUtils.indexOfThrowable(withCause, ExceptionWithCause.class));
545         assertEquals(1, ExceptionUtils.indexOfThrowable(withCause, NestableException.class));
546         assertEquals(2, ExceptionUtils.indexOfThrowable(withCause, ExceptionWithoutCause.class));
547 
548         assertEquals(-1, ExceptionUtils.indexOfThrowable(withCause, Exception.class));
549         assertEquals(-1, ExceptionUtils.indexOfThrowable(withCause, Throwable.class));
550     }
551 
552     @Test
553     void testIndexOf_ThrowableClassInt() {
554         assertEquals(-1, ExceptionUtils.indexOfThrowable(null, null, 0));
555         assertEquals(-1, ExceptionUtils.indexOfThrowable(null, NestableException.class, 0));
556 
557         assertEquals(-1, ExceptionUtils.indexOfThrowable(withoutCause, null));
558         assertEquals(-1, ExceptionUtils.indexOfThrowable(withoutCause, ExceptionWithCause.class, 0));
559         assertEquals(-1, ExceptionUtils.indexOfThrowable(withoutCause, NestableException.class, 0));
560         assertEquals(0, ExceptionUtils.indexOfThrowable(withoutCause, ExceptionWithoutCause.class, 0));
561 
562         assertEquals(-1, ExceptionUtils.indexOfThrowable(nested, null, 0));
563         assertEquals(-1, ExceptionUtils.indexOfThrowable(nested, ExceptionWithCause.class, 0));
564         assertEquals(0, ExceptionUtils.indexOfThrowable(nested, NestableException.class, 0));
565         assertEquals(1, ExceptionUtils.indexOfThrowable(nested, ExceptionWithoutCause.class, 0));
566 
567         assertEquals(-1, ExceptionUtils.indexOfThrowable(withCause, null));
568         assertEquals(0, ExceptionUtils.indexOfThrowable(withCause, ExceptionWithCause.class, 0));
569         assertEquals(1, ExceptionUtils.indexOfThrowable(withCause, NestableException.class, 0));
570         assertEquals(2, ExceptionUtils.indexOfThrowable(withCause, ExceptionWithoutCause.class, 0));
571 
572         assertEquals(0, ExceptionUtils.indexOfThrowable(withCause, ExceptionWithCause.class, -1));
573         assertEquals(0, ExceptionUtils.indexOfThrowable(withCause, ExceptionWithCause.class, 0));
574         assertEquals(-1, ExceptionUtils.indexOfThrowable(withCause, ExceptionWithCause.class, 1));
575         assertEquals(-1, ExceptionUtils.indexOfThrowable(withCause, ExceptionWithCause.class, 9));
576 
577         assertEquals(-1, ExceptionUtils.indexOfThrowable(withCause, Exception.class, 0));
578         assertEquals(-1, ExceptionUtils.indexOfThrowable(withCause, Throwable.class, 0));
579     }
580 
581     @Test
582     void testIndexOfType_ThrowableClass() {
583         assertEquals(-1, ExceptionUtils.indexOfType(null, null));
584         assertEquals(-1, ExceptionUtils.indexOfType(null, NestableException.class));
585 
586         assertEquals(-1, ExceptionUtils.indexOfType(withoutCause, null));
587         assertEquals(-1, ExceptionUtils.indexOfType(withoutCause, ExceptionWithCause.class));
588         assertEquals(-1, ExceptionUtils.indexOfType(withoutCause, NestableException.class));
589         assertEquals(0, ExceptionUtils.indexOfType(withoutCause, ExceptionWithoutCause.class));
590 
591         assertEquals(-1, ExceptionUtils.indexOfType(nested, null));
592         assertEquals(-1, ExceptionUtils.indexOfType(nested, ExceptionWithCause.class));
593         assertEquals(0, ExceptionUtils.indexOfType(nested, NestableException.class));
594         assertEquals(1, ExceptionUtils.indexOfType(nested, ExceptionWithoutCause.class));
595 
596         assertEquals(-1, ExceptionUtils.indexOfType(withCause, null));
597         assertEquals(0, ExceptionUtils.indexOfType(withCause, ExceptionWithCause.class));
598         assertEquals(1, ExceptionUtils.indexOfType(withCause, NestableException.class));
599         assertEquals(2, ExceptionUtils.indexOfType(withCause, ExceptionWithoutCause.class));
600 
601         assertEquals(0, ExceptionUtils.indexOfType(withCause, Exception.class));
602         assertEquals(0, ExceptionUtils.indexOfType(withCause, Throwable.class));
603     }
604 
605     @Test
606     void testIndexOfType_ThrowableClassInt() {
607         assertEquals(-1, ExceptionUtils.indexOfType(null, null, 0));
608         assertEquals(-1, ExceptionUtils.indexOfType(null, NestableException.class, 0));
609 
610         assertEquals(-1, ExceptionUtils.indexOfType(withoutCause, null));
611         assertEquals(-1, ExceptionUtils.indexOfType(withoutCause, ExceptionWithCause.class, 0));
612         assertEquals(-1, ExceptionUtils.indexOfType(withoutCause, NestableException.class, 0));
613         assertEquals(0, ExceptionUtils.indexOfType(withoutCause, ExceptionWithoutCause.class, 0));
614 
615         assertEquals(-1, ExceptionUtils.indexOfType(nested, null, 0));
616         assertEquals(-1, ExceptionUtils.indexOfType(nested, ExceptionWithCause.class, 0));
617         assertEquals(0, ExceptionUtils.indexOfType(nested, NestableException.class, 0));
618         assertEquals(1, ExceptionUtils.indexOfType(nested, ExceptionWithoutCause.class, 0));
619 
620         assertEquals(-1, ExceptionUtils.indexOfType(withCause, null));
621         assertEquals(0, ExceptionUtils.indexOfType(withCause, ExceptionWithCause.class, 0));
622         assertEquals(1, ExceptionUtils.indexOfType(withCause, NestableException.class, 0));
623         assertEquals(2, ExceptionUtils.indexOfType(withCause, ExceptionWithoutCause.class, 0));
624 
625         assertEquals(0, ExceptionUtils.indexOfType(withCause, ExceptionWithCause.class, -1));
626         assertEquals(0, ExceptionUtils.indexOfType(withCause, ExceptionWithCause.class, 0));
627         assertEquals(-1, ExceptionUtils.indexOfType(withCause, ExceptionWithCause.class, 1));
628         assertEquals(-1, ExceptionUtils.indexOfType(withCause, ExceptionWithCause.class, 9));
629 
630         assertEquals(0, ExceptionUtils.indexOfType(withCause, Exception.class, 0));
631         assertEquals(0, ExceptionUtils.indexOfType(withCause, Throwable.class, 0));
632     }
633 
634     @Test
635     void testIsChecked_checked() {
636         assertTrue(ExceptionUtils.isChecked(new IOException()));
637     }
638 
639     @Test
640     void testIsChecked_error() {
641         assertFalse(ExceptionUtils.isChecked(new StackOverflowError()));
642     }
643 
644     @Test
645     void testIsChecked_null() {
646         assertFalse(ExceptionUtils.isChecked(null));
647     }
648 
649     @Test
650     void testIsChecked_unchecked() {
651         assertFalse(ExceptionUtils.isChecked(new IllegalArgumentException()));
652     }
653 
654     @Test
655     void testIsCheckedCustomThrowable() {
656         assertTrue(ExceptionUtils.isChecked(new TestThrowable()));
657     }
658 
659     @Test
660     void testIsUnchecked_checked() {
661         assertFalse(ExceptionUtils.isUnchecked(new IOException()));
662     }
663 
664     @Test
665     void testIsUnchecked_error() {
666         assertTrue(ExceptionUtils.isUnchecked(new StackOverflowError()));
667     }
668 
669     @Test
670     void testIsUnchecked_null() {
671         assertFalse(ExceptionUtils.isUnchecked(null));
672     }
673 
674     @Test
675     void testIsUnchecked_unchecked() {
676         assertTrue(ExceptionUtils.isUnchecked(new IllegalArgumentException()));
677     }
678 
679     @Test
680     void testIsUnCheckedCustomThrowable() {
681         assertFalse(ExceptionUtils.isUnchecked(new TestThrowable()));
682     }
683 
684     @Test
685     void testPrintRootCauseStackTrace_Throwable() {
686         ExceptionUtils.printRootCauseStackTrace(null);
687         // could pipe system.err to a known stream, but not much point as
688         // internally this method calls stream method anyway
689     }
690 
691     @Test
692     void testPrintRootCauseStackTrace_ThrowableStream() {
693         ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
694         ExceptionUtils.printRootCauseStackTrace(null, (PrintStream) null);
695         ExceptionUtils.printRootCauseStackTrace(null, new PrintStream(out));
696         assertEquals(0, out.toString().length());
697         assertNullPointerException(() -> ExceptionUtils.printRootCauseStackTrace(withCause, (PrintStream) null));
698         out = new ByteArrayOutputStream(1024);
699         final Throwable cause = createExceptionWithCause();
700         ExceptionUtils.printRootCauseStackTrace(cause, new PrintStream(out));
701         String stackTrace = out.toString();
702         assertTrue(stackTrace.contains(ExceptionUtils.WRAPPED_MARKER));
703         out = new ByteArrayOutputStream(1024);
704         ExceptionUtils.printRootCauseStackTrace(withoutCause, new PrintStream(out));
705         stackTrace = out.toString();
706         assertFalse(stackTrace.contains(ExceptionUtils.WRAPPED_MARKER));
707     }
708 
709     @Test
710     void testPrintRootCauseStackTrace_ThrowableWriter() {
711         StringWriter writer = new StringWriter(1024);
712         ExceptionUtils.printRootCauseStackTrace(null, (PrintWriter) null);
713         ExceptionUtils.printRootCauseStackTrace(null, new PrintWriter(writer));
714         assertEquals(0, writer.getBuffer().length());
715         assertNullPointerException(() -> ExceptionUtils.printRootCauseStackTrace(withCause, (PrintWriter) null));
716         writer = new StringWriter(1024);
717         final Throwable cause = createExceptionWithCause();
718         ExceptionUtils.printRootCauseStackTrace(cause, new PrintWriter(writer));
719         String stackTrace = writer.toString();
720         assertTrue(stackTrace.contains(ExceptionUtils.WRAPPED_MARKER));
721         writer = new StringWriter(1024);
722         ExceptionUtils.printRootCauseStackTrace(withoutCause, new PrintWriter(writer));
723         stackTrace = writer.toString();
724         assertFalse(stackTrace.contains(ExceptionUtils.WRAPPED_MARKER));
725     }
726 
727     @Test
728     void testRemoveCommonFrames_ListList() {
729         assertNullPointerException(() -> ExceptionUtils.removeCommonFrames(null, null));
730     }
731 
732     @Test
733     void testRethrow() {
734         final Exception expected = new InterruptedException();
735         // API return typed to compile to Object
736         assertThrows(expected.getClass(), () -> {
737             @SuppressWarnings("unused")
738             final Object retVal = ExceptionUtils.rethrow(expected);
739         });
740         // API return typed to compile to Object subclass
741         assertThrows(expected.getClass(), () -> {
742             @SuppressWarnings("unused")
743             final String retVal = ExceptionUtils.rethrow(expected);
744         });
745         // API return typed to compile to primitive
746         assertThrows(expected.getClass(), () -> {
747             @SuppressWarnings("unused")
748             final int retVal = ExceptionUtils.rethrow(expected);
749         });
750         //
751         assertSame(expected, assertThrows(expected.getClass(), () -> ExceptionUtils.rethrow(expected)));
752         assertNotSame(expected, assertThrows(expected.getClass(), () -> ExceptionUtils.rethrow(new InterruptedException())));
753     }
754 
755     @Test
756     void testStream_jdkNoCause() {
757         assertEquals(1, ExceptionUtils.stream(jdkNoCause).count());
758         assertSame(jdkNoCause, ExceptionUtils.stream(jdkNoCause).toArray()[0]);
759     }
760 
761     @Test
762     void testStream_nested() {
763         assertEquals(2, ExceptionUtils.stream(nested).count());
764         final Object[] array = ExceptionUtils.stream(nested).toArray();
765         assertSame(nested, array[0]);
766         assertSame(withoutCause, array[1]);
767     }
768 
769     @Test
770     void testStream_null() {
771         assertEquals(0, ExceptionUtils.stream(null).count());
772     }
773 
774     @Test
775     void testStream_recursiveCause() {
776         final List<?> throwables = ExceptionUtils.stream(cyclicCause).collect(Collectors.toList());
777         assertEquals(3, throwables.size());
778         assertSame(cyclicCause, throwables.get(0));
779         assertSame(cyclicCause.getCause(), throwables.get(1));
780         assertSame(cyclicCause.getCause().getCause(), throwables.get(2));
781     }
782 
783     @Test
784     void testStream_withCause() {
785         final List<?> throwables = ExceptionUtils.stream(withCause).collect(Collectors.toList());
786         assertEquals(3, throwables.size());
787         assertSame(withCause, throwables.get(0));
788         assertSame(nested, throwables.get(1));
789         assertSame(withoutCause, throwables.get(2));
790     }
791 
792     @Test
793     void testStream_withoutCause() {
794         final List<?> throwables = ExceptionUtils.stream(withoutCause).collect(Collectors.toList());
795         assertEquals(1, throwables.size());
796         assertSame(withoutCause, throwables.get(0));
797     }
798 
799     @Test
800     void testThrowableOf_ThrowableClass() {
801         assertNull(ExceptionUtils.throwableOfThrowable(null, null));
802         assertNull(ExceptionUtils.throwableOfThrowable(null, NestableException.class));
803 
804         assertNull(ExceptionUtils.throwableOfThrowable(withoutCause, null));
805         assertNull(ExceptionUtils.throwableOfThrowable(withoutCause, ExceptionWithCause.class));
806         assertNull(ExceptionUtils.throwableOfThrowable(withoutCause, NestableException.class));
807         assertEquals(withoutCause, ExceptionUtils.throwableOfThrowable(withoutCause, ExceptionWithoutCause.class));
808 
809         assertNull(ExceptionUtils.throwableOfThrowable(nested, null));
810         assertNull(ExceptionUtils.throwableOfThrowable(nested, ExceptionWithCause.class));
811         assertEquals(nested, ExceptionUtils.throwableOfThrowable(nested, NestableException.class));
812         assertEquals(nested.getCause(), ExceptionUtils.throwableOfThrowable(nested, ExceptionWithoutCause.class));
813 
814         assertNull(ExceptionUtils.throwableOfThrowable(withCause, null));
815         assertEquals(withCause, ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class));
816         assertEquals(withCause.getCause(), ExceptionUtils.throwableOfThrowable(withCause, NestableException.class));
817         assertEquals(withCause.getCause().getCause(), ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithoutCause.class));
818 
819         assertNull(ExceptionUtils.throwableOfThrowable(withCause, Exception.class));
820         assertNull(ExceptionUtils.throwableOfThrowable(withCause, Throwable.class));
821     }
822 
823     @Test
824     void testThrowableOf_ThrowableClassInt() {
825         assertNull(ExceptionUtils.throwableOfThrowable(null, null, 0));
826         assertNull(ExceptionUtils.throwableOfThrowable(null, NestableException.class, 0));
827 
828         assertNull(ExceptionUtils.throwableOfThrowable(withoutCause, null));
829         assertNull(ExceptionUtils.throwableOfThrowable(withoutCause, ExceptionWithCause.class, 0));
830         assertNull(ExceptionUtils.throwableOfThrowable(withoutCause, NestableException.class, 0));
831         assertEquals(withoutCause, ExceptionUtils.throwableOfThrowable(withoutCause, ExceptionWithoutCause.class, 0));
832 
833         assertNull(ExceptionUtils.throwableOfThrowable(nested, null, 0));
834         assertNull(ExceptionUtils.throwableOfThrowable(nested, ExceptionWithCause.class, 0));
835         assertEquals(nested, ExceptionUtils.throwableOfThrowable(nested, NestableException.class, 0));
836         assertEquals(nested.getCause(), ExceptionUtils.throwableOfThrowable(nested, ExceptionWithoutCause.class, 0));
837 
838         assertNull(ExceptionUtils.throwableOfThrowable(withCause, null));
839         assertEquals(withCause, ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class, 0));
840         assertEquals(withCause.getCause(), ExceptionUtils.throwableOfThrowable(withCause, NestableException.class, 0));
841         assertEquals(withCause.getCause().getCause(), ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithoutCause.class, 0));
842 
843         assertEquals(withCause, ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class, -1));
844         assertEquals(withCause, ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class, 0));
845         assertNull(ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class, 1));
846         assertNull(ExceptionUtils.throwableOfThrowable(withCause, ExceptionWithCause.class, 9));
847 
848         assertNull(ExceptionUtils.throwableOfThrowable(withCause, Exception.class, 0));
849         assertNull(ExceptionUtils.throwableOfThrowable(withCause, Throwable.class, 0));
850     }
851 
852     @Test
853     void testThrowableOfType_ThrowableClass() {
854         assertNull(ExceptionUtils.throwableOfType(null, null));
855         assertNull(ExceptionUtils.throwableOfType(null, NestableException.class));
856 
857         assertNull(ExceptionUtils.throwableOfType(withoutCause, null));
858         assertNull(ExceptionUtils.throwableOfType(withoutCause, ExceptionWithCause.class));
859         assertNull(ExceptionUtils.throwableOfType(withoutCause, NestableException.class));
860         assertEquals(withoutCause, ExceptionUtils.throwableOfType(withoutCause, ExceptionWithoutCause.class));
861 
862         assertNull(ExceptionUtils.throwableOfType(nested, null));
863         assertNull(ExceptionUtils.throwableOfType(nested, ExceptionWithCause.class));
864         assertEquals(nested, ExceptionUtils.throwableOfType(nested, NestableException.class));
865         assertEquals(nested.getCause(), ExceptionUtils.throwableOfType(nested, ExceptionWithoutCause.class));
866 
867         assertNull(ExceptionUtils.throwableOfType(withCause, null));
868         assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class));
869         assertEquals(withCause.getCause(), ExceptionUtils.throwableOfType(withCause, NestableException.class));
870         assertEquals(withCause.getCause().getCause(), ExceptionUtils.throwableOfType(withCause, ExceptionWithoutCause.class));
871 
872         assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, Exception.class));
873         assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, Throwable.class));
874     }
875 
876     @Test
877     void testThrowableOfType_ThrowableClassInt() {
878         assertNull(ExceptionUtils.throwableOfType(null, null, 0));
879         assertNull(ExceptionUtils.throwableOfType(null, NestableException.class, 0));
880 
881         assertNull(ExceptionUtils.throwableOfType(withoutCause, null));
882         assertNull(ExceptionUtils.throwableOfType(withoutCause, ExceptionWithCause.class, 0));
883         assertNull(ExceptionUtils.throwableOfType(withoutCause, NestableException.class, 0));
884         assertEquals(withoutCause, ExceptionUtils.throwableOfType(withoutCause, ExceptionWithoutCause.class, 0));
885 
886         assertNull(ExceptionUtils.throwableOfType(nested, null, 0));
887         assertNull(ExceptionUtils.throwableOfType(nested, ExceptionWithCause.class, 0));
888         assertEquals(nested, ExceptionUtils.throwableOfType(nested, NestableException.class, 0));
889         assertEquals(nested.getCause(), ExceptionUtils.throwableOfType(nested, ExceptionWithoutCause.class, 0));
890 
891         assertNull(ExceptionUtils.throwableOfType(withCause, null));
892         assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class, 0));
893         assertEquals(withCause.getCause(), ExceptionUtils.throwableOfType(withCause, NestableException.class, 0));
894         assertEquals(withCause.getCause().getCause(), ExceptionUtils.throwableOfType(withCause, ExceptionWithoutCause.class, 0));
895 
896         assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class, -1));
897         assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class, 0));
898         assertNull(ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class, 1));
899         assertNull(ExceptionUtils.throwableOfType(withCause, ExceptionWithCause.class, 9));
900 
901         assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, Exception.class, 0));
902         assertEquals(withCause, ExceptionUtils.throwableOfType(withCause, Throwable.class, 0));
903     }
904 
905     @Test
906     void testWrapAndUnwrapCheckedException() {
907         final Throwable t = assertThrows(Throwable.class, () -> ExceptionUtils.wrapAndThrow(new IOException()));
908         assertTrue(ExceptionUtils.hasCause(t, IOException.class));
909     }
910 
911     @Test
912     void testWrapAndUnwrapError() {
913         final Throwable t = assertThrows(Throwable.class, () -> ExceptionUtils.wrapAndThrow(new OutOfMemoryError()));
914         assertTrue(ExceptionUtils.hasCause(t, Error.class));
915     }
916 
917     @Test
918     void testWrapAndUnwrapRuntimeException() {
919         final Throwable t = assertThrows(Throwable.class, () -> ExceptionUtils.wrapAndThrow(new IllegalArgumentException()));
920         assertTrue(ExceptionUtils.hasCause(t, RuntimeException.class));
921     }
922 
923     @Test
924     void testWrapAndUnwrapThrowable() {
925         final Throwable t = assertThrows(Throwable.class, () -> ExceptionUtils.wrapAndThrow(new TestThrowable()));
926         assertTrue(ExceptionUtils.hasCause(t, TestThrowable.class));
927     }
928 }