77import org .junit .jupiter .api .extension .BeforeEachCallback ;
88import org .junit .jupiter .api .extension .BeforeTestExecutionCallback ;
99import org .junit .jupiter .api .extension .Extension ;
10+ import org .junit .jupiter .api .extension .LifecycleMethodExecutionExceptionHandler ;
1011import org .junit .jupiter .api .extension .TestExecutionExceptionHandler ;
1112import org .junit .jupiter .api .extension .TestInstancePostProcessor ;
1213import org .junit .jupiter .api .extension .TestInstancePreDestroyCallback ;
1617import org .junit .platform .commons .util .ExceptionUtils ;
1718import org .junit .platform .commons .util .UnrecoverableExceptions ;
1819import org .junit .platform .engine .support .hierarchical .ThrowableCollector ;
20+ import ru .vyarus .spock .jupiter .engine .ExtensionRegistry ;
1921import ru .vyarus .spock .jupiter .engine .context .AbstractContext ;
2022import ru .vyarus .spock .jupiter .engine .context .ClassContext ;
2123import ru .vyarus .spock .jupiter .engine .context .MethodContext ;
@@ -71,11 +73,11 @@ public void beforeAll(final ClassContext context) {
7173 // org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance
7274 public void instancePostProcessors (final ClassContext context , final Object instance ) {
7375 context .getCollector ().execute (() -> {
74- getExtensions (context , TestInstancePostProcessor .class ).forEach (
75- extension -> executeAndMaskThrowable (() ->
76- extension .postProcessTestInstance (instance , context )));
77- // init non-static field extensions
78- context .getRegistry ().initializeExtensions (context .getRequiredTestClass (), instance );
76+ getExtensions (context , TestInstancePostProcessor .class ).forEach (
77+ extension -> executeAndMaskThrowable (() ->
78+ extension .postProcessTestInstance (instance , context )));
79+ // init non-static field extensions
80+ context .getRegistry ().initializeExtensions (context .getRequiredTestClass (), instance );
7981 });
8082 context .getCollector ().assertEmpty ();
8183
@@ -131,15 +133,43 @@ public void instancePreDestroy(final MethodContext context) {
131133
132134 // org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeAfterAllCallbacks
133135 public void afterAll (final ClassContext context ) {
134- final ThrowableCollector collector = context .getCollector ();
135- afterAllExtensions .forEach (
136- extension -> collector .execute (() -> extension .afterAll (context )));
137- collector .assertEmpty ();
136+ // null should be not possible, but just in case
137+ if (afterAllExtensions != null ) {
138+ final ThrowableCollector collector = context .getCollector ();
139+ afterAllExtensions .forEach (
140+ extension -> collector .execute (() -> extension .afterAll (context )));
141+ collector .assertEmpty ();
142+ }
143+ }
144+
145+ public void handleTestException (final MethodContext context , final Throwable throwable ) {
146+ invokeExecutionExceptionHandlers (TestExecutionExceptionHandler .class , context .getRegistry (), throwable ,
147+ (handler , handledThrowable ) -> handler
148+ .handleTestExecutionException (context , handledThrowable ));
149+ }
150+
151+ public void handleSetupSpecMethodException (final AbstractContext context , final Throwable throwable ) {
152+ invokeExecutionExceptionHandlers (LifecycleMethodExecutionExceptionHandler .class , context .getRegistry (),
153+ throwable , (handler , handledThrowable ) -> handler
154+ .handleBeforeAllMethodExecutionException (context , handledThrowable ));
155+ }
156+
157+ public void handleSetupMethodException (final AbstractContext context , final Throwable throwable ) {
158+ invokeExecutionExceptionHandlers (LifecycleMethodExecutionExceptionHandler .class , context .getRegistry (),
159+ throwable , (handler , handledThrowable ) -> handler
160+ .handleBeforeEachMethodExecutionException (context , handledThrowable ));
161+ }
162+
163+ public void handleCleanupMethodException (final AbstractContext context , final Throwable throwable ) {
164+ invokeExecutionExceptionHandlers (LifecycleMethodExecutionExceptionHandler .class , context .getRegistry (),
165+ throwable , (handler , handledThrowable ) -> handler
166+ .handleAfterEachMethodExecutionException (context , handledThrowable ));
138167 }
139168
140- // org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestExecutionExceptionHandlers
141- public void handleTestException (final MethodContext context , final Throwable error ) {
142- processTestException (context , getReversedExtensions (context , TestExecutionExceptionHandler .class ), error );
169+ public void handleCleanupSpecMethodException (final AbstractContext context , final Throwable throwable ) {
170+ invokeExecutionExceptionHandlers (LifecycleMethodExecutionExceptionHandler .class , context .getRegistry (),
171+ throwable , (handler , handledThrowable ) -> handler
172+ .handleAfterAllMethodExecutionException (context , handledThrowable ));
143173 }
144174
145175 private <T extends Extension > List <T > getReversedExtensions (final AbstractContext context , final Class <T > type ) {
@@ -165,29 +195,56 @@ private <T extends Extension> List<T> getExtensions(final AbstractContext contex
165195 return exts ;
166196 }
167197
168- private void executeAndMaskThrowable (final Executable executable ) {
169- try {
170- executable .execute ();
171- } catch (Throwable throwable ) {
172- ExceptionUtils .throwAsUncheckedException (throwable );
173- }
198+ /**
199+ * Invoke exception handlers for the supplied {@code Throwable} one-by-one
200+ * until none are left or the throwable to handle has been swallowed.
201+ */
202+ // org.junit.jupiter.engine.descriptor.JupiterTestDescriptor.invokeExecutionExceptionHandlers
203+ private <E extends Extension > void invokeExecutionExceptionHandlers (
204+ final Class <E > handlerType ,
205+ final ExtensionRegistry registry ,
206+ final Throwable throwable ,
207+ final ExceptionHandlerInvoker <E > handlerInvoker ) {
208+
209+ final List <E > extensions = registry .getReversedExtensions (handlerType );
210+ invokeExecutionExceptionHandlers (extensions , throwable , handlerInvoker );
174211 }
175212
176213 // org.junit.jupiter.engine.descriptor.JupiterTestDescriptor.invokeExecutionExceptionHandlers
177- private void processTestException (final MethodContext context ,
178- final List <TestExecutionExceptionHandler > handlers ,
179- final Throwable error ) {
214+ private <E extends Extension > void invokeExecutionExceptionHandlers (
215+ final List <E > exceptionHandlers ,
216+ final Throwable throwable ,
217+ final ExceptionHandlerInvoker <E > handlerInvoker ) {
218+
180219 // No handlers left?
181- if (handlers .isEmpty ()) {
182- ExceptionUtils .throwAsUncheckedException (error );
220+ if (exceptionHandlers .isEmpty ()) {
221+ throw ExceptionUtils .throwAsUncheckedException (throwable );
183222 }
184223
185224 try {
186225 // Invoke next available handler
187- handlers . remove (0 ). handleTestExecutionException ( context , error );
226+ handlerInvoker . invoke ( exceptionHandlers . remove (0 ), throwable );
188227 } catch (Throwable handledThrowable ) {
189228 UnrecoverableExceptions .rethrowIfUnrecoverable (handledThrowable );
190- processTestException ( context , handlers , handledThrowable );
229+ invokeExecutionExceptionHandlers ( exceptionHandlers , handledThrowable , handlerInvoker );
191230 }
192231 }
232+
233+ private void executeAndMaskThrowable (final Executable executable ) {
234+ try {
235+ executable .execute ();
236+ } catch (Throwable throwable ) {
237+ ExceptionUtils .throwAsUncheckedException (throwable );
238+ }
239+ }
240+
241+ @ FunctionalInterface
242+ interface ExceptionHandlerInvoker <E extends Extension > {
243+
244+ /**
245+ * Invoke the supplied {@code exceptionHandler} with the supplied {@code throwable}.
246+ */
247+ void invoke (E exceptionHandler , Throwable throwable ) throws Throwable ;
248+
249+ }
193250}
0 commit comments