Skip to content

Commit

Permalink
Fix block listeners for interactions
Browse files Browse the repository at this point in the history
  • Loading branch information
leonard84 committed Mar 21, 2024
1 parent fd4207a commit 353cbf9
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 166 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,59 +58,6 @@ public DeepBlockRewriter(IRewriteResources resources) {
@Override
public void visit(Block block) {
super.visit(block);
addBlockEnterCall(block);
}

private void addBlockEnterCall(Block block) {
BlockParseInfo blockType = block.getParseInfo();
if (blockType == BlockParseInfo.WHERE
|| blockType == BlockParseInfo.METHOD_END
|| blockType == BlockParseInfo.ANONYMOUS) return;

// SpockRuntime.enterBlock(getSpecificationContext(), new BlockInfo(blockKind, [blockTexts]))
MethodCallExpression enterBlockCall = createBlockListenerCall(block, blockType, resources.getAstNodeCache().SpockRuntime_CallEnterBlock);
// SpockRuntime.exitedBlock(getSpecificationContext(), new BlockInfo(blockKind, [blockTexts]))
MethodCallExpression exitBlockCall = createBlockListenerCall(block, blockType, resources.getAstNodeCache().SpockRuntime_CallExitBlock);

// As the cleanup block finalizes the specification, it would override any previous block in ErrorInfo,
// so we only call enterBlock if there is no error yet.
if (blockType == BlockParseInfo.CLEANUP) {
block.getAst().add(0, ifThrowableIsNull(enterBlockCall));
block.getAst().add(ifThrowableIsNull(exitBlockCall));
} else {
block.getAst().add(0, new ExpressionStatement(enterBlockCall));
block.getAst().add( new ExpressionStatement(exitBlockCall));
}
}

private IfStatement ifThrowableIsNull(MethodCallExpression methodCall) {
return new IfStatement(
// if ($spock_feature_throwable == null)
new BooleanExpression(AstUtil.createVariableIsNullExpression(new VariableExpression(SpecRewriter.SPOCK_FEATURE_THROWABLE, resources.getAstNodeCache().Throwable))),
new ExpressionStatement(methodCall),
EmptyStatement.INSTANCE
);
}

private MethodCallExpression createBlockListenerCall(Block block, BlockParseInfo blockType, MethodNode blockListenerMethod) {
return createDirectMethodCall(
new ClassExpression(resources.getAstNodeCache().SpockRuntime),
blockListenerMethod,
new ArgumentListExpression(
createDirectMethodCall(VariableExpression.THIS_EXPRESSION,
resources.getAstNodeCache().SpecInternals_GetSpecificationContext,
ArgumentListExpression.EMPTY_ARGUMENTS),
new ConstructorCallExpression(resources.getAstNodeCache().BlockInfo,
new ArgumentListExpression(
new PropertyExpression(
new ClassExpression(resources.getAstNodeCache().BlockKind),
blockType.name()
),
new ListExpression(
block.getDescriptions().stream().map(ConstantExpression::new).collect(Collectors.toList())
)
))
));
}

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

import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.stream.Collectors;

import org.codehaus.groovy.ast.*;
import org.codehaus.groovy.ast.expr.*;
Expand Down Expand Up @@ -384,8 +385,11 @@ public void visitMethodAgain(Method method) {
this.block = null;

if (!movedStatsBackToMethod)
for (Block b : method.getBlocks())
for (Block b : method.getBlocks()) {
// this will only have the blocks if there was no 'cleanup' block in the method
addBlockListeners(b);
method.getStatements().addAll(b.getAst());
}

// for global required interactions
if (method instanceof FeatureMethod)
Expand All @@ -402,6 +406,59 @@ public void visitMethodAgain(Method method) {
clearCurrentBlockOnExit(method.getStatements());
}


private void addBlockListeners(Block block) {
BlockParseInfo blockType = block.getParseInfo();
if (blockType == BlockParseInfo.WHERE
|| blockType == BlockParseInfo.METHOD_END
|| blockType == BlockParseInfo.ANONYMOUS) return;

// SpockRuntime.enterBlock(getSpecificationContext(), new BlockInfo(blockKind, [blockTexts]))
MethodCallExpression enterBlockCall = createBlockListenerCall(block, blockType, nodeCache.SpockRuntime_CallEnterBlock);
// SpockRuntime.exitedBlock(getSpecificationContext(), new BlockInfo(blockKind, [blockTexts]))
MethodCallExpression exitBlockCall = createBlockListenerCall(block, blockType, nodeCache.SpockRuntime_CallExitBlock);

// As the cleanup block finalizes the specification, it would override any previous block in ErrorInfo,
// so we only call enterBlock if there is no error yet.
if (blockType == BlockParseInfo.CLEANUP) {
block.getAst().add(0, ifThrowableIsNull(enterBlockCall));
block.getAst().add(ifThrowableIsNull(exitBlockCall));
} else {
block.getAst().add(0, new ExpressionStatement(enterBlockCall));
block.getAst().add( new ExpressionStatement(exitBlockCall));
}
}

private IfStatement ifThrowableIsNull(MethodCallExpression methodCall) {
return new IfStatement(
// if ($spock_feature_throwable == null)
new BooleanExpression(AstUtil.createVariableIsNullExpression(new VariableExpression(SpecRewriter.SPOCK_FEATURE_THROWABLE, nodeCache.Throwable))),
new ExpressionStatement(methodCall),
EmptyStatement.INSTANCE
);
}

private MethodCallExpression createBlockListenerCall(Block block, BlockParseInfo blockType, MethodNode blockListenerMethod) {
return createDirectMethodCall(
new ClassExpression(nodeCache.SpockRuntime),
blockListenerMethod,
new ArgumentListExpression(
createDirectMethodCall(VariableExpression.THIS_EXPRESSION,
nodeCache.SpecInternals_GetSpecificationContext,
ArgumentListExpression.EMPTY_ARGUMENTS),
new ConstructorCallExpression(nodeCache.BlockInfo,
new ArgumentListExpression(
new PropertyExpression(
new ClassExpression(nodeCache.BlockKind),
blockType.name()
),
new ListExpression(
block.getDescriptions().stream().map(ConstantExpression::new).collect(Collectors.toList())
)
))
));
}

@Override
public void visitAnyBlock(Block block) {
this.block = block;
Expand Down Expand Up @@ -480,6 +537,9 @@ private Statement createMockControllerCall(MethodNode method) {
@Override
public void visitCleanupBlock(CleanupBlock block) {
for (Block b : method.getBlocks()) {
// call addBlockListeners() here, as this method will consume the blocks,
// so we need to transform here as they will be gone in visitMethodAgain where we normally add them
addBlockListeners(b);
if (b == block) break;
moveVariableDeclarations(b.getAst(), method.getStatements());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class BlockListenerSpec extends Specification {
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]
assert exitBlocks.kind == [BlockKind.SETUP, BlockKind.EXPECT, BlockKind.WHEN, BlockKind.THEN]
}

def "SpecificationContext holds a reference to the current block"() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,19 +137,6 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)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
ACONST_NULL
LDC Ljava/lang/Throwable;.class
INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
CHECKCAST java/lang/Throwable
INVOKEVIRTUAL org/spockframework/runtime/SpecificationContext.setThrownException (Ljava/lang/Throwable;)V
ACONST_NULL
POP
L4
ALOAD 0
INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
LDC Lorg/spockframework/runtime/SpecificationContext;.class
Expand All @@ -174,39 +161,27 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
ACONST_NULL
POP
L17
LINENUMBER 6 L17
ICONST_1
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
ALOAD 1
LDC 8
AALOAD
LDC Lorg/spockframework/runtime/model/BlockInfo;.class
ALOAD 1
LDC 9
AALOAD
LDC Lorg/spockframework/runtime/model/BlockKind;.class
INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
ICONST_0
ANEWARRAY java/lang/Object
INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)
LDC Lorg/spockframework/runtime/model/BlockInfo;.class
ACONST_NULL
LDC Ljava/lang/Throwable;.class
INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
CHECKCAST org/spockframework/runtime/model/BlockInfo
INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
CHECKCAST java/lang/Throwable
INVOKEVIRTUAL org/spockframework/runtime/SpecificationContext.setThrownException (Ljava/lang/Throwable;)V
ACONST_NULL
POP
GOTO L18
L4
LINENUMBER 6 L4
ICONST_1
POP
GOTO L17
L5
FRAME SAME1 java/lang/Throwable
ASTORE 6
L19
L18
ALOAD 0
INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
LDC Lorg/spockframework/runtime/SpecificationContext;.class
Expand All @@ -218,17 +193,41 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
POP
NOP
L7
GOTO L18
L18
GOTO L17
L17
FRAME SAME
GOTO L20
GOTO L19
L6
FRAME SAME1 java/lang/Throwable
ASTORE 7
ALOAD 7
ATHROW
L20
L19
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
ALOAD 1
LDC 8
AALOAD
LDC Lorg/spockframework/runtime/model/BlockInfo;.class
ALOAD 1
LDC 9
AALOAD
LDC Lorg/spockframework/runtime/model/BlockKind;.class
INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
ICONST_0
ANEWARRAY java/lang/Object
INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)
LDC Lorg/spockframework/runtime/model/BlockInfo;.class
INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
CHECKCAST org/spockframework/runtime/model/BlockInfo
INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
ACONST_NULL
POP
ALOAD 0
INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
LDC Lorg/spockframework/runtime/SpecificationContext;.class
Expand All @@ -253,8 +252,8 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
ACONST_NULL
POP
L21
LINENUMBER 8 L21
L20
LINENUMBER 8 L20
ALOAD 1
LDC 12
AALOAD
Expand Down Expand Up @@ -300,7 +299,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
INVOKEVIRTUAL org/spockframework/mock/runtime/MockController.leaveScope ()V
ACONST_NULL
POP
L22
L21
GOTO L9
L9
FRAME SAME
Expand All @@ -312,7 +311,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
INVOKESTATIC org/spockframework/runtime/SpockRuntime.clearCurrentBlock (Lorg/spockframework/runtime/SpecificationContext;)V
ACONST_NULL
POP
GOTO L23
GOTO L22
L10
FRAME FULL [apackage/TestSpec [Lorg/codehaus/groovy/runtime/callsite/CallSite;] [java/lang/Throwable]
ASTORE 8
Expand All @@ -326,14 +325,14 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
POP
ALOAD 8
ATHROW
L23
L22
FRAME APPEND [org/spockframework/runtime/ErrorCollector org/spockframework/runtime/ValueRecorder]
RETURN
LOCALVARIABLE this Lapackage/TestSpec; L11 L23 0
LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L12 L22 2
LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L13 L22 3
LOCALVARIABLE this Lapackage/TestSpec; L11 L22 0
LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L12 L21 2
LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L13 L21 3
LOCALVARIABLE $spock_condition_throwable Ljava/lang/Throwable; L15 L3 4
LOCALVARIABLE $spock_ex Ljava/lang/Throwable; L19 L7 6
LOCALVARIABLE $spock_ex Ljava/lang/Throwable; L18 L7 6
MAXSTACK = 9
MAXLOCALS = 9
}
Loading

0 comments on commit 353cbf9

Please sign in to comment.