/* * Copyright (C) 2007 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.common.base; import static com.google.common.base.Throwables.getStackTraceAsString; import static java.util.Arrays.asList; import static java.util.regex.Pattern.quote; import com.google.common.collect.Iterables; import com.google.common.testing.NullPointerTester; import junit.framework.TestCase; import java.io.FileNotFoundException; import java.util.List; /** * Unit test for {@link Throwables}. * * @author Kevin Bourrillion */ @SuppressWarnings("serial") // this warning is silly for exceptions in tests public class ThrowablesTest extends TestCase { public void testPropagateIfPossible_NoneDeclared_NoneThrown() { Sample sample = new Sample() { @Override public void noneDeclared() { try { methodThatDoesntThrowAnything(); } catch (Throwable t) { Throwables.propagateIfPossible(t); throw new SomeChainingException(t); } } }; // Expect no exception to be thrown sample.noneDeclared(); } public void testPropagateIfPossible_NoneDeclared_UncheckedThrown() { Sample sample = new Sample() { @Override public void noneDeclared() { try { methodThatThrowsUnchecked(); } catch (Throwable t) { Throwables.propagateIfPossible(t); throw new SomeChainingException(t); } } }; // Expect the unchecked exception to propagate as-is try { sample.noneDeclared(); fail(); } catch (SomeUncheckedException expected) { } } public void testPropagateIfPossible_NoneDeclared_UndeclaredThrown() { Sample sample = new Sample() { @Override public void noneDeclared() { try { methodThatThrowsUndeclaredChecked(); } catch (Throwable t) { Throwables.propagateIfPossible(t); throw new SomeChainingException(t); } } }; // Expect the undeclared exception to have been chained inside another try { sample.noneDeclared(); fail(); } catch (SomeChainingException expected) { } } public void testPropagateIfPossible_OneDeclared_NoneThrown() throws SomeCheckedException { Sample sample = new Sample() { @Override public void oneDeclared() throws SomeCheckedException { try { methodThatDoesntThrowAnything(); } catch (Throwable t) { // yes, this block is never reached, but for purposes of illustration // we're keeping it the same in each test Throwables.propagateIfPossible(t, SomeCheckedException.class); throw new SomeChainingException(t); } } }; // Expect no exception to be thrown sample.oneDeclared(); } public void testPropagateIfPossible_OneDeclared_UncheckedThrown() throws SomeCheckedException { Sample sample = new Sample() { @Override public void oneDeclared() throws SomeCheckedException { try { methodThatThrowsUnchecked(); } catch (Throwable t) { Throwables.propagateIfPossible(t, SomeCheckedException.class); throw new SomeChainingException(t); } } }; // Expect the unchecked exception to propagate as-is try { sample.oneDeclared(); fail(); } catch (SomeUncheckedException expected) { } } public void testPropagateIfPossible_OneDeclared_CheckedThrown() { Sample sample = new Sample() { @Override public void oneDeclared() throws SomeCheckedException { try { methodThatThrowsChecked(); } catch (Throwable t) { Throwables.propagateIfPossible(t, SomeCheckedException.class); throw new SomeChainingException(t); } } }; // Expect the checked exception to propagate as-is try { sample.oneDeclared(); fail(); } catch (SomeCheckedException expected) { } } public void testPropagateIfPossible_OneDeclared_UndeclaredThrown() throws SomeCheckedException { Sample sample = new Sample() { @Override public void oneDeclared() throws SomeCheckedException { try { methodThatThrowsUndeclaredChecked(); } catch (Throwable t) { Throwables.propagateIfPossible(t, SomeCheckedException.class); throw new SomeChainingException(t); } } }; // Expect the undeclared exception to have been chained inside another try { sample.oneDeclared(); fail(); } catch (SomeChainingException expected) { } } public void testPropagateIfPossible_TwoDeclared_NoneThrown() throws SomeCheckedException, SomeOtherCheckedException { Sample sample = new Sample() { @Override public void twoDeclared() throws SomeCheckedException, SomeOtherCheckedException { try { methodThatDoesntThrowAnything(); } catch (Throwable t) { Throwables.propagateIfPossible(t, SomeCheckedException.class, SomeOtherCheckedException.class); throw new SomeChainingException(t); } } }; // Expect no exception to be thrown sample.twoDeclared(); } public void testPropagateIfPossible_TwoDeclared_UncheckedThrown() throws SomeCheckedException, SomeOtherCheckedException { Sample sample = new Sample() { @Override public void twoDeclared() throws SomeCheckedException, SomeOtherCheckedException { try { methodThatThrowsUnchecked(); } catch (Throwable t) { Throwables.propagateIfPossible(t, SomeCheckedException.class, SomeOtherCheckedException.class); throw new SomeChainingException(t); } } }; // Expect the unchecked exception to propagate as-is try { sample.twoDeclared(); fail(); } catch (SomeUncheckedException expected) { } } public void testPropagateIfPossible_TwoDeclared_CheckedThrown() throws SomeOtherCheckedException { Sample sample = new Sample() { @Override public void twoDeclared() throws SomeCheckedException, SomeOtherCheckedException { try { methodThatThrowsChecked(); } catch (Throwable t) { Throwables.propagateIfPossible(t, SomeCheckedException.class, SomeOtherCheckedException.class); throw new SomeChainingException(t); } } }; // Expect the checked exception to propagate as-is try { sample.twoDeclared(); fail(); } catch (SomeCheckedException expected) { } } public void testPropagateIfPossible_TwoDeclared_OtherCheckedThrown() throws SomeCheckedException { Sample sample = new Sample() { @Override public void twoDeclared() throws SomeCheckedException, SomeOtherCheckedException { try { methodThatThrowsOtherChecked(); } catch (Throwable t) { Throwables.propagateIfPossible(t, SomeCheckedException.class, SomeOtherCheckedException.class); throw new SomeChainingException(t); } } }; // Expect the checked exception to propagate as-is try { sample.twoDeclared(); fail(); } catch (SomeOtherCheckedException expected) { } } public void testPropageIfPossible_null() throws SomeCheckedException { Throwables.propagateIfPossible(null); Throwables.propagateIfPossible(null, SomeCheckedException.class); Throwables.propagateIfPossible(null, SomeCheckedException.class, SomeUncheckedException.class); } public void testPropagate_NoneDeclared_NoneThrown() { Sample sample = new Sample() { @Override public void noneDeclared() { try { methodThatDoesntThrowAnything(); } catch (Throwable t) { throw Throwables.propagate(t); } } }; // Expect no exception to be thrown sample.noneDeclared(); } public void testPropagate_NoneDeclared_UncheckedThrown() { Sample sample = new Sample() { @Override public void noneDeclared() { try { methodThatThrowsUnchecked(); } catch (Throwable t) { throw Throwables.propagate(t); } } }; // Expect the unchecked exception to propagate as-is try { sample.noneDeclared(); fail(); } catch (SomeUncheckedException expected) { } } public void testPropagate_NoneDeclared_ErrorThrown() { Sample sample = new Sample() { @Override public void noneDeclared() { try { methodThatThrowsError(); } catch (Throwable t) { throw Throwables.propagate(t); } } }; // Expect the error to propagate as-is try { sample.noneDeclared(); fail(); } catch (SomeError expected) { } } public void testPropagate_NoneDeclared_CheckedThrown() { Sample sample = new Sample() { @Override public void noneDeclared() { try { methodThatThrowsChecked(); } catch (Throwable t) { throw Throwables.propagate(t); } } }; // Expect the undeclared exception to have been chained inside another try { sample.noneDeclared(); fail(); } catch (RuntimeException expected) { assertTrue(expected.getCause() instanceof SomeCheckedException); } } public void testPropagateIfInstanceOf_NoneThrown() throws SomeCheckedException { Sample sample = new Sample() { @Override public void oneDeclared() throws SomeCheckedException { try { methodThatDoesntThrowAnything(); } catch (Throwable t) { Throwables.propagateIfInstanceOf(t, SomeCheckedException.class); throw Throwables.propagate(t); } } }; // Expect no exception to be thrown sample.oneDeclared(); } public void testPropagateIfInstanceOf_DeclaredThrown() { Sample sample = new Sample() { @Override public void oneDeclared() throws SomeCheckedException { try { methodThatThrowsChecked(); } catch (Throwable t) { Throwables.propagateIfInstanceOf(t, SomeCheckedException.class); throw Throwables.propagate(t); } } }; // Expect declared exception to be thrown as-is try { sample.oneDeclared(); fail(); } catch (SomeCheckedException e) { } } public void testPropagateIfInstanceOf_UncheckedThrown() throws SomeCheckedException { Sample sample = new Sample() { @Override public void oneDeclared() throws SomeCheckedException { try { methodThatThrowsUnchecked(); } catch (Throwable t) { Throwables.propagateIfInstanceOf(t, SomeCheckedException.class); throw Throwables.propagate(t); } } }; // Expect unchecked exception to be thrown as-is try { sample.oneDeclared(); fail(); } catch (SomeUncheckedException e) { } } public void testPropagateIfInstanceOf_UndeclaredThrown() throws SomeCheckedException { Sample sample = new Sample() { @Override public void oneDeclared() throws SomeCheckedException { try { methodThatThrowsOtherChecked(); } catch (Throwable t) { Throwables.propagateIfInstanceOf(t, SomeCheckedException.class); throw Throwables.propagate(t); } } }; // Expect undeclared exception wrapped by RuntimeException to be thrown try { sample.oneDeclared(); fail(); } catch (RuntimeException e) { assertTrue(e.getCause() instanceof SomeOtherCheckedException); } } public void testPropageIfInstanceOf_null() throws SomeCheckedException { Throwables.propagateIfInstanceOf(null, SomeCheckedException.class); } public void testGetRootCause_NoCause() { SomeCheckedException exception = new SomeCheckedException(); assertSame(exception, Throwables.getRootCause(exception)); } public void testGetRootCause_SingleWrapped() { SomeCheckedException cause = new SomeCheckedException(); SomeChainingException exception = new SomeChainingException(cause); assertSame(cause, Throwables.getRootCause(exception)); } public void testGetRootCause_DoubleWrapped() { SomeCheckedException cause = new SomeCheckedException(); SomeChainingException exception = new SomeChainingException(new SomeChainingException(cause)); assertSame(cause, Throwables.getRootCause(exception)); } private static class SomeThrowable extends Throwable {} private static class SomeError extends Error {} private static class SomeCheckedException extends Exception {} private static class SomeOtherCheckedException extends Exception {} private static class SomeUncheckedException extends RuntimeException {} private static class SomeUndeclaredCheckedException extends Exception {} private static class SomeChainingException extends RuntimeException { public SomeChainingException(Throwable cause) { super(cause); } } static class Sample { void noneDeclared() {} /* * Subclasses of Sample will define methods with these signatures that throw * these exceptions, so we must declare them in the throws clause here. * Eclipse doesn't think being thrown from a subclass's non-public, * non-protected method with the same signature counts as being "used." */ @SuppressWarnings("unused") void oneDeclared() throws SomeCheckedException {} @SuppressWarnings("unused") void twoDeclared() throws SomeCheckedException, SomeOtherCheckedException {} } static void methodThatDoesntThrowAnything() {} static void methodThatThrowsError() { throw new SomeError(); } static void methodThatThrowsUnchecked() { throw new SomeUncheckedException(); } static void methodThatThrowsChecked() throws SomeCheckedException { throw new SomeCheckedException(); } static void methodThatThrowsOtherChecked() throws SomeOtherCheckedException { throw new SomeOtherCheckedException(); } static void methodThatThrowsUndeclaredChecked() throws SomeUndeclaredCheckedException { throw new SomeUndeclaredCheckedException(); } public void testGetStackTraceAsString() { class StackTraceException extends Exception { StackTraceException(String message) { super(message); } } StackTraceException e = new StackTraceException("my message"); String firstLine = quote(e.getClass().getName() + ": " + e.getMessage()); String secondLine = "\\s*at " + ThrowablesTest.class.getName() + "\\..*"; String moreLines = "(?:.*\n?)*"; String expected = firstLine + "\n" + secondLine + "\n" + moreLines; assertTrue(getStackTraceAsString(e).matches(expected)); } public void testGetCausalChain() { FileNotFoundException fnfe = new FileNotFoundException(); IllegalArgumentException iae = new IllegalArgumentException(fnfe); RuntimeException re = new RuntimeException(iae); IllegalStateException ex = new IllegalStateException(re); assertEquals(asList(ex, re, iae, fnfe), Throwables.getCausalChain(ex)); assertSame(fnfe, Iterables.getOnlyElement(Throwables.getCausalChain(fnfe))); try { Throwables.getCausalChain(null); fail("Should have throw NPE"); } catch (NullPointerException expected) { } List<Throwable> causes = Throwables.getCausalChain(ex); try { causes.add(new RuntimeException()); fail("List should be unmodifiable"); } catch (UnsupportedOperationException expected) { } } public void testNullPointers() throws Exception { NullPointerTester tester = new NullPointerTester(); tester.setDefault(Throwable.class, new SomeCheckedException()); tester.setDefault(Class.class, SomeCheckedException.class); tester.testAllPublicStaticMethods(Throwables.class); } }