Skip to content

Commit

Permalink
Verify and fix interceptor contexts (#1676)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vampire authored Dec 17, 2023
1 parent aff45fa commit 490a6fa
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,14 @@ public void runFeature(SpockExecutionContext context, Runnable feature) {
if (currentFeature.isSkipped()) {
throw new InternalSpockError("Invalid state, feature is executed although it should have been skipped");
}
getSpecificationContext(context).setCurrentFeature(currentFeature);

supervisor.beforeFeature(currentFeature);
invoke(context, this, createMethodInfoForDoRunFeature(context, feature));
supervisor.afterFeature(currentFeature);

runCloseContextStoreProvider(context, MethodKind.CLEANUP);
getSpecificationContext(context).setCurrentFeature(null);
}

private MethodInfo createMethodInfoForDoRunFeature(SpockExecutionContext context, Runnable feature) {
Expand Down Expand Up @@ -254,6 +257,8 @@ void runParameterizedFeature(SpockExecutionContext context, ParameterizedFeature
}

void runInitializer(SpockExecutionContext context) {
getSpecificationContext(context).setCurrentFeature(context.getCurrentFeature());
getSpecificationContext(context).setCurrentIteration(context.getCurrentIteration());
runInitializer(context, context.getSpec());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

public class SpecificationContext implements ISpecificationContext {
private volatile SpecInfo currentSpec;
private volatile FeatureInfo currentFeature;
private volatile IterationInfo currentIteration;

private volatile Specification sharedInstance;
Expand Down Expand Up @@ -36,16 +37,20 @@ public void setCurrentSpec(SpecInfo currentSpec) {

@Override
public FeatureInfo getCurrentFeature() {
if (currentIteration == null) {
if (currentFeature == null) {
throw new IllegalStateException("Cannot request current feature in @Shared context");
}
return getCurrentIteration().getFeature();
return currentFeature;
}

public void setCurrentFeature(FeatureInfo currentFeature) {
this.currentFeature = currentFeature;
}

@Override
public IterationInfo getCurrentIteration() {
if (currentIteration == null) {
throw new IllegalStateException("Cannot request current iteration in @Shared context");
throw new IllegalStateException("Cannot request current iteration in @Shared context, or feature context");
}
return currentIteration;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class Interceptors extends EmbeddedSpecification {
}

@ResourceLock("LifecycleRecorderAndContextTesterExtension.lifecycleOutline")
def "interceptors are called in the correct order"() {
def "interceptors are called in the correct order and with the correct context information"() {
when:
runner.runWithImports """
abstract class SuperSpec extends Specification {
Expand Down Expand Up @@ -155,64 +155,191 @@ class SubSpec extends SuperSpec {
@Override
void visitSpecAnnotation(LifecycleTest annotation, SpecInfo specInfo) {
specInfo.specsBottomToTop*.addSharedInitializerInterceptor {
assertSpecContext(it)
proceed(it, 'shared initializer', "$it.spec.name")
}
specInfo.allSharedInitializerMethods*.addInterceptor {
assertSpecMethodContext(it)
proceed(it, 'shared initializer method', "$it.spec.name.$it.method.name()")
}
specInfo.addInterceptor {
assertSpecContext(it)
proceed(it, 'specification', "$it.spec.name")
}
specInfo.specsBottomToTop*.addSetupSpecInterceptor {
assertSpecContext(it)
proceed(it, 'setup spec', "$it.spec.name")
}
specInfo.allSetupSpecMethods*.addInterceptor {
assertSpecMethodContext(it)
proceed(it, 'setup spec method', "$it.spec.name.$it.method.name()")
}
specInfo.allFeatures*.addInterceptor {
assertFeatureContext(it)
proceed(it, 'feature', "$it.spec.name.$it.feature.name")
}
specInfo.specsBottomToTop.each { spec ->
spec.addInitializerInterceptor {
assertIterationContext(it)
proceed(it, 'initializer', "$it.spec.name.$it.feature.name / $spec.name")
}
}
specInfo.allInitializerMethods*.addInterceptor {
assertIterationMethodContext(it)
proceed(it, 'initializer method', "$it.feature.parent.name.$it.feature.name[#$it.iteration.iterationIndex] / $it.spec.name.$it.method.name()")
}
specInfo.allFeatures*.addIterationInterceptor {
assertIterationContext(it)
proceed(it, 'iteration', "$it.spec.name.$it.feature.name[#$it.iteration.iterationIndex]")
}
specInfo.specsBottomToTop.each { spec ->
spec.addSetupInterceptor {
assertIterationContext(it)
proceed(it, 'setup', "$it.spec.name.$it.feature.name[#$it.iteration.iterationIndex] / $spec.name")
}
}
specInfo.allSetupMethods*.addInterceptor {
assertIterationMethodContext(it)
proceed(it, 'setup method', "$it.feature.parent.name.$it.feature.name[#$it.iteration.iterationIndex] / $it.spec.name.$it.method.name()")
}
specInfo.allFeatures*.featureMethod*.addInterceptor {
assertIterationMethodContext(it)
proceed(it, 'feature method', "$it.feature.parent.name.$it.feature.name[#$it.iteration.iterationIndex] / $it.spec.name.$it.method.name()")
}
specInfo.specsBottomToTop.each { spec ->
spec.addCleanupInterceptor {
assertIterationContext(it)
proceed(it, 'cleanup', "$it.spec.name.$it.feature.name[#$it.iteration.iterationIndex] / $spec.name")
}
}
specInfo.allCleanupMethods*.addInterceptor {
assertIterationMethodContext(it)
proceed(it, 'cleanup method', "$it.feature.parent.name.$it.feature.name[#$it.iteration.iterationIndex] / $it.spec.name.$it.method.name()")
}
specInfo.specsBottomToTop*.addCleanupSpecInterceptor {
assertSpecContext(it)
proceed(it, 'cleanup spec', "$it.spec.name")
}
specInfo.allCleanupSpecMethods*.addInterceptor {
assertSpecMethodContext(it)
proceed(it, 'cleanup spec method', "$it.spec.name.$it.method.name()")
}
specInfo.allFixtureMethods*.addInterceptor {
it.with {
def specFixture = method.name.endsWith('Spec')
if (specFixture) {
assertSpecMethodContext(it)
} else {
assertIterationMethodContext(it)
}
}
proceed(it, 'fixture method', "${it.feature?.with { feature -> "$feature.parent.name.$feature.name[#$it.iteration.iterationIndex] / " } ?: ''}$it.spec.name.$it.method.name()")
}
}

static assertSpecCommonContext(IMethodInvocation invocation) {
invocation.with {
assert spec
assert !feature
assert !iteration
assert instance == sharedInstance
assert method
instance.specificationContext.with {
assert currentSpec
try {
currentFeature
assert false: 'currentFeature should not be set'
} catch (IllegalStateException ise) {
assert ise.message == 'Cannot request current feature in @Shared context'
}
try {
currentIteration
assert false: 'currentIteration should not be set'
} catch (IllegalStateException ise) {
assert ise.message == 'Cannot request current iteration in @Shared context, or feature context'
}
}
}
}

static assertSpecContext(IMethodInvocation invocation) {
assertSpecCommonContext(invocation)
invocation.with {
assert target != instance
assert !method.reflection
assert !method.name
}
}

static assertSpecMethodContext(IMethodInvocation invocation) {
assertSpecCommonContext(invocation)
invocation.with {
assert target == instance
assert method.reflection
assert method.name
}
}

static assertFeatureCommonContext(IMethodInvocation invocation) {
invocation.with {
assert spec
assert feature
assert method
instance.specificationContext.with {
assert currentSpec
assert currentFeature
}
}
}

static assertFeatureContext(IMethodInvocation invocation) {
assertFeatureCommonContext(invocation)
invocation.with {
assert !iteration
assert instance == sharedInstance
assert target != instance
assert !method.reflection
assert !method.name
instance.specificationContext.with {
try {
currentIteration
assert false: 'currentIteration should not be set'
} catch (IllegalStateException ise) {
assert ise.message == 'Cannot request current iteration in @Shared context, or feature context'
}
}
}
}

static assertIterationCommonContext(IMethodInvocation invocation) {
assertFeatureCommonContext(invocation)
invocation.with {
assert iteration
assert instance != sharedInstance
instance.specificationContext.with {
assert currentIteration
}
}
}

static assertIterationContext(IMethodInvocation invocation) {
assertIterationCommonContext(invocation)
invocation.with {
assert target != instance
assert !method.reflection
assert !method.name
}
}

static assertIterationMethodContext(IMethodInvocation invocation) {
assertIterationCommonContext(invocation)
invocation.with {
assert target == instance
assert method.reflection
assert method.name
}
}

void proceed(IMethodInvocation invocation, String type, String name) {
lifecycleOutline += "$indent$type start ($name)\n"
indent += ' '
Expand Down

0 comments on commit 490a6fa

Please sign in to comment.