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