Skip to content

Commit 4abd191

Browse files
committed
Enable implicit non-top-level conditions (#1160)
1 parent 9c6342c commit 4abd191

File tree

10 files changed

+64
-16
lines changed

10 files changed

+64
-16
lines changed

docs/release_notes.adoc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,27 @@ include::include.adoc[]
66

77
== 2.5 (tbd)
88

9+
=== Breaking Changes
10+
11+
* It is no longer true that only top-level expression statements are treated as implicit conditions.
12+
Just like within `with { ... }`, `verifyAll { ... }`, `@ConditionBlock` methods, and other places,
13+
each expression statement is now considered an implicit condition, except for statements in unknown
14+
closures.
15+
+
16+
This enables the usage of `if` and similar while also being able to still safely use `.every { ... }`,
17+
but without the regular confusion why a test is not failing if the condition is inside an `if`.
18+
You can even have interactions in those nested places now.
19+
+
20+
As now expression statements are considered implicit conditions that previously were not,
21+
this can lead to compilation errors in existing code-bases, because such nested statements were
22+
previously simply ignored by the Spock compiler and now also must be valid conditions.
23+
+
24+
If those compile errors are not accidentally written assignments that should have been conditions
25+
or similar, you can for example use the <<spock_primer.adoc#opt-out-of-condition-handling,`!!` prefix operator>>
26+
to declare an expression statement explicitly as not being an implicit condition.
27+
+
28+
spockPull:2250[]
29+
930
=== Enhancements
1031

1132
* Improve `TooManyInvocationsError` now reports unsatisfied interactions with argument mismatch details, making it easier to diagnose why invocations didn't match expected interactions spockPull:2315[]

docs/spock_primer.adoc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,9 @@ digestible form. Nice, isn't it?
230230
===== Implicit and explicit conditions
231231

232232
Conditions are an essential ingredient of `then` blocks and `expect` blocks. Except for calls to `void` methods and
233-
expressions classified as interactions, all top-level expressions in these blocks are implicitly treated as conditions.
234-
To use conditions in other places, you need to designate them with Groovy's assert keyword:
233+
expressions classified as interactions, all expression statements in these blocks are implicitly treated as conditions
234+
except if they happen inside some unknown closure.
235+
To use conditions in other places, you need to designate them with Groovy's `assert` keyword:
235236

236237
[source,groovy]
237238
----

spock-core/src/main/java/org/spockframework/compiler/AbstractDeepBlockRewriter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ public final void visitClosureExpression(ClosureExpression expr) {
151151
groupConditionFound = false;
152152
ISpecialMethodCall oldSpecialMethodCall = currSpecialMethodCall;
153153
if (!currSpecialMethodCall.isMatch(expr)) {
154-
currSpecialMethodCall = NoSpecialMethodCall.INSTANCE; // unrelated closure terminates currSpecialMethodCall scope
154+
currSpecialMethodCall = NoSpecialMethodCall.CLOSURE_INSTANCE; // unrelated closure terminates currSpecialMethodCall scope
155155
}
156156
try {
157157
Statement code = expr.getCode();

spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ private boolean handleInteraction(InteractionRewriter rewriter, ExpressionStatem
201201
}
202202

203203
private boolean handleImplicitCondition(ExpressionStatement stat) {
204-
if (!(stat == currTopLevelStat && isThenOrExpectOrFilterBlock()
204+
if (!(((currSpecialMethodCall == NoSpecialMethodCall.INSTANCE) && isThenOrExpectOrFilterBlock())
205205
|| currSpecialMethodCall.isConditionMethodCall()
206206
|| currSpecialMethodCall.isConditionBlock()
207207
|| currSpecialMethodCall.isGroupConditionBlock()

spock-core/src/main/java/org/spockframework/compiler/NoSpecialMethodCall.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
public class NoSpecialMethodCall implements ISpecialMethodCall {
2525
public static final ISpecialMethodCall INSTANCE = new NoSpecialMethodCall();
26+
public static final ISpecialMethodCall CLOSURE_INSTANCE = new NoSpecialMethodCall();
2627

2728
@Override
2829
public boolean isMethodName(String name) {

spock-junit4/src/test/groovy/org/spockframework/junit4/junit/JUnitFixtureMethods.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ class JUnitFixtureMethods extends JUnitBaseSpec {
151151
then:
152152
def e = result.failures[exceptionPos].exception
153153
if (suppressed) {
154-
e = e.suppressed[0]
154+
!!(e = e.suppressed[0])
155155
}
156156
e instanceof RuntimeException
157157
e.message == name

spock-specs/src/test/groovy/org/spockframework/smoke/condition/ConditionEvaluation.groovy

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,19 @@ class ConditionEvaluation extends EmbeddedSpecification {
5959
7
6060
}
6161

62+
@FailsWith(ConditionNotSatisfiedError)
63+
def "failing non-top-level condition"() {
64+
expect:
65+
if (true) {
66+
2 * 3 == 7
67+
}
68+
}
69+
70+
def "failing non-top-level non-condition"() {
71+
expect:
72+
[1, 2, 3].any { it == 2 }
73+
}
74+
6275
def "MethodCallExpression"() {
6376
expect:
6477
[1, 2, 3].size() == 3

spock-specs/src/test/groovy/org/spockframework/smoke/extension/TempDirExtensionSpec.groovy

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,12 @@ class TempDirExtensionSpec extends EmbeddedSpecification {
8888
def tempFile = aaabbb.resolve("tmp.txt")
8989
Files.createDirectories(aaabbb)
9090
Files.write(tempFile, "ewfwf".bytes)
91-
aaabbb.toFile().writable = false
92-
aaa.toFile().writable = false
93-
tempFile.toFile().writable = false
94-
previousIteration = iterationDir
91+
!!(aaabbb.toFile().writable = false)
92+
!!(aaa.toFile().writable = false)
93+
!!(tempFile.toFile().writable = false)
94+
!!(previousIteration = iterationDir)
9595
} else if (i == 1) {
96-
assert !previousIteration.exists()
96+
!previousIteration.exists()
9797
}
9898

9999
where:

spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_in_private_methods.groovy

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,15 @@ public void $spock_feature_0_0() {
8484
finally {
8585
}
8686
if (true) {
87-
[true, false].any({ ->
88-
it
89-
})
87+
try {
88+
org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[true, false].any { it }', 5, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), false)]), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), 'any'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), { ->
89+
it
90+
})}, $spock_valueRecorder.realizeNas(7, false), false, 6)
91+
}
92+
catch (java.lang.Throwable $spock_condition_throwable) {
93+
org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '[true, false].any { it }', 5, 9, null, $spock_condition_throwable)}
94+
finally {
95+
}
9096
}
9197
this.with({ ->
9298
org.spockframework.runtime.ValueRecorder $spock_valueRecorder1 = new org.spockframework.runtime.ValueRecorder()

spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_in_private_methods.groovy

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,15 @@ public void $spock_feature_0_0() {
8080
finally {
8181
}
8282
if (true) {
83-
[true, false].any({ ->
84-
it
85-
})
83+
try {
84+
org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), '[true, false].any { it }', 5, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), false)]), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), 'any'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), { ->
85+
it
86+
})}, $spock_valueRecorder.realizeNas(7, false), false, 6)
87+
}
88+
catch (java.lang.Throwable $spock_condition_throwable) {
89+
org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '[true, false].any { it }', 5, 9, null, $spock_condition_throwable)}
90+
finally {
91+
}
8692
}
8793
this.with({ ->
8894
org.spockframework.runtime.ValueRecorder $spock_valueRecorder1 = new org.spockframework.runtime.ValueRecorder()

0 commit comments

Comments
 (0)