Skip to content

Commit 3628004

Browse files
committed
Fix VerifierError with closures in Spock specs with @Rollback - Fixes #9837
1 parent 5e4eea8 commit 3628004

File tree

2 files changed

+57
-5
lines changed

2 files changed

+57
-5
lines changed

grails-core/src/main/groovy/org/grails/transaction/transform/TransactionalTransform.groovy

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import grails.compiler.DelegatingMethod
2020
import grails.transaction.Rollback
2121
import groovy.transform.TypeChecked
2222
import org.codehaus.groovy.ast.stmt.Statement
23+
import org.codehaus.groovy.classgen.VariableScopeVisitor
24+
import org.codehaus.groovy.control.ErrorCollector
2325
import org.springframework.beans.factory.annotation.Autowired
2426
import org.springframework.beans.factory.annotation.Qualifier
2527
import org.springframework.transaction.annotation.Propagation
@@ -292,14 +294,26 @@ class TransactionalTransform implements ASTTransformation{
292294
final transactionStatusParameter = new Parameter(ClassHelper.make(TransactionStatus), "transactionStatus")
293295
def newParameters = methodNode.getParameters() ? (copyParameters(((methodNode.getParameters() as List) + [transactionStatusParameter]) as Parameter[])) : [transactionStatusParameter] as Parameter[]
294296

297+
298+
def body = methodNode.code
295299
MethodNode renamedMethodNode = new MethodNode(
296300
renamedMethodName,
297301
Modifier.PROTECTED, methodNode.getReturnType().getPlainNodeReference(),
298302
newParameters,
299303
GrailsArtefactClassInjector.EMPTY_CLASS_ARRAY,
300-
methodNode.code
304+
body
301305
);
302306

307+
308+
def newVariableScope = new VariableScope()
309+
for(p in newParameters) {
310+
newVariableScope.putDeclaredVariable(p)
311+
}
312+
313+
renamedMethodNode.setVariableScope(
314+
newVariableScope
315+
)
316+
303317
// GrailsCompileStatic and GrailsTypeChecked are not explicitly addressed
304318
// here but they will be picked up because they are @AnnotationCollector annotations
305319
// which use CompileStatic and TypeChecked...
@@ -309,14 +323,19 @@ class TransactionalTransform implements ASTTransformation{
309323
methodNode.setCode(null)
310324
classNode.addMethod(renamedMethodNode)
311325

312-
if( !isSpockTest(classNode) ) {
313-
processVariableScopes(source, classNode, renamedMethodNode)
326+
// Use a dummy source unit to process the variable scopes to avoid the issue where this is run twice producing an error
327+
VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(new SourceUnit("dummy", "dummy", source.getConfiguration(), source.getClassLoader(), new ErrorCollector(source.getConfiguration())));
328+
if(methodNode == null) {
329+
scopeVisitor.visitClass(classNode);
330+
} else {
331+
scopeVisitor.prepareVisit(classNode);
332+
scopeVisitor.visitMethod(renamedMethodNode);
314333
}
315334

316335
final originalMethodCall = new MethodCallExpression(new VariableExpression("this"), renamedMethodName, new ArgumentListExpression(renamedMethodNode.parameters))
317336
originalMethodCall.setImplicitThis(false)
318337
originalMethodCall.setMethodTarget(renamedMethodNode)
319-
338+
320339
originalMethodCall
321340
}
322341

grails-core/src/test/groovy/grails/transaction/TransactionalTransformSpec.groovy

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,41 @@ import javax.sql.DataSource
2626
*/
2727
class TransactionalTransformSpec extends Specification {
2828

29+
@Issue('https://github.com/grails/grails-core/issues/9837')
30+
void "Test @Rollback when applied to Spock specifications with closures combined with where queries"() {
31+
when: "A new instance of a class with a @Transactional method is created that subclasses another transactional class"
32+
Class mySpec = new GroovyShell().evaluate('''
33+
import grails.transaction.*
34+
import spock.lang.Specification
35+
36+
@Rollback
37+
class MySpec extends Specification {
38+
void 'test something'() {
39+
expect:
40+
def someClosure = { println x }
41+
x + y == sum
42+
43+
where:
44+
x | y | sum
45+
4 | 2 | 6
46+
}
47+
}
48+
MySpec
49+
''')
50+
51+
then: "It implements TransactionManagerAware"
52+
TransactionManagerAware.isAssignableFrom(mySpec)
53+
mySpec.getDeclaredMethod('$spock_feature_0_0', Object, Object, Object)
54+
mySpec.getDeclaredMethod('$tt__$spock_feature_0_0', Object, Object, Object, TransactionStatus)
55+
56+
and:"The spec can be called"
57+
mySpec.newInstance().'$tt__$spock_feature_0_0'(2,2,4,new DefaultTransactionStatus(new Object(), true, true, false, false, null))
58+
59+
60+
}
61+
2962
@Issue('https://github.com/grails/grails-core/issues/9646')
30-
void "Test @Rollback when applied to Spock specifications with closures in there blocks"() {
63+
void "Test @Rollback when applied to Spock specifications with closures in then blocks"() {
3164
when: "A new instance of a class with a @Transactional method is created that subclasses another transactional class"
3265
Class mySpec = new GroovyShell().evaluate('''
3366
import grails.transaction.*

0 commit comments

Comments
 (0)