/* * Copyright 2015-2017 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.jupiter.engine.descriptor; import static org.junit.platform.commons.meta.API.Usage.Internal; import java.lang.reflect.Method; import java.util.List; import java.util.Optional; import java.util.function.BiFunction; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.AfterTestExecutionCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.BeforeTestExecutionCallback; import org.junit.jupiter.api.extension.Extension; import org.junit.jupiter.api.extension.TestExecutionExceptionHandler; import org.junit.jupiter.api.extension.TestExtensionContext; import org.junit.jupiter.api.function.Executable; import org.junit.jupiter.engine.execution.AfterEachMethodAdapter; import org.junit.jupiter.engine.execution.BeforeEachMethodAdapter; import org.junit.jupiter.engine.execution.ExecutableInvoker; import org.junit.jupiter.engine.execution.JupiterEngineExecutionContext; import org.junit.jupiter.engine.execution.ThrowableCollector; import org.junit.jupiter.engine.extension.ExtensionRegistry; import org.junit.platform.commons.meta.API; import org.junit.platform.commons.util.ExceptionUtils; import org.junit.platform.engine.TestDescriptor; import org.junit.platform.engine.UniqueId; /** * {@link TestDescriptor} for tests based on Java methods. * * <h3>Default Display Names</h3> * * <p>The default display name for a test method is the name of the method * concatenated with a comma-separated list of parameter types in parentheses. * The names of parameter types are retrieved using {@link Class#getSimpleName()}. * For example, the default display name for the following test method is * {@code testUser(TestInfo, User)}. * * <pre class="code"> * {@literal @}Test * void testUser(TestInfo testInfo, {@literal @}Mock User user) { ... } * </pre> * * @since 5.0 */ @API(Internal) public class MethodTestDescriptor extends MethodBasedTestDescriptor { private static final ExecutableInvoker executableInvoker = new ExecutableInvoker(); public MethodTestDescriptor(UniqueId uniqueId, Class<?> testClass, Method testMethod) { super(uniqueId, testClass, testMethod); } MethodTestDescriptor(UniqueId uniqueId, String displayName, Class<?> testClass, Method testMethod) { super(uniqueId, displayName, testClass, testMethod); } @Override public Type getType() { return Type.TEST; } // --- Node ---------------------------------------------------------------- @Override public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) throws Exception { ExtensionRegistry registry = populateNewExtensionRegistry(context); Object testInstance = context.getTestInstanceProvider().getTestInstance(Optional.of(registry)); ThrowableCollector throwableCollector = new ThrowableCollector(); TestExtensionContext testExtensionContext = new MethodBasedTestExtensionContext(context.getExtensionContext(), context.getExecutionListener(), this, testInstance, throwableCollector); // @formatter:off return context.extend() .withExtensionRegistry(registry) .withExtensionContext(testExtensionContext) .withThrowableCollector(throwableCollector) .build(); // @formatter:on } protected ExtensionRegistry populateNewExtensionRegistry(JupiterEngineExecutionContext context) { return populateNewExtensionRegistryFromExtendWith(getTestMethod(), context.getExtensionRegistry()); } @Override public SkipResult shouldBeSkipped(JupiterEngineExecutionContext context) throws Exception { return shouldTestBeSkipped(context); } @Override public JupiterEngineExecutionContext execute(JupiterEngineExecutionContext context, DynamicTestExecutor dynamicTestExecutor) throws Exception { ThrowableCollector throwableCollector = context.getThrowableCollector(); // @formatter:off invokeBeforeEachCallbacks(context); if (throwableCollector.isEmpty()) { invokeBeforeEachMethods(context); if (throwableCollector.isEmpty()) { invokeBeforeTestExecutionCallbacks(context); if (throwableCollector.isEmpty()) { invokeTestMethod(context, dynamicTestExecutor); } invokeAfterTestExecutionCallbacks(context); } invokeAfterEachMethods(context); } invokeAfterEachCallbacks(context); // @formatter:on throwableCollector.assertEmpty(); return context; } private void invokeBeforeEachCallbacks(JupiterEngineExecutionContext context) { invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(context, ((extensionContext, callback) -> () -> callback.beforeEach(extensionContext)), BeforeEachCallback.class); } private void invokeBeforeEachMethods(JupiterEngineExecutionContext context) { ExtensionRegistry registry = context.getExtensionRegistry(); invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(context, ((extensionContext, adapter) -> () -> adapter.invokeBeforeEachMethod(extensionContext, registry)), BeforeEachMethodAdapter.class); } private void invokeBeforeTestExecutionCallbacks(JupiterEngineExecutionContext context) { invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(context, ((extensionContext, callback) -> () -> callback.beforeTestExecution(extensionContext)), BeforeTestExecutionCallback.class); } private <T extends Extension> void invokeBeforeMethodsOrCallbacksUntilExceptionOccurs( JupiterEngineExecutionContext context, BiFunction<TestExtensionContext, T, Executable> generator, Class<T> type) { ExtensionRegistry registry = context.getExtensionRegistry(); TestExtensionContext testExtensionContext = (TestExtensionContext) context.getExtensionContext(); ThrowableCollector throwableCollector = context.getThrowableCollector(); for (T callback : registry.getExtensions(type)) { Executable executable = generator.apply(testExtensionContext, callback); throwableCollector.execute(executable); if (throwableCollector.isNotEmpty()) { break; } } } protected void invokeTestMethod(JupiterEngineExecutionContext context, DynamicTestExecutor dynamicTestExecutor) { TestExtensionContext testExtensionContext = (TestExtensionContext) context.getExtensionContext(); ThrowableCollector throwableCollector = context.getThrowableCollector(); throwableCollector.execute(() -> { try { Method testMethod = getTestMethod(); Object instance = testExtensionContext.getTestInstance(); executableInvoker.invoke(testMethod, instance, testExtensionContext, context.getExtensionRegistry()); } catch (Throwable throwable) { invokeTestExecutionExceptionHandlers(context.getExtensionRegistry(), testExtensionContext, throwable); } }); } private void invokeTestExecutionExceptionHandlers(ExtensionRegistry registry, TestExtensionContext context, Throwable ex) { invokeTestExecutionExceptionHandlers(ex, registry.getReversedExtensions(TestExecutionExceptionHandler.class), context); } private void invokeTestExecutionExceptionHandlers(Throwable ex, List<TestExecutionExceptionHandler> handlers, TestExtensionContext context) { // No handlers left? if (handlers.isEmpty()) { ExceptionUtils.throwAsUncheckedException(ex); } try { // Invoke next available handler handlers.remove(0).handleTestExecutionException(context, ex); } catch (Throwable t) { invokeTestExecutionExceptionHandlers(t, handlers, context); } } private void invokeAfterTestExecutionCallbacks(JupiterEngineExecutionContext context) { invokeAllAfterMethodsOrCallbacks(context, ((extensionContext, callback) -> () -> callback.afterTestExecution(extensionContext)), AfterTestExecutionCallback.class); } private void invokeAfterEachMethods(JupiterEngineExecutionContext context) { ExtensionRegistry registry = context.getExtensionRegistry(); invokeAllAfterMethodsOrCallbacks(context, ((extensionContext, adapter) -> () -> adapter.invokeAfterEachMethod(extensionContext, registry)), AfterEachMethodAdapter.class); } private void invokeAfterEachCallbacks(JupiterEngineExecutionContext context) { invokeAllAfterMethodsOrCallbacks(context, ((extensionContext, callback) -> () -> callback.afterEach(extensionContext)), AfterEachCallback.class); } private <T extends Extension> void invokeAllAfterMethodsOrCallbacks(JupiterEngineExecutionContext context, BiFunction<TestExtensionContext, T, Executable> generator, Class<T> type) { ExtensionRegistry registry = context.getExtensionRegistry(); TestExtensionContext testExtensionContext = (TestExtensionContext) context.getExtensionContext(); ThrowableCollector throwableCollector = context.getThrowableCollector(); registry.getReversedExtensions(type).forEach(callback -> { Executable executable = generator.apply(testExtensionContext, callback); throwableCollector.execute(executable); }); } }