com.github.dakusui.jcunit8.runners.junit4.JCUnit8 Maven / Gradle / Ivy
package com.github.dakusui.jcunit8.runners.junit4;
import com.github.dakusui.jcunit.core.tuples.Tuple;
import com.github.dakusui.jcunit8.core.Utils;
import com.github.dakusui.jcunit8.exceptions.TestDefinitionException;
import com.github.dakusui.jcunit8.factorspace.Constraint;
import com.github.dakusui.jcunit8.factorspace.ParameterSpace;
import com.github.dakusui.jcunit8.pipeline.Config;
import com.github.dakusui.jcunit8.pipeline.Pipeline;
import com.github.dakusui.jcunit8.pipeline.stages.ConfigFactory;
import com.github.dakusui.jcunit8.runners.core.NodeUtils;
import com.github.dakusui.jcunit8.runners.junit4.annotations.*;
import com.github.dakusui.jcunit8.runners.junit4.utils.InternalUtils;
import com.github.dakusui.jcunit8.testsuite.TestCase;
import com.github.dakusui.jcunit8.testsuite.TestSuite;
import org.junit.*;
import org.junit.internal.runners.rules.RuleMemberValidator;
import org.junit.internal.runners.statements.RunAfters;
import org.junit.internal.runners.statements.RunBefores;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.ParentRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestClass;
import org.junit.validator.AnnotationsValidator;
import org.junit.validator.PublicClassValidator;
import org.junit.validator.TestClassValidator;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.github.dakusui.jcunit8.core.Utils.createTestClassMock;
import static com.github.dakusui.jcunit8.exceptions.FrameworkException.unexpectedByDesign;
import static com.github.dakusui.jcunit8.exceptions.TestDefinitionException.parameterWithoutAnnotation;
import static com.github.dakusui.jcunit8.factorspace.Parameter.Factory;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
public class JCUnit8 extends org.junit.runners.Parameterized {
private final TestSuite testSuite;
private final List runners;
public JCUnit8(Class> klass) throws Throwable {
super(klass);
ConfigFactory configFactory = getConfigFactory();
TestClass parameterSpaceDefinition = createParameterSpaceDefinitionTestClass();
Collection involvedParameterNames = InternalUtils.involvedParameters(new TestClass(klass));
this.testSuite = buildTestSuite(
configFactory.create(),
buildParameterSpace(
new ArrayList<>(
buildParameterMap(parameterSpaceDefinition).values()
).stream(
).filter(
parameter -> involvedParameterNames.contains(parameter.getName())
).collect(
toList()
),
NodeUtils.allTestPredicates(createParameterSpaceDefinitionTestClass()).values().stream()
.filter(each -> each instanceof Constraint)
.map(Constraint.class::cast)
.collect(toList())
));
this.runners = createRunners();
}
@Override
protected void collectInitializationErrors(List errors) {
this.applyValidators(errors);
}
private void applyValidators(List errors) {
if (getTestClass().getJavaClass() != null) {
for (TestClassValidator each : createValidatorsFor(createParameterSpaceDefinitionTestClass())) {
errors.addAll(each.validateTestClass(getTestClass()));
}
}
}
@Override
protected List getChildren() {
return this.runners;
}
/**
* Mock {@code Parameterized} runner of JUnit 4.12.
*/
@Override
protected TestClass createTestClass(Class> testClass) {
return createTestClassMock(super.createTestClass(testClass));
}
private TestClassValidator[] createValidatorsFor(TestClass parameterSpaceDefinitionClass) {
return new TestClassValidator[] {
new AnnotationsValidator(),
new PublicClassValidator(),
new TestClassValidator() {
@Override
public List validateTestClass(TestClass testClass) {
return new LinkedList() {
{
validateFromAnnotationsAreReferencingExistingParameterSourceMethods(BeforeTestCase.class, testClass, this);
validateFromAnnotationsAreReferencingExistingParameterSourceMethods(Before.class, testClass, this);
validateFromAnnotationsAreReferencingExistingParameterSourceMethods(Test.class, testClass, this);
validateFromAnnotationsAreReferencingExistingParameterSourceMethods(After.class, testClass, this);
validateFromAnnotationsAreReferencingExistingParameterSourceMethods(AfterTestCase.class, testClass, this);
validateAtLeastOneTestMethod(testClass, this);
}
};
}
private void validateAtLeastOneTestMethod(TestClass testClass, LinkedList errors) {
if (testClass.getAnnotatedMethods(Test.class).isEmpty()) {
errors.add(new Exception("No runnable methods"));
}
}
private void validateFromAnnotationsAreReferencingExistingParameterSourceMethods(Class extends Annotation> ann, TestClass testClass, List errors) {
testClass.getAnnotatedMethods(ann)
.forEach(
frameworkMethod -> Stream.of(frameworkMethod.getMethod().getParameterAnnotations())
.forEach((Annotation[] annotations) -> Stream.of(annotations)
.filter((Annotation annotation) -> annotation instanceof From)
.forEach((Annotation annotation) -> {
List methods = parameterSpaceDefinitionClass.getAnnotatedMethods(ParameterSource.class).stream()
.filter(
(FrameworkMethod each) ->
Objects.equals(each.getName(), From.class.cast(annotation).value()))
.collect(toList());
if (methods.isEmpty())
errors.add(new Exception(
format(
"A method '%s' annotated with '%s' is not defined in '%s'",
From.class.cast(annotation).value(),
ParameterSource.class.getSimpleName(),
parameterSpaceDefinitionClass.getJavaClass().getCanonicalName()
)));
})));
}
}
};
}
private ConfigFactory getConfigFactory() {
try {
return getConfigureWithAnnotation().value().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw TestDefinitionException.wrap(e);
}
}
private TestClass createParameterSpaceDefinitionTestClass() {
Class parameterSpaceClass = getConfigureWithAnnotation().parameterSpace();
return Objects.equals(parameterSpaceClass, ConfigureWith.DEFAULT_INSTANCE.parameterSpace()) ?
this.getTestClass() :
new TestClass(parameterSpaceClass);
}
private ConfigureWith getConfigureWithAnnotation() {
ConfigureWith ret = this.getTestClass().getAnnotation(ConfigureWith.class);
if (ret == null)
ret = ConfigureWith.DEFAULT_INSTANCE;
return ret;
}
private ParameterSpace buildParameterSpace(List parameters, List constraints) {
return new ParameterSpace.Builder()
.addAllParameters(parameters)
.addAllConstraints(constraints)
.build();
}
private List createRunners() {
AtomicInteger i = new AtomicInteger(0);
return this.testSuite.stream()
.map((Function) tupleTestCase -> {
try {
return new TestCaseRunner(i.getAndIncrement(), tupleTestCase);
} catch (InitializationError initializationError) {
throw unexpectedByDesign(formatInitializationErrorMessage(initializationError));
}
})
.collect(toList());
}
private static String formatInitializationErrorMessage(InitializationError e) {
return e.getCauses().stream().map(Throwable::getMessage).collect(Collectors.joining());
}
private static SortedMap buildParameterMap(TestClass parameterSpaceDefinitionTestClass) {
return new TreeMap() {
{
parameterSpaceDefinitionTestClass.getAnnotatedMethods(ParameterSource.class).forEach(
frameworkMethod -> put(frameworkMethod.getName(),
buildParameterFactoryCreatorFrom(frameworkMethod)
.apply(Utils.createInstanceOf(parameterSpaceDefinitionTestClass))
.create(frameworkMethod.getName())
));
}
};
}
private static TestSuite buildTestSuite(Config config, ParameterSpace parameterSpace) {
return Pipeline.Standard.create().execute(config, parameterSpace);
}
private static Function © 2015 - 2025 Weber Informatics LLC | Privacy Policy