diff --git a/spock-core/src/main/java/org/spockframework/runtime/PlatformSpecRunner.java b/spock-core/src/main/java/org/spockframework/runtime/PlatformSpecRunner.java index 6fdae65687..c81cec5f02 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/PlatformSpecRunner.java +++ b/spock-core/src/main/java/org/spockframework/runtime/PlatformSpecRunner.java @@ -188,7 +188,11 @@ 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); + + // at this point the specification context is the one of the shared instance, + // so the current feature cannot be set as features can run in parallel + // so getting the current feature from the specification context would not properly work + getSpecificationContext(context).pushStoreProvider(context.getStoreProvider()); supervisor.beforeFeature(currentFeature); @@ -196,7 +200,6 @@ public void runFeature(SpockExecutionContext context, Runnable feature) { supervisor.afterFeature(currentFeature); runCloseContextStoreProvider(context, MethodKind.CLEANUP); - getSpecificationContext(context).setCurrentFeature(null); getSpecificationContext(context).popStoreProvider(); } @@ -260,7 +263,6 @@ void runParameterizedFeature(SpockExecutionContext context, ParameterizedFeature } void runInitializer(SpockExecutionContext context) { - getSpecificationContext(context).setCurrentFeature(context.getCurrentFeature()); getSpecificationContext(context).setCurrentIteration(context.getCurrentIteration()); runInitializer(context, context.getSpec()); } diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java b/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java index 35b8a65660..d250afc157 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java +++ b/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java @@ -15,7 +15,6 @@ public class SpecificationContext implements ISpecificationContext { private volatile SpecInfo currentSpec; - private volatile FeatureInfo currentFeature; private volatile IterationInfo currentIteration; private volatile BlockInfo currentBlock; @@ -47,19 +46,18 @@ public void setCurrentSpec(SpecInfo currentSpec) { @Override public FeatureInfo getCurrentFeature() { - if (currentFeature == null) { - throw new IllegalStateException("Cannot request current feature in @Shared context"); + // before an iteration is available we are in the shared context, even in the feature interceptor, + // so the current feature cannot be set as features can run in parallel + // so getting the current feature from the specification context would not properly work + if (currentIteration == null) { + throw new IllegalStateException("Cannot request current feature in @Shared context, or feature context"); } - return currentFeature; + return currentIteration.getFeature(); } @Nullable FeatureInfo getCurrentFeatureOrNull() { - return currentFeature; - } - - public void setCurrentFeature(@Nullable FeatureInfo currentFeature) { - this.currentFeature = currentFeature; + return currentIteration == null ? null : currentIteration.getFeature(); } @Override diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/Interceptors.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/Interceptors.groovy index 2eb2a3cc04..f5bc2f3652 100644 --- a/spock-specs/src/test/groovy/org/spockframework/smoke/Interceptors.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/Interceptors.groovy @@ -157,104 +157,126 @@ class SubSpec extends SuperSpec { specInfo.specsBottomToTop*.addSharedInitializerInterceptor { assertSpecContext(it) proceed(it, 'shared initializer', "$it.spec.name") + assertSpecContext(it) } specInfo.allSharedInitializerMethods*.addInterceptor { assertSpecMethodContext(it) proceed(it, 'shared initializer method', "$it.spec.name.$it.method.name()") + assertSpecMethodContext(it) } specInfo.addInterceptor { assertSpecContext(it) proceed(it, 'specification', "$it.spec.name") + assertSpecContext(it) } specInfo.specsBottomToTop*.addSetupSpecInterceptor { assertSpecContext(it) proceed(it, 'setup spec', "$it.spec.name") + assertSpecContext(it) } specInfo.allSetupSpecMethods*.addInterceptor { assertSpecMethodContext(it) proceed(it, 'setup spec method', "$it.spec.name.$it.method.name()") + assertSpecMethodContext(it) } allFeatures*.addInterceptor { assertFeatureContext(it) proceed(it, 'feature', "$it.spec.name.$it.feature.name") + assertFeatureContext(it) } specInfo.specsBottomToTop.each { spec -> spec.addInitializerInterceptor { assertIterationContext(it) proceed(it, 'initializer', "$it.spec.name.$it.feature.name[#$it.iteration.iterationIndex] / $spec.name") + assertIterationContext(it) } } 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()") + assertIterationMethodContext(it) } allFeatures.each { feature -> specInfo.allInitializerMethods.each { mi -> feature.addScopedMethodInterceptor(mi) { assertIterationMethodContext(it) proceed(it, 'feature scoped initializer method', "$it.feature.parent.name.$it.feature.name[#$it.iteration.iterationIndex] / $it.spec.name.$it.method.name() / from $feature.name") + assertIterationMethodContext(it) } } specInfo.allSetupMethods.each { mi -> feature.addScopedMethodInterceptor(mi) { assertIterationMethodContext(it) proceed(it, 'feature scoped setup method', "$it.feature.parent.name.$it.feature.name[#$it.iteration.iterationIndex] / $it.spec.name.$it.method.name() / from $feature.name") + assertIterationMethodContext(it) } } specInfo.allCleanupMethods.each { mi -> feature.addScopedMethodInterceptor(mi) { assertIterationMethodContext(it) proceed(it, 'feature scoped cleanup method', "$it.feature.parent.name.$it.feature.name[#$it.iteration.iterationIndex] / $it.spec.name.$it.method.name() / from $feature.name") + assertIterationMethodContext(it) } } } allFeatures*.addInitializerInterceptor { assertIterationContext(it) proceed(it, 'feature scoped initializer', "$it.spec.name.$it.feature.name[#$it.iteration.iterationIndex]") + assertIterationContext(it) } allFeatures*.addIterationInterceptor { assertIterationContext(it) proceed(it, 'iteration', "$it.spec.name.$it.feature.name[#$it.iteration.iterationIndex]") + assertIterationContext(it) } specInfo.specsBottomToTop.each { spec -> spec.addSetupInterceptor { assertIterationContext(it) proceed(it, 'setup', "$it.spec.name.$it.feature.name[#$it.iteration.iterationIndex] / $spec.name") + assertIterationContext(it) } } allFeatures*.addSetupInterceptor { assertIterationContext(it) proceed(it, 'feature scoped setup', "$it.spec.name.$it.feature.name[#$it.iteration.iterationIndex]") + assertIterationContext(it) } 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()") + assertIterationMethodContext(it) } 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()") + assertIterationMethodContext(it) } specInfo.specsBottomToTop.each { spec -> spec.addCleanupInterceptor { assertIterationContext(it) proceed(it, 'cleanup', "$it.spec.name.$it.feature.name[#$it.iteration.iterationIndex] / $spec.name") + assertIterationContext(it) } } allFeatures*.addCleanupInterceptor { assertIterationContext(it) proceed(it, 'feature scoped cleanup', "$it.spec.name.$it.feature.name[#$it.iteration.iterationIndex]") + assertIterationContext(it) } 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()") + assertIterationMethodContext(it) } specInfo.specsBottomToTop*.addCleanupSpecInterceptor { assertSpecContext(it) proceed(it, 'cleanup spec', "$it.spec.name") + assertSpecContext(it) } specInfo.allCleanupSpecMethods*.addInterceptor { assertSpecMethodContext(it) proceed(it, 'cleanup spec method', "$it.spec.name.$it.method.name()") + assertSpecMethodContext(it) } specInfo.allFixtureMethods*.addInterceptor { it.with { @@ -266,6 +288,14 @@ class SubSpec extends SuperSpec { } } proceed(it, 'fixture method', "${it.feature?.with { feature -> "$feature.parent.name.$feature.name[#$it.iteration.iterationIndex] / " } ?: ''}$it.spec.name.$it.method.name()") + it.with { + def specFixture = method.name.endsWith('Spec') + if (specFixture) { + assertSpecMethodContext(it) + } else { + assertIterationMethodContext(it) + } + } } } @@ -282,7 +312,7 @@ class SubSpec extends SuperSpec { currentFeature assert false: 'currentFeature should not be set' } catch (IllegalStateException ise) { - assert ise.message == 'Cannot request current feature in @Shared context' + assert ise.message == 'Cannot request current feature in @Shared context, or feature context' } try { currentIteration @@ -319,7 +349,6 @@ class SubSpec extends SuperSpec { assert method instance.specificationContext.with { assert currentSpec - assert currentFeature } } } @@ -333,6 +362,12 @@ class SubSpec extends SuperSpec { assert !method.reflection assert !method.name instance.specificationContext.with { + try { + currentFeature + assert false: 'currentFeature should not be set' + } catch (IllegalStateException ise) { + assert ise.message == 'Cannot request current feature in @Shared context, or feature context' + } try { currentIteration assert false: 'currentIteration should not be set' @@ -349,6 +384,7 @@ class SubSpec extends SuperSpec { assert iteration assert instance != sharedInstance instance.specificationContext.with { + assert currentFeature assert currentIteration } }