/*
* 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.Iterator;
import java.util.stream.Stream;
import org.junit.jupiter.api.DynamicContainer;
import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.extension.TestExtensionContext;
import org.junit.jupiter.engine.execution.ExecutableInvoker;
import org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;
import org.junit.platform.commons.JUnitException;
import org.junit.platform.commons.meta.API;
import org.junit.platform.commons.util.CollectionUtils;
import org.junit.platform.commons.util.PreconditionViolationException;
import org.junit.platform.engine.TestSource;
import org.junit.platform.engine.UniqueId;
/**
* {@link org.junit.platform.engine.TestDescriptor TestDescriptor} for {@link org.junit.jupiter.api.TestFactory @TestFactory}
* methods.
*
* @since 5.0
*/
@API(Internal)
public class TestFactoryTestDescriptor extends MethodTestDescriptor {
public static final String DYNAMIC_CONTAINER_SEGMENT_TYPE = "dynamic-container";
public static final String DYNAMIC_TEST_SEGMENT_TYPE = "dynamic-test";
private static final ExecutableInvoker executableInvoker = new ExecutableInvoker();
public TestFactoryTestDescriptor(UniqueId uniqueId, Class<?> testClass, Method testMethod) {
super(uniqueId, testClass, testMethod);
}
// --- TestDescriptor ------------------------------------------------------
@Override
public Type getType() {
return Type.CONTAINER;
}
@Override
public boolean hasTests() {
return true;
}
// --- Node ----------------------------------------------------------------
@Override
protected void invokeTestMethod(JupiterEngineExecutionContext context, DynamicTestExecutor dynamicTestExecutor) {
TestExtensionContext testExtensionContext = (TestExtensionContext) context.getExtensionContext();
context.getThrowableCollector().execute(() -> {
Object instance = testExtensionContext.getTestInstance();
Object testFactoryMethodResult = executableInvoker.invoke(getTestMethod(), instance, testExtensionContext,
context.getExtensionRegistry());
TestSource source = getSource().orElseThrow(() -> new JUnitException("Test source must be present"));
try (Stream<DynamicNode> dynamicNodeStream = toDynamicNodeStream(testFactoryMethodResult)) {
int index = 1;
Iterator<DynamicNode> iterator = dynamicNodeStream.iterator();
while (iterator.hasNext()) {
DynamicNode dynamicNode = iterator.next();
JupiterTestDescriptor descriptor = createDynamicDescriptor(this, dynamicNode, index++, source);
dynamicTestExecutor.execute(descriptor);
}
}
catch (ClassCastException ex) {
throw invalidReturnTypeException(ex);
}
});
}
@SuppressWarnings("unchecked")
private Stream<DynamicNode> toDynamicNodeStream(Object testFactoryMethodResult) {
try {
return (Stream<DynamicNode>) CollectionUtils.toStream(testFactoryMethodResult);
}
catch (PreconditionViolationException ex) {
throw invalidReturnTypeException(ex);
}
}
static JupiterTestDescriptor createDynamicDescriptor(JupiterTestDescriptor parent, DynamicNode node, int index,
TestSource source) {
JupiterTestDescriptor descriptor;
if (node instanceof DynamicTest) {
DynamicTest test = (DynamicTest) node;
UniqueId uniqueId = parent.getUniqueId().append(DYNAMIC_TEST_SEGMENT_TYPE, "#" + index);
descriptor = new DynamicTestTestDescriptor(uniqueId, test, source);
}
else {
DynamicContainer container = (DynamicContainer) node;
UniqueId uniqueId = parent.getUniqueId().append(DYNAMIC_CONTAINER_SEGMENT_TYPE, "#" + index);
descriptor = new DynamicContainerTestDescriptor(uniqueId, container, source);
}
parent.addChild(descriptor);
return descriptor;
}
private JUnitException invalidReturnTypeException(Throwable cause) {
String message = String.format(
"@TestFactory method [%s] must return a Stream, Collection, Iterable, or Iterator of %s.",
getTestMethod().toGenericString(), DynamicNode.class.getName());
return new JUnitException(message, cause);
}
}