package com.equalexperts.logging;
import org.junit.runner.Runner;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.Suite;
import org.junit.runners.model.FrameworkField;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class EnumContractRunner extends Suite {
private static final List<Runner> NO_RUNNERS = Collections.emptyList();
private final List<Runner> runners = new ArrayList<>();
public EnumContractRunner(Class<?> klass) throws Throwable {
super(klass, NO_RUNNERS);
Class<? extends Enum<?>> enumClass = getEnumType(klass);
for(Enum<?> e : enumClass.getEnumConstants()) {
runners.add(new TestClassRunnerForEnum(klass, e));
}
}
<T extends java.lang.Enum<T>> Class<T> getEnumType(Class<?> klass) {
EnumToTest annotation = klass.getAnnotation(EnumToTest.class);
if (annotation == null) {
throw new AssertionError("Test class must be annotated with @EnumToTest");
}
Class<?> potentialEnumClass = annotation.value();
if (!potentialEnumClass.isEnum()) {
throw new AssertionError("@EnumToTest value() must be an Enum");
}
@SuppressWarnings("unchecked") //checked above
Class<T> enumClass = (Class<T>) potentialEnumClass;
return enumClass;
}
@Override
protected List<Runner> getChildren() {
return runners;
}
private class TestClassRunnerForEnum extends BlockJUnit4ClassRunner {
private final Enum<?> enumValue;
public TestClassRunnerForEnum(Class<?> klass, Enum<?> enumValue) throws InitializationError {
super(klass);
this.enumValue = enumValue;
}
@Override
protected Object createTest() throws Exception {
Object test = getTestClass().getOnlyConstructor().newInstance();
getTestClass().getAnnotatedFields(EnumField.class).get(0).getField().set(test, enumValue);
return test;
}
@Override
protected String getName() {
return enumValue.name();
}
@Override
protected String testName(FrameworkMethod method) {
return getName() + "_" + method.getName();
}
@Override
protected void validateConstructor(List<Throwable> errors) {
validateZeroArgConstructor(errors);
}
@Override
protected void validateFields(List<Throwable> errors) {
super.validateFields(errors);
List<FrameworkField> fields = getTestClass().getAnnotatedFields(EnumField.class);
if (fields.size() != 1) {
errors.add(new Exception("Need exactly one field annotated with @EnumField"));
}
if (!fields.get(0).isPublic()) {
fields.get(0).getField().setAccessible(true);
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public static @interface EnumToTest {
public Class value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public static @interface EnumField {
}
}