info.javaspec.spec.FieldSpec Maven / Gradle / Ivy
package info.javaspec.spec;
import info.javaspec.dsl.Before;
import info.javaspec.dsl.Cleanup;
import info.javaspec.dsl.It;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import java.lang.reflect.Field;
import java.util.List;
import static java.util.stream.Collectors.toList;
final class FieldSpec extends Spec {
private final Description testDescription;
private SpecState state;
FieldSpec(String id, Description testDescription, Field it, List beforeSpecFields, List afterSpecFields) {
super(id);
this.testDescription = testDescription;
this.state = new DeclaredState(it, beforeSpecFields, afterSpecFields);
}
@Override
public Description getDescription() { return testDescription; }
@Override
public void addDescriptionTo(Description suite) {
suite.addChild(testDescription);
}
@Override
public void run(RunNotifier notifier) {
try {
state = state.instantiate();
} catch(TestSetupFailed ex) {
notifier.fireTestFailure(new Failure(getDescription(), ex));
return;
}
state.run(notifier);
}
private final class DeclaredState implements SpecState {
private final Field assertionField;
private final List beforeSpecFields;
private final List afterSpecFields;
public DeclaredState(Field it, List beforeSpecFields, List afterSpecFields) {
this.assertionField = it;
this.beforeSpecFields = beforeSpecFields;
this.afterSpecFields = afterSpecFields;
}
@Override
public SpecState instantiate() {
SpecExecutionContext context = SpecExecutionContext.forDeclaringClass(assertionField.getDeclaringClass());
try {
List beforeThunks = beforeSpecFields.stream()
.map(context::getAssignedValue)
.map(Before.class::cast)
.collect(toList());
List afterThunks = afterSpecFields.stream()
.map(context::getAssignedValue)
.map(Cleanup.class::cast)
.collect(toList());
It assertionThunk = (It)context.getAssignedValue(assertionField);
if(beforeThunks.contains(null) || afterThunks.contains(null) || assertionThunk == null) {
return new PendingState();
} else {
return new RunnableState(assertionThunk, beforeThunks, afterThunks);
}
} catch(Throwable t) {
throw TestSetupFailed.forClass(assertionField.getDeclaringClass(), t);
}
}
@Override
public void run(RunNotifier notifier) {
throw new IllegalStateException("Spec has not been instantiated yet by creating a runnable state");
}
}
private final class PendingState implements SpecState {
@Override
public SpecState instantiate() { return this; }
@Override
public void run(RunNotifier notifier) {
notifier.fireTestIgnored(getDescription());
}
}
private final class RunnableState implements SpecState {
private final It assertionThunk;
private final List beforeThunks;
private final List afterThunks;
public RunnableState(It assertionThunk, List beforeThunks, List afterThunks) {
this.assertionThunk = assertionThunk;
this.beforeThunks = beforeThunks;
this.afterThunks = afterThunks;
}
@Override
public SpecState instantiate() { return this; }
@Override
public void run(RunNotifier notifier) {
notifier.fireTestStarted(getDescription());
try {
beforeSpec();
assertionThunk.run();
} catch(Exception | AssertionError ex) {
notifier.fireTestFailure(new Failure(getDescription(), ex));
return;
} finally {
try {
afterSpec();
} catch(Exception | AssertionError ex) {
notifier.fireTestFailure(new Failure(getDescription(), ex));
}
}
notifier.fireTestFinished(getDescription());
}
private void beforeSpec() throws Exception {
for(Before before : beforeThunks)
before.run();
}
private void afterSpec() throws Exception {
for(Cleanup after : afterThunks)
after.run();
}
}
private interface SpecState {
SpecState instantiate();
void run(RunNotifier notifier);
}
}