package org.junit.jupiter.engine.descriptor;
import static java.util.stream.Collectors.toList;
import static org.apiguardian.api.API.Status.INTERNAL;
import static org.junit.jupiter.engine.descriptor.ExtensionUtils.populateNewExtensionRegistryFromExtendWithAnnotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import org.apiguardian.api.API;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestInstances;
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;
import org.junit.jupiter.engine.config.JupiterConfiguration;
import org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;
import org.junit.jupiter.engine.extension.ExtensionRegistry;
import org.junit.platform.commons.util.Preconditions;
import org.junit.platform.engine.TestDescriptor;
import org.junit.platform.engine.UniqueId;
@API(status = INTERNAL, since = "5.0")
public class TestTemplateTestDescriptor extends MethodBasedTestDescriptor implements Filterable {
private final DynamicDescendantFilter dynamicDescendantFilter = new DynamicDescendantFilter();
public TestTemplateTestDescriptor(UniqueId uniqueId, Class<?> testClass, Method templateMethod,
JupiterConfiguration configuration) {
super(uniqueId, testClass, templateMethod, configuration);
}
@Override
public DynamicDescendantFilter getDynamicDescendantFilter() {
return dynamicDescendantFilter;
}
@Override
public Type getType() {
return Type.CONTAINER;
}
@Override
public boolean mayRegisterTests() {
return true;
}
@Override
public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) throws Exception {
ExtensionRegistry registry = populateNewExtensionRegistryFromExtendWithAnnotation(
context.getExtensionRegistry(), getTestMethod());
TestInstances testInstances = context.getExtensionContext().getTestInstances().orElse(null);
ExtensionContext extensionContext = new TestTemplateExtensionContext(context.getExtensionContext(),
context.getExecutionListener(), this, context.getConfiguration(), testInstances);
return context.extend()
.withExtensionRegistry(registry)
.withExtensionContext(extensionContext)
.build();
}
@Override
public JupiterEngineExecutionContext execute(JupiterEngineExecutionContext context,
DynamicTestExecutor dynamicTestExecutor) throws Exception {
ExtensionContext extensionContext = context.getExtensionContext();
List<TestTemplateInvocationContextProvider> providers = validateProviders(extensionContext,
context.getExtensionRegistry());
AtomicInteger invocationIndex = new AtomicInteger();
providers.stream()
.flatMap(provider -> provider.provideTestTemplateInvocationContexts(extensionContext))
.map(invocationContext -> createInvocationTestDescriptor(invocationContext, invocationIndex.incrementAndGet()))
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(invocationTestDescriptor -> execute(dynamicTestExecutor, invocationTestDescriptor));
validateWasAtLeastInvokedOnce(invocationIndex.get());
return context;
}
private List<TestTemplateInvocationContextProvider> validateProviders(ExtensionContext extensionContext,
ExtensionRegistry extensionRegistry) {
List<TestTemplateInvocationContextProvider> providers = extensionRegistry.stream(TestTemplateInvocationContextProvider.class)
.filter(provider -> provider.supportsTestTemplate(extensionContext))
.collect(toList());
return Preconditions.notEmpty(providers,
() -> String.format("You must register at least one %s that supports @TestTemplate method [%s]",
TestTemplateInvocationContextProvider.class.getSimpleName(), getTestMethod()));
}
private Optional<TestDescriptor> createInvocationTestDescriptor(TestTemplateInvocationContext invocationContext,
int index) {
UniqueId uniqueId = getUniqueId().append(TestTemplateInvocationTestDescriptor.SEGMENT_TYPE, "#" + index);
if (getDynamicDescendantFilter().test(uniqueId)) {
return Optional.of(new TestTemplateInvocationTestDescriptor(uniqueId, getTestClass(), getTestMethod(),
invocationContext, index, configuration));
}
return Optional.empty();
}
private void execute(DynamicTestExecutor dynamicTestExecutor, TestDescriptor testDescriptor) {
addChild(testDescriptor);
dynamicTestExecutor.execute(testDescriptor);
}
private void validateWasAtLeastInvokedOnce(int invocationIndex) {
Preconditions.condition(invocationIndex > 0, () -> "No supporting "
+ TestTemplateInvocationContextProvider.class.getSimpleName() + " provided an invocation context");
}
}