Skip to content

Commit a88b966

Browse files
authored
Fix handling of condition method calls (#2162)
1 parent c7cae30 commit a88b966

File tree

25 files changed

+751
-6
lines changed

25 files changed

+751
-6
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import static java.util.Collections.singletonList;
4242
import static org.spockframework.compiler.AstUtil.createDirectMethodCall;
4343
import static org.spockframework.compiler.AstUtil.primitiveConstExpression;
44+
import static org.spockframework.compiler.SpecialMethodCall.checkIsConditionMethodCall;
4445

4546
// NOTE: currently some conversions reference old expression objects rather than copying them;
4647
// this can potentially lead to aliasing problems (e.g. for Condition.originalExpression)
@@ -641,8 +642,7 @@ private Statement rewriteCondition(Expression expr, Expression message, boolean
641642
// method conditions with spread operator are not lifted because MOP doesn't support spreading
642643
if (expr instanceof MethodCallExpression && !((MethodCallExpression) expr).isSpreadSafe()) {
643644
MethodCallExpression methodCallExpression = (MethodCallExpression)expr;
644-
String methodName = AstUtil.getMethodName(methodCallExpression);
645-
if ((Identifiers.CONDITION_METHODS.contains(methodName))) {
645+
if (checkIsConditionMethodCall(methodCallExpression)) {
646646
return surroundSpecialTryCatch(expr);
647647
}
648648
return rewriteMethodCondition(methodCallExpression, message, explicit, optOut);

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,17 @@ private boolean handleImplicitCondition(ExpressionStatement stat) {
204204

205205
checkIsValidImplicitCondition(stat, resources.getErrorReporter());
206206

207-
String methodName = AstUtil.getMethodName(stat.getExpression());
208-
boolean isConditionMethodCall = Identifiers.CONDITION_METHODS.contains(methodName);
207+
boolean isConditionMethodCall;
208+
if (stat.getExpression() instanceof MethodCallExpression) {
209+
isConditionMethodCall = SpecialMethodCall.checkIsConditionMethodCall(((MethodCallExpression) stat.getExpression()));
210+
} else {
211+
isConditionMethodCall = false;
212+
}
209213

210214
if (isConditionMethodCall) {
211215
groupConditionFound = currSpecialMethodCall.isGroupConditionBlock();
212-
} else {
216+
}
217+
if (!isConditionMethodCall || currSpecialMethodCall.isConditionMethodCall() || currSpecialMethodCall.isGroupConditionBlock()) {
213218
conditionFound();
214219
}
215220

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,11 @@ public String toString() {
269269
methodName, inferredName, inferredType, methodCallExpr, closureExpr, conditionBlock);
270270
}
271271

272+
static boolean checkIsConditionMethodCall(MethodCallExpression expr) {
273+
if (!AstUtil.isThisOrSuperExpression(expr.getObjectExpression())) return false;
274+
return Identifiers.CONDITION_METHODS.contains(expr.getMethodAsString());
275+
}
276+
272277
private static boolean checkIsBuiltInMethodCall(MethodCallExpression expr) {
273278
if (!AstUtil.isThisOrSuperExpression(expr.getObjectExpression())) return false;
274279
return Identifiers.BUILT_IN_METHODS.contains(expr.getMethodAsString());

spock-specs/src/test/groovy/org/spockframework/smoke/WithBlockFailingConditions.groovy

Lines changed: 144 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414

1515
package org.spockframework.smoke
1616

17+
import org.spockframework.EmbeddedSpecification
1718
import org.spockframework.runtime.*
1819
import spock.lang.*
1920

20-
class WithBlockFailingConditions extends Specification {
21+
class WithBlockFailingConditions extends EmbeddedSpecification {
2122
@FailsWith(ConditionNotSatisfiedError)
2223
def "basic usage"() {
2324
def list = [1, 2]
@@ -155,4 +156,146 @@ class WithBlockFailingConditions extends Specification {
155156
size() == 3
156157
}
157158
}
159+
160+
@FailsWith(ConditionNotSatisfiedError)
161+
def "GDK method that looks like built-in method as implicit condition"() {
162+
expect:
163+
null.with {
164+
false
165+
}
166+
}
167+
168+
@FailsWith(ConditionNotSatisfiedError)
169+
def "GDK method that looks like built-in method as explicit condition"() {
170+
expect:
171+
assert null.with {
172+
false
173+
}
174+
}
175+
176+
def "condition method #conditionMethod within condition method #conditionMethod"() {
177+
when:
178+
runner.runFeatureBody("""
179+
expect:
180+
$conditionMethod(['']) {
181+
$conditionMethod(['']) {
182+
false
183+
}
184+
}
185+
""")
186+
187+
then:
188+
thrown(expectedException)
189+
190+
where:
191+
conditionMethod || expectedException
192+
'with' || ConditionNotSatisfiedError
193+
'verifyAll' || ConditionNotSatisfiedError
194+
'verifyEach' || SpockAssertionError
195+
}
196+
197+
def "condition method #conditionMethod within condition method #conditionMethod with exception"() {
198+
when:
199+
runner.runFeatureBody("""
200+
expect:
201+
$conditionMethod(['']) {
202+
$conditionMethod(['']) {
203+
true
204+
throw new Exception('foo')
205+
}
206+
}
207+
""")
208+
209+
then:
210+
def exception = thrown(expectedException)
211+
exception.message."${expectedException == Exception ? 'equals' : 'contains'}"('foo')
212+
213+
where:
214+
conditionMethod || expectedException
215+
'with' || Exception
216+
'verifyAll' || Exception
217+
'verifyEach' || SpockAssertionError
218+
}
219+
220+
def "condition method #conditionMethod within condition method #conditionMethod with only exception"() {
221+
when:
222+
runner.runFeatureBody("""
223+
expect:
224+
$conditionMethod(['']) {
225+
$conditionMethod(['']) {
226+
throw new Exception('foo')
227+
}
228+
}
229+
""")
230+
231+
then:
232+
def exception = thrown(expectedException)
233+
exception.message."${expectedException == Exception ? 'equals' : 'contains'}"('foo')
234+
235+
where:
236+
conditionMethod || expectedException
237+
'with' || Exception
238+
'verifyAll' || Exception
239+
'verifyEach' || SpockAssertionError
240+
}
241+
242+
def "condition method #conditionMethod"() {
243+
when:
244+
runner.runFeatureBody("""
245+
expect:
246+
$conditionMethod(['']) {
247+
false
248+
}
249+
""")
250+
251+
then:
252+
thrown(expectedException)
253+
254+
where:
255+
conditionMethod || expectedException
256+
'with' || ConditionNotSatisfiedError
257+
'verifyAll' || ConditionNotSatisfiedError
258+
'verifyEach' || SpockAssertionError
259+
}
260+
261+
def "condition method #conditionMethod with exception"() {
262+
when:
263+
runner.runFeatureBody("""
264+
expect:
265+
$conditionMethod(['']) {
266+
true
267+
throw new Exception('foo')
268+
}
269+
""")
270+
271+
then:
272+
def exception = thrown(expectedException)
273+
exception.message."${expectedException == Exception ? 'equals' : 'contains'}"('foo')
274+
275+
where:
276+
conditionMethod || expectedException
277+
'with' || Exception
278+
'verifyAll' || Exception
279+
'verifyEach' || SpockAssertionError
280+
}
281+
282+
def "condition method #conditionMethod with only exception"() {
283+
when:
284+
runner.runFeatureBody("""
285+
expect:
286+
$conditionMethod(['']) {
287+
throw new Exception('foo')
288+
}
289+
""")
290+
291+
then:
292+
def exception = thrown(expectedException)
293+
exception.message."${expectedException == Exception ? 'equals' : 'contains'}"('foo')
294+
295+
where:
296+
conditionMethod || expectedException
297+
'with' || Exception
298+
'verifyAll' || Exception
299+
'verifyEach' || SpockAssertionError
300+
}
158301
}
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* https://www.apache.org/licenses/LICENSE-2.0
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*
14+
*/
15+
16+
package org.spockframework.smoke.ast.condition
17+
18+
19+
import org.spockframework.EmbeddedSpecification
20+
import org.spockframework.specs.extension.SpockSnapshotter
21+
import spock.lang.Snapshot
22+
23+
class ConditionMethodsAstSpec extends EmbeddedSpecification {
24+
@Snapshot(extension = 'groovy')
25+
SpockSnapshotter snapshotter
26+
27+
def "GDK method that looks like built-in method as implicit condition"() {
28+
when:
29+
def result = compiler.transpileFeatureBody('''
30+
expect:
31+
null.with {
32+
false
33+
}
34+
''')
35+
36+
then:
37+
snapshotter.assertThat(result.source).matchesSnapshot()
38+
}
39+
40+
def "GDK method that looks like built-in method as explicit condition"() {
41+
when:
42+
def result = compiler.transpileFeatureBody('''
43+
expect:
44+
assert null.with {
45+
false
46+
}
47+
''')
48+
49+
then:
50+
snapshotter.assertThat(result.source).matchesSnapshot()
51+
}
52+
53+
def "condition method #conditionMethod within condition method #conditionMethod"() {
54+
when:
55+
def result = compiler.transpileFeatureBody("""
56+
expect:
57+
$conditionMethod(['']) {
58+
$conditionMethod(['']) {
59+
false
60+
}
61+
}
62+
""")
63+
64+
then:
65+
snapshotter.assertThat(result.source).matchesSnapshot()
66+
67+
where:
68+
conditionMethod << [
69+
'with',
70+
'verifyAll',
71+
'verifyEach'
72+
]
73+
}
74+
75+
def "condition method #conditionMethod within condition method #conditionMethod with exception"() {
76+
when:
77+
def result = compiler.transpileFeatureBody("""
78+
expect:
79+
$conditionMethod(['']) {
80+
$conditionMethod(['']) {
81+
true
82+
throw new Exception('foo')
83+
}
84+
}
85+
""")
86+
87+
then:
88+
snapshotter.assertThat(result.source).matchesSnapshot()
89+
90+
where:
91+
conditionMethod << [
92+
'with',
93+
'verifyAll',
94+
'verifyEach'
95+
]
96+
}
97+
98+
def "condition method #conditionMethod within condition method #conditionMethod with only exception"() {
99+
when:
100+
def result = compiler.transpileFeatureBody("""
101+
expect:
102+
$conditionMethod(['']) {
103+
$conditionMethod(['']) {
104+
throw new Exception('foo')
105+
}
106+
}
107+
""")
108+
109+
then:
110+
snapshotter.assertThat(result.source).matchesSnapshot()
111+
112+
where:
113+
conditionMethod << [
114+
'with',
115+
'verifyAll',
116+
'verifyEach'
117+
]
118+
}
119+
120+
def "condition method #conditionMethod"() {
121+
when:
122+
def result = compiler.transpileFeatureBody("""
123+
expect:
124+
$conditionMethod(['']) {
125+
false
126+
}
127+
""")
128+
129+
then:
130+
snapshotter.assertThat(result.source).matchesSnapshot()
131+
132+
where:
133+
conditionMethod << [
134+
'with',
135+
'verifyAll',
136+
'verifyEach'
137+
]
138+
}
139+
140+
def "condition method #conditionMethod with exception"() {
141+
when:
142+
def result = compiler.transpileFeatureBody("""
143+
expect:
144+
$conditionMethod(['']) {
145+
true
146+
throw new Exception('foo')
147+
}
148+
""")
149+
150+
then:
151+
snapshotter.assertThat(result.source).matchesSnapshot()
152+
153+
where:
154+
conditionMethod << [
155+
'with',
156+
'verifyAll',
157+
'verifyEach'
158+
]
159+
}
160+
161+
def "condition method #conditionMethod with only exception"() {
162+
when:
163+
def result = compiler.transpileFeatureBody("""
164+
expect:
165+
$conditionMethod(['']) {
166+
throw new Exception('foo')
167+
}
168+
""")
169+
170+
then:
171+
snapshotter.assertThat(result.source).matchesSnapshot()
172+
173+
where:
174+
conditionMethod << [
175+
'with',
176+
'verifyAll',
177+
'verifyEach'
178+
]
179+
}
180+
}

0 commit comments

Comments
 (0)