-
Notifications
You must be signed in to change notification settings - Fork 470
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add BlockListener support... #1575
base: master
Are you sure you want to change the base?
Add BlockListener support... #1575
Conversation
spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java
Outdated
Show resolved
Hide resolved
Hi @leonard84. I cannot perform a formal code review, because I am not a committer, but I hope that during the weekend I can find a small time slice to build and play around with it. I simply wanted to say thanks in advance for taking care of this feature request. It has not gone unnoticed. Quick question: Are you planning to add more commits to this PR? Code changes? User manual? I am just asking, not demanding anything. I simply do not want to start testing too early. As for the user manual, I can of course take a look at the unit tests and take it from there. But that might not be true for all future extension developers, I am just speaking for myself. |
e49b0e3
to
f1d1840
Compare
try { | ||
org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, [])) | ||
foobar = this.foobar() | ||
org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, [])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💬 this won't be called, fixing this makes for some awkward interactions between the individual AST transformations,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried moving the blockListener logic where the blocks are written back in SpecRewriter#L388
, however this didn't work for some cases, e.g. cleanup
as it already replaced all Blocks with an anonymous block.
spock-specs/src/test/groovy/org/spockframework/smoke/ast/mock/MocksAstSpec.groovy
Outdated
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java
Outdated
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java
Outdated
Show resolved
Hide resolved
c3f30eb
to
0f3197e
Compare
As this PR seems a little bit stalled, I pushed the code I initially created ~6 months ago as a PoC for the BlockListener support. It a very raw version (with a lot of diagnostic stuff to be amended in the future), but functional in some basic scenarios. The JPA session, where requested, is flushed after the https://github.com/szpak/spock-jpa-flush-enforcer/tree/preview1 I would like to awake discussion about future shape of this PR. @leonard84 @kriegaex WDYT? |
It mostly hangs on the problems that adding the exit listener introduces #1575 (comment) |
0f3197e
to
ae38bda
Compare
ae38bda
to
353cbf9
Compare
I think I've fixed the issues, but I need another fresh set of eyes to verify that everything looks good, I've stared at too many snapshots already. |
spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
Outdated
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java
Show resolved
Hide resolved
@AndreasTu, as I mentioned earlier, I'll write some documentation and polish it later. Thanks for your comments in any case. However, I'm looking for a review of the correctness of the implementation and generated code and general usability. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've switched my experimental (and heavily work-in-progress) extension to the latest version and it still works as expected without any modification. Nice.
I will think about the possible corner cases, I might encounter there.
Btw, I wonder, if it is still a recommended way to decide if the block listener was intended for the current iteration?
spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
Outdated
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java
Show resolved
Hide resolved
spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy
Outdated
Show resolved
Hide resolved
spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy
Outdated
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/runtime/model/BlockInfo.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some things missing in multiple places:
- license headers
- JavaDoc
- documentation
@Beta
@since
spock-core/src/main/java/org/spockframework/compiler/AstUtil.java
Outdated
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
Outdated
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
Outdated
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java
Outdated
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
Show resolved
Hide resolved
Btw. could we also support |
I don't think it would be intuitive or really helpful.
However, you are most familiar with that part of the code. |
No idea. :-D But we can also start without and see where and how need arises. |
Forgot to publish my responses 😅 |
spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
Outdated
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java
Outdated
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java
Outdated
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/lang/ISpecificationContext.java
Outdated
Show resolved
Hide resolved
@szpak, can you motivate this use case more? At first glance, I'd try to register a block listener that can handle all iterations instead of one per iteration. |
Hmm, I think the main problem was to get the |
5a2f3f2
to
c9ee05f
Compare
We could easily pass in the current instance. The question is whether we should. I've also been debating whether I should give access to the current |
Definitely that's the good question. What are the design difference between interceptors (which have access to
Do you see any good cases where it would be necessary for listeners in practice? To read the values set by the other extensions/interceptors? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a friendly reminder about the documentation (preferably with a small example of usage), before merging it.
@@ -27,6 +29,15 @@ public class BlockInfo { | |||
private BlockKind kind; | |||
private List<String> texts; | |||
|
|||
public BlockInfo() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could mark it as deprecated and possibly remove it in Spock 3.0 (or so)? Spock itself no longer calls the old constructor.
The setters could be also marked as deprecated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some more comments. :-)
Also, docs are of course still missing.
@@ -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_GetBlockCurrentBlock = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public final MethodNode SpecificationContext_GetBlockCurrentBlock = | |
public final MethodNode SpecificationContext_GetCurrentBlock = |
public final MethodNode SpecificationContext_GetBlockCurrentBlock = | ||
SpecificationContext.getDeclaredMethods(org.spockframework.runtime.SpecificationContext.GET_CURRENT_BLOCK).get(0); | ||
|
||
public final MethodNode SpecificationContext_SetBlockCurrentBlock = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public final MethodNode SpecificationContext_SetBlockCurrentBlock = | |
public final MethodNode SpecificationContext_SetCurrentBlock = |
|
||
public static BinaryExpression createVariableIsNotNullExpression(VariableExpression var) { | ||
return new BinaryExpression( | ||
new VariableExpression(var), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to create a new VariableExpression
from the supplied one?
|
||
public static BinaryExpression createVariableIsNullExpression(VariableExpression var) { | ||
return new BinaryExpression( | ||
new VariableExpression(var), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to create a new VariableExpression
from the supplied one?
new ConstantExpression(null)); | ||
} | ||
|
||
public static BinaryExpression createVariableIsNullExpression(VariableExpression var) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it intended that this unused method is added for some future use or is it a left-over from being used at some point?
@@ -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 SpockSnapshotter snapshotter) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def "data variable with asserting closure produces error rethrower variable in data processor method"(@Snapshot SpockSnapshotter snapshotter) { | |
def "data variable with asserting closure produces error rethrower variable in data processor method"(@Snapshot(extension = 'groovy') SpockSnapshotter snapshotter) { |
@@ -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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// set the block metaData index for each block this must be equal to the index of the block in the @BlockMetadata annotation | |
// set the block metadata index for each block this must be equal to the index of the block in the @BlockMetadata annotation |
@@ -31,6 +31,7 @@ public abstract class Block extends Node<Method, List<Statement>> { | |||
private final List<String> descriptions = new ArrayList<>(3); | |||
private Block prev; | |||
private Block next; | |||
private int blockMetaDataIndex = -1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private int blockMetaDataIndex = -1; | |
private int blockMetadataIndex = -1; |
@@ -80,5 +81,25 @@ public boolean isFirstInChain() { | |||
return isFirst() || getClass() != prev.getClass(); | |||
} | |||
|
|||
public void setBlockMetaDataIndex(int blockMetaDataIndex) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public void setBlockMetaDataIndex(int blockMetaDataIndex) { | |
public void setBlockMetadataIndex(int blockMetadataIndex) { |
this.blockMetaDataIndex = blockMetaDataIndex; | ||
} | ||
|
||
public int getBlockMetaDataIndex() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public int getBlockMetaDataIndex() { | |
public int getBlockMetadataIndex() { |
This feature allows extension authors to register a IBlockListener for a feature to observe the execution of a feature in more detail. This surfaces some of Spock's idiosyncrasies, for example interaction assertions are actually setup right before entering the preceding `when`-block as well as being evaluated on leaving the `when`-block before actually entering the `then`-block. The only valid block description is a constant String, although some users mistakenly try to use a dynamic GString. Using anything other than a String, will be treated as a separate statement and thus ignored.
Prior to this commit, IRunListener.error(ErrorInfo) didn't give any context where the error happened.
c34f817
to
bd9e694
Compare
|
||
private void addBlockListeners(Block block) { | ||
BlockParseInfo blockType = block.getParseInfo(); | ||
if (blockType == BlockParseInfo.WHERE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is FILTER
missing here?
This feature allows extension authors to register a IBlockListener for
a feature to observe the execution of a feature in more detail.
This surfaces some of Spock's idiosyncrasies, for example interaction
assertions are actually setup right before entering the preceding
when
-block as well as being evaluated on leaving thewhen
-blockbefore actually entering the
then
-block.The only valid block description is a constant String, although some
users mistakenly try to use a dynamic GString. Using anything other
than a String, will be treated as a separate statement and thus ignored.
fixes #538
fixes #111