diff --git a/docs/extensions.adoc b/docs/extensions.adoc index cf4eb85155..781b3ca5f8 100644 --- a/docs/extensions.adoc +++ b/docs/extensions.adoc @@ -1380,3 +1380,26 @@ It is primarily for framework developers who want to provide a default value for Or users of a framework that doesn't provide default values for their special types. If you want to change the default response behavior for `Stub` have a look at <> and how to use your own `org.spockframework.mock.IDefaultResponse`. + +=== Listeners + +Extensions can register listeners to receive notifications about the progress of the test run. +These listeners are intended to be used for reporting, logging, or other monitoring purposes. +They are not intended to modify the test run in any way. +You can register the same listener instance on multiple specifications or features. +Please consult the JavaDoc of the respective listener interfaces for more information. + +==== `IRunListener` + +The `org.spockframework.runtime.IRunListener` can be registered via `SpecInfo.addListener(IRunListener)` and will receive notifications about the progress of the test run of a single specification. + +[#block-listener] +==== `IBlockListener` + +The `org.spockframework.runtime.extension.IBlockListener` can be registered on a feature via, `FeatureInfo.addBlockListener(IBlockListener)` and will receive notifications about the progress of the feature. + +It will be called once when entering a block (`blockEntered`) and once when exiting a block (`blockExited`). + +When an exception is thrown in a block, the `blockExited` will not be called for that block. +The failed block will be part of the `ErrorContext` in `ErrorInfo` that is passed to `IRunListener.error(ErrorInfo)`. +If a `cleanup` block is present the cleanup block listener methods will still be called. diff --git a/docs/release_notes.adoc b/docs/release_notes.adoc index 6f3cd71548..b489c3b179 100644 --- a/docs/release_notes.adoc +++ b/docs/release_notes.adoc @@ -9,6 +9,7 @@ include::include.adoc[] * Add support for combining two or more data providers using cartesian product spockIssue:1062[] * Add support for a `filter` block after a `where` block to filter out unwanted iterations spockPull:1927[] +* Add <> extension point to listen to block execution events within feature methods spockPull:1575[] === Misc diff --git a/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java b/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java index 5c3990af3f..bbe9395a20 100644 --- a/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java +++ b/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java @@ -46,6 +46,7 @@ public class AstNodeCache { public final ClassNode SpecificationContext = ClassHelper.makeWithoutCaching(SpecificationContext.class); public final ClassNode DataVariableMultiplication = ClassHelper.makeWithoutCaching(DataVariableMultiplication.class); public final ClassNode DataVariableMultiplicationFactor = ClassHelper.makeWithoutCaching(DataVariableMultiplicationFactor.class); + public final ClassNode BlockInfo = ClassHelper.makeWithoutCaching(BlockInfo.class); public final MethodNode SpecInternals_GetSpecificationContext = SpecInternals.getDeclaredMethods(Identifiers.GET_SPECIFICATION_CONTEXT).get(0); @@ -71,6 +72,12 @@ public class AstNodeCache { public final MethodNode SpockRuntime_DespreadList = SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.DESPREAD_LIST).get(0); + public final MethodNode SpockRuntime_CallBlockEntered = + SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.CALL_BLOCK_ENTERED).get(0); + + public final MethodNode SpockRuntime_CallBlockExited = + SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.CALL_BLOCK_EXITED).get(0); + public final MethodNode ValueRecorder_Reset = ValueRecorder.getDeclaredMethods(org.spockframework.runtime.ValueRecorder.RESET).get(0); @@ -107,6 +114,12 @@ public class AstNodeCache { public final MethodNode SpecificationContext_GetSharedInstance = SpecificationContext.getDeclaredMethods(org.spockframework.runtime.SpecificationContext.GET_SHARED_INSTANCE).get(0); + public final MethodNode SpecificationContext_GetCurrentBlock = + SpecificationContext.getDeclaredMethods(org.spockframework.runtime.SpecificationContext.GET_CURRENT_BLOCK).get(0); + + public final MethodNode SpecificationContext_SetCurrentBlock = + SpecificationContext.getDeclaredMethods(org.spockframework.runtime.SpecificationContext.SET_CURRENT_BLOCK).get(0); + public final MethodNode List_Get = ClassHelper.LIST_TYPE.getDeclaredMethods("get").get(0); diff --git a/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java b/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java index 61c1287c40..14554eacfe 100644 --- a/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java +++ b/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java @@ -17,6 +17,7 @@ package org.spockframework.compiler; import org.codehaus.groovy.syntax.Token; +import org.codehaus.groovy.syntax.Types; import org.spockframework.lang.Wildcard; import org.spockframework.util.*; import spock.lang.Specification; @@ -390,4 +391,12 @@ public static ConstantExpression primitiveConstExpression(int value) { public static ConstantExpression primitiveConstExpression(boolean value) { return new ConstantExpression(value, true); } + + public static BinaryExpression createVariableIsNotNullExpression(VariableExpression var) { + return new BinaryExpression( + var, + Token.newSymbol(Types.COMPARE_NOT_EQUAL, -1, -1), + new ConstantExpression(null)); + } + } diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecAnnotator.java b/spock-core/src/main/java/org/spockframework/compiler/SpecAnnotator.java index 877203e093..3a7a180112 100644 --- a/spock-core/src/main/java/org/spockframework/compiler/SpecAnnotator.java +++ b/spock-core/src/main/java/org/spockframework/compiler/SpecAnnotator.java @@ -30,6 +30,7 @@ import org.codehaus.groovy.ast.*; import org.codehaus.groovy.ast.expr.*; +import org.spockframework.util.Assert; import static java.util.stream.Collectors.*; import static org.spockframework.compiler.AstUtil.*; @@ -190,8 +191,9 @@ private void addFeatureMetadata(FeatureMethod feature) { ann.setMember(FeatureMetadata.BLOCKS, blockAnnElems = new ListExpression()); ListExpression paramNames = new ListExpression(); - for (Parameter param : feature.getAst().getParameters()) + for (Parameter param : feature.getAst().getParameters()) { paramNames.addExpression(new ConstantExpression(param.getName())); + } ann.setMember(FeatureMetadata.PARAMETER_NAMES, paramNames); feature.getAst().addAnnotation(ann); @@ -202,9 +204,13 @@ private void addBlockMetadata(Block block, BlockKind kind) { blockAnn.setMember(BlockMetadata.KIND, new PropertyExpression( new ClassExpression(nodeCache.BlockKind), kind.name())); ListExpression textExprs = new ListExpression(); - for (String text : block.getDescriptions()) + for (String text : block.getDescriptions()) { textExprs.addExpression(new ConstantExpression(text)); + } blockAnn.setMember(BlockMetadata.TEXTS, textExprs); + int index = blockAnnElems.getExpressions().size(); + Assert.that(index == block.getBlockMetaDataIndex(), + () -> kind + " block mismatch of index: " + index + ", block.getBlockMetaDataIndex(): " + block.getBlockMetaDataIndex()); blockAnnElems.addExpression(new AnnotationConstantExpression(blockAnn)); } diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecParser.java b/spock-core/src/main/java/org/spockframework/compiler/SpecParser.java index f7a32ffb8b..f26d76efab 100644 --- a/spock-core/src/main/java/org/spockframework/compiler/SpecParser.java +++ b/spock-core/src/main/java/org/spockframework/compiler/SpecParser.java @@ -200,6 +200,12 @@ private void buildBlocks(Method method) throws InvalidSpecCompileException { checkIsValidSuccessor(method, BlockParseInfo.METHOD_END, method.getAst().getLastLineNumber(), method.getAst().getLastColumnNumber()); + // set the block metadata index for each block this must be equal to the index of the block in the @BlockMetadata annotation + int i = -1; + for (Block block : method.getBlocks()) { + if(!block.hasBlockMetadata()) continue; + block.setBlockMetaDataIndex(++i); + } // now that statements have been copied to blocks, the original statement // list is cleared; statements will be copied back after rewriting is done stats.clear(); diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java index 3b02908707..254d1d04ae 100644 --- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java +++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java @@ -16,19 +16,22 @@ package org.spockframework.compiler; -import org.spockframework.compiler.model.*; -import org.spockframework.runtime.SpockException; -import org.spockframework.util.*; - -import java.lang.reflect.InvocationTargetException; -import java.util.*; - import org.codehaus.groovy.ast.*; import org.codehaus.groovy.ast.expr.*; import org.codehaus.groovy.ast.stmt.*; import org.codehaus.groovy.runtime.MetaClassHelper; -import org.codehaus.groovy.syntax.*; +import org.codehaus.groovy.syntax.Token; +import org.codehaus.groovy.syntax.Types; +import org.jetbrains.annotations.NotNull; import org.objectweb.asm.Opcodes; +import org.spockframework.compiler.model.*; +import org.spockframework.runtime.SpockException; +import org.spockframework.util.InternalIdentifiers; +import org.spockframework.util.ObjectUtil; +import org.spockframework.util.ReflectionUtil; + +import java.lang.reflect.InvocationTargetException; +import java.util.*; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; @@ -159,7 +162,7 @@ private void createFinalFieldGetter(Field field) { private void createSharedFieldSetter(Field field) { String setterName = "set" + MetaClassHelper.capitalize(field.getName()); - Parameter[] params = new Parameter[] { new Parameter(field.getAst().getType(), "$spock_value") }; + Parameter[] params = new Parameter[] { new Parameter(field.getAst().getType(), SpockNames.SPOCK_VALUE) }; MethodNode setter = spec.getAst().getMethod(setterName, params); if (setter != null) { errorReporter.error(field.getAst(), @@ -180,7 +183,7 @@ private void createSharedFieldSetter(Field field) { // use internal name new ConstantExpression(field.getAst().getName())), Token.newSymbol(Types.ASSIGN, -1, -1), - new VariableExpression("$spock_value")))); + new VariableExpression(SpockNames.SPOCK_VALUE)))); setter.setSourcePosition(field.getAst()); spec.getAst().addMethod(setter); @@ -390,13 +393,20 @@ private void handleWhereBlock(Method method) { public void visitMethodAgain(Method method) { this.block = null; - if (!movedStatsBackToMethod) - for (Block b : method.getBlocks()) + if (!movedStatsBackToMethod) { + for (Block b : method.getBlocks()) { + // This will only run if there was no 'cleanup' block in the method. + // Otherwise, the blocks have already been copied to try block by visitCleanupBlock. + // We need to run as late as possible, so we'll have to do the handling here and in visitCleanupBlock. + addBlockListeners(b); method.getStatements().addAll(b.getAst()); + } + } // for global required interactions - if (method instanceof FeatureMethod) + if (method instanceof FeatureMethod) { method.getStatements().add(createMockControllerCall(nodeCache.MockController_LeaveScope)); + } if (methodHasCondition) { defineValueRecorder(method.getStatements(), ""); @@ -406,6 +416,56 @@ public void visitMethodAgain(Method method) { } } + + private void addBlockListeners(Block block) { + BlockParseInfo blockType = block.getParseInfo(); + if (!blockType.isSupportingBlockListeners()) return; + + // SpockRuntime.callBlockEntered(getSpecificationContext(), blockMetadataIndex) + MethodCallExpression blockEnteredCall = createBlockListenerCall(block, blockType, nodeCache.SpockRuntime_CallBlockEntered); + // SpockRuntime.callBlockExited(getSpecificationContext(), blockMetadataIndex) + MethodCallExpression blockExitedCall = createBlockListenerCall(block, blockType, nodeCache.SpockRuntime_CallBlockExited); + + block.getAst().add(0, new ExpressionStatement(blockEnteredCall)); + if (blockType == BlockParseInfo.CLEANUP) { + // In case of a cleanup block we need store a reference of the previously `currentBlock` in case that an exception occurred + // and restore it at the end of the cleanup block, so that the correct `BlockInfo` is available for the `IErrorContext`. + // The restoration happens in the `finally` statement created by `createCleanupTryCatch`. + VariableExpression failedBlock = new VariableExpression(SpockNames.FAILED_BLOCK, nodeCache.BlockInfo); + block.getAst().add(0, ifThrowableIsNotNull(storeFailedBlock(failedBlock))); + } + block.getAst().add(new ExpressionStatement(blockExitedCall)); + } + + private @NotNull Statement storeFailedBlock(VariableExpression failedBlock) { + MethodCallExpression getCurrentBlock = createDirectMethodCall(getSpecificationContext(), nodeCache.SpecificationContext_GetCurrentBlock, ArgumentListExpression.EMPTY_ARGUMENTS); + return new ExpressionStatement(new BinaryExpression(failedBlock, Token.newSymbol(Types.ASSIGN, -1, -1), getCurrentBlock)); + } + + private @NotNull Statement restoreFailedBlock(VariableExpression failedBlock) { + return new ExpressionStatement(createDirectMethodCall(new CastExpression(nodeCache.SpecificationContext, getSpecificationContext()), nodeCache.SpecificationContext_SetCurrentBlock, new ArgumentListExpression(failedBlock))); + } + + private IfStatement ifThrowableIsNotNull(Statement statement) { + return new IfStatement( + // if ($spock_feature_throwable != null) + new BooleanExpression(AstUtil.createVariableIsNotNullExpression(new VariableExpression(SpockNames.SPOCK_FEATURE_THROWABLE, nodeCache.Throwable))), + statement, + EmptyStatement.INSTANCE + ); + } + + private MethodCallExpression createBlockListenerCall(Block block, BlockParseInfo blockType, MethodNode blockListenerMethod) { + if (block.getBlockMetaDataIndex() < 0) throw new SpockException("Block metadata index not set: " + block); + return createDirectMethodCall( + new ClassExpression(nodeCache.SpockRuntime), + blockListenerMethod, + new ArgumentListExpression( + getSpecificationContext(), + new ConstantExpression(block.getBlockMetaDataIndex(), true) + )); + } + @Override public void visitAnyBlock(Block block) { this.block = block; @@ -484,12 +544,15 @@ private Statement createMockControllerCall(MethodNode method) { @Override public void visitCleanupBlock(CleanupBlock block) { for (Block b : method.getBlocks()) { + // call addBlockListeners() here, as this method will already copy the contents of the blocks, + // so we need to transform the block listeners here as they won't be copied in visitMethodAgain where we normally add them + addBlockListeners(b); if (b == block) break; moveVariableDeclarations(b.getAst(), method.getStatements()); } VariableExpression featureThrowableVar = - new VariableExpression("$spock_feature_throwable", nodeCache.Throwable); + new VariableExpression(SpockNames.SPOCK_FEATURE_THROWABLE, nodeCache.Throwable); method.getStatements().add(createVariableDeclarationStatement(featureThrowableVar)); List featureStats = new ArrayList<>(); @@ -499,9 +562,10 @@ public void visitCleanupBlock(CleanupBlock block) { } CatchStatement featureCatchStat = createThrowableAssignmentAndRethrowCatchStatement(featureThrowableVar); - - List cleanupStats = singletonList( - createCleanupTryCatch(block, featureThrowableVar)); + VariableExpression failedBlock = new VariableExpression(SpockNames.FAILED_BLOCK, nodeCache.BlockInfo); + List cleanupStats = asList( + new ExpressionStatement(new DeclarationExpression(failedBlock, Token.newSymbol(Types.ASSIGN, -1, -1), ConstantExpression.NULL)), + createCleanupTryCatch(block, featureThrowableVar, failedBlock)); TryCatchStatement tryFinally = new TryCatchStatement( @@ -517,13 +581,6 @@ public void visitCleanupBlock(CleanupBlock block) { movedStatsBackToMethod = true; } - private BinaryExpression createVariableNotNullExpression(VariableExpression var) { - return new BinaryExpression( - new VariableExpression(var), - Token.newSymbol(Types.COMPARE_NOT_EQUAL, -1, -1), - new ConstantExpression(null)); - } - private Statement createVariableDeclarationStatement(VariableExpression var) { DeclarationExpression throwableDecl = new DeclarationExpression( @@ -534,13 +591,13 @@ private Statement createVariableDeclarationStatement(VariableExpression var) { return new ExpressionStatement(throwableDecl); } - private TryCatchStatement createCleanupTryCatch(CleanupBlock block, VariableExpression featureThrowableVar) { + private TryCatchStatement createCleanupTryCatch(CleanupBlock block, VariableExpression featureThrowableVar, VariableExpression failedBlock) { List cleanupStats = new ArrayList<>(block.getAst()); - TryCatchStatement tryCatchStat = new TryCatchStatement( new BlockStatement(cleanupStats, null), - EmptyStatement.INSTANCE); + ifThrowableIsNotNull(restoreFailedBlock(failedBlock)) + ); tryCatchStat.addCatch(createHandleSuppressedThrowableStatement(featureThrowableVar)); @@ -548,7 +605,7 @@ private TryCatchStatement createCleanupTryCatch(CleanupBlock block, VariableExpr } private CatchStatement createThrowableAssignmentAndRethrowCatchStatement(VariableExpression assignmentVar) { - Parameter catchParameter = new Parameter(nodeCache.Throwable, "$spock_tmp_throwable"); + Parameter catchParameter = new Parameter(nodeCache.Throwable, SpockNames.SPOCK_TMP_THROWABLE); BinaryExpression assignThrowableExpr = new BinaryExpression( @@ -565,9 +622,9 @@ private CatchStatement createThrowableAssignmentAndRethrowCatchStatement(Variabl } private CatchStatement createHandleSuppressedThrowableStatement(VariableExpression featureThrowableVar) { - Parameter catchParameter = new Parameter(nodeCache.Throwable, "$spock_tmp_throwable"); + Parameter catchParameter = new Parameter(nodeCache.Throwable, SpockNames.SPOCK_TMP_THROWABLE); - BinaryExpression featureThrowableNotNullExpr = createVariableNotNullExpression(featureThrowableVar); + BinaryExpression featureThrowableNotNullExpr = AstUtil.createVariableIsNotNullExpression(featureThrowableVar); List addSuppressedStats = singletonList(new ExpressionStatement( diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpockNames.java b/spock-core/src/main/java/org/spockframework/compiler/SpockNames.java index aa4edf9024..5353720184 100644 --- a/spock-core/src/main/java/org/spockframework/compiler/SpockNames.java +++ b/spock-core/src/main/java/org/spockframework/compiler/SpockNames.java @@ -1,8 +1,12 @@ package org.spockframework.compiler; public class SpockNames { - public static final String VALUE_RECORDER = "$spock_valueRecorder"; public static final String ERROR_COLLECTOR = "$spock_errorCollector"; + public static final String FAILED_BLOCK = "$spock_failedBlock"; public static final String OLD_VALUE = "$spock_oldValue"; public static final String SPOCK_EX = "$spock_ex"; + public static final String SPOCK_FEATURE_THROWABLE = "$spock_feature_throwable"; + public static final String SPOCK_TMP_THROWABLE = "$spock_tmp_throwable"; + public static final String SPOCK_VALUE = "$spock_value"; + public static final String VALUE_RECORDER = "$spock_valueRecorder"; } diff --git a/spock-core/src/main/java/org/spockframework/compiler/model/AnonymousBlock.java b/spock-core/src/main/java/org/spockframework/compiler/model/AnonymousBlock.java index fd1a7528c6..94c5224938 100644 --- a/spock-core/src/main/java/org/spockframework/compiler/model/AnonymousBlock.java +++ b/spock-core/src/main/java/org/spockframework/compiler/model/AnonymousBlock.java @@ -37,4 +37,9 @@ public void accept(ISpecVisitor visitor) throws Exception { public BlockParseInfo getParseInfo() { return BlockParseInfo.ANONYMOUS; } + + @Override + public boolean hasBlockMetadata() { + return false; + } } diff --git a/spock-core/src/main/java/org/spockframework/compiler/model/Block.java b/spock-core/src/main/java/org/spockframework/compiler/model/Block.java index 49ba575512..1ee316b772 100644 --- a/spock-core/src/main/java/org/spockframework/compiler/model/Block.java +++ b/spock-core/src/main/java/org/spockframework/compiler/model/Block.java @@ -31,6 +31,7 @@ public abstract class Block extends Node> { private final List descriptions = new ArrayList<>(3); private Block prev; private Block next; + private int blockMetaDataIndex = -1; public Block(Method parent) { super(parent, new ArrayList<>()); @@ -80,5 +81,25 @@ public boolean isFirstInChain() { return isFirst() || getClass() != prev.getClass(); } + public void setBlockMetaDataIndex(int blockMetaDataIndex) { + this.blockMetaDataIndex = blockMetaDataIndex; + } + + public int getBlockMetaDataIndex() { + return blockMetaDataIndex; + } + + /** + * Returns whether this block will be written to the {@link org.spockframework.runtime.model.BlockMetadata}. + */ + public boolean hasBlockMetadata() { + return true; + } + public abstract BlockParseInfo getParseInfo(); + + @Override + public String toString() { + return "Block kind: " + getClass().getSimpleName() + ", descriptions: " + descriptions; + } } diff --git a/spock-core/src/main/java/org/spockframework/compiler/model/BlockParseInfo.java b/spock-core/src/main/java/org/spockframework/compiler/model/BlockParseInfo.java index ebf8fc0288..d57ff267b6 100644 --- a/spock-core/src/main/java/org/spockframework/compiler/model/BlockParseInfo.java +++ b/spock-core/src/main/java/org/spockframework/compiler/model/BlockParseInfo.java @@ -18,6 +18,8 @@ import java.util.*; +import org.spockframework.util.InternalSpockError; + /** * * @author Peter Niederwieser @@ -32,6 +34,10 @@ public EnumSet getSuccessors(Method method) { public Block addNewBlock(Method method) { return method.getLastBlock(); } + @Override + public boolean isSupportingBlockListeners() { + throw new InternalSpockError("AND block should have been replaced by a more specific block"); + } }, ANONYMOUS { @@ -43,6 +49,10 @@ public Block addNewBlock(Method method) { public EnumSet getSuccessors(Method method) { return EnumSet.of(SETUP, GIVEN, EXPECT, WHEN, CLEANUP, WHERE, METHOD_END); } + @Override + public boolean isSupportingBlockListeners() { + return false; + } }, SETUP { @@ -54,6 +64,10 @@ public Block addNewBlock(Method method) { public EnumSet getSuccessors(Method method) { return EnumSet.of(AND, EXPECT, WHEN, CLEANUP, WHERE, METHOD_END); } + @Override + public boolean isSupportingBlockListeners() { + return true; + } }, GIVEN { @@ -65,6 +79,10 @@ public Block addNewBlock(Method method) { public EnumSet getSuccessors(Method method) { return SETUP.getSuccessors(method); } + @Override + public boolean isSupportingBlockListeners() { + throw new InternalSpockError("GIVEN block should have been replaced by a SETUP block"); + } }, EXPECT { @@ -76,6 +94,10 @@ public Block addNewBlock(Method method) { public EnumSet getSuccessors(Method method) { return EnumSet.of(AND, WHEN, CLEANUP, WHERE, METHOD_END); } + @Override + public boolean isSupportingBlockListeners() { + return true; + } }, WHEN { @@ -87,6 +109,10 @@ public Block addNewBlock(Method method) { public EnumSet getSuccessors(Method method) { return EnumSet.of(AND, THEN); } + @Override + public boolean isSupportingBlockListeners() { + return true; + } }, THEN { @@ -98,6 +124,10 @@ public Block addNewBlock(Method method) { public EnumSet getSuccessors(Method method) { return EnumSet.of(AND, EXPECT, WHEN, THEN, CLEANUP, WHERE, METHOD_END); } + @Override + public boolean isSupportingBlockListeners() { + return true; + } }, CLEANUP { @@ -109,6 +139,10 @@ public Block addNewBlock(Method method) { public EnumSet getSuccessors(Method method) { return EnumSet.of(AND, WHERE, METHOD_END); } + @Override + public boolean isSupportingBlockListeners() { + return true; + } }, WHERE { @@ -120,6 +154,10 @@ public Block addNewBlock(Method method) { public EnumSet getSuccessors(Method method) { return EnumSet.of(AND, COMBINED, FILTER, METHOD_END); } + @Override + public boolean isSupportingBlockListeners() { + return false; + } }, COMBINED { @@ -131,6 +169,10 @@ public Block addNewBlock(Method method) { public EnumSet getSuccessors(Method method) { return WHERE.getSuccessors(method); } + @Override + public boolean isSupportingBlockListeners() { + return false; + } }, FILTER { @@ -142,6 +184,10 @@ public Block addNewBlock(Method method) { public EnumSet getSuccessors(Method method) { return EnumSet.of(AND, METHOD_END); } + @Override + public boolean isSupportingBlockListeners() { + return false; + } }, METHOD_END { @@ -153,6 +199,10 @@ public Block addNewBlock(Method method) { public EnumSet getSuccessors(Method method) { throw new UnsupportedOperationException("getSuccessors"); } + @Override + public boolean isSupportingBlockListeners() { + return false; + } public String toString() { return "end-of-method"; } @@ -162,7 +212,20 @@ public String toString() { return super.toString().toLowerCase(Locale.ROOT); } + /** + * Adds a new block of this type to the given method. + *

+ * Allows for a block to substitute another block type. + */ public abstract Block addNewBlock(Method method); + /** + * Returns the block types that can follow this block type in the given method. + */ public abstract EnumSet getSuccessors(Method method); + + /** + * Indicates whether this block type supports block listeners. + */ + public abstract boolean isSupportingBlockListeners(); } diff --git a/spock-core/src/main/java/org/spockframework/lang/ISpecificationContext.java b/spock-core/src/main/java/org/spockframework/lang/ISpecificationContext.java index 28475c3c21..8e3b01674a 100644 --- a/spock-core/src/main/java/org/spockframework/lang/ISpecificationContext.java +++ b/spock-core/src/main/java/org/spockframework/lang/ISpecificationContext.java @@ -15,6 +15,7 @@ package org.spockframework.lang; import org.spockframework.mock.IThreadAwareMockController; +import org.spockframework.runtime.model.BlockInfo; import org.spockframework.runtime.model.FeatureInfo; import org.spockframework.runtime.model.SpecInfo; import org.spockframework.util.Beta; @@ -24,14 +25,20 @@ @Beta public interface ISpecificationContext { + @Nullable SpecInfo getCurrentSpec(); + FeatureInfo getCurrentFeature(); + IterationInfo getCurrentIteration(); + @Nullable + BlockInfo getCurrentBlock(); + @Nullable Throwable getThrownException(); IMockController getMockController(); - + IThreadAwareMockController getThreadAwareMockController(); } diff --git a/spock-core/src/main/java/org/spockframework/runtime/DataIteratorFactory.java b/spock-core/src/main/java/org/spockframework/runtime/DataIteratorFactory.java index 68b7dcb764..235b439dd8 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/DataIteratorFactory.java +++ b/spock-core/src/main/java/org/spockframework/runtime/DataIteratorFactory.java @@ -44,11 +44,15 @@ protected Object invokeRaw(Object target, MethodInfo method, Object... arguments try { return method.invoke(target, arguments); } catch (Throwable throwable) { - supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(method, throwable)); + supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(method, throwable, getErrorContext())); return null; } } + protected IErrorContext getErrorContext() { + return ErrorContext.from((SpecificationContext) context.getCurrentInstance().getSpecificationContext()); + } + protected int estimateNumIterations(@Nullable Object dataProvider) { if (context.getErrorInfoCollector().hasErrors()) { return UNKNOWN_ITERATIONS; @@ -110,12 +114,12 @@ protected boolean haveNext(Iterator[] iterators, List dataP } else if (result != hasNext) { DataProviderInfo provider = dataProviderInfos.get(i); supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(provider.getDataProviderMethod(), - createDifferentNumberOfDataValuesException(provider, hasNext))); + createDifferentNumberOfDataValuesException(provider, hasNext), getErrorContext())); return false; } } catch (Throwable t) { - supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(dataProviderInfos.get(i).getDataProviderMethod(), t)); + supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(dataProviderInfos.get(i).getDataProviderMethod(), t, getErrorContext())); return false; } @@ -131,12 +135,12 @@ protected Iterator createIterator(Object dataProvider, DataProviderInfo dataP Iterator iter = GroovyRuntimeUtil.asIterator(dataProvider); if (iter == null) { supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(dataProviderInfo.getDataProviderMethod(), - new SpockExecutionException("Data provider's iterator() method returned null"))); + new SpockExecutionException("Data provider's iterator() method returned null"), getErrorContext())); return null; } return iter; } catch (Throwable t) { - supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(dataProviderInfo.getDataProviderMethod(), t)); + supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(dataProviderInfo.getDataProviderMethod(), t, getErrorContext())); return null; } } @@ -245,7 +249,7 @@ public Object[] next() { try { return (Object[]) invokeRaw(context.getSharedInstance(), context.getCurrentFeature().getDataProcessorMethod(), next); } catch (Throwable t) { - supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(context.getCurrentFeature().getDataProcessorMethod(), t)); + supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(context.getCurrentFeature().getDataProcessorMethod(), t, getErrorContext())); return null; } } @@ -322,7 +326,7 @@ public Object[] next() { System.arraycopy(nextValues, 0, next, i, nextValues.length); i += nextValues.length; } catch (Throwable t) { - supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(context.getCurrentFeature().getDataProviders().get(i).getDataProviderMethod(), t)); + supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(context.getCurrentFeature().getDataProviders().get(i).getDataProviderMethod(), t, getErrorContext())); return null; } } @@ -353,7 +357,7 @@ public Object[] next() { } // filter block does not like these values, try next ones if available } catch (Throwable t) { - supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(filterMethod, t)); + supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(filterMethod, t, getErrorContext())); return null; } } @@ -396,7 +400,7 @@ private Object[] createDataProviders() { break; } else if (provider == null) { SpockExecutionException error = new SpockExecutionException("Data provider is null!"); - supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(method, error)); + supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(method, error, getErrorContext())); break; } @@ -419,7 +423,7 @@ private IDataIterator[] createDataProviderIterators() { dataVariableMultiplications = Arrays.stream(((DataVariableMultiplication[]) invokeRaw(null, dataVariableMultiplicationsMethod))).iterator(); nextDataVariableMultiplication = dataVariableMultiplications.next(); } catch (Throwable t) { - supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(dataVariableMultiplicationsMethod, t)); + supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(dataVariableMultiplicationsMethod, t, getErrorContext())); return null; } } else { @@ -578,7 +582,7 @@ public boolean hasNext() { try { return iterator.hasNext(); } catch (Throwable t) { - supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(providerInfo.getDataProviderMethod(), t)); + supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(providerInfo.getDataProviderMethod(), t, getErrorContext())); return false; } } @@ -592,7 +596,7 @@ public Object[] next() { try { return new Object[]{iterator.next()}; } catch (Throwable t) { - supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(providerInfo.getDataProviderMethod(), t)); + supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(providerInfo.getDataProviderMethod(), t, getErrorContext())); return null; } } @@ -870,7 +874,7 @@ public Object[] next() { try { multiplicandIterators[i] = collectedMultiplicandValues.get(i).iterator(); } catch (Throwable t) { - supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(multiplicandProviderInfos.get(i).getDataProviderMethod(), t)); + supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(multiplicandProviderInfos.get(i).getDataProviderMethod(), t, getErrorContext())); return null; } } @@ -948,7 +952,7 @@ protected Object[] extractNextValues(Iterator[] iterators, ListSpecInfo.addListener(). See * {@link StepwiseExtension} for an example of how to use a listener. * + * @see org.spockframework.runtime.extension.IBlockListener * @author Peter Niederwieser */ public interface IRunListener { diff --git a/spock-core/src/main/java/org/spockframework/runtime/MasterRunSupervisor.java b/spock-core/src/main/java/org/spockframework/runtime/MasterRunSupervisor.java index 802a2306e1..904f5d5de6 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/MasterRunSupervisor.java +++ b/spock-core/src/main/java/org/spockframework/runtime/MasterRunSupervisor.java @@ -45,7 +45,7 @@ public void error(ErrorInfoCollector errorInfoCollector, ErrorInfo error) { exception = transform(exception); - ErrorInfo transformedError = new ErrorInfo(error.getMethod(), exception); + ErrorInfo transformedError = new ErrorInfo(error.getMethod(), exception, error.getErrorContext()); if (exception instanceof TestAbortedException || exception instanceof TestSkippedException) { // Spock has no concept of "aborted tests", so we don't notify Spock listeners } else { @@ -58,7 +58,7 @@ public void error(ErrorInfoCollector errorInfoCollector, ErrorInfo error) { private void handleMultipleFailures(ErrorInfoCollector errorInfoCollector, ErrorInfo error) { MultipleFailuresError multiFailure = (MultipleFailuresError) error.getException(); for (Throwable failure : multiFailure.getFailures()) - error(errorInfoCollector, new ErrorInfo(error.getMethod(), failure)); + error(errorInfoCollector, new ErrorInfo(error.getMethod(), failure, error.getErrorContext())); } 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 9257395514..93d26f2801 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/PlatformSpecRunner.java +++ b/spock-core/src/main/java/org/spockframework/runtime/PlatformSpecRunner.java @@ -332,6 +332,7 @@ void runFeatureMethod(SpockExecutionContext context) { Object[] dataValues = context.getCurrentIteration().getDataValues(); invoke(context, context.getCurrentInstance(), featureIteration, dataValues); + getSpecificationContext(context).setCurrentBlock(null); } void runCleanup(SpockExecutionContext context) { @@ -376,7 +377,7 @@ private void runIterationCleanups(SpockExecutionContext context) { try { cleanup.run(); } catch (Throwable t) { - ErrorInfo error = new ErrorInfo(CollectionUtil.getFirstElement(context.getSpec().getCleanupMethods()), t); + ErrorInfo error = new ErrorInfo(CollectionUtil.getFirstElement(context.getSpec().getCleanupMethods()), t, ErrorContext.from(getSpecificationContext(context))); supervisor.error(context.getErrorInfoCollector(), error); } } @@ -429,7 +430,7 @@ protected void invoke(SpockExecutionContext context, Object target, MethodInfo m try { invocation.proceed(); } catch (Throwable throwable) { - ErrorInfo error = new ErrorInfo(method, throwable); + ErrorInfo error = new ErrorInfo(method, throwable, ErrorContext.from(getSpecificationContext(context))); supervisor.error(context.getErrorInfoCollector(), error); } } @@ -438,7 +439,7 @@ protected Object invokeRaw(SpockExecutionContext context, Object target, MethodI try { return method.invoke(target, arguments); } catch (Throwable throwable) { - supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(method, throwable)); + supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(method, throwable, ErrorContext.from(getSpecificationContext(context)))); return null; } } diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpecInfoBuilder.java b/spock-core/src/main/java/org/spockframework/runtime/SpecInfoBuilder.java index 7c7f4445fb..4eb810a245 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/SpecInfoBuilder.java +++ b/spock-core/src/main/java/org/spockframework/runtime/SpecInfoBuilder.java @@ -169,10 +169,7 @@ private FeatureInfo createFeature(Method method, FeatureMetadata featureMetadata } for (BlockMetadata blockMetadata : featureMetadata.blocks()) { - BlockInfo block = new BlockInfo(); - block.setKind(blockMetadata.kind()); - block.setTexts(asList(blockMetadata.texts())); - feature.addBlock(block); + feature.addBlock(new BlockInfo(blockMetadata.kind(), asList(blockMetadata.texts()))); } return feature; 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 d54fb0f541..a8249ae00c 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java +++ b/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java @@ -5,6 +5,7 @@ import org.spockframework.mock.IThreadAwareMockController; import org.spockframework.mock.runtime.MockController; import org.spockframework.runtime.model.*; +import org.spockframework.util.Nullable; import spock.lang.Specification; public class SpecificationContext implements ISpecificationContext { @@ -12,6 +13,8 @@ public class SpecificationContext implements ISpecificationContext { private volatile FeatureInfo currentFeature; private volatile IterationInfo currentIteration; + private volatile BlockInfo currentBlock; + private volatile Specification sharedInstance; private volatile Throwable thrownException; @@ -44,7 +47,12 @@ public FeatureInfo getCurrentFeature() { return currentFeature; } - public void setCurrentFeature(FeatureInfo currentFeature) { + @Nullable + FeatureInfo getCurrentFeatureOrNull() { + return currentFeature; + } + + public void setCurrentFeature(@Nullable FeatureInfo currentFeature) { this.currentFeature = currentFeature; } @@ -56,10 +64,28 @@ public IterationInfo getCurrentIteration() { return currentIteration; } - public void setCurrentIteration(IterationInfo currentIteration) { + @Nullable + IterationInfo getCurrentIterationOrNull() { + return currentIteration; + } + + public void setCurrentIteration(@Nullable IterationInfo currentIteration) { this.currentIteration = currentIteration; } + public static final String SET_CURRENT_BLOCK = "setCurrentBlock"; + public void setCurrentBlock(@Nullable BlockInfo blockInfo) { + this.currentBlock = blockInfo; + } + + public static final String GET_CURRENT_BLOCK = "getCurrentBlock"; + + @Nullable + @Override + public BlockInfo getCurrentBlock() { + return currentBlock; + } + @Override public Throwable getThrownException() { return thrownException; @@ -80,4 +106,5 @@ public IMockController getMockController() { public IThreadAwareMockController getThreadAwareMockController() { return mockController; } + } diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java b/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java index 31a58fb195..04a5163833 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java +++ b/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java @@ -24,13 +24,17 @@ import org.hamcrest.Matcher; import org.hamcrest.collection.IsIterableContainingInAnyOrder; import org.opentest4j.MultipleFailuresError; +import org.spockframework.runtime.extension.IBlockListener; +import org.spockframework.runtime.model.BlockInfo; import org.spockframework.runtime.model.ExpressionInfo; +import org.spockframework.runtime.model.IterationInfo; import org.spockframework.runtime.model.TextPosition; import org.spockframework.util.CollectionUtil; import org.spockframework.util.ExceptionUtil; import org.spockframework.util.Nullable; import java.util.*; +import java.util.function.Consumer; import java.util.function.Function; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -225,6 +229,30 @@ public static Object[] despreadList(Object[] args, Object[] spreads, int[] posit return GroovyRuntimeUtil.despreadList(args, spreads, positions); } + public static final String CALL_BLOCK_ENTERED = "callBlockEntered"; + + public static void callBlockEntered(SpecificationContext context, int blockInfoIndex) { + IterationInfo currentIteration = context.getCurrentIteration(); + BlockInfo blockInfo = context.getCurrentFeature().getBlocks().get(blockInfoIndex); + context.setCurrentBlock(blockInfo); + notifyBlockListener(currentIteration, blockListener -> blockListener.blockEntered(currentIteration, blockInfo)); + } + + private static void notifyBlockListener(IterationInfo currentIteration, Consumer consumer) { + List blockListeners = currentIteration.getFeature().getBlockListeners(); + if (blockListeners.isEmpty()) return; + blockListeners.forEach(consumer); + } + + public static final String CALL_BLOCK_EXITED = "callBlockExited"; + + public static void callBlockExited(SpecificationContext context, int blockInfoIndex) { + IterationInfo currentIteration = context.getCurrentIteration(); + BlockInfo blockInfo = context.getCurrentFeature().getBlocks().get(blockInfoIndex); + notifyBlockListener(currentIteration, blockListener -> blockListener.blockExited(currentIteration, blockInfo)); + context.setCurrentBlock(null); + } + private static List getValues(ValueRecorder recorder) { return recorder == null ? null : recorder.getValues(); } diff --git a/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java b/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java new file mode 100644 index 0000000000..df06c4c363 --- /dev/null +++ b/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java @@ -0,0 +1,57 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * https://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.spockframework.runtime.extension; + +import org.spockframework.runtime.model.BlockInfo; +import org.spockframework.runtime.model.ErrorInfo; +import org.spockframework.runtime.model.IterationInfo; +import org.spockframework.util.Beta; + +/** + * Listens to block events during the execution of a feature. + *

+ * Usually used in conjunction with {@link org.spockframework.runtime.IRunListener}. + * Currently, only extensions can register listeners. + * They do so by invoking {@link org.spockframework.runtime.model.FeatureInfo#addBlockListener(IBlockListener)}. + * It is preferred to use a single instance of this. + *

+ * It is discouraged to perform long-running operations in the listener methods, + * as they are called during the execution of the specification. + * It is discouraged to perform any side effects affecting the tests. + *

+ * When an exception is thrown in a block, the {@code blockExited} will not be called for that block. + * If a cleanup block is present the cleanup block listener methods will still be called. + * + * @see org.spockframework.runtime.IRunListener + * @author Leonard Brünings + * @since 2.4 + */ +@Beta +public interface IBlockListener { + + /** + * Called when a block is entered. + */ + default void blockEntered(IterationInfo iterationInfo, BlockInfo blockInfo) {} + + /** + * Called when a block is exited. + *

+ * This method is not called if an exception is thrown in the block. + * The block that was active will be available in the {@link org.spockframework.runtime.model.IErrorContext} + * and can be observed via {@link org.spockframework.runtime.IRunListener#error(ErrorInfo)}. + */ + default void blockExited(IterationInfo iterationInfo, BlockInfo blockInfo) {} +} diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/BlockInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/BlockInfo.java index 6ef57529ec..4768b46604 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/model/BlockInfo.java +++ b/spock-core/src/main/java/org/spockframework/runtime/model/BlockInfo.java @@ -16,6 +16,8 @@ package org.spockframework.runtime.model; +import org.spockframework.util.Nullable; + import java.util.List; /** @@ -27,6 +29,21 @@ public class BlockInfo { private BlockKind kind; private List texts; + /** + * Only for backwards compatibility. + *

+ * @deprecated Use {@link #BlockInfo(BlockKind, List)} instead. + */ + @Deprecated + public BlockInfo() { + } + + public BlockInfo(BlockKind kind, List texts) { + this.kind = kind; + this.texts = texts; + } + + @Nullable public BlockKind getKind() { return kind; } @@ -35,6 +52,7 @@ public void setKind(BlockKind kind) { this.kind = kind; } + @Nullable public List getTexts() { return texts; } diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java index a730779216..f9c4bf6f99 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java +++ b/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java @@ -17,10 +17,12 @@ public class ErrorInfo { private final MethodInfo method; private final Throwable error; + private final IErrorContext errorContext; - public ErrorInfo(MethodInfo method, Throwable error) { + public ErrorInfo(MethodInfo method, Throwable error, IErrorContext errorContext) { this.method = method; this.error = error; + this.errorContext = errorContext; } public MethodInfo getMethod() { @@ -30,4 +32,22 @@ public MethodInfo getMethod() { public Throwable getException() { return error; } + + /** + * {@return the error context} + * + * @since 2.4 + */ + public IErrorContext getErrorContext() { + return errorContext; + } + + @Override + public String toString() { + return "ErrorInfo{" + + "method=" + method + + ", errorContext=" + errorContext + + ", error=" + error + + '}'; + } } diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java index 81cfab9b74..53ee0d5744 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java +++ b/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java @@ -15,6 +15,7 @@ package org.spockframework.runtime.model; +import org.spockframework.runtime.extension.IBlockListener; import org.spockframework.runtime.extension.IDataDriver; import org.spockframework.runtime.extension.IMethodInterceptor; import org.spockframework.runtime.model.parallel.ExclusiveResource; @@ -42,6 +43,8 @@ public class FeatureInfo extends SpecElementInfo imp private final List initializerInterceptors = new ArrayList<>(); private final Map> scopedMethodInterceptors = new HashMap<>(); + private final List blockListeners = new ArrayList<>(); + private final Set exclusiveResources = new HashSet<>(); private final Set testTags = new HashSet<>(); @@ -165,7 +168,7 @@ public List getInitializerInterceptors() { } /** - * Adds a initializer interceptor for this feature. + * Adds an initializer interceptor for this feature. *

* The feature-scoped interceptors will execute before the spec interceptors. * @@ -221,6 +224,22 @@ public void addIterationInterceptor(IMethodInterceptor interceptor) { iterationInterceptors.add(interceptor); } + /** + * @since 2.4 + */ + @Beta + public List getBlockListeners() { + return blockListeners; + } + + /** + * @since 2.4 + */ + @Beta + public void addBlockListener(IBlockListener blockListener) { + blockListeners.add(blockListener); + } + public MethodInfo getFeatureMethod() { return featureMethod; } diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java b/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java new file mode 100644 index 0000000000..a2eb30c24b --- /dev/null +++ b/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java @@ -0,0 +1,40 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * https://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.spockframework.runtime.model; + +import org.spockframework.util.Beta; +import org.spockframework.util.Nullable; + +/** + * Provides context information for an error that occurred during the execution of a specification. + *

+ * Depending on the context in which the error occurred, some of the methods may return {@code null}. + * + * @since 2.4 + */ +@Beta +public interface IErrorContext { + @Nullable + SpecInfo getSpec(); + + @Nullable + FeatureInfo getFeature(); + + @Nullable + IterationInfo getIteration(); + + @Nullable + BlockInfo getBlock(); +} diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/AsyncRunListenerSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/AsyncRunListenerSpec.groovy index 3a33687d87..b33cfb6493 100644 --- a/spock-specs/src/test/groovy/org/spockframework/runtime/AsyncRunListenerSpec.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/runtime/AsyncRunListenerSpec.groovy @@ -13,7 +13,7 @@ class AsyncRunListenerSpec extends Specification { def specInfo = new SpecInfoBuilder(getClass()).build() def featureInfo = specInfo.features[0] def iterationInfo = new IterationInfo(featureInfo, 0, [] as Object[], 1) - def errorInfo = new ErrorInfo(featureInfo.featureMethod, new Exception()) + def errorInfo = new ErrorInfo(featureInfo.featureMethod, new Exception(), new ErrorContext(specInfo, featureInfo, iterationInfo, null)) when: asyncListener.start() diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy new file mode 100644 index 0000000000..59326f9a02 --- /dev/null +++ b/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy @@ -0,0 +1,81 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * https://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.spockframework.runtime + +import org.spockframework.runtime.extension.IBlockListener +import org.spockframework.runtime.model.BlockInfo +import org.spockframework.runtime.model.BlockKind +import org.spockframework.runtime.model.IterationInfo +import spock.lang.Specification + +class BlockListenerSpec extends Specification { + + List blocks = [] + List exitBlocks = [] + + def setup() { + specificationContext.currentIteration.feature.addBlockListener([ + blockEntered: { IterationInfo i, BlockInfo b -> + blocks << b + }, + blockExited: { IterationInfo i, BlockInfo b -> + exitBlocks << b + }] as IBlockListener) + } + + def "BlockListener is called for each Block with text"() { + given: "setup" + expect: "precondition" + when: "action" + then: "assertion" + + cleanup: "cleanup" + assert blocks.kind == [BlockKind.SETUP, BlockKind.EXPECT, BlockKind.WHEN, BlockKind.THEN, BlockKind.CLEANUP] + assert blocks.texts == [["setup"], ["precondition"], ["action"], ["assertion"], ["cleanup"]] + assert exitBlocks.kind == [BlockKind.SETUP, BlockKind.EXPECT, BlockKind.WHEN, BlockKind.THEN] + } + + def "SpecificationContext holds a reference to the current block"() { + assert specificationContext.currentBlock == null + given: "setup" + assert specificationContext.currentBlock.kind == BlockKind.SETUP + expect: "precondition" + specificationContext.currentBlock.kind == BlockKind.EXPECT + when: "action" + assert specificationContext.currentBlock.kind == BlockKind.WHEN + then: "assertion" + specificationContext.currentBlock.kind == BlockKind.THEN + + cleanup: "cleanup" + assert specificationContext.currentBlock.kind == BlockKind.CLEANUP + } + + def "blocks extended with and: are treated as singular block with multiple texts"() { + given: "setup" + and: "setup2" + expect: "precondition" + and: "precondition2" + when: "action" + and: "action2" + then: "assertion" + and: "assertion2" + + cleanup: "cleanup" + assert blocks.kind == [BlockKind.SETUP, BlockKind.EXPECT, BlockKind.WHEN, BlockKind.THEN, BlockKind.CLEANUP] + and: "cleanup2" + assert blocks.texts == [["setup", "setup2"], ["precondition", "precondition2"], ["action", "action2"], ["assertion", "assertion2"], ["cleanup", "cleanup2"]] + assert exitBlocks.kind == [BlockKind.SETUP, BlockKind.EXPECT, BlockKind.WHEN, BlockKind.THEN] + } +} diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy index 2f8255ed25..e061e8e2aa 100644 --- a/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy @@ -1,15 +1,22 @@ package org.spockframework.runtime import org.spockframework.EmbeddedSpecification -import org.spockframework.runtime.extension.* +import org.spockframework.runtime.extension.ExtensionAnnotation +import org.spockframework.runtime.extension.IAnnotationDrivenExtension +import org.spockframework.runtime.model.BlockKind +import org.spockframework.runtime.model.ErrorInfo import org.spockframework.runtime.model.SpecInfo import org.spockframework.runtime.model.parallel.ExecutionMode import spock.lang.Execution import spock.lang.Ignore import spock.lang.Issue import spock.lang.Specification +import spock.lang.Unroll -import java.lang.annotation.* +import java.lang.annotation.ElementType +import java.lang.annotation.Retention +import java.lang.annotation.RetentionPolicy +import java.lang.annotation.Target @Execution( value = ExecutionMode.SAME_THREAD, @@ -120,6 +127,133 @@ class ASpec extends Specification { cleanup: RunListenerDelegate.delegate = null } + + def "IRunListener gets called for errors"() { + given: + RunListenerDelegate.delegate = runListener + runner.addPackageImport(Specification.package) + runner.addClassImport(RegisterRunListener) + runner.throwFailure = false + + when: + runner.runWithImports ''' +@RegisterRunListener +class ASpec extends Specification { + def "a test"() { + expect: "failing expect" + false + + cleanup: "failing cleanup" + throw new RuntimeException("failing cleanup") + } +} +''' + + then: + 1 * runListener.beforeSpec(_) + then: + 1 * runListener.beforeFeature(_) + then: + 1 * runListener.beforeIteration(_) + then: + 1 * runListener.error(_) >> { ErrorInfo errorInfo -> + with(errorInfo.errorContext.block) { + it.kind == BlockKind.EXPECT + it.texts == ["failing expect"] + } + assert errorInfo.exception instanceof AssertionError + assert errorInfo.exception.suppressed[0].message == "failing cleanup" + } + then: + 1 * runListener.afterIteration(_) + then: + 1 * runListener.afterFeature(_) + then: + 1 * runListener.afterSpec(_) + then: + 0 * runListener._ + + cleanup: + RunListenerDelegate.delegate = null + } + + @Unroll("IRunListener.error gets called for #errorLocation") + def "IRunListener gets called for different error locations"() { + given: + RunListenerDelegate.delegate = runListener + runner.addPackageImport(Specification.package) + runner.addClassImport(RegisterRunListener) + runner.throwFailure = false + ErrorInfo errorInfo + + when: + runner.runWithImports """ +@RegisterRunListener +class ASpec extends Specification { + def setupSpec() { + assert "$errorLocation" != "setupSpec" + } + def setup() { + assert "$errorLocation" != "setup" + } + + def "a test"() { + assert "$errorLocation" != "feature start" + + given: "setup label" + assert "$errorLocation" != "feature setup" + + expect: "expect label" + "$errorLocation" != "feature expect" + + when: "when label" + assert "$errorLocation" != "feature when" + + then: "then label" + "$errorLocation" != "feature then" + + cleanup: "cleanup label" + assert "$errorLocation" != "feature cleanup" + } + + def cleanup() { + assert "$errorLocation" != "cleanup" + } + + def cleanupSpec() { + assert "$errorLocation" != "cleanupSpec" + } +} +""" + + then: + 1 * runListener.error(_) >> { ErrorInfo it -> errorInfo = it } + + if (block != null) { + with(errorInfo.errorContext.block) { + it.kind == block + it.texts == blockTexts + } + } else { + assert errorInfo.errorContext.block == null + } + + cleanup: + RunListenerDelegate.delegate = null + + where: + errorLocation | block | blockTexts + "setupSpec" | null | [] + "setup" | null | [] + "feature start" | null | [] + "feature setup" | BlockKind.SETUP | ["setup label"] + "feature expect" | BlockKind.EXPECT | ["expect label"] + "feature when" | BlockKind.WHEN | ["when label"] + "feature then" | BlockKind.THEN | ["then label"] + "feature cleanup" | BlockKind.CLEANUP | ["cleanup label"] + "cleanup" | null | [] + "cleanupSpec" | null | [] + } } @Target(ElementType.TYPE) diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/ast/BlocksAst.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/BlocksAst.groovy new file mode 100644 index 0000000000..ccc4ba3571 --- /dev/null +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/BlocksAst.groovy @@ -0,0 +1,88 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * https://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.spockframework.smoke.ast + +import org.spockframework.EmbeddedSpecification +import org.spockframework.specs.extension.SpockSnapshotter +import spock.lang.Snapshot + +class BlocksAst extends EmbeddedSpecification { + @Snapshot(extension = 'groovy') + SpockSnapshotter snapshotter + + def "all observable blocks with empty labels"() { + given: + snapshotter.featureBody() + + when: + def result = compiler.transpileFeatureBody(''' + given: '' + expect: '' + when: '' + then: '' + cleanup: '' + where: '' + combined: '' + filter: '' + ''') + + then: + snapshotter.assertThat(result.source).matchesSnapshot() + } + def "all observable blocks with labels and blocks"() { + given: + snapshotter.featureBody() + + when: + def result = compiler.transpileFeatureBody(''' + given: 'given' + and: 'and given' + expect: 'expect' + and: 'and expect' + when: 'when' + and: 'and when' + then: 'then' + and: 'and then' + then: 'then2' + and: 'and then2' + cleanup: 'cleanup' + and: 'and cleanup' + where: 'where' + combined: 'combine' + filter: 'only one execution' + ''') + + then: + snapshotter.assertThat(result.source).matchesSnapshot() + } + + def "all observable blocks with GString labels"() { + given: + snapshotter.featureBody() + + when: + def result = compiler.transpileFeatureBody(''' + int idx = 0 + given: "given ${idx++}" + expect: "expect ${idx++}" + when: "when ${idx++}" + then: "then ${idx++}" + ''') + + then: + snapshotter.assertThat(result.source).matchesSnapshot() + } +} diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/ast/mock/MocksAstSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/mock/MocksAstSpec.groovy new file mode 100644 index 0000000000..001677a268 --- /dev/null +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/mock/MocksAstSpec.groovy @@ -0,0 +1,44 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * https://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.spockframework.smoke.ast.mock + +import org.spockframework.EmbeddedSpecification +import org.spockframework.specs.extension.SpockSnapshotter +import spock.lang.Snapshot + +class MocksAstSpec extends EmbeddedSpecification { + + @Snapshot(extension = 'groovy') + SpockSnapshotter snapshotter + + def "simple interaction"() { + given: + snapshotter.featureBody() + + when: + def result = compiler.transpileFeatureBody(""" + given: + List list = Mock() + + when: + list.add(1) + + then: + 1 * list.add(1) +""") + then: + snapshotter.assertThat(result.source).matchesSnapshot() + } +} diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy index 9563c41188..e27abaaf18 100644 --- a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy @@ -18,6 +18,7 @@ package org.spockframework.smoke.parameterization import org.spockframework.EmbeddedSpecification import org.spockframework.runtime.SpockExecutionException +import org.spockframework.specs.extension.SpockSnapshotter import spock.lang.* import spock.util.Show @@ -103,7 +104,10 @@ where: x << [] } @Issue("https://github.com/spockframework/spock/issues/1287") - def "data provider with asserting closure produces error rethrower variable in data provider method"() { + def "data provider with asserting closure produces error rethrower variable in data provider method"(@Snapshot(extension = 'groovy') SpockSnapshotter snapshotter) { + given: + snapshotter.specBody() + when: def result = compiler.transpileFeatureBody(''' where: @@ -112,30 +116,7 @@ where: x << [] ''', EnumSet.of(Show.METHODS)) then: - result.source == '''\ -public void $spock_feature_0_0(java.lang.Object dataPipe, java.lang.Object dataVariable) { - this.getSpecificationContext().getMockController().leaveScope() -} - -public java.lang.Object $spock_feature_0_0prov0() { - org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE - return [{ -> - org.spockframework.runtime.ValueRecorder $spock_valueRecorder1 = new org.spockframework.runtime.ValueRecorder() - try { - org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder1.reset(), 'true', 2, 29, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true)) - } - catch (java.lang.Throwable $spock_condition_throwable) { - org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder1, 'true', 2, 29, null, $spock_condition_throwable)} - finally { - } - }] -} - -public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0) { - java.lang.Object dataPipe = (( $spock_p0 ) as java.lang.Object) - java.lang.Object dataVariable = ((null) as java.lang.Object) - return new java.lang.Object[]{ dataPipe , dataVariable } -}''' + snapshotter.assertThat(result.source).matchesSnapshot() } @Issue("https://github.com/spockframework/spock/issues/1287") @@ -152,7 +133,10 @@ public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0) { } @Issue("https://github.com/spockframework/spock/issues/1287") - def "data variable with asserting closure produces error rethrower variable in data processor method"() { + def "data variable with asserting closure produces error rethrower variable in data processor method"(@Snapshot(extension = 'groovy') SpockSnapshotter snapshotter) { + given: + snapshotter.specBody() + when: def result = compiler.transpileFeatureBody(''' where: @@ -161,30 +145,7 @@ public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0) { ''', EnumSet.of(Show.METHODS)) then: - result.source == '''\ -public void $spock_feature_0_0(java.lang.Object dataPipe, java.lang.Object dataVariable) { - this.getSpecificationContext().getMockController().leaveScope() -} - -public java.lang.Object $spock_feature_0_0prov0() { - return [null] -} - -public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0) { - org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE - java.lang.Object dataPipe = (( $spock_p0 ) as java.lang.Object) - java.lang.Object dataVariable = (({ -> - org.spockframework.runtime.ValueRecorder $spock_valueRecorder1 = new org.spockframework.runtime.ValueRecorder() - try { - org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder1.reset(), 'true', 3, 31, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true)) - } - catch (java.lang.Throwable $spock_condition_throwable) { - org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder1, 'true', 3, 31, null, $spock_condition_throwable)} - finally { - } - }) as java.lang.Object) - return new java.lang.Object[]{ dataPipe , dataVariable } -}''' + snapshotter.assertThat(result.source).matchesSnapshot() } @Issue("https://github.com/spockframework/spock/issues/1287") diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt index 36f30e3f84..50a2e480b4 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt @@ -40,6 +40,15 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov L10 ALOAD 3 POP + ALOAD 0 + INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext; + LDC Lorg/spockframework/runtime/SpecificationContext;.class + INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; + CHECKCAST org/spockframework/runtime/SpecificationContext + ICONST_0 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockEntered (Lorg/spockframework/runtime/SpecificationContext;I)V + ACONST_NULL + POP L0 LINENUMBER 4 L0 ALOAD 2 @@ -87,6 +96,24 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov ATHROW L13 FRAME SAME + ALOAD 0 + INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext; + LDC Lorg/spockframework/runtime/SpecificationContext;.class + INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; + CHECKCAST org/spockframework/runtime/SpecificationContext + ICONST_0 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockExited (Lorg/spockframework/runtime/SpecificationContext;I)V + ACONST_NULL + POP + ALOAD 0 + INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext; + LDC Lorg/spockframework/runtime/SpecificationContext;.class + INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; + CHECKCAST org/spockframework/runtime/SpecificationContext + ICONST_1 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockEntered (Lorg/spockframework/runtime/SpecificationContext;I)V + ACONST_NULL + POP ALOAD 0 INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext; LDC Lorg/spockframework/runtime/SpecificationContext;.class @@ -129,8 +156,27 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov ALOAD 7 ATHROW L16 - LINENUMBER 8 L16 FRAME SAME + ALOAD 0 + INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext; + LDC Lorg/spockframework/runtime/SpecificationContext;.class + INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; + CHECKCAST org/spockframework/runtime/SpecificationContext + ICONST_1 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockExited (Lorg/spockframework/runtime/SpecificationContext;I)V + ACONST_NULL + POP + ALOAD 0 + INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext; + LDC Lorg/spockframework/runtime/SpecificationContext;.class + INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; + CHECKCAST org/spockframework/runtime/SpecificationContext + ICONST_2 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockEntered (Lorg/spockframework/runtime/SpecificationContext;I)V + ACONST_NULL + POP + L17 + LINENUMBER 8 L17 ALOAD 1 LDC 2 AALOAD @@ -145,6 +191,15 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov LDC Lorg/spockframework/runtime/SpecificationContext;.class INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; CHECKCAST org/spockframework/runtime/SpecificationContext + ICONST_2 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockExited (Lorg/spockframework/runtime/SpecificationContext;I)V + ACONST_NULL + POP + ALOAD 0 + INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext; + LDC Lorg/spockframework/runtime/SpecificationContext;.class + INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; + CHECKCAST org/spockframework/runtime/SpecificationContext INVOKEVIRTUAL org/spockframework/runtime/SpecificationContext.getMockController ()Lorg/spockframework/mock/IMockController; LDC Lorg/spockframework/mock/runtime/MockController;.class INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; @@ -152,11 +207,11 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov INVOKEVIRTUAL org/spockframework/mock/runtime/MockController.leaveScope ()V ACONST_NULL POP - L17 + L18 RETURN - LOCALVARIABLE this Lapackage/TestSpec; L8 L17 0 - LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L9 L17 2 - LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L10 L17 3 + LOCALVARIABLE this Lapackage/TestSpec; L8 L18 0 + LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L9 L18 2 + LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L10 L18 3 LOCALVARIABLE $spock_condition_throwable Ljava/lang/Throwable; L12 L3 4 LOCALVARIABLE $spock_ex Ljava/lang/Throwable; L15 L7 6 MAXSTACK = 9 diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt index 14d0a2cdcd..0887d0dff6 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt @@ -51,6 +51,19 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov L10 ALOAD 2 POP + ALOAD 0 + INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext; + INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [ + // handle kind 0x6 : INVOKESTATIC + org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; + // arguments: + "()", + 0 + ] + ICONST_0 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockEntered (Lorg/spockframework/runtime/SpecificationContext;I)V + ACONST_NULL + POP L0 LINENUMBER 4 L0 ALOAD 1 @@ -98,6 +111,32 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov ATHROW L13 FRAME SAME + ALOAD 0 + INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext; + INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [ + // handle kind 0x6 : INVOKESTATIC + org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; + // arguments: + "()", + 0 + ] + ICONST_0 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockExited (Lorg/spockframework/runtime/SpecificationContext;I)V + ACONST_NULL + POP + ALOAD 0 + INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext; + INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [ + // handle kind 0x6 : INVOKESTATIC + org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; + // arguments: + "()", + 0 + ] + ICONST_1 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockEntered (Lorg/spockframework/runtime/SpecificationContext;I)V + ACONST_NULL + POP ALOAD 0 INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext; INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [ @@ -145,8 +184,35 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov ALOAD 6 ATHROW L16 - LINENUMBER 8 L16 FRAME SAME + ALOAD 0 + INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext; + INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [ + // handle kind 0x6 : INVOKESTATIC + org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; + // arguments: + "()", + 0 + ] + ICONST_1 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockExited (Lorg/spockframework/runtime/SpecificationContext;I)V + ACONST_NULL + POP + ALOAD 0 + INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext; + INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [ + // handle kind 0x6 : INVOKESTATIC + org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; + // arguments: + "()", + 0 + ] + ICONST_2 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockEntered (Lorg/spockframework/runtime/SpecificationContext;I)V + ACONST_NULL + POP + L17 + LINENUMBER 8 L17 ALOAD 0 ACONST_NULL ACONST_NULL @@ -168,6 +234,19 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov "()", 0 ] + ICONST_2 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockExited (Lorg/spockframework/runtime/SpecificationContext;I)V + ACONST_NULL + POP + ALOAD 0 + INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext; + INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [ + // handle kind 0x6 : INVOKESTATIC + org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; + // arguments: + "()", + 0 + ] INVOKEVIRTUAL org/spockframework/runtime/SpecificationContext.getMockController ()Lorg/spockframework/mock/IMockController; INVOKEDYNAMIC cast(Lorg/spockframework/mock/IMockController;)Lorg/spockframework/mock/runtime/MockController; [ // handle kind 0x6 : INVOKESTATIC @@ -179,12 +258,12 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov INVOKEVIRTUAL org/spockframework/mock/runtime/MockController.leaveScope ()V ACONST_NULL POP - L17 - LINENUMBER 9 L17 + L18 + LINENUMBER 9 L18 RETURN - LOCALVARIABLE this Lapackage/TestSpec; L8 L17 0 - LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L9 L17 1 - LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L10 L17 2 + LOCALVARIABLE this Lapackage/TestSpec; L8 L18 0 + LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L9 L18 1 + LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L10 L18 2 LOCALVARIABLE $spock_condition_throwable Ljava/lang/Throwable; L12 L3 3 LOCALVARIABLE $spock_ex Ljava/lang/Throwable; L15 L7 5 MAXSTACK = 9 diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy index 863a604b80..ce864b0430 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy @@ -7,7 +7,9 @@ public class apackage.ASpec extends spock.lang.Specification { @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = []) public void $spock_feature_0_0() { + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) java.lang.Object nothing = null + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) this.getSpecificationContext().getMockController().leaveScope() } diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy index 57dc76b278..11ddc426bf 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy @@ -7,7 +7,9 @@ public class apackage.ASpec extends spock.lang.Specification implements groovy.l @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = []) public void $spock_feature_0_0() { + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) java.lang.Object nothing = null + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) this.getSpecificationContext().getMockController().leaveScope() } diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy index 39cb8e739b..a690ad70c0 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy @@ -6,7 +6,9 @@ class ASpec extends Specification { /*--------- tag::snapshot[] ---------*/ @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = []) public void $spock_feature_0_0() { + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) java.lang.Object nothing = null + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) this.getSpecificationContext().getMockController().leaveScope() } /*--------- end::snapshot[] ---------*/ diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy index 4a955d2daa..31d9795be5 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy @@ -12,7 +12,9 @@ private java.lang.Object $spock_initializeFields() { @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 3, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = []) public void $spock_feature_0_0() { + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) java.lang.Object nothing = null + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) this.getSpecificationContext().getMockController().leaveScope() } /*--------- end::snapshot[] ---------*/ diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_GString_labels.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_GString_labels.groovy new file mode 100644 index 0000000000..00650532dc --- /dev/null +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_GString_labels.groovy @@ -0,0 +1,40 @@ +package aPackage +import spock.lang.* + +class ASpec extends Specification { + def "aFeature"() { +/*--------- tag::snapshot[] ---------*/ +@org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHEN, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.THEN, texts = [])], parameterNames = []) +public void $spock_feature_0_0() { + org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE + org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder() + java.lang.Integer idx = 0 + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) + "given ${( idx )++}" + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1) + try { + org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), '\"expect \${idx++}\"', 3, 13, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), "${$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), ( idx )++)}expect ")) + } + catch (java.lang.Throwable $spock_condition_throwable) { + org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '\"expect \${idx++}\"', 3, 13, null, $spock_condition_throwable)} + finally { + } + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 2) + "when ${( idx )++}" + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 2) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 3) + try { + org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), '\"then \${idx++}\"', 5, 11, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), "${$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), ( idx )++)}then ")) + } + catch (java.lang.Throwable $spock_condition_throwable) { + org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '\"then \${idx++}\"', 5, 11, null, $spock_condition_throwable)} + finally { + } + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 3) + this.getSpecificationContext().getMockController().leaveScope() +} +/*--------- end::snapshot[] ---------*/ + } +} diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_empty_labels.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_empty_labels.groovy new file mode 100644 index 0000000000..bdf57f9c28 --- /dev/null +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_empty_labels.groovy @@ -0,0 +1,50 @@ +package aPackage +import spock.lang.* + +class ASpec extends Specification { + def "aFeature"() { +/*--------- tag::snapshot[] ---------*/ +@org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = ['']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = ['']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHEN, texts = ['']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.THEN, texts = ['']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.CLEANUP, texts = ['']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHERE, texts = ['', '']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.FILTER, texts = [''])], parameterNames = []) +public void $spock_feature_0_0() { + java.lang.Throwable $spock_feature_throwable + try { + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1) + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 2) + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 2) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 3) + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 3) + } + catch (java.lang.Throwable $spock_tmp_throwable) { + $spock_feature_throwable = $spock_tmp_throwable + throw $spock_tmp_throwable + } + finally { + org.spockframework.runtime.model.BlockInfo $spock_failedBlock = null + try { + if ( $spock_feature_throwable != null) { + $spock_failedBlock = this.getSpecificationContext().getCurrentBlock() + } + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 4) + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 4) + } + catch (java.lang.Throwable $spock_tmp_throwable) { + if ( $spock_feature_throwable != null) { + $spock_feature_throwable.addSuppressed($spock_tmp_throwable) + } else { + throw $spock_tmp_throwable + } + } + finally { + if ( $spock_feature_throwable != null) { + ((org.spockframework.runtime.SpecificationContext) this.getSpecificationContext()).setCurrentBlock($spock_failedBlock) + } + } + } + this.getSpecificationContext().getMockController().leaveScope() +} +/*--------- end::snapshot[] ---------*/ + } +} diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_labels_and_blocks.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_labels_and_blocks.groovy new file mode 100644 index 0000000000..69434818ef --- /dev/null +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_labels_and_blocks.groovy @@ -0,0 +1,52 @@ +package aPackage +import spock.lang.* + +class ASpec extends Specification { + def "aFeature"() { +/*--------- tag::snapshot[] ---------*/ +@org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = ['given', 'and given']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = ['expect', 'and expect']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHEN, texts = ['when', 'and when']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.THEN, texts = ['then', 'and then']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.THEN, texts = ['then2', 'and then2']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.CLEANUP, texts = ['cleanup', 'and cleanup']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHERE, texts = ['where', 'combine']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.FILTER, texts = ['only one execution'])], parameterNames = []) +public void $spock_feature_0_0() { + java.lang.Throwable $spock_feature_throwable + try { + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1) + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 2) + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 2) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 3) + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 3) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 4) + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 4) + } + catch (java.lang.Throwable $spock_tmp_throwable) { + $spock_feature_throwable = $spock_tmp_throwable + throw $spock_tmp_throwable + } + finally { + org.spockframework.runtime.model.BlockInfo $spock_failedBlock = null + try { + if ( $spock_feature_throwable != null) { + $spock_failedBlock = this.getSpecificationContext().getCurrentBlock() + } + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 5) + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 5) + } + catch (java.lang.Throwable $spock_tmp_throwable) { + if ( $spock_feature_throwable != null) { + $spock_feature_throwable.addSuppressed($spock_tmp_throwable) + } else { + throw $spock_tmp_throwable + } + } + finally { + if ( $spock_feature_throwable != null) { + ((org.spockframework.runtime.SpecificationContext) this.getSpecificationContext()).setCurrentBlock($spock_failedBlock) + } + } + } + this.getSpecificationContext().getMockController().leaveScope() +} +/*--------- end::snapshot[] ---------*/ + } +} diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy index c65f20fc26..12acd40e97 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy @@ -13,7 +13,10 @@ public void $spock_feature_0_0() { java.lang.Object foobar java.lang.Throwable $spock_feature_throwable try { + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) foobar = this.foobar() + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1) try { org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3) } @@ -21,14 +24,21 @@ public void $spock_feature_0_0() { org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)} finally { } + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1) } catch (java.lang.Throwable $spock_tmp_throwable) { $spock_feature_throwable = $spock_tmp_throwable throw $spock_tmp_throwable } finally { + org.spockframework.runtime.model.BlockInfo $spock_failedBlock = null try { + if ( $spock_feature_throwable != null) { + $spock_failedBlock = this.getSpecificationContext().getCurrentBlock() + } + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 2) foobar.size() + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 2) } catch (java.lang.Throwable $spock_tmp_throwable) { if ( $spock_feature_throwable != null) { @@ -38,6 +48,9 @@ public void $spock_feature_0_0() { } } finally { + if ( $spock_feature_throwable != null) { + ((org.spockframework.runtime.SpecificationContext) this.getSpecificationContext()).setCurrentBlock($spock_failedBlock) + } } } this.getSpecificationContext().getMockController().leaveScope() diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy index c1d899d23d..9b4a45cfc3 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy @@ -13,7 +13,10 @@ public void $spock_feature_0_0() { def (java.lang.Object foobar, java.lang.Object b) = [null, null] java.lang.Throwable $spock_feature_throwable try { + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) (foobar, b) = this.foobar() + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1) try { org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3) } @@ -21,14 +24,21 @@ public void $spock_feature_0_0() { org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)} finally { } + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1) } catch (java.lang.Throwable $spock_tmp_throwable) { $spock_feature_throwable = $spock_tmp_throwable throw $spock_tmp_throwable } finally { + org.spockframework.runtime.model.BlockInfo $spock_failedBlock = null try { + if ( $spock_feature_throwable != null) { + $spock_failedBlock = this.getSpecificationContext().getCurrentBlock() + } + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 2) foobar.size() + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 2) } catch (java.lang.Throwable $spock_tmp_throwable) { if ( $spock_feature_throwable != null) { @@ -38,6 +48,9 @@ public void $spock_feature_0_0() { } } finally { + if ( $spock_feature_throwable != null) { + ((org.spockframework.runtime.SpecificationContext) this.getSpecificationContext()).setCurrentBlock($spock_failedBlock) + } } } this.getSpecificationContext().getMockController().leaveScope() diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy index 20b6d371e4..201d1c5fd8 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy @@ -8,6 +8,7 @@ class ASpec extends Specification { public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) { org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder() + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) try { org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'a == b', 1, 83, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), a) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), b))) } @@ -15,6 +16,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) { org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a == b', 1, 83, null, $spock_condition_throwable)} finally { } + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) this.getSpecificationContext().getMockController().leaveScope() } diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy index 9fe8ae6883..215215d6ce 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy @@ -8,6 +8,7 @@ class ASpec extends Specification { public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) { org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder() + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) try { org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'a == b', 1, 83, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), a) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), b))) } @@ -15,6 +16,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) { org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a == b', 1, 83, null, $spock_condition_throwable)} finally { } + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) this.getSpecificationContext().getMockController().leaveScope() } diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy index 140341467e..3ad733861b 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy @@ -7,6 +7,7 @@ class ASpec extends Specification { public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang.Object c) { org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder() + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) try { org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true)) } @@ -14,6 +15,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)} finally { } + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) this.getSpecificationContext().getMockController().leaveScope() } diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy index 140341467e..3ad733861b 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy @@ -7,6 +7,7 @@ class ASpec extends Specification { public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang.Object c) { org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder() + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) try { org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true)) } @@ -14,6 +15,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)} finally { } + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) this.getSpecificationContext().getMockController().leaveScope() } diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy index 140341467e..3ad733861b 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy @@ -7,6 +7,7 @@ class ASpec extends Specification { public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang.Object c) { org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder() + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) try { org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true)) } @@ -14,6 +15,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)} finally { } + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) this.getSpecificationContext().getMockController().leaveScope() } diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy index a6dda8e53b..eb157c19b4 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy @@ -7,6 +7,7 @@ class ASpec extends Specification { public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) { org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder() + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) try { org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true)) } @@ -14,6 +15,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) { org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)} finally { } + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) this.getSpecificationContext().getMockController().leaveScope() } diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy index 1e090f19a3..e11a1a15e8 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy @@ -8,7 +8,10 @@ class ASpec extends Specification { public void $spock_feature_0_0() { org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder() + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) java.lang.Object x = [1] + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1) try { org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsAsSet'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5) } @@ -16,6 +19,7 @@ public void $spock_feature_0_0() { org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ [1]', 4, 9, null, $spock_condition_throwable)} finally { } + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1) this.getSpecificationContext().getMockController().leaveScope() } /*--------- end::snapshot[] ---------*/ diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy index 7973626ae8..6e11bd81a6 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy @@ -8,7 +8,10 @@ class ASpec extends Specification { public void $spock_feature_0_0() { org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder() + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) java.lang.Object x = [1] + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1) try { org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsInAnyOrder'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5) } @@ -16,6 +19,7 @@ public void $spock_feature_0_0() { org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ [1]', 4, 9, null, $spock_condition_throwable)} finally { } + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1) this.getSpecificationContext().getMockController().leaveScope() } /*--------- end::snapshot[] ---------*/ diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy index 867b2cac29..00647881b6 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy @@ -8,7 +8,10 @@ class ASpec extends Specification { public void $spock_feature_0_0() { org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder() + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) java.lang.Object x = '[1]' + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1) try { org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ /\\d/', 4, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), org.spockframework.runtime.SpockRuntime.matchCollectionsAsSet($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), '\\d')))) } @@ -16,6 +19,7 @@ public void $spock_feature_0_0() { org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ /\\d/', 4, 9, null, $spock_condition_throwable)} finally { } + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1) this.getSpecificationContext().getMockController().leaveScope() } /*--------- end::snapshot[] ---------*/ diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy index 63bc98d5d2..4ce13e20d9 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy @@ -8,7 +8,10 @@ class ASpec extends Specification { public void $spock_feature_0_0() { org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder() + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) java.lang.Object x = 'a1b' + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1) try { org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ /a\\db/', 4, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), org.spockframework.runtime.SpockRuntime.matchCollectionsInAnyOrder($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), 'a\\db')))) } @@ -16,6 +19,7 @@ public void $spock_feature_0_0() { org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ /a\\db/', 4, 9, null, $spock_condition_throwable)} finally { } + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1) this.getSpecificationContext().getMockController().leaveScope() } /*--------- end::snapshot[] ---------*/ diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy index c76b5be927..2a5315379b 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy @@ -9,6 +9,7 @@ public java.lang.Object foobar() { public void $spock_feature_0_0() { java.lang.Object foobar + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) this.getSpecificationContext().setThrownException(null) try { foobar = this.foobar() @@ -18,7 +19,10 @@ public void $spock_feature_0_0() { } finally { } + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1) this.thrownImpl(null, null, java.lang.IllegalStateException) + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1) this.getSpecificationContext().getMockController().leaveScope() } /*--------- end::snapshot[] ---------*/ diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy index 9f3f3590e9..9a592abdd8 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy @@ -9,6 +9,7 @@ public java.lang.Object foobar() { public void $spock_feature_0_0() { def (java.lang.Object foobar, java.lang.Object b) = [null, null] + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) this.getSpecificationContext().setThrownException(null) try { (foobar, b) = this.foobar() @@ -18,7 +19,10 @@ public void $spock_feature_0_0() { } finally { } + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1) this.thrownImpl(null, null, java.lang.IllegalStateException) + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1) this.getSpecificationContext().getMockController().leaveScope() } /*--------- end::snapshot[] ---------*/ diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy new file mode 100644 index 0000000000..4a79964dc0 --- /dev/null +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy @@ -0,0 +1,24 @@ +package aPackage +import spock.lang.* + +class ASpec extends Specification { + def "aFeature"() { +/*--------- tag::snapshot[] ---------*/ +@org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHEN, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.THEN, texts = [])], parameterNames = []) +public void $spock_feature_0_0() { + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0) + java.util.List list = this.MockImpl('list', java.util.List) + this.getSpecificationContext().getMockController().enterScope() + this.getSpecificationContext().getMockController().addInteraction(new org.spockframework.mock.runtime.InteractionBuilder(8, 5, '1 * list.add(1)').setFixedCount(1).addEqualTarget(list).addEqualMethodName('add').setArgListKind(true, false).addEqualArg(1).build()) + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1) + list.add(1) + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 2) + this.getSpecificationContext().getMockController().leaveScope() + org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 2) + this.getSpecificationContext().getMockController().leaveScope() +} +/*--------- end::snapshot[] ---------*/ + } +} diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.groovy new file mode 100644 index 0000000000..f5929cc270 --- /dev/null +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.groovy @@ -0,0 +1,30 @@ +package aPackage +import spock.lang.* + +class ASpec extends Specification { +/*--------- tag::snapshot[] ---------*/ +public void $spock_feature_0_0(java.lang.Object dataPipe, java.lang.Object dataVariable) { + this.getSpecificationContext().getMockController().leaveScope() +} + +public java.lang.Object $spock_feature_0_0prov0() { + org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE + return [{ -> + org.spockframework.runtime.ValueRecorder $spock_valueRecorder1 = new org.spockframework.runtime.ValueRecorder() + try { + org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder1.reset(), 'true', 2, 29, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true)) + } + catch (java.lang.Throwable $spock_condition_throwable) { + org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder1, 'true', 2, 29, null, $spock_condition_throwable)} + finally { + } + }] +} + +public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0) { + java.lang.Object dataPipe = (( $spock_p0 ) as java.lang.Object) + java.lang.Object dataVariable = ((null) as java.lang.Object) + return new java.lang.Object[]{ dataPipe , dataVariable } +} +/*--------- end::snapshot[] ---------*/ +} diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.groovy new file mode 100644 index 0000000000..97a553191b --- /dev/null +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.groovy @@ -0,0 +1,30 @@ +package aPackage +import spock.lang.* + +class ASpec extends Specification { +/*--------- tag::snapshot[] ---------*/ +public void $spock_feature_0_0(java.lang.Object dataPipe, java.lang.Object dataVariable) { + this.getSpecificationContext().getMockController().leaveScope() +} + +public java.lang.Object $spock_feature_0_0prov0() { + return [null] +} + +public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0) { + org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE + java.lang.Object dataPipe = (( $spock_p0 ) as java.lang.Object) + java.lang.Object dataVariable = (({ -> + org.spockframework.runtime.ValueRecorder $spock_valueRecorder1 = new org.spockframework.runtime.ValueRecorder() + try { + org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder1.reset(), 'true', 3, 31, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true)) + } + catch (java.lang.Throwable $spock_condition_throwable) { + org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder1, 'true', 3, 31, null, $spock_condition_throwable)} + finally { + } + }) as java.lang.Object) + return new java.lang.Object[]{ dataPipe , dataVariable } +} +/*--------- end::snapshot[] ---------*/ +}