diff --git a/docs/extensions.adoc b/docs/extensions.adoc index 2366ab1baa..508bfc8f71 100644 --- a/docs/extensions.adoc +++ b/docs/extensions.adoc @@ -763,13 +763,17 @@ fully-qualified class name in a file `META-INF/services/org.spockframework.runti class path. As soon as these two conditions are satisfied, the extension is automatically loaded and used when Spock is running. -`IGlobalExtension` has the following three methods: +`IGlobalExtension` has the following four methods: `start()`:: This is called once at the very start of the Spock execution. +`initSpecs(Collection specs)`:: + This is called once, before visiting single specifications later on in `visitSpec`. It enables global extensions to + view all specifications as an ensemble, e.g. for iterating over them and rearranging their execution order. + `visitSpec(SpecInfo spec)`:: - This is called once for each specification. In this method you can prepare a specification with your extension magic, + This is called once for each specification. In this method, you can prepare a specification with your extension magic, like attaching interceptors to various interception points as described in the chapter <>. `stop()`:: diff --git a/spock-core/src/main/java/org/spockframework/runtime/ExtensionRunner.java b/spock-core/src/main/java/org/spockframework/runtime/ExtensionRunner.java index 687bb37619..ecb81804e5 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/ExtensionRunner.java +++ b/spock-core/src/main/java/org/spockframework/runtime/ExtensionRunner.java @@ -14,6 +14,7 @@ package org.spockframework.runtime; +import org.junit.platform.engine.TestDescriptor; import org.spockframework.runtime.extension.*; import org.spockframework.runtime.model.*; import org.spockframework.util.Nullable; @@ -30,6 +31,8 @@ */ @SuppressWarnings("rawtypes") public class ExtensionRunner { + private static final SpecInfo EMPTY_SPEC = new SpecInfo(); + private final SpecInfo spec; private final IExtensionRegistry extensionRegistry; private final IConfigurationRegistry configurationRegistry; @@ -42,6 +45,20 @@ public ExtensionRunner(SpecInfo spec, IExtensionRegistry extensionRegistry, ICon this.configurationRegistry = configurationRegistry; } + public ExtensionRunner(IExtensionRegistry extensionRegistry, IConfigurationRegistry configurationRegistry) { + this(EMPTY_SPEC, extensionRegistry, configurationRegistry); + } + + public void initGlobalExtensions(Set testDescriptors) { + List specs = testDescriptors.stream() + .filter(testDescriptor -> testDescriptor instanceof SpecNode) + .map(testDescriptor -> ((SpecNode) testDescriptor).getNodeInfo()) + .collect(toList()); + for (IGlobalExtension extension : extensionRegistry.getGlobalExtensions()) { + extension.initSpecs(specs); + } + } + public void run() { runGlobalExtensions(); runAnnotationDrivenExtensions(); diff --git a/spock-core/src/main/java/org/spockframework/runtime/RunContext.java b/spock-core/src/main/java/org/spockframework/runtime/RunContext.java index eefe913732..c1301e09ae 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/RunContext.java +++ b/spock-core/src/main/java/org/spockframework/runtime/RunContext.java @@ -81,6 +81,10 @@ public ExtensionRunner createExtensionRunner(SpecInfo spec) { return new ExtensionRunner(spec, globalExtensionRegistry, globalExtensionRegistry); } + public ExtensionRunner createExtensionRunner() { + return new ExtensionRunner(globalExtensionRegistry, globalExtensionRegistry); + } + public PlatformParameterizedSpecRunner createSpecRunner(SpecInfo spec) { return new PlatformParameterizedSpecRunner( new MasterRunSupervisor(spec, createStackTraceFilter(spec), diffedObjectRenderer)); diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpockEngineDiscoveryPostProcessor.java b/spock-core/src/main/java/org/spockframework/runtime/SpockEngineDiscoveryPostProcessor.java index d01d4e29cc..961a95b7bc 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/SpockEngineDiscoveryPostProcessor.java +++ b/spock-core/src/main/java/org/spockframework/runtime/SpockEngineDiscoveryPostProcessor.java @@ -5,6 +5,8 @@ import org.junit.platform.engine.*; +import java.util.Set; + import static java.util.Comparator.comparingInt; class SpockEngineDiscoveryPostProcessor { @@ -14,7 +16,9 @@ class SpockEngineDiscoveryPostProcessor { SpockEngineDescriptor postProcessEngineDescriptor(UniqueId uniqueId, RunContext runContext, SpockEngineDescriptor engineDescriptor) { SpockEngineDescriptor processedEngineDescriptor = new SpockEngineDescriptor(uniqueId, runContext); - engineDescriptor.getChildren().stream() + Set testDescriptors = engineDescriptor.getChildren(); + initSpecNodes(testDescriptors, runContext); + testDescriptors.stream() .map(child -> processSpecNode(child, runContext)) .sorted(comparingInt(child -> child instanceof SpecNode ? ((SpecNode) child).getNodeInfo().getExecutionOrder() : 0)) .forEach(processedEngineDescriptor::addChild); @@ -46,6 +50,10 @@ private UniqueId toUniqueId(UniqueId parentId, FeatureInfo feature) { return parentId.append("feature", feature.getFeatureMethod().getReflection().getName()); } + private void initSpecNodes(Set testDescriptors, RunContext runContext) { + runContext.createExtensionRunner().initGlobalExtensions(testDescriptors); + } + private TestDescriptor processSpecNode(TestDescriptor child, RunContext runContext) { if (child instanceof SpecNode) { SpecNode specNode = (SpecNode) child; diff --git a/spock-core/src/main/java/org/spockframework/runtime/extension/IGlobalExtension.java b/spock-core/src/main/java/org/spockframework/runtime/extension/IGlobalExtension.java index f9315864c7..059438e5a7 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/extension/IGlobalExtension.java +++ b/spock-core/src/main/java/org/spockframework/runtime/extension/IGlobalExtension.java @@ -18,10 +18,13 @@ import org.spockframework.runtime.model.SpecInfo; +import java.util.Collection; + // TODO: start/stop lifecycle // TODO: design threading model public interface IGlobalExtension { - default void start() {}; - default void visitSpec(SpecInfo spec){}; - default void stop(){}; + default void start() {} + default void initSpecs(Collection specs) {} + default void visitSpec(SpecInfo spec) {} + default void stop() {} }