Skip to content

Commit

Permalink
Introduce ExceptionUtils
Browse files Browse the repository at this point in the history
Issue: #64

------------------------------------------------------------------------
On behalf of the community, the JUnit Lambda Team thanks msg systems ag
(http://www.msg-systems.com) for supporting the JUnit crowdfunding
campaign!
------------------------------------------------------------------------
  • Loading branch information
sbrannen committed Jan 3, 2016
1 parent 435f886 commit 1c9edfa
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 47 deletions.
@@ -0,0 +1,46 @@
/*
* Copyright 2015-2016 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/

package org.junit.gen5.commons.util;

/**
* Collection of utilities for working with exceptions.
*
* @since 5.0
*/
public final class ExceptionUtils {

/**
* Throw the supplied {@link Throwable}, <em>masked</em> as a
* {@link RuntimeException}.
*
* <p>The supplied {@code Throwable} will not be wrapped. Rather, it
* will be thrown as-is using a hack based on generics and type erasure
* that tricks the Java compiler into believing that the thrown exception
* is an unchecked exception.
*
* @param t the Throwable to throw as a {@code RuntimeException}
* @return this method always throws an exception and therefore never
* returns anything; the return type is merely present to allow this
* method to be supplied as the operand in a {@code throw} statement
*/
public static RuntimeException throwAsRuntimeException(Throwable t) {
ExceptionUtils.<RuntimeException> throwAs(t);

// Appeasing the compiler: the following line will never be executed.
return null;

This comment has been minimized.

Copy link
@bjmi

bjmi Dec 27, 2023

Contributor

ExceptionUtils.throwAsUncheckedException(t) emits the following warning in IntelliJ

Result of 'throwAsUncheckedException()' not thrown

and throw ExceptionUtils.throwAsUncheckedException(t) also emits a warning

Dereference of 'ExceptionUtils.throwAsUncheckedException(getUnderlyingCause(t))' may produce 'NullPointerException'

Therefore following recommendation: switch to throw new AssertionError("Appeasing the compiler: this will never be executed");

This comment has been minimized.

Copy link
@sbrannen

sbrannen Dec 27, 2023

Author Member

Instead of commenting on a commit from (almost) 8 years ago, may I suggest you open a discussion or issue instead? 😉

}

@SuppressWarnings("unchecked")
private static <T extends Throwable> void throwAs(Throwable t) throws T {
throw (T) t;
}

}
Expand Up @@ -29,7 +29,7 @@
import java.util.function.Predicate;

/**
* Collection of utilities for working with Java reflection APIs.
* Collection of utilities for working with the Java reflection APIs.
*
* @since 5.0
*/
Expand Down Expand Up @@ -91,13 +91,13 @@ public static boolean isStatic(Member member) {
* arguments.
*
* <p>The constructor will be made accessible if necessary, and any checked
* exception will be {@linkplain #throwAsRuntimeException masked} as a
* {@code RuntimeException}.
* exception will be {@linkplain ExceptionUtils#throwAsRuntimeException masked}
* as a {@code RuntimeException}.
*
* @param clazz the class to instantiate; never {@code null}
* @param args the arguments to pass to the constructor
* @return the new instance
* @see #throwAsRuntimeException(Throwable)
* @see ExceptionUtils#throwAsRuntimeException(Throwable)
*/
public static <T> T newInstance(Class<T> clazz, Object... args) {
Preconditions.notNull(clazz, "class must not be null");
Expand All @@ -108,26 +108,23 @@ public static <T> T newInstance(Class<T> clazz, Object... args) {
makeAccessible(constructor);
return constructor.newInstance(args);
}
catch (Throwable ex) {
handleException(ex);
catch (Throwable t) {
throw ExceptionUtils.throwAsRuntimeException(getUnderlyingCause(t));
}

// Appeasing the compiler: this should hopefully never happen...
throw new IllegalStateException("Exception handling algorithm in ReflectionUtils is incomplete");
}

/**
* Invoke the supplied method, making it accessible if necessary and
* {@linkplain #throwAsRuntimeException masking} any checked exception
* as a {@code RuntimeException}.
* {@linkplain ExceptionUtils#throwAsRuntimeException masking} any
* checked exception as a {@code RuntimeException}.
*
* @param method the method to invoke; never {@code null}
* @param target the object on which to invoke the method; may be
* {@code null} if the method is {@code static}
* @param args the arguments to pass to the method
* @return the value returned by the method invocation or {@code null}
* if the return type is {@code void}
* @see #throwAsRuntimeException(Throwable)
* @see ExceptionUtils#throwAsRuntimeException(Throwable)
*/
public static Object invokeMethod(Method method, Object target, Object... args) {
Preconditions.notNull(method, "method must not be null");
Expand All @@ -138,12 +135,9 @@ public static Object invokeMethod(Method method, Object target, Object... args)
makeAccessible(method);
return method.invoke(target, args);
}
catch (Throwable ex) {
handleException(ex);
catch (Throwable t) {
throw ExceptionUtils.throwAsRuntimeException(getUnderlyingCause(t));
}

// Appeasing the compiler: this should hopefully never happen...
throw new IllegalStateException("Exception handling algorithm in ReflectionUtils is incomplete");
}

public static Optional<Class<?>> loadClass(String name) {
Expand Down Expand Up @@ -399,31 +393,21 @@ private static void makeAccessible(AccessibleObject object) {
}
}

private static void handleException(Throwable t) {
if (t instanceof InvocationTargetException) {
handleException(((InvocationTargetException) t).getTargetException());
}
throwAsRuntimeException(t);
}

/**
* Throw the supplied {@link Throwable}, <em>masked</em> as a
* {@link RuntimeException}.
* Get the underlying cause of the supplied {@link Throwable}.
*
* <p>The supplied {@code Throwable} will not be wrapped. Rather, it
* will be thrown as-is using a hack based on generics and type erasure
* that tricks the Java compiler into believing that the thrown exception
* is an unchecked exception.
*
* @param t the Throwable to throw as a {@code RuntimeException}
* <p>If the supplied {@code Throwable} is an instance of
* {@link InvocationTargetException}, this method will be invoked
* recursively with the underlying
* {@linkplain InvocationTargetException#getTargetException() target
* exception}; otherwise, this method simply returns the supplied
* {@code Throwable}.
*/
public static void throwAsRuntimeException(Throwable t) {
ReflectionUtils.<RuntimeException> throwAs(t);
}

@SuppressWarnings("unchecked")
private static <T extends Throwable> void throwAs(Throwable t) throws T {
throw (T) t;
private static Throwable getUnderlyingCause(Throwable t) {
if (t instanceof InvocationTargetException) {
return getUnderlyingCause(((InvocationTargetException) t).getTargetException());
}
return t;
}

}
Expand Up @@ -26,7 +26,7 @@
import org.junit.gen5.api.Tag;
import org.junit.gen5.api.extension.ExtendWith;
import org.junit.gen5.api.extension.TestExtension;
import org.junit.gen5.commons.util.ReflectionUtils;
import org.junit.gen5.commons.util.ExceptionUtils;
import org.junit.gen5.commons.util.StringUtils;
import org.junit.gen5.engine.AbstractTestDescriptor;
import org.junit.gen5.engine.TestTag;
Expand Down Expand Up @@ -84,7 +84,7 @@ protected void executeAndMaskThrowable(Executable executable) {
executable.execute();
}
catch (Throwable throwable) {
ReflectionUtils.throwAsRuntimeException(throwable);
ExceptionUtils.throwAsRuntimeException(throwable);
}
}

Expand Down
Expand Up @@ -15,8 +15,8 @@
import java.util.List;

import org.junit.gen5.api.Executable;
import org.junit.gen5.commons.util.ExceptionUtils;
import org.junit.gen5.commons.util.Preconditions;
import org.junit.gen5.commons.util.ReflectionUtils;

/**
* Simple component that can be used to collect one or more instances of
Expand Down Expand Up @@ -47,7 +47,7 @@ void execute(Executable executable) {
}

/**
* Add the supplied {@link Throwable} to this collector.
* Add the supplied {@link Throwable} to this {@code ThrowableCollector}.
*
* @param t the {@code Throwable} to add
* @see #execute(Executable)
Expand All @@ -63,28 +63,32 @@ void add(Throwable t) {
* Get the list of {@link Throwable Throwables} collected by this
* {@code ThrowableCollector}.
*
* @return an unmodifiable list of throwables
* @return an unmodifiable list of the throwables collected by this
* {@code ThrowableCollector}
*/
List<Throwable> getThrowables() {
return Collections.unmodifiableList(this.throwables);
}

/**
* Assert that this {@code ThrowableCollector} is <em>empty</em>.
* Assert that this {@code ThrowableCollector} is <em>empty</em> (i.e.,
* has not collected any {@link Throwable Throwables}).
*
* <p>If this collector is not empty, the first collected {@link Throwable}
* <p>If this collector is not empty, the first collected {@code Throwable}
* will be thrown with any additional throwables
* {@linkplain Throwable#addSuppressed(Throwable) suppressed} in the
* first {@code Throwable}. Note, however, that the {@code Throwable}
* will not be wrapped. Rather, it will be thrown as-is using a hack
* based on generics and type erasure that tricks the Java compiler
* into believing that the thrown exception is an unchecked exception.
*
* @see ExceptionUtils#throwAsRuntimeException(Throwable)
*/
void assertEmpty() {
if (!this.throwables.isEmpty()) {
Throwable t = this.throwables.get(0);
this.throwables.stream().skip(1).forEach(t::addSuppressed);
ReflectionUtils.throwAsRuntimeException(t);
ExceptionUtils.throwAsRuntimeException(t);
}
}

Expand Down

0 comments on commit 1c9edfa

Please sign in to comment.