1df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregor/*
2df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregor * Copyright (C) 2007 The Guava Authors
3df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregor *
4df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregor * Licensed under the Apache License, Version 2.0 (the "License");
5df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregor * you may not use this file except in compliance with the License.
6df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregor * You may obtain a copy of the License at
7df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregor *
8df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregor * http://www.apache.org/licenses/LICENSE-2.0
9df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregor *
10df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregor * Unless required by applicable law or agreed to in writing, software
11df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregor * distributed under the License is distributed on an "AS IS" BASIS,
12df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregor * See the License for the specific language governing permissions and
14df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregor * limitations under the License.
15df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregor */
16df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregor
17df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregorpackage com.google.common.base;
18df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregor
19df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregorimport static com.google.common.base.Throwables.getStackTraceAsString;
20df8995765161a347c52ddf7b8dfb541ce6365586Douglas Gregorimport static java.util.Arrays.asList;
21import static java.util.regex.Pattern.quote;
22
23import com.google.common.collect.Iterables;
24import com.google.common.testing.NullPointerTester;
25
26import junit.framework.TestCase;
27
28import java.io.FileNotFoundException;
29import java.util.List;
30
31/**
32 * Unit test for {@link Throwables}.
33 *
34 * @author Kevin Bourrillion
35 */
36public class ThrowablesTest extends TestCase {
37  public void testPropagateIfPossible_NoneDeclared_NoneThrown() {
38    Sample sample = new Sample() {
39      @Override public void noneDeclared() {
40        try {
41          methodThatDoesntThrowAnything();
42        } catch (Throwable t) {
43          Throwables.propagateIfPossible(t);
44          throw new SomeChainingException(t);
45        }
46      }
47    };
48
49    // Expect no exception to be thrown
50    sample.noneDeclared();
51  }
52
53  public void testPropagateIfPossible_NoneDeclared_UncheckedThrown() {
54    Sample sample = new Sample() {
55      @Override public void noneDeclared() {
56        try {
57          methodThatThrowsUnchecked();
58        } catch (Throwable t) {
59          Throwables.propagateIfPossible(t);
60          throw new SomeChainingException(t);
61        }
62      }
63    };
64
65    // Expect the unchecked exception to propagate as-is
66    try {
67      sample.noneDeclared();
68      fail();
69    } catch (SomeUncheckedException expected) {
70    }
71  }
72
73  public void testPropagateIfPossible_NoneDeclared_UndeclaredThrown() {
74    Sample sample = new Sample() {
75      @Override public void noneDeclared() {
76        try {
77          methodThatThrowsUndeclaredChecked();
78        } catch (Throwable t) {
79          Throwables.propagateIfPossible(t);
80          throw new SomeChainingException(t);
81        }
82      }
83    };
84
85    // Expect the undeclared exception to have been chained inside another
86    try {
87      sample.noneDeclared();
88      fail();
89    } catch (SomeChainingException expected) {
90    }
91  }
92
93  public void testPropagateIfPossible_OneDeclared_NoneThrown()
94      throws SomeCheckedException {
95    Sample sample = new Sample() {
96      @Override public void oneDeclared() throws SomeCheckedException {
97        try {
98          methodThatDoesntThrowAnything();
99        } catch (Throwable t) {
100          // yes, this block is never reached, but for purposes of illustration
101          // we're keeping it the same in each test
102          Throwables.propagateIfPossible(t, SomeCheckedException.class);
103          throw new SomeChainingException(t);
104        }
105      }
106    };
107
108    // Expect no exception to be thrown
109    sample.oneDeclared();
110  }
111
112  public void testPropagateIfPossible_OneDeclared_UncheckedThrown()
113      throws SomeCheckedException {
114    Sample sample = new Sample() {
115      @Override public void oneDeclared() throws SomeCheckedException {
116        try {
117          methodThatThrowsUnchecked();
118        } catch (Throwable t) {
119          Throwables.propagateIfPossible(t, SomeCheckedException.class);
120          throw new SomeChainingException(t);
121        }
122      }
123    };
124
125    // Expect the unchecked exception to propagate as-is
126    try {
127      sample.oneDeclared();
128      fail();
129    } catch (SomeUncheckedException expected) {
130    }
131  }
132
133  public void testPropagateIfPossible_OneDeclared_CheckedThrown() {
134    Sample sample = new Sample() {
135      @Override public void oneDeclared() throws SomeCheckedException {
136        try {
137          methodThatThrowsChecked();
138        } catch (Throwable t) {
139          Throwables.propagateIfPossible(t, SomeCheckedException.class);
140          throw new SomeChainingException(t);
141        }
142      }
143    };
144
145    // Expect the checked exception to propagate as-is
146    try {
147      sample.oneDeclared();
148      fail();
149    } catch (SomeCheckedException expected) {
150    }
151  }
152
153  public void testPropagateIfPossible_OneDeclared_UndeclaredThrown()
154      throws SomeCheckedException {
155    Sample sample = new Sample() {
156      @Override public void oneDeclared() throws SomeCheckedException {
157        try {
158          methodThatThrowsUndeclaredChecked();
159        } catch (Throwable t) {
160          Throwables.propagateIfPossible(t, SomeCheckedException.class);
161          throw new SomeChainingException(t);
162        }
163      }
164    };
165
166    // Expect the undeclared exception to have been chained inside another
167    try {
168      sample.oneDeclared();
169      fail();
170    } catch (SomeChainingException expected) {
171    }
172  }
173
174  public void testPropagateIfPossible_TwoDeclared_NoneThrown()
175      throws SomeCheckedException, SomeOtherCheckedException {
176    Sample sample = new Sample() {
177      @Override public void twoDeclared() throws SomeCheckedException,
178          SomeOtherCheckedException {
179        try {
180          methodThatDoesntThrowAnything();
181        } catch (Throwable t) {
182          Throwables.propagateIfPossible(t, SomeCheckedException.class,
183              SomeOtherCheckedException.class);
184          throw new SomeChainingException(t);
185        }
186      }
187    };
188
189    // Expect no exception to be thrown
190    sample.twoDeclared();
191  }
192
193  public void testPropagateIfPossible_TwoDeclared_UncheckedThrown()
194      throws SomeCheckedException, SomeOtherCheckedException {
195    Sample sample = new Sample() {
196      @Override public void twoDeclared() throws SomeCheckedException,
197          SomeOtherCheckedException {
198        try {
199          methodThatThrowsUnchecked();
200        } catch (Throwable t) {
201          Throwables.propagateIfPossible(t, SomeCheckedException.class,
202              SomeOtherCheckedException.class);
203          throw new SomeChainingException(t);
204        }
205      }
206    };
207
208    // Expect the unchecked exception to propagate as-is
209    try {
210      sample.twoDeclared();
211      fail();
212    } catch (SomeUncheckedException expected) {
213    }
214  }
215
216  public void testPropagateIfPossible_TwoDeclared_CheckedThrown()
217      throws SomeOtherCheckedException {
218    Sample sample = new Sample() {
219      @Override public void twoDeclared() throws SomeCheckedException,
220          SomeOtherCheckedException {
221        try {
222          methodThatThrowsChecked();
223        } catch (Throwable t) {
224          Throwables.propagateIfPossible(t, SomeCheckedException.class,
225              SomeOtherCheckedException.class);
226          throw new SomeChainingException(t);
227        }
228      }
229    };
230
231    // Expect the checked exception to propagate as-is
232    try {
233      sample.twoDeclared();
234      fail();
235    } catch (SomeCheckedException expected) {
236    }
237  }
238
239  public void testPropagateIfPossible_TwoDeclared_OtherCheckedThrown()
240      throws SomeCheckedException {
241    Sample sample = new Sample() {
242      @Override public void twoDeclared() throws SomeCheckedException,
243          SomeOtherCheckedException {
244        try {
245          methodThatThrowsOtherChecked();
246        } catch (Throwable t) {
247          Throwables.propagateIfPossible(t, SomeCheckedException.class,
248              SomeOtherCheckedException.class);
249          throw new SomeChainingException(t);
250        }
251      }
252    };
253
254    // Expect the checked exception to propagate as-is
255    try {
256      sample.twoDeclared();
257      fail();
258    } catch (SomeOtherCheckedException expected) {
259    }
260  }
261
262  public void testPropageIfPossible_null() throws SomeCheckedException {
263    Throwables.propagateIfPossible(null);
264    Throwables.propagateIfPossible(null, SomeCheckedException.class);
265    Throwables.propagateIfPossible(null, SomeCheckedException.class,
266        SomeUncheckedException.class);
267  }
268
269  public void testPropagate_NoneDeclared_NoneThrown() {
270    Sample sample = new Sample() {
271      @Override public void noneDeclared() {
272        try {
273          methodThatDoesntThrowAnything();
274        } catch (Throwable t) {
275          throw Throwables.propagate(t);
276        }
277      }
278    };
279
280    // Expect no exception to be thrown
281    sample.noneDeclared();
282  }
283
284  public void testPropagate_NoneDeclared_UncheckedThrown() {
285    Sample sample = new Sample() {
286      @Override public void noneDeclared() {
287        try {
288          methodThatThrowsUnchecked();
289        } catch (Throwable t) {
290          throw Throwables.propagate(t);
291        }
292      }
293    };
294
295    // Expect the unchecked exception to propagate as-is
296    try {
297      sample.noneDeclared();
298      fail();
299    } catch (SomeUncheckedException expected) {
300    }
301  }
302
303  public void testPropagate_NoneDeclared_ErrorThrown() {
304    Sample sample = new Sample() {
305      @Override public void noneDeclared() {
306        try {
307          methodThatThrowsError();
308        } catch (Throwable t) {
309          throw Throwables.propagate(t);
310        }
311      }
312    };
313
314    // Expect the error to propagate as-is
315    try {
316      sample.noneDeclared();
317      fail();
318    } catch (SomeError expected) {
319    }
320  }
321
322  public void testPropagate_NoneDeclared_CheckedThrown() {
323    Sample sample = new Sample() {
324      @Override public void noneDeclared() {
325        try {
326          methodThatThrowsChecked();
327        } catch (Throwable t) {
328          throw Throwables.propagate(t);
329        }
330      }
331    };
332
333    // Expect the undeclared exception to have been chained inside another
334    try {
335      sample.noneDeclared();
336      fail();
337    } catch (RuntimeException expected) {
338      assertTrue(expected.getCause() instanceof SomeCheckedException);
339    }
340  }
341
342  public void testPropagateIfInstanceOf_NoneThrown()
343      throws SomeCheckedException {
344    Sample sample = new Sample() {
345      @Override public void oneDeclared() throws SomeCheckedException {
346        try {
347          methodThatDoesntThrowAnything();
348        } catch (Throwable t) {
349          Throwables.propagateIfInstanceOf(t, SomeCheckedException.class);
350          throw Throwables.propagate(t);
351        }
352      }
353    };
354
355    // Expect no exception to be thrown
356    sample.oneDeclared();
357  }
358
359  public void testPropagateIfInstanceOf_DeclaredThrown() {
360    Sample sample = new Sample() {
361      @Override public void oneDeclared() throws SomeCheckedException {
362        try {
363          methodThatThrowsChecked();
364        } catch (Throwable t) {
365          Throwables.propagateIfInstanceOf(t, SomeCheckedException.class);
366          throw Throwables.propagate(t);
367        }
368      }
369    };
370
371    // Expect declared exception to be thrown as-is
372    try {
373      sample.oneDeclared();
374      fail();
375    } catch (SomeCheckedException e) {
376    }
377  }
378
379  public void testPropagateIfInstanceOf_UncheckedThrown()
380      throws SomeCheckedException {
381    Sample sample = new Sample() {
382      @Override public void oneDeclared() throws SomeCheckedException {
383        try {
384          methodThatThrowsUnchecked();
385        } catch (Throwable t) {
386          Throwables.propagateIfInstanceOf(t, SomeCheckedException.class);
387          throw Throwables.propagate(t);
388        }
389      }
390    };
391
392    // Expect unchecked exception to be thrown as-is
393    try {
394      sample.oneDeclared();
395      fail();
396    } catch (SomeUncheckedException e) {
397    }
398  }
399
400  public void testPropagateIfInstanceOf_UndeclaredThrown()
401      throws SomeCheckedException {
402    Sample sample = new Sample() {
403      @Override public void oneDeclared() throws SomeCheckedException {
404        try {
405          methodThatThrowsOtherChecked();
406        } catch (Throwable t) {
407          Throwables.propagateIfInstanceOf(t, SomeCheckedException.class);
408          throw Throwables.propagate(t);
409        }
410      }
411    };
412
413    // Expect undeclared exception wrapped by RuntimeException to be thrown
414    try {
415      sample.oneDeclared();
416      fail();
417    } catch (RuntimeException e) {
418      assertTrue(e.getCause() instanceof SomeOtherCheckedException);
419    }
420  }
421
422  public void testPropageIfInstanceOf_null() throws SomeCheckedException {
423    Throwables.propagateIfInstanceOf(null, SomeCheckedException.class);
424  }
425
426  public void testGetRootCause_NoCause() {
427    SomeCheckedException exception = new SomeCheckedException();
428    assertSame(exception, Throwables.getRootCause(exception));
429  }
430
431  public void testGetRootCause_SingleWrapped() {
432    SomeCheckedException cause = new SomeCheckedException();
433    SomeChainingException exception = new SomeChainingException(cause);
434    assertSame(cause, Throwables.getRootCause(exception));
435  }
436
437  public void testGetRootCause_DoubleWrapped() {
438    SomeCheckedException cause = new SomeCheckedException();
439    SomeChainingException exception =
440        new SomeChainingException(new SomeChainingException(cause));
441    assertSame(cause, Throwables.getRootCause(exception));
442  }
443
444  private static class SomeError extends Error {}
445  private static class SomeCheckedException extends Exception {}
446  private static class SomeOtherCheckedException extends Exception {}
447  private static class SomeUncheckedException extends RuntimeException {}
448  private static class SomeUndeclaredCheckedException extends Exception {}
449  private static class SomeChainingException extends RuntimeException {
450    public SomeChainingException(Throwable cause) {
451      super(cause);
452    }
453  }
454
455  static class Sample {
456    void noneDeclared() {}
457    void oneDeclared() throws SomeCheckedException {}
458    void twoDeclared() throws SomeCheckedException, SomeOtherCheckedException {}
459  }
460
461  static void methodThatDoesntThrowAnything() {}
462  static void methodThatThrowsError() {
463    throw new SomeError();
464  }
465  static void methodThatThrowsUnchecked() {
466    throw new SomeUncheckedException();
467  }
468  static void methodThatThrowsChecked() throws SomeCheckedException {
469    throw new SomeCheckedException();
470  }
471  static void methodThatThrowsOtherChecked() throws SomeOtherCheckedException {
472    throw new SomeOtherCheckedException();
473  }
474  static void methodThatThrowsUndeclaredChecked()
475      throws SomeUndeclaredCheckedException {
476    throw new SomeUndeclaredCheckedException();
477  }
478
479  public void testGetStackTraceAsString() {
480    class StackTraceException extends Exception {
481      StackTraceException(String message) {
482        super(message);
483      }
484    }
485
486    StackTraceException e = new StackTraceException("my message");
487
488    String firstLine = quote(e.getClass().getName() + ": " + e.getMessage());
489    String secondLine = "\\s*at " + ThrowablesTest.class.getName() + "\\..*";
490    String moreLines = "(?:.*\n?)*";
491    String expected = firstLine + "\n" + secondLine + "\n" + moreLines;
492    assertTrue(getStackTraceAsString(e).matches(expected));
493  }
494
495  public void testGetCausalChain() {
496    FileNotFoundException fnfe = new FileNotFoundException();
497    IllegalArgumentException iae = new IllegalArgumentException(fnfe);
498    RuntimeException re = new RuntimeException(iae);
499    IllegalStateException ex = new IllegalStateException(re);
500
501    assertEquals(asList(ex, re, iae, fnfe), Throwables.getCausalChain(ex));
502    assertSame(fnfe, Iterables.getOnlyElement(Throwables.getCausalChain(fnfe)));
503    try {
504      Throwables.getCausalChain(null);
505      fail("Should have throw NPE");
506    } catch (NullPointerException expected) {
507    }
508
509    List<Throwable> causes = Throwables.getCausalChain(ex);
510    try {
511      causes.add(new RuntimeException());
512      fail("List should be unmodifiable");
513    } catch (UnsupportedOperationException expected) {
514    }
515  }
516
517  public void testNullPointers() {
518    new NullPointerTester().testAllPublicStaticMethods(Throwables.class);
519  }
520}
521