Skip to content

Commit 8aaac85

Browse files
committed
Polish contribution
See #1454
1 parent 2844ee4 commit 8aaac85

File tree

6 files changed

+115
-128
lines changed

6 files changed

+115
-128
lines changed

junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/LifecycleMethodExecutionExceptionHandler.java

Lines changed: 47 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -10,127 +10,114 @@
1010

1111
package org.junit.jupiter.api.extension;
1212

13-
import static org.apiguardian.api.API.Status.STABLE;
13+
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
1414

1515
import org.apiguardian.api.API;
1616

1717
/**
1818
* {@code LifecycleMethodExecutionExceptionHandler} defines the API for
1919
* {@link Extension Extensions} that wish to handle exceptions thrown during
20-
* execution of lifecycle methods (annotated with {@code @BeforeAll},
21-
* {@code @BeforeEach}, {@code @AfterEach} and {@code @AfterAll}.
20+
* the execution of {@code @BeforeAll}, {@code @BeforeEach}, {@code @AfterEach},
21+
* and {@code @AfterAll} lifecycle methods.
2222
*
2323
* <p>Common use cases include swallowing an exception if it's anticipated,
24-
* logging or rolling back a transaction in certain error scenarios.
24+
* logging errors, or rolling back a transaction in certain error scenarios.
2525
*
26-
* <p>This extension needs to be declared on a class level if class level methods
27-
* ({@code @BeforeAll}, {@code @AfterAll}) are to be covered. If declared on Test
28-
* level, only handlers for {@code @BeforeEach} and {@code @AfterEach} will execute
26+
* <p>Implementations of this extension API must be registered at the class level
27+
* if exceptions thrown from {@code @BeforeAll} or {@code @AfterAll} methods are
28+
* to be handled. When registered at the test level, only exceptions thrown from
29+
* {@code @BeforeEach} or {@code @AfterEach} methods will be handled.
2930
*
3031
* <h3>Constructor Requirements</h3>
3132
*
3233
* <p>Consult the documentation in {@link Extension} for details on constructor
3334
* requirements.
3435
*
35-
* @see TestExecutionExceptionHandler
36+
* <h3 id="implementation-guidelines">Implementation Guidelines</h3>
37+
*
38+
* <p>An implementation of an exception handler method defined in this API must
39+
* perform one of the following.
40+
*
41+
* <ol>
42+
* <li>Rethrow the supplied {@code Throwable} <em>as is</em>, which is the default implementation.</li>
43+
* <li>Swallow the supplied {@code Throwable}, thereby preventing propagation.</li>
44+
* <li>Throw a new exception, potentially wrapping the supplied {@code Throwable}.</li>
45+
* </ol>
46+
*
47+
* <p>If the supplied {@code Throwable} is swallowed by a handler method, subsequent
48+
* handler methods for the same lifecycle will not be invoked; otherwise, the
49+
* corresponding handler method of the next registered
50+
* {@code LifecycleMethodExecutionExceptionHandler} (if there is one) will be
51+
* invoked with any {@link Throwable} thrown by the previous handler.
3652
*
53+
* @see TestExecutionExceptionHandler
3754
* @since 5.5
3855
*/
39-
@API(status = STABLE, since = "5.5")
56+
@API(status = EXPERIMENTAL, since = "5.5")
4057
public interface LifecycleMethodExecutionExceptionHandler extends Extension {
4158

4259
/**
43-
* Handle the supplied {@link Throwable throwable}.
44-
*
45-
* <p>Implementors must perform one of the following.
46-
* <ol>
47-
* <li>Rethrow the supplied {@code throwable} <em>as is</em> which is the default implementation</li>
48-
* <li>Swallow the supplied {@code throwable}, thereby preventing propagation.</li>
49-
* <li>Throw a new exception, potentially wrapping the supplied {@code throwable}.</li>
50-
* </ol>
60+
* Handle the supplied {@link Throwable} that was thrown during execution of
61+
* a {@code @BeforeAll} lifecycle method.
5162
*
52-
* <p>If the supplied {@code throwable} is swallowed, subsequent
53-
* {@code LifecycleMethodExecutionExceptionHandler} will not be invoked;
54-
* otherwise, the next registered {@code LifecycleMethodExecutionExceptionHandler}
55-
* (if there is one) will be invoked with any {@link Throwable} thrown by
56-
* this handler.
63+
* <p>Please refer to the class-level Javadoc for
64+
* <a href="#implementation-guidelines">Implementation Guidelines</a>.
5765
*
5866
* @param context the current extension context; never {@code null}
5967
* @param throwable the {@code Throwable} to handle; never {@code null}
6068
*/
6169
default void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable throwable)
6270
throws Throwable {
71+
6372
throw throwable;
6473
}
6574

6675
/**
67-
* Handle the supplied {@link Throwable throwable}.
76+
* Handle the supplied {@link Throwable} that was thrown during execution of
77+
* a {@code @BeforeEach} lifecycle method.
6878
*
69-
* <p>Implementors must perform one of the following.
70-
* <ol>
71-
* <li>Rethrow the supplied {@code throwable} <em>as is</em> which is the default implementation</li>
72-
* <li>Swallow the supplied {@code throwable}, thereby preventing propagation.</li>
73-
* <li>Throw a new exception, potentially wrapping the supplied {@code throwable}.</li>
74-
* </ol>
75-
*
76-
* <p>If the supplied {@code throwable} is swallowed, subsequent
77-
* {@code LifecycleMethodExecutionExceptionHandler}
78-
* will not be invoked; otherwise, the next registered
79-
* {@code LifecycleMethodExecutionExceptionHandler} (if there is one)
80-
* will be invoked with any {@link Throwable} thrown by this handler.
79+
* <p>Please refer to the class-level Javadoc for
80+
* <a href="#implementation-guidelines">Implementation Guidelines</a>.
8181
*
8282
* @param context the current extension context; never {@code null}
8383
* @param throwable the {@code Throwable} to handle; never {@code null}
8484
*/
8585
default void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable throwable)
8686
throws Throwable {
87+
8788
throw throwable;
8889
}
8990

9091
/**
91-
* Handle the supplied {@link Throwable throwable}.
92+
* Handle the supplied {@link Throwable} that was thrown during execution of
93+
* a {@code @AfterEach} lifecycle method.
9294
*
93-
* <p>Implementors must perform one of the following.
94-
* <ol>
95-
* <li>Rethrow the supplied {@code throwable} <em>as is</em> which is the default implementation</li>
96-
* <li>Swallow the supplied {@code throwable}, thereby preventing propagation.</li>
97-
* <li>Throw a new exception, potentially wrapping the supplied {@code throwable}.</li>
98-
* </ol>
99-
*
100-
* <p>If the supplied {@code throwable} is swallowed, subsequent
101-
* {@code LifecycleMethodExecutionExceptionHandler} will not be invoked;
102-
* otherwise, the next registered {@code LifecycleMethodExecutionExceptionHandler}
103-
* (if there is one) will be invoked with any {@link Throwable} thrown by
104-
* this handler.
95+
* <p>Please refer to the class-level Javadoc for
96+
* <a href="#implementation-guidelines">Implementation Guidelines</a>.
10597
*
10698
* @param context the current extension context; never {@code null}
10799
* @param throwable the {@code Throwable} to handle; never {@code null}
108100
*/
109101
default void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable throwable)
110102
throws Throwable {
103+
111104
throw throwable;
112105
}
113106

114107
/**
115-
* Handle the supplied {@link Throwable throwable}.
108+
* Handle the supplied {@link Throwable} that was thrown during execution of
109+
* a {@code @AfterAll} lifecycle method.
116110
*
117-
* <p>Implementors must perform one of the following.
118-
* <ol>
119-
* <li>Rethrow the supplied {@code throwable} <em>as is</em> which is the default implementation</li>
120-
* <li>Swallow the supplied {@code throwable}, thereby preventing propagation.</li>
121-
* <li>Throw a new exception, potentially wrapping the supplied {@code throwable}.</li>
122-
* </ol>
123-
*
124-
* <p>If the supplied {@code throwable} is swallowed, subsequent
125-
* {@code LifecycleMethodExecutionExceptionHandler} will not be invoked; otherwise,
126-
* the next registered {@code LifecycleMethodExecutionExceptionHandler} (if there
127-
* is one) will be invoked with any {@link Throwable} thrown by this handler.
111+
* <p>Please refer to the class-level Javadoc for
112+
* <a href="#implementation-guidelines">Implementation Guidelines</a>.
128113
*
129114
* @param context the current extension context; never {@code null}
130115
* @param throwable the {@code Throwable} to handle; never {@code null}
131116
*/
132117
default void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable throwable)
133118
throws Throwable {
119+
134120
throw throwable;
135121
}
122+
136123
}

junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestExecutionExceptionHandler.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
* constructor requirements.
3232
*
3333
* @see LifecycleMethodExecutionExceptionHandler
34-
*
3534
* @since 5.0
3635
*/
3736
@FunctionalInterface

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassBasedTestDescriptor.java

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ private void invokeBeforeAllMethods(JupiterEngineExecutionContext context) {
371371
ReflectiveInterceptorCall.ofVoidMethod(InvocationInterceptor::interceptBeforeAllMethod));
372372
}
373373
catch (Throwable throwable) {
374-
invokeBeforeAllExecutionExceptionHandlers(registry, extensionContext, throwable);
374+
invokeBeforeAllMethodExecutionExceptionHandlers(registry, extensionContext, throwable);
375375
}
376376
});
377377
if (throwableCollector.isNotEmpty()) {
@@ -380,13 +380,11 @@ private void invokeBeforeAllMethods(JupiterEngineExecutionContext context) {
380380
}
381381
}
382382

383-
private void invokeBeforeAllExecutionExceptionHandlers(ExtensionRegistry registry, ExtensionContext context,
383+
private void invokeBeforeAllMethodExecutionExceptionHandlers(ExtensionRegistry registry, ExtensionContext context,
384384
Throwable throwable) {
385385

386-
invokeExecutionExceptionHandlers(throwable,
387-
registry.getReversedExtensions(LifecycleMethodExecutionExceptionHandler.class),
388-
(ex, handler) -> () -> ((LifecycleMethodExecutionExceptionHandler) handler).handleBeforeAllMethodExecutionException(
389-
context, ex));
386+
invokeExecutionExceptionHandlers(LifecycleMethodExecutionExceptionHandler.class, registry, throwable,
387+
(handler, handledThrowable) -> handler.handleBeforeAllMethodExecutionException(context, handledThrowable));
390388
}
391389

392390
private void invokeAfterAllMethods(JupiterEngineExecutionContext context) {
@@ -401,18 +399,16 @@ private void invokeAfterAllMethods(JupiterEngineExecutionContext context) {
401399
ReflectiveInterceptorCall.ofVoidMethod(InvocationInterceptor::interceptAfterAllMethod));
402400
}
403401
catch (Throwable throwable) {
404-
invokeAfterAllExecutionExceptionHandlers(registry, extensionContext, throwable);
402+
invokeAfterAllMethodExecutionExceptionHandlers(registry, extensionContext, throwable);
405403
}
406404
}));
407405
}
408406

409-
private void invokeAfterAllExecutionExceptionHandlers(ExtensionRegistry registry, ExtensionContext context,
407+
private void invokeAfterAllMethodExecutionExceptionHandlers(ExtensionRegistry registry, ExtensionContext context,
410408
Throwable throwable) {
411409

412-
invokeExecutionExceptionHandlers(throwable,
413-
registry.getReversedExtensions(LifecycleMethodExecutionExceptionHandler.class),
414-
(ex, handler) -> () -> ((LifecycleMethodExecutionExceptionHandler) handler).handleAfterAllMethodExecutionException(
415-
context, ex));
410+
invokeExecutionExceptionHandlers(LifecycleMethodExecutionExceptionHandler.class, registry, throwable,
411+
(handler, handledThrowable) -> handler.handleAfterAllMethodExecutionException(context, handledThrowable));
416412
}
417413

418414
private void invokeAfterAllCallbacks(JupiterEngineExecutionContext context) {

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptor.java

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import java.util.List;
2525
import java.util.Optional;
2626
import java.util.Set;
27-
import java.util.function.BiFunction;
2827
import java.util.function.Supplier;
2928

3029
import org.apiguardian.api.API;
@@ -37,6 +36,7 @@
3736
import org.junit.jupiter.engine.config.JupiterConfiguration;
3837
import org.junit.jupiter.engine.execution.ConditionEvaluator;
3938
import org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;
39+
import org.junit.jupiter.engine.extension.ExtensionRegistry;
4040
import org.junit.platform.commons.JUnitException;
4141
import org.junit.platform.commons.logging.Logger;
4242
import org.junit.platform.commons.logging.LoggerFactory;
@@ -50,7 +50,6 @@
5050
import org.junit.platform.engine.support.hierarchical.ExclusiveResource;
5151
import org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode;
5252
import org.junit.platform.engine.support.hierarchical.Node;
53-
import org.junit.platform.engine.support.hierarchical.ThrowableCollector;
5453

5554
/**
5655
* @since 5.0
@@ -63,7 +62,7 @@ public abstract class JupiterTestDescriptor extends AbstractTestDescriptor
6362

6463
private static final ConditionEvaluator conditionEvaluator = new ConditionEvaluator();
6564

66-
protected final JupiterConfiguration configuration;
65+
final JupiterConfiguration configuration;
6766

6867
JupiterTestDescriptor(UniqueId uniqueId, AnnotatedElement element, Supplier<String> displayNameSupplier,
6968
TestSource source, JupiterConfiguration configuration) {
@@ -78,7 +77,7 @@ public abstract class JupiterTestDescriptor extends AbstractTestDescriptor
7877

7978
// --- TestDescriptor ------------------------------------------------------
8079

81-
protected static Set<TestTag> getTags(AnnotatedElement element) {
80+
static Set<TestTag> getTags(AnnotatedElement element) {
8281
// @formatter:off
8382
return findRepeatableAnnotations(element, Tag.class).stream()
8483
.map(Tag::value)
@@ -102,24 +101,30 @@ protected static Set<TestTag> getTags(AnnotatedElement element) {
102101
}
103102

104103
/**
105-
* Invokes handlers on the {@code Throwable} one by one until none are left or the throwable to handle
106-
* has been swallowed.
104+
* Invoke exception handlers for the supplied {@code Throwable} one by one until
105+
* none are left or the throwable to handle has been swallowed.
107106
*/
108-
void invokeExecutionExceptionHandlers(Throwable throwable, List<? extends Extension> handlers,
109-
BiFunction<Throwable, Extension, ThrowableCollector.Executable> generator) {
107+
<T extends Extension> void invokeExecutionExceptionHandlers(Class<T> handlerType, ExtensionRegistry registry,
108+
Throwable throwable, ExceptionHandlerInvoker<T> handlerInvoker) {
109+
110+
invokeExecutionExceptionHandlers(registry.getReversedExtensions(handlerType), throwable, handlerInvoker);
111+
}
112+
113+
private <T extends Extension> void invokeExecutionExceptionHandlers(List<T> handlers, Throwable throwable,
114+
ExceptionHandlerInvoker<T> handlerInvoker) {
115+
110116
// No handlers left?
111117
if (handlers.isEmpty()) {
112118
ExceptionUtils.throwAsUncheckedException(throwable);
113119
}
114120

115121
try {
116122
// Invoke next available handler
117-
ThrowableCollector.Executable executable = generator.apply(throwable, handlers.remove(0));
118-
executable.execute();
123+
handlerInvoker.invoke(handlers.remove(0), throwable);
119124
}
120125
catch (Throwable handledThrowable) {
121126
BlacklistedExceptions.rethrowIfBlacklisted(handledThrowable);
122-
invokeExecutionExceptionHandlers(handledThrowable, handlers, generator);
127+
invokeExecutionExceptionHandlers(handlers, handledThrowable, handlerInvoker);
123128
}
124129
}
125130

@@ -147,15 +152,15 @@ public ExecutionMode getExecutionMode() {
147152
return toExecutionMode(configuration.getDefaultExecutionMode());
148153
}
149154

150-
protected Optional<ExecutionMode> getExplicitExecutionMode() {
155+
Optional<ExecutionMode> getExplicitExecutionMode() {
151156
return Optional.empty();
152157
}
153158

154-
protected Optional<ExecutionMode> getDefaultChildExecutionMode() {
159+
Optional<ExecutionMode> getDefaultChildExecutionMode() {
155160
return Optional.empty();
156161
}
157162

158-
protected Optional<ExecutionMode> getExecutionModeFromAnnotation(AnnotatedElement element) {
163+
Optional<ExecutionMode> getExecutionModeFromAnnotation(AnnotatedElement element) {
159164
// @formatter:off
160165
return findAnnotation(element, Execution.class)
161166
.map(Execution::value)
@@ -173,7 +178,7 @@ public static ExecutionMode toExecutionMode(org.junit.jupiter.api.parallel.Execu
173178
throw new JUnitException("Unknown ExecutionMode: " + mode);
174179
}
175180

176-
protected Set<ExclusiveResource> getExclusiveResourcesFromAnnotation(AnnotatedElement element) {
181+
Set<ExclusiveResource> getExclusiveResourcesFromAnnotation(AnnotatedElement element) {
177182
// @formatter:off
178183
return findRepeatableAnnotations(element, ResourceLock.class).stream()
179184
.map(resource -> new ExclusiveResource(resource.value(), toLockMode(resource.mode())))
@@ -217,4 +222,11 @@ public void cleanUp(JupiterEngineExecutionContext context) throws Exception {
217222
context.close();
218223
}
219224

225+
@FunctionalInterface
226+
interface ExceptionHandlerInvoker<T extends Extension> {
227+
228+
void invoke(T t, Throwable throwable) throws Throwable;
229+
230+
}
231+
220232
}

0 commit comments

Comments
 (0)