This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch GROOVY_4_0_X in repository https://gitbox.apache.org/repos/asf/groovy.git
commit f80662a7dbc9d470217aa16039655c009481f361 Author: Eric Milles <[email protected]> AuthorDate: Sat Apr 23 15:17:25 2022 -0500 stop writing classes to project basedir --- ...onditionalInterruptibleASTTransformation.groovy | 23 +- .../transform/ConditionalInterruptTest.groovy | 261 ++++++------ src/test/groovy/transform/LazyTest.groovy | 118 +++--- src/test/groovy/transform/ReadWriteLockTest.groovy | 295 +++++++------- .../groovy/transform/ThreadInterruptTest.groovy | 432 ++++++++++---------- .../groovy/transform/TimedInterruptTest.groovy | 450 ++++++++++----------- .../ClosureWriterGeneratedAnnotationTest.groovy | 75 ++-- .../classgen/asm/sc/BugsStaticCompileTest.groovy | 3 +- ...ticCompileClosureGeneratedAnnotationTest.groovy | 81 ++-- .../TransformsAndCustomClassLoadersTest.groovy | 2 +- 10 files changed, 853 insertions(+), 887 deletions(-) diff --git a/src/main/groovy/org/codehaus/groovy/transform/ConditionalInterruptibleASTTransformation.groovy b/src/main/groovy/org/codehaus/groovy/transform/ConditionalInterruptibleASTTransformation.groovy index 90fce9c7c6..594f87b41d 100644 --- a/src/main/groovy/org/codehaus/groovy/transform/ConditionalInterruptibleASTTransformation.groovy +++ b/src/main/groovy/org/codehaus/groovy/transform/ConditionalInterruptibleASTTransformation.groovy @@ -28,12 +28,10 @@ import org.codehaus.groovy.ast.FieldNode import org.codehaus.groovy.ast.MethodNode import org.codehaus.groovy.ast.Parameter import org.codehaus.groovy.ast.PropertyNode -import org.codehaus.groovy.ast.expr.ArgumentListExpression import org.codehaus.groovy.ast.expr.ClosureExpression import org.codehaus.groovy.ast.expr.Expression -import org.codehaus.groovy.ast.expr.MethodCallExpression -import org.codehaus.groovy.ast.expr.VariableExpression import org.codehaus.groovy.ast.tools.ClosureUtils +import org.codehaus.groovy.ast.tools.GeneralUtils import org.codehaus.groovy.control.CompilePhase /** @@ -48,25 +46,21 @@ import org.codehaus.groovy.control.CompilePhase @GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION) class ConditionalInterruptibleASTTransformation extends AbstractInterruptibleASTTransformation { - private static final ClassNode MY_TYPE = ClassHelper.make(ConditionalInterrupt) - private ClosureExpression conditionNode private String conditionMethod - private MethodCallExpression conditionCallExpression private ClassNode currentClass protected ClassNode type() { - MY_TYPE + ClassHelper.make(ConditionalInterrupt) } - @SuppressWarnings('Instanceof') protected void setupTransform(AnnotationNode node) { super.setupTransform(node) - ClosureExpression member = (ClosureExpression) node.getMember('value') - if (!member || !(member instanceof ClosureExpression)) internalError("Expected closure value for annotation parameter 'value'. Found $member") - conditionNode = member + def member = node.getMember('value') + if (member !instanceof ClosureExpression) + internalError("Expected closure value for annotation parameter 'value'. Found $member") + conditionNode = (ClosureExpression) member conditionMethod = 'conditionalTransform' + node.hashCode() + '$condition' - conditionCallExpression = new MethodCallExpression(new VariableExpression('this'), conditionMethod, new ArgumentListExpression()) } protected String getErrorMessage() { @@ -75,15 +69,14 @@ class ConditionalInterruptibleASTTransformation extends AbstractInterruptibleAST void visitClass(ClassNode type) { currentClass = type - def method = type.addMethod(conditionMethod, ACC_PRIVATE | ACC_SYNTHETIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, conditionNode.code) - method.synthetic = true + type.addSyntheticMethod(conditionMethod, ACC_PRIVATE, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, conditionNode.code) if (applyToAllMembers) { super.visitClass(type) } } protected Expression createCondition() { - conditionCallExpression + GeneralUtils.callThisX(conditionMethod) } @Override diff --git a/src/test/groovy/transform/ConditionalInterruptTest.groovy b/src/test/groovy/transform/ConditionalInterruptTest.groovy index 60b218fdeb..cb86990292 100644 --- a/src/test/groovy/transform/ConditionalInterruptTest.groovy +++ b/src/test/groovy/transform/ConditionalInterruptTest.groovy @@ -18,186 +18,193 @@ */ package groovy.transform -import groovy.test.GroovyTestCase +import org.codehaus.groovy.control.CompilerConfiguration +import org.codehaus.groovy.control.customizers.ImportCustomizer +import org.junit.Test + +import static groovy.test.GroovyAssert.assertScript /** - * Test for {@link ConditionalInterrupt} AST Transformation. + * Tests for the {@link ConditionalInterrupt} AST transform. */ -class ConditionalInterruptTest extends GroovyTestCase { +final class ConditionalInterruptTest { - void testMethodIsVisited_AndExceptionMessage() { + private final GroovyShell shell = new GroovyShell(new CompilerConfiguration().addCompilationCustomizers( + new ImportCustomizer().tap { + addStarImports('groovy.transform') + addStaticImport('groovy.test.GroovyAssert', 'shouldFail') + } + )) - def c = new GroovyClassLoader().parseClass(''' - import groovy.transform.ConditionalInterrupt - @ConditionalInterrupt({ visited = true }) - class MyClass { - boolean visited = false - def myMethod() { } + @Test + void testMethodIsVisited_AndExceptionMessage() { + assertScript shell, ''' + @ConditionalInterrupt(applyToAllClasses=false, value={ visited = true }) + class C { + protected boolean visited + def m() { } } - ''') - def instance = c.newInstance() - def message = shouldFail(InterruptedException) { - instance.myMethod() - } - assert message == 'Execution interrupted. The following condition failed: { visited = true }' - assert instance.visited + def obj = new C() + def err = shouldFail(InterruptedException) { + obj.m() + } + assert obj.visited + assert err.message == 'Execution interrupted. The following condition failed: { visited = true }' + ''' } + @Test void testMethodIsVisitedCompileStatic() { - def c = new GroovyClassLoader().parseClass(''' - import groovy.transform.* + assertScript shell, ''' @CompileStatic - @ConditionalInterrupt({ visited = true }) - class MyClass { - boolean visited = false - def myMethod() { } - } - ''') - def instance = c.newInstance() - def message = shouldFail(InterruptedException) { - instance.myMethod() - } - assert message == 'Execution interrupted. The following condition failed: { visited = true }' - assert instance.visited + @ConditionalInterrupt(applyToAllClasses=false, value={ visited = true }) + class C { + protected boolean visited + def m() { } + } + + def obj = new C() + def err = shouldFail(InterruptedException) { + obj.m() + } + assert obj.visited + assert err.message == 'Execution interrupted. The following condition failed: { visited = true }' + ''' } + @Test void testMethodIsVisited_AndCustomExceptionMessage() { - - def c = new GroovyClassLoader(this.class.classLoader).parseClass(''' - import groovy.transform.ConditionalInterrupt - @ConditionalInterrupt(thrown=groovy.transform.CustomException, value={ visited = true }) - class MyClass { - boolean visited = false - def myMethod() { } + assertScript shell, ''' + @ConditionalInterrupt(applyToAllClasses=false, thrown=CustomException, value={ visited = true }) + class C { + protected boolean visited + def m() { } } - ''') - def instance = c.newInstance() - def message = shouldFail(CustomException) { - instance.myMethod() - } - assert message == 'Execution interrupted. The following condition failed: { visited = true }' - assert instance.visited + def obj = new C() + def err = shouldFail(CustomException) { + obj.m() + } + assert obj.visited + assert err.message == 'Execution interrupted. The following condition failed: { visited = true }' + ''' } + @Test void testStaticMethodIsNotVisited() { - def c = new GroovyClassLoader().parseClass(''' - import groovy.transform.ConditionalInterrupt - @ConditionalInterrupt({ visited = true }) - class MyClass { - boolean visited = false - static def myMethod() { } - } - ''') - - def instance = c.newInstance() - instance.myMethod() - assert !instance.visited + assertScript shell, ''' + @ConditionalInterrupt(applyToAllClasses=false, value={ visited = true }) + class C { + protected boolean visited + static m() { } + } + + def obj = new C() + obj.m() + + assert !obj.visited + ''' } + @Test void testClosureFieldIsVisited() { - - def c = new GroovyClassLoader().parseClass(''' - import groovy.transform.ConditionalInterrupt - @ConditionalInterrupt({ visited = true }) - class MyClass { - boolean visited = false - def myMethod = { } + assertScript shell, ''' + @ConditionalInterrupt(applyToAllClasses=false, value={ visited = true }) + class C { + protected boolean visited + def m = { -> } } - ''') - def instance = c.newInstance() - shouldFail(InterruptedException) { - instance.myMethod() - } - assert instance.visited + def obj = new C() + shouldFail(InterruptedException) { + obj.m() + } + assert obj.visited + ''' } + @Test void testWhileLoopVisited() { - def c = new GroovyClassLoader().parseClass(''' - import groovy.transform.ConditionalInterrupt - @ConditionalInterrupt({ count > 5 }) - class MyClass { - int count = 0 - def myMethod = { + assertScript shell, ''' + @ConditionalInterrupt(applyToAllClasses=false, value={ count > 5 }) + class C { + protected int count + def m = { -> while (count < 10) { - count++ + count += 1 } } } - ''') - def instance = c.newInstance() - shouldFail(InterruptedException) { - instance.myMethod() - } - assert 6 == instance.count + def obj = new C() + shouldFail(InterruptedException) { + obj.m() + } + assert obj.count == 6 + ''' } + @Test void testForLoopVisited() { - - def c = new GroovyClassLoader().parseClass(''' - import groovy.transform.ConditionalInterrupt - @ConditionalInterrupt({ count > 5 }) - class MyClass { - int count = 0 - def myMethod = { - for (int x = 0; x < 10; x++) { - count++ + assertScript shell, ''' + @ConditionalInterrupt(applyToAllClasses=false, value={ count > 5 }) + class C { + protected int count + def m = { + for (int i = 0; i < 10; i += 1) { + count += 1 } } } - ''') - def instance = c.newInstance() - shouldFail(InterruptedException) { - instance.myMethod() - } - assert 6 == instance.count + def obj = new C() + shouldFail(InterruptedException) { + obj.m() + } + assert obj.count == 6 + ''' } + @Test void testStaticClosureFieldNotVisited() { - - def c = new GroovyClassLoader().parseClass(''' - import groovy.transform.ConditionalInterrupt - @ConditionalInterrupt({ visited = true }) - class MyClass { - boolean visited = false - static def myMethod = { } + assertScript shell, ''' + @ConditionalInterrupt(applyToAllClasses=false, value={ visited = true }) + class C { + protected boolean visited + static m = { -> } } - ''') - def instance = c.newInstance() - instance.myMethod() - assert !instance.visited + def obj = new C() + obj.m() + + assert !obj.visited + ''' } + @Test void testSharedContext() { - def shell = new GroovyShell() - - def script = shell.parse(''' - import groovy.transform.ConditionalInterrupt - + assertScript shell, ''' class Helper { - static int i=0 - static def shouldInterrupt() { i++>1 } + static int i + static def shouldInterrupt() { ++i > 1 } } - @ConditionalInterrupt({ Helper.shouldInterrupt() }) - class MyClass { - def myMethod() { } + @ConditionalInterrupt(applyToAllClasses=false, value={ Helper.shouldInterrupt() }) + class C { + def m = { -> } } - @ConditionalInterrupt({ Helper.shouldInterrupt() }) - class MyOtherClass { - def myOtherMethod() { new MyClass().myMethod() } + @ConditionalInterrupt(applyToAllClasses=false, value={ Helper.shouldInterrupt() }) + class D { + def m() { + new C().m() + } } - new MyOtherClass().myOtherMethod() - ''', 'myScript') - shouldFail(InterruptedException) { - script.run() - } + shouldFail(InterruptedException) { + new D().m() + } + ''' } } diff --git a/src/test/groovy/transform/LazyTest.groovy b/src/test/groovy/transform/LazyTest.groovy index 1e22cd1a45..bdfc19e059 100644 --- a/src/test/groovy/transform/LazyTest.groovy +++ b/src/test/groovy/transform/LazyTest.groovy @@ -18,72 +18,82 @@ */ package groovy.transform -import groovy.test.GroovyTestCase +import org.codehaus.groovy.control.CompilerConfiguration +import org.codehaus.groovy.control.customizers.ImportCustomizer +import org.junit.Test -import java.lang.ref.SoftReference -import java.lang.reflect.Modifier +import static groovy.test.GroovyAssert.assertScript /** - * Unit tests for the Lazy annotation + * Tests for the {@link Lazy} AST transform. */ -class LazyTest extends GroovyTestCase { +final class LazyTest { + + private final GroovyShell shell = new GroovyShell(new CompilerConfiguration().addCompilationCustomizers( + new ImportCustomizer().tap { addStaticStars('java.lang.reflect.Modifier') } + )) + + @Test void testLazyPrimitiveWrapping() { - def tester = new GroovyClassLoader().parseClass( - '''class MyClass { - | @Lazy int index = { -> - | 1 - | } - |}'''.stripMargin() ) - // Should be a private non-volatile Integer - def field = tester.getDeclaredField( '$index' ) - assert field - assert Modifier.isPrivate(field.modifiers) - assert !Modifier.isVolatile(field.modifiers) - assert field.type == Integer + assertScript shell, ''' + class C { + @Lazy int index = { -> 1 } + } + + def field = C.getDeclaredField('$index') + assert field + assert field.type == Integer + assert isPrivate(field.modifiers) + assert !isVolatile(field.modifiers) + ''' } + @Test void testLazyVolatilePrimitiveWrapping() { - def tester = new GroovyClassLoader().parseClass( - '''class MyClass { - | @Lazy volatile int index = { -> - | 1 - | } - |}'''.stripMargin() ) - // Should be a private volatile Integer - def field = tester.getDeclaredField( '$index' ) - assert field - assert Modifier.isPrivate(field.modifiers) - assert Modifier.isVolatile(field.modifiers) - assert field.type == Integer + assertScript shell, ''' + class C { + @Lazy volatile int index = { -> 1 } + } + + def field = C.getDeclaredField('$index') + assert field + assert field.type == Integer + assert isPrivate(field.modifiers) + assert isVolatile(field.modifiers) + ''' } + @Test void testLazySoftPrimitiveWrapping() { - def tester = new GroovyClassLoader().parseClass( - '''class MyClass { - | @Lazy(soft=true) int index = { -> - | 1 - | } - |}'''.stripMargin() ) - // Should be a private non-volatile SoftReference - def field = tester.getDeclaredField( '$index' ) - assert field - assert Modifier.isPrivate(field.modifiers) - assert !Modifier.isVolatile(field.modifiers) - assert field.type == SoftReference + assertScript shell, ''' + import java.lang.ref.SoftReference as SoftRef + + class C { + @Lazy(soft=true) int index = { -> 1 } + } + + def field = C.getDeclaredField('$index') + assert field + assert field.type == SoftRef + assert isPrivate(field.modifiers) + assert !isVolatile(field.modifiers) + ''' } + @Test void testLazyVolatileSoftPrimitiveWrapping() { - def tester = new GroovyClassLoader().parseClass( - '''class MyClass { - | @Lazy(soft=true) volatile int index = { -> - | 1 - | } - |}'''.stripMargin() ) - // Should be a private volatile SoftReference - def field = tester.getDeclaredField( '$index' ) - assert field - assert Modifier.isPrivate(field.modifiers) - assert Modifier.isVolatile(field.modifiers) - assert field.type == SoftReference + assertScript shell, ''' + import java.lang.ref.SoftReference as SoftRef + + class C { + @Lazy(soft=true) volatile int index = { -> 1 } + } + + def field = C.getDeclaredField('$index') + assert field + assert field.type == SoftRef + assert isPrivate(field.modifiers) + assert isVolatile(field.modifiers) + ''' } -} \ No newline at end of file +} diff --git a/src/test/groovy/transform/ReadWriteLockTest.groovy b/src/test/groovy/transform/ReadWriteLockTest.groovy index a36fcbae27..7a77bb05a9 100644 --- a/src/test/groovy/transform/ReadWriteLockTest.groovy +++ b/src/test/groovy/transform/ReadWriteLockTest.groovy @@ -18,82 +18,92 @@ */ package groovy.transform -import groovy.test.GroovyTestCase +import org.codehaus.groovy.control.CompilerConfiguration +import org.codehaus.groovy.control.customizers.ImportCustomizer +import org.junit.Test -import java.util.concurrent.locks.ReentrantReadWriteLock -import java.lang.reflect.Modifier +import static groovy.test.GroovyAssert.assertScript +import static groovy.test.GroovyAssert.shouldFail /** - * Unit test for WithReadLock and WithWriteLock annotations. + * Tests for the {@link WithReadLock} and {@link WithWriteLock} AST transforms. */ -class ReadWriteLockTest extends GroovyTestCase { +final class ReadWriteLockTest { - void testLockFieldDefaultsForReadLock() { - def tester = new GroovyClassLoader().parseClass(''' - class MyClass { - @groovy.transform.WithReadLock - public void readerMethod1() { } + private final GroovyShell shell = new GroovyShell(new CompilerConfiguration().addCompilationCustomizers( + new ImportCustomizer().tap { + addStarImports('groovy.transform') + addStaticStars('java.lang.reflect.Modifier') + addImport('java.util.concurrent.locks.ReentrantReadWriteLock') } -''') - def field = tester.getDeclaredField('$reentrantlock') - assert Modifier.isPrivate(field.modifiers) - assert !Modifier.isTransient(field.modifiers) - assert Modifier.isFinal(field.modifiers) - assert !Modifier.isStatic(field.modifiers) - - assert field.type == ReentrantReadWriteLock + )) + + @Test + void testLockFieldDefaultsForReadLock() { + assertScript shell, ''' + class C { + @WithReadLock void m() { } + } + + def field = C.getDeclaredField('$reentrantlock') + assert field.type == ReentrantReadWriteLock + assert !isTransient(field.modifiers) + assert isPrivate(field.modifiers) + assert !isStatic(field.modifiers) + assert isFinal(field.modifiers) + ''' } + @Test void testLockFieldDefaultsForWriteLock() { - def tester = new GroovyClassLoader().parseClass(''' - class MyClass { - @groovy.transform.WithWriteLock - public void readerMethod1() { } - } -''') - def field = tester.getDeclaredField('$reentrantlock') - assert Modifier.isPrivate(field.modifiers) - assert !Modifier.isTransient(field.modifiers) - assert Modifier.isFinal(field.modifiers) - assert !Modifier.isStatic(field.modifiers) - - assert field.type == ReentrantReadWriteLock + assertScript shell, ''' + class C { + @WithWriteLock void m() { } + } + + def field = C.getDeclaredField('$reentrantlock') + assert field.type == ReentrantReadWriteLock + assert !isTransient(field.modifiers) + assert isPrivate(field.modifiers) + assert !isStatic(field.modifiers) + assert isFinal(field.modifiers) + ''' } + @Test void testLockFieldDefaultsForStaticReadLock() { - def tester = new GroovyClassLoader().parseClass(''' - class MyClass { - @groovy.transform.WithReadLock - public static void readerMethod1() { } - } -''') - def field = tester.getDeclaredField('$REENTRANTLOCK') - assert Modifier.isPrivate(field.modifiers) - assert !Modifier.isTransient(field.modifiers) - assert Modifier.isFinal(field.modifiers) - assert Modifier.isStatic(field.modifiers) - - assert field.type == ReentrantReadWriteLock + assertScript shell, ''' + class C { + @WithReadLock static void m() { } + } + + def field = C.getDeclaredField('$REENTRANTLOCK') + assert field.type == ReentrantReadWriteLock + assert !isTransient(field.modifiers) + assert isPrivate(field.modifiers) + assert isStatic(field.modifiers) + assert isFinal(field.modifiers) + ''' } + @Test void testLockFieldDefaultsForStaticWriteLock() { - def tester = new GroovyClassLoader().parseClass(''' - class MyClass { - @groovy.transform.WithWriteLock - public static void readerMethod1() { } - } -''') - def field = tester.getDeclaredField('$REENTRANTLOCK') - assert Modifier.isPrivate(field.modifiers) - assert !Modifier.isTransient(field.modifiers) - assert Modifier.isFinal(field.modifiers) - assert Modifier.isStatic(field.modifiers) - - assert field.type == ReentrantReadWriteLock + assertScript shell, ''' + class C { + @WithWriteLock static void m() { } + } + + def field = C.getDeclaredField('$REENTRANTLOCK') + assert field.type == ReentrantReadWriteLock + assert !isTransient(field.modifiers) + assert isPrivate(field.modifiers) + assert isStatic(field.modifiers) + assert isFinal(field.modifiers) + ''' } + @Test void testLocking() { - def tester = new MyClass() tester.readerMethod1() tester.readerMethod2() @@ -111,8 +121,8 @@ class ReadWriteLockTest extends GroovyTestCase { tester.readerMethod1() } + @Test void testStaticLocking() { - def tester = new MyClass() tester.staticReaderMethod1() tester.staticReaderMethod2() @@ -130,10 +140,11 @@ class ReadWriteLockTest extends GroovyTestCase { tester.staticReaderMethod1() } + @Test void testDeadlockingDoesNotOccur() { def tester = new MyClass() - // this tests for deadlocks from not releaseing in finally block + // this tests for deadlocks from not releaseing in finally block shouldFail { tester.namedReaderMethod1() } shouldFail { tester.namedReaderMethod2() } shouldFail { tester.namedWriterMethod1() } @@ -145,41 +156,40 @@ class ReadWriteLockTest extends GroovyTestCase { shouldFail { tester.namedReaderMethod1() } } + @Test void testCompileError_NamingConflict() { - shouldFail("lock field with name 'unknown' not found") { - ''' + def err = shouldFail shell, ''' class MyClass { @groovy.transform.WithWriteLock('unknown') public static void readerMethod1() { } - } ''' - } + } + ''' + assert err =~ /lock field with name 'unknown' not found/ - shouldFail("lock field with name 'myLock' should be static") { - ''' + err = shouldFail shell, ''' class MyClass { - def myLock = new java.util.concurrent.locks.ReentrantReadWriteLock() + def myLock = new ReentrantReadWriteLock() @groovy.transform.WithWriteLock('myLock') public static void readerMethod1() { } - } ''' - } + } + ''' + assert err =~ /lock field with name 'myLock' should be static/ - shouldFail("lock field with name 'myLock' should not be static") { - ''' + err = shouldFail shell, ''' class MyClass { - static def myLock = new java.util.concurrent.locks.ReentrantReadWriteLock() + static def myLock = new ReentrantReadWriteLock() @groovy.transform.WithWriteLock('myLock') public void readerMethod1() { } - } ''' - } + } + ''' + assert err =~ /lock field with name 'myLock' should not be static/ } - // GROOVY-8758 + @Test // GROOVY-8758 void testShouldBeAllowedInInnerClassWithCompileStatic() { - assertScript ''' - import groovy.transform.* - + assertScript shell, ''' @CompileStatic class A { private class B { @@ -198,77 +208,68 @@ class ReadWriteLockTest extends GroovyTestCase { ''' } - def shouldFail(String expectedText, Closure c) { - String script = c() - try { - new GroovyClassLoader().parseClass(script) - fail('Failure Expected') - } catch (Exception e) { - assert e.getMessage().contains(expectedText) + //-------------------------------------------------------------------------- + + static class MyClass { + def readerMethod1Called = false + def readerMethod2Called = false + def writerMethod1Called = false + def writerMethod2Called = false + def staticReaderMethod1Called = false + def staticReaderMethod2Called = false + def staticWriterMethod1Called = false + def staticWriterMethod2Called = false + def myLock = new java.util.concurrent.locks.ReentrantReadWriteLock() + + @WithReadLock + void readerMethod1() { + readerMethod1Called = true + } + @WithReadLock + void readerMethod2() { + readerMethod2Called = true + } + @WithWriteLock + void writerMethod1() { + writerMethod1Called = true + } + @WithWriteLock + void writerMethod2() { + writerMethod2Called = true } - } -} - -class MyClass { - - def readerMethod1Called = false - def readerMethod2Called = false - def writerMethod1Called = false - def writerMethod2Called = false - def staticReaderMethod1Called = false - def staticReaderMethod2Called = false - def staticWriterMethod1Called = false - def staticWriterMethod2Called = false - def myLock = new ReentrantReadWriteLock() - - @WithReadLock - void readerMethod1() { - readerMethod1Called = true - } - @WithReadLock - void readerMethod2() { - readerMethod2Called = true - } - @WithWriteLock - void writerMethod1() { - writerMethod1Called = true - } - @WithWriteLock - void writerMethod2() { - writerMethod2Called = true - } - @WithReadLock('myLock') - void namedReaderMethod1() { - throw new Exception() - } - @WithReadLock('myLock') - void namedReaderMethod2() { - throw new Exception() - } - @WithWriteLock('myLock') - void namedWriterMethod1() { - throw new Exception() - } - @WithWriteLock('myLock') - void namedWriterMethod2() { - throw new Exception() - } + @WithReadLock('myLock') + void namedReaderMethod1() { + throw new Exception() + } + @WithReadLock('myLock') + void namedReaderMethod2() { + throw new Exception() + } + @WithWriteLock('myLock') + void namedWriterMethod1() { + throw new Exception() + } + @WithWriteLock('myLock') + void namedWriterMethod2() { + throw new Exception() + } - @WithReadLock - void staticReaderMethod1() { - staticReaderMethod1Called = true - } - @WithReadLock - void staticReaderMethod2() { - staticReaderMethod2Called = true - } - @WithWriteLock - void staticWriterMethod1() { - staticWriterMethod1Called = true - } - @WithWriteLock - void staticWriterMethod2() { - staticWriterMethod2Called = true + @WithReadLock + void staticReaderMethod1() { + staticReaderMethod1Called = true + } + @WithReadLock + void staticReaderMethod2() { + staticReaderMethod2Called = true + } + @WithWriteLock + void staticWriterMethod1() { + staticWriterMethod1Called = true + } + @WithWriteLock + void staticWriterMethod2() { + staticWriterMethod2Called = true + } } } diff --git a/src/test/groovy/transform/ThreadInterruptTest.groovy b/src/test/groovy/transform/ThreadInterruptTest.groovy index 4747d2d3c1..bc7f3a2b22 100644 --- a/src/test/groovy/transform/ThreadInterruptTest.groovy +++ b/src/test/groovy/transform/ThreadInterruptTest.groovy @@ -20,6 +20,8 @@ package groovy.transform import groovy.mock.interceptor.StubFor import org.codehaus.groovy.ast.MethodNode +import org.codehaus.groovy.control.CompilerConfiguration +import org.codehaus.groovy.control.customizers.ImportCustomizer import org.codehaus.groovy.transform.ThreadInterruptibleASTTransformation import org.junit.After import org.junit.Before @@ -27,14 +29,22 @@ import org.junit.Test import java.lang.reflect.Modifier +import static groovy.test.GroovyAssert.assertScript import static groovy.test.GroovyAssert.isAtLeastJdk import static groovy.test.GroovyAssert.shouldFail import static org.junit.Assume.assumeTrue /** - * Test for @ThreadInterrupt. + * Tests for the {@link ThreadInterrupt} AST transform. */ -class ThreadInterruptTest { +final class ThreadInterruptTest { + + private final GroovyShell shell = new GroovyShell(new CompilerConfiguration().addCompilationCustomizers( + new ImportCustomizer().tap { + addStarImports('groovy.transform') + addImport('groovy.mock.interceptor.StubFor') + } + )) private static final boolean jdk12plus = isAtLeastJdk('12.0') private Map<String, MethodNode> oldValues = [:] @@ -44,7 +54,7 @@ class ThreadInterruptTest { Thread.metaClass = null ['CURRENTTHREAD_METHOD', 'ISINTERRUPTED_METHOD'].each { def ov = ThreadInterruptibleASTTransformation.getDeclaredField(it) - def modifiersField = ov.class.getDeclaredField("modifiers") + def modifiersField = ov.class.getDeclaredField('modifiers') modifiersField.accessible = true modifiersField.setInt(ov, ov.modifiers & ~Modifier.FINAL) ov.accessible = true @@ -53,15 +63,15 @@ class ThreadInterruptTest { } @Before - void setUp() throws Exception { + void setUp() { // JDK12+ doesn't allow adjusting static final fields even via reflection, so // skip all tests on such JDK versions - it is only test code that's affected // and currently we have coverage from builds with lower JDK versions. - assumeTrue !jdk12plus + assumeTrue(!jdk12plus) ['CURRENTTHREAD_METHOD', 'ISINTERRUPTED_METHOD'].each { def ov = ThreadInterruptibleASTTransformation.getDeclaredField(it) - def modifiersField = ov.class.getDeclaredField("modifiers") + def modifiersField = ov.class.getDeclaredField('modifiers') modifiersField.accessible = true modifiersField.setInt(ov, ov.modifiers & ~Modifier.FINAL) ov.accessible = true @@ -72,365 +82,343 @@ class ThreadInterruptTest { @Test void testDefaultParameters_Method() { - - def c = new GroovyClassLoader().parseClass(""" - @groovy.transform.ThreadInterrupt - class MyClass { - def myMethod() { } + assertScript shell, ''' + @ThreadInterrupt(applyToAllClasses=false) + class C { + def m() { } } - """) - def counter = new CountingThread() - def mocker = new StubFor(Thread.class) - mocker.demand.currentThread(1..Integer.MAX_VALUE) { counter } - mocker.use { - c.newInstance().myMethod() - } - assert 1 == counter.interruptedCheckCount + def mocker = new StubFor(Thread) + def counter = new CountingThread() + mocker.demand.currentThread(1..Integer.MAX_VALUE) { counter } + mocker.use { + new C().m() + } + assert 1 == counter.interruptedCheckCount + ''' } @Test void testNoMethodCheck_Method() { - - def c = new GroovyClassLoader().parseClass(""" - @groovy.transform.ThreadInterrupt(checkOnMethodStart = false) - class MyClass { - def myMethod() { } + assertScript shell, ''' + @ThreadInterrupt(applyToAllClasses=false, checkOnMethodStart=false) + class C { + def m() { } } - """) - def mocker = new StubFor(Thread.class) - mocker.demand.currentThread(1..Integer.MAX_VALUE) { new InterruptingThread() } - mocker.use { - c.newInstance().myMethod() - } - // no exception means success + def mocker = new StubFor(Thread) + mocker.demand.currentThread(1..Integer.MAX_VALUE) { new InterruptingThread() } + mocker.use { + new C().m() + } + // no exception means success + ''' } @Test void testDefaultParameters_ForLoop() { - - def c = new GroovyClassLoader().parseClass(""" - @groovy.transform.ThreadInterrupt - class MyClass { - def myMethod() { - for (int i in (1..99)) { - // do something - } - } + assertScript shell, ''' + @ThreadInterrupt(applyToAllClasses=false) + class C { + def m() { + for (int i in (1..99)) { + // do something + } + } } - """) - def counter = new CountingThread() - def mocker = new StubFor(Thread.class) - mocker.demand.currentThread(1..Integer.MAX_VALUE) { counter } - mocker.use { - c.newInstance().myMethod() - } - assert 100 == counter.interruptedCheckCount + def mocker = new StubFor(Thread) + def counter = new CountingThread() + mocker.demand.currentThread(1..Integer.MAX_VALUE) { counter } + mocker.use { + new C().m() + } + assert 100 == counter.interruptedCheckCount + ''' } @Test void testDefaultParameters_WhileLoop() { - - def c = new GroovyClassLoader().parseClass(""" - @groovy.transform.ThreadInterrupt - class MyClass { - def myMethod() { - int x = 99 - while (x > 0) { - x-- - } - } + assertScript shell, ''' + @ThreadInterrupt(applyToAllClasses=false) + class C { + def m() { + int x = 99 + while (x > 0) { + x-- + } + } } - """) - def counter = new CountingThread() - def mocker = new StubFor(Thread.class) - mocker.demand.currentThread(1..Integer.MAX_VALUE) { counter } - mocker.use { - c.newInstance().myMethod() - } - assert 100 == counter.interruptedCheckCount + def mocker = new StubFor(Thread) + def counter = new CountingThread() + mocker.demand.currentThread(1..Integer.MAX_VALUE) { counter } + mocker.use { + new C().m() + } + assert 100 == counter.interruptedCheckCount + ''' } @Test void testDefaultParameters_Closure() { - - def c = new GroovyClassLoader().parseClass(""" - @groovy.transform.ThreadInterrupt - class MyClass { - def myMethod() { - 99.times { - // do something - } - } + assertScript shell, ''' + @ThreadInterrupt(applyToAllClasses=false) + class C { + def m() { + 99.times { + // do something + } + } } - """) - def counter = new CountingThread() - def mocker = new StubFor(Thread.class) - mocker.demand.currentThread(1..Integer.MAX_VALUE) { counter } - mocker.use { - c.newInstance().myMethod() - } - assert 100 == counter.interruptedCheckCount + def mocker = new StubFor(Thread) + def counter = new CountingThread() + mocker.demand.currentThread(1..Integer.MAX_VALUE) { counter } + mocker.use { + new C().m() + } + assert 100 == counter.interruptedCheckCount + ''' } @Test void testInterrupt_Method_AndTestExceptionMessage() { - - def c = new GroovyClassLoader().parseClass(""" - @groovy.transform.ThreadInterrupt - class MyClass { - def myMethod() { } + def err = shouldFail shell, InterruptedException, ''' + @ThreadInterrupt(applyToAllClasses=false) + class C { + def m() { } } - """) - def mocker = new StubFor(Thread.class) - mocker.demand.currentThread(1..Integer.MAX_VALUE) { new InterruptingThread() } - mocker.use { - def ex = shouldFail(InterruptedException) { c.newInstance().myMethod() } - assert ex.message == 'Execution interrupted. The current thread has been interrupted.' - } + def mocker = new StubFor(Thread) + mocker.demand.currentThread(1..Integer.MAX_VALUE) { new InterruptingThread() } + mocker.use { + new C().m() + } + ''' + assert err.message == 'Execution interrupted. The current thread has been interrupted.' } @Test void testInterrupt_ForLoop() { - - def c = new GroovyClassLoader().parseClass(""" - @groovy.transform.ThreadInterrupt - class MyClass { - def myMethod() { - for (int i in (1..99)) { - // do something - } - } + shouldFail shell, InterruptedException, ''' + @ThreadInterrupt(applyToAllClasses=false) + class C { + def m() { + for (int i in (1..99)) { + // do something + } + } } - """) - def mocker = new StubFor(Thread.class) - mocker.demand.currentThread(1..Integer.MAX_VALUE) { new InterruptingThread() } - mocker.use { - shouldFail(InterruptedException) { c.newInstance().myMethod() } - } + def mocker = new StubFor(Thread) + mocker.demand.currentThread(1..Integer.MAX_VALUE) { new InterruptingThread() } + mocker.use { + new C().m() + } + ''' } @Test void testInterrupt_WhileLoop() { - - def c = new GroovyClassLoader().parseClass(""" - @groovy.transform.ThreadInterrupt - class MyClass { - def myMethod() { - int x = 99 - while (x > 0) { - x-- - } - } + shouldFail shell, InterruptedException, ''' + @ThreadInterrupt(applyToAllClasses=false) + class C { + def m() { + int x = 99 + while (x > 0) { + x-- + } + } } - """) - def mocker = new StubFor(Thread.class) - mocker.demand.currentThread(1..Integer.MAX_VALUE) { new InterruptingThread() } - mocker.use { - shouldFail(InterruptedException) { c.newInstance().myMethod() } - } + def mocker = new StubFor(Thread) + mocker.demand.currentThread(1..Integer.MAX_VALUE) { new InterruptingThread() } + mocker.use { + new C().m() + } + ''' } @Test void testInterrupt_Closure() { - - def c = new GroovyClassLoader().parseClass(""" - @groovy.transform.ThreadInterrupt - class MyClass { - def myMethod() { - 99.times { - // do something - } - } + shouldFail shell, InterruptedException, ''' + @ThreadInterrupt(applyToAllClasses=false) + class C { + def m() { + 99.times { + // do something + } + } } - """) - def mocker = new StubFor(Thread.class) - mocker.demand.currentThread(1..Integer.MAX_VALUE) { new InterruptingThread() } - mocker.use { - shouldFail(InterruptedException) { c.newInstance().myMethod() } - } + def mocker = new StubFor(Thread) + mocker.demand.currentThread(1..Integer.MAX_VALUE) { new InterruptingThread() } + mocker.use { + new C().m() + } + ''' } @Test void testInterrupt_ClosureWithCustomExceptionType() { - - def c = new GroovyClassLoader(this.class.classLoader).parseClass(""" - @groovy.transform.ThreadInterrupt(thrown=groovy.transform.CustomException) - class MyClass { - def myMethod() { - 99.times { - // do something - } - } + shouldFail shell, CustomException, ''' + @ThreadInterrupt(applyToAllClasses=false, thrown=CustomException) + class C { + def m() { + 99.times { + // do something + } + } } - """) - def mocker = new StubFor(Thread.class) - mocker.demand.currentThread(1..Integer.MAX_VALUE) { new InterruptingThread() } - mocker.use { - shouldFail(CustomException) { c.newInstance().myMethod() } - } + def mocker = new StubFor(Thread) + mocker.demand.currentThread(1..Integer.MAX_VALUE) { new InterruptingThread() } + mocker.use { + new C().m() + } + ''' } @Test void testEntireCompileUnitIsAffected() { - def script = ''' def scriptMethod() { // this method should inherit the checks from the annotation defined later } - @groovy.transform.ThreadInterrupt - class MyClass { - - def myMethod() { - // this method should also be guarded - } + @ThreadInterrupt + class C { + def m() { + // this method should also be guarded + } } + scriptMethod() - new MyClass().myMethod() - ''' + new C().m() + ''' + def mocker = new StubFor(Thread) def counter = new CountingThread() - def mocker = new StubFor(Thread.class) mocker.demand.currentThread(1..Integer.MAX_VALUE) { counter } mocker.use { - new GroovyShell().evaluate(script) + shell.evaluate(script) } - // 3 is once for run(), once for scriptMethod() and once for myMethod() - assert 3 == counter.interruptedCheckCount + assert counter.interruptedCheckCount == 3 // once for run(), once for scriptMethod() and once for m() } @Test void testOnlyScriptAffected() { - def script = ''' - @groovy.transform.ThreadInterrupt(applyToAllClasses = false) + @ThreadInterrupt(applyToAllClasses=false) def scriptMethod() { // should be affected } - class MyClass { - def myMethod() { - // should not be affected - } + class C { + def m() { + // should not be affected + } } scriptMethod() - new MyClass().myMethod() - ''' + new C().m() + ''' + def mocker = new StubFor(Thread) def counter = new CountingThread() - def mocker = new StubFor(Thread.class) mocker.demand.currentThread(1..Integer.MAX_VALUE) { counter } mocker.use { - new GroovyShell().evaluate(script) + shell.evaluate(script) } - // 2 is once for run() and once for scriptMethod() - assert 2 == counter.interruptedCheckCount + assert counter.interruptedCheckCount == 2 // once for run() and once for scriptMethod() } @Test void testAnnotationOnImport() { - def script = ''' - @groovy.transform.ThreadInterrupt + @ThreadInterrupt import java.lang.String 3.times { // should be affected } - ''' + ''' + def mocker = new StubFor(Thread) def counter = new CountingThread() - def mocker = new StubFor(Thread.class) mocker.demand.currentThread(1..Integer.MAX_VALUE) { counter } mocker.use { - new GroovyShell().evaluate(script) + shell.evaluate(script) } - // 4 is once for run() plus 3 for times loop - assert 4 == counter.interruptedCheckCount + assert counter.interruptedCheckCount == 4 // once for run() plus 3 for times loop } @Test void testOnlyClassAffected() { - def script = ''' def scriptMethod() { // this should not be affected } - @groovy.transform.ThreadInterrupt(applyToAllClasses = false) - class MyClass { - def myMethod() { - // this should be affected - } + @ThreadInterrupt(applyToAllClasses=false) + class C { + def m() { + // this should be affected + } } scriptMethod() - new MyClass().myMethod() - ''' + new C().m() + ''' + def mocker = new StubFor(Thread) def counter = new CountingThread() - def mocker = new StubFor(Thread.class) mocker.demand.currentThread(1..Integer.MAX_VALUE) { counter } mocker.use { - new GroovyShell(ThreadInterruptibleASTTransformation.getClassLoader()).evaluate(script) + shell.evaluate(script) } - // 1 is once for myMethod() - assert 1 == counter.interruptedCheckCount + assert counter.interruptedCheckCount == 1 // once for m() } @Test void testThreadInterruptOnAbstractClass() { def script = ''' - @groovy.transform.ThreadInterrupt - abstract class MyAbstractClass { - abstract void myMethod() + @ThreadInterrupt + abstract class A { + abstract void m() } - class Concrete extends MyAbstractClass { - void myMethod() { + class C extends A { + void m() { 99.times { // do something } } } - new Concrete().myMethod() + new C().m() ''' - def mocker = new StubFor(Thread.class) + def mocker = new StubFor(Thread) mocker.demand.currentThread(1..Integer.MAX_VALUE) { new InterruptingThread() } mocker.use { - shouldFail(InterruptedException) { - new GroovyShell(ThreadInterruptibleASTTransformation.getClassLoader()).evaluate(script) - } + shouldFail(shell, InterruptedException, script) } - } } -class InterruptingThread extends Thread { +//-------------------------------------------------------------------------- + +class CountingThread extends Thread { + int interruptedCheckCount = 0 @Override boolean isInterrupted() { - true + interruptedCheckCount += 1 + false } } -class CountingThread extends Thread { - def interruptedCheckCount = 0 - +class InterruptingThread extends Thread { @Override boolean isInterrupted() { - interruptedCheckCount++ - false + true } } [email protected] class CustomException extends Exception { - CustomException(final String message) { - super(message) - } -} \ No newline at end of file +} diff --git a/src/test/groovy/transform/TimedInterruptTest.groovy b/src/test/groovy/transform/TimedInterruptTest.groovy index adbc74a02b..b4aadbde3e 100644 --- a/src/test/groovy/transform/TimedInterruptTest.groovy +++ b/src/test/groovy/transform/TimedInterruptTest.groovy @@ -19,271 +19,249 @@ package groovy.transform import groovy.mock.interceptor.StubFor -import groovy.test.GroovyTestCase +import org.codehaus.groovy.control.CompilerConfiguration +import org.codehaus.groovy.control.MultipleCompilationErrorsException +import org.codehaus.groovy.control.customizers.ImportCustomizer +import org.junit.Test -import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException -import org.codehaus.groovy.control.MultipleCompilationErrorsException -import org.codehaus.groovy.transform.TimedInterruptibleASTTransformation + +import static groovy.test.GroovyAssert.assertScript +import static groovy.test.GroovyAssert.shouldFail /** - * Test for TimedInterrupt. + * Tests for the {@link TimedInterrupt} AST transform. */ -class TimedInterruptTest extends GroovyTestCase { - - void testClassMethodIsVisited() { - def c = new GroovyClassLoader().parseClass(''' - import groovy.transform.TimedInterrupt - - @TimedInterrupt(value = 1L) - class MyClass { - def myMethod() { } - } - ''') - assertPassesNormalFailsSlowExecution(c) - } - - void testClassMethodIsVisitedAndCustomExceptionThrown() { - def c = new GroovyClassLoader(this.class.classLoader).parseClass(''' - import groovy.transform.TimedInterrupt - - @TimedInterrupt(thrown=groovy.transform.CustomException,value = 1L) - class MyClass { - def myMethod() { } - } - ''') - assertPassesNormalFailsSlowExecution(c, 1000000666L, '1', 'myMethod', CustomException) - } - - void testScriptMethodIsVisited() { - def c = new GroovyClassLoader().parseClass(''' - import groovy.transform.TimedInterrupt - - @TimedInterrupt(value = 1L) - def myMethod() { } - ''') - assertPassesNormalFailsSlowExecution(c) - } - - void testStaticMethodIsNotVisited() { - def c = new GroovyClassLoader().parseClass(''' - import groovy.transform.TimedInterrupt - - @TimedInterrupt(value = 1L) - class MyClass { - static def myMethod() { } - } - ''') - assertPassesSlowExecution(c) - } - - void testClosureFieldIsVisited() { - def c = new GroovyClassLoader().parseClass(''' - import groovy.transform.TimedInterrupt - - @TimedInterrupt(value = 1L) - class MyClass { - def myMethod = { } - } - ''') - assertPassesNormalFailsSlowExecution(c) - } - - void testClosureInScriptIsVisited_CheckOnMethodStartIsFalse() { - def c = new GroovyClassLoader().parseClass(''' - import groovy.transform.TimedInterrupt - - @TimedInterrupt(checkOnMethodStart = false, value = 1L) - def myMethod = { } - myMethod() - ''') - assertPassesNormalFailsSlowExecution(c, 1000000666L, '1', 'run') - } - - void testWhileInScriptIsVisited_CheckOnMethodStartIsFalse() { - def c = new GroovyClassLoader().parseClass(''' - @TimedInterrupt(checkOnMethodStart = false, value = 1L) - import groovy.transform.TimedInterrupt - import java.util.concurrent.TimeUnit - - int x = 1 - while (x < 2) { x = 2 } - ''') - assertPassesNormalFailsSlowExecution(c, 1000000666L, '1', 'run') - } - - void testForInScriptIsVisited_CheckOnMethodStartIsFalse() { - def c = new GroovyClassLoader().parseClass(''' - @TimedInterrupt(checkOnMethodStart = false, value = 1L) - import groovy.transform.TimedInterrupt - - def x = [1] - for (def o : x) { o++ } - ''') - assertPassesNormalFailsSlowExecution(c, 1000000666L, '1', 'run') - } - - void testStaticClosureFieldNotVisited() { - def c = new GroovyClassLoader().parseClass(''' - import groovy.transform.TimedInterrupt - - @TimedInterrupt(value = 1L) - class MyClass { - static def myMethod = { } - } - ''') - assertPassesSlowExecution(c) - } - - void testAnnotationParameters() { - def c = new GroovyClassLoader().parseClass(''' - import groovy.transform.TimedInterrupt - import java.util.concurrent.TimeUnit - - @TimedInterrupt(value = 18000000L, unit = TimeUnit.MILLISECONDS) - def myMethod() { } - ''') - assertPassesNormalFailsSlowExecution(c, 18000000000666, '18000000', 'myMethod', TimeoutException, 'milliseconds') //5 hours in future - } - - // TODO not sure all these tests are pulling their weight - testing Groovy annotation type handing not subject - void testErrorHandling() { - shouldFail(MultipleCompilationErrorsException) { - new GroovyClassLoader().parseClass(''' - import groovy.transform.TimedInterrupt - @TimedInterrupt(value = "5") - def myMethod() { } - ''') +final class TimedInterruptTest { + + private final GroovyShell shell = new GroovyShell(new CompilerConfiguration().addCompilationCustomizers( + new ImportCustomizer().tap { + addStarImports('groovy.transform') + addStaticStars(TimedInterruptTest.name) + addImport('groovy.mock.interceptor.StubFor') + } + )) + + @Test + void testClassMethodIsVisited() { + assertScript shell, ''' + @TimedInterrupt(applyToAllClasses=false, value=1L) + class C { + def m() { } + } + assertPassesNormalFailsSlowExecution(C, methodName: 'm') + ''' } - shouldFail(MultipleCompilationErrorsException) { - new GroovyClassLoader().parseClass(''' - import groovy.transform.TimedInterrupt - @TimedInterrupt(value = foo()) - def myMethod() { } - ''') + @Test + void testClassMethodIsVisitedAndCustomExceptionThrown() { + assertScript shell, ''' + @TimedInterrupt(applyToAllClasses=false, thrown=CustomException, value=1L) + class C { + def m() { } + } + assertPassesNormalFailsSlowExecution(C, methodName: 'm', exception: CustomException) + ''' } - shouldFail(MultipleCompilationErrorsException) { - new GroovyClassLoader().parseClass(''' - import groovy.transform.TimedInterrupt - @TimedInterrupt(value = 5L, applyToAllClasses = 5) - def myMethod() { } - ''') + @Test + void testScriptMethodIsVisited() { + assertScript shell, ''' + @TimedInterrupt(applyToAllClasses=false, value=1L) def m() { } + assertPassesNormalFailsSlowExecution(this.class, methodName: 'm') + ''' } - shouldFail(MultipleCompilationErrorsException) { - new GroovyClassLoader().parseClass(''' - import groovy.transform.TimedInterrupt - @TimedInterrupt(value = 5L, applyToAllClasses = foo()) - def myMethod() { } - ''') + @Test + void testStaticMethodIsNotVisited() { + assertScript shell, ''' + @TimedInterrupt(applyToAllClasses=false, value=1L) + class C { + static myMethod() { } + } + assertPassesSlowExecution(C) + ''' } - shouldFail(MultipleCompilationErrorsException) { - new GroovyClassLoader().parseClass(''' - import groovy.transform.TimedInterrupt - @TimedInterrupt(value = 5L, checkOnMethodStart = 5) - def myMethod() { } - ''') + @Test + void testClosureFieldIsVisited() { + assertScript shell, ''' + @TimedInterrupt(applyToAllClasses=false, value=1L) + class C { + def m = { -> } + } + assertPassesNormalFailsSlowExecution(C, methodName: 'm') + ''' } - shouldFail(MultipleCompilationErrorsException) { - new GroovyClassLoader().parseClass(''' - import groovy.transform.TimedInterrupt - @TimedInterrupt(value = 5L, checkOnMethodStart = foo()) - def myMethod() { } - ''') + @Test + void testClosureInScriptIsVisited_CheckOnMethodStartIsFalse() { + def script = shell.parse ''' + @TimedInterrupt(applyToAllClasses=false, applyToAllMembers=false, checkOnMethodStart=false, value=1L) + def m = { -> } + m() + ''' + assertPassesNormalFailsSlowExecution(script.class, methodName: 'run') } - shouldFail(MultipleCompilationErrorsException) { - new GroovyClassLoader().parseClass(''' - import groovy.transform.TimedInterrupt - @TimedInterrupt(value = 5L, unit = 5) - def myMethod() { } - ''') + @Test + void testWhileInScriptIsVisited_CheckOnMethodStartIsFalse() { + def script = shell.parse ''' + @TimedInterrupt(applyToAllClasses=false, checkOnMethodStart=false, value=1L) + int x = 1 + while (x < 2) { x = 2 } + ''' + assertPassesNormalFailsSlowExecution(script.class, methodName: 'run') } - shouldFail(MultipleCompilationErrorsException) { - new GroovyClassLoader().parseClass(''' - import groovy.transform.TimedInterrupt - @TimedInterrupt(value = 5L, unit = foo()) - def myMethod() { } - ''') + @Test + void testForInScriptIsVisited_CheckOnMethodStartIsFalse() { + def script = shell.parse ''' + @TimedInterrupt(applyToAllClasses=false, checkOnMethodStart=false, value=1L) + def x = [1] + for (def o : x) { o++ } + ''' + assertPassesNormalFailsSlowExecution(script.class, methodName: 'run') } - } - - void testTimedInterruptOnAbstractClass() { - def script = ''' - @groovy.transform.TimedInterrupt(value = 1L) - abstract class MyAbstractClass { - abstract void myMethod() - } - - class Concrete extends MyAbstractClass { - void myMethod() { - 99.times { - // do something - } - } - } - new Concrete() - ''' - def system = new StubFor(System) + @Test + void testStaticClosureFieldNotVisited() { + assertScript shell, ''' + @TimedInterrupt(applyToAllClasses=false, value=1L) + class C { + static myMethod = { -> } + } + assertPassesSlowExecution(C) + ''' + } - // start time initialized to the Long of the Beast - system.demand.nanoTime(4) { 666L } // 2 times to cover full instantiation - system.demand.nanoTime() { 1000000667L } + @Test + void testAnnotationParameters() { + assertScript shell, ''' + import static java.util.concurrent.TimeUnit.* - system.use { - def instance = new GroovyShell(TimedInterruptibleASTTransformation.getClassLoader()).evaluate(script) - // may get false positives if multiple annotations with the same expireTime defined in test script - assert instance.dump().matches('.*timedInterrupt\\S+\\$expireTime=1000000666 .*') + @TimedInterrupt(applyToAllClasses=false, value=18000000L, unit=MILLISECONDS) + def myMethod() { } - shouldFail(TimeoutException) { - instance.myMethod() - } + assertPassesNormalFailsSlowExecution(this.class, expireTime: 18000000000666L, units: '18000000', timeUnitName: 'milliseconds') // 5 hours in future + ''' } - } - - private void assertPassesNormalFailsSlowExecution(c, long expireTime=1000000666L, units='1', methodName='myMethod', exception=TimeoutException, timeUnitName='seconds') { - def system = new StubFor(System) - // start time initialized to the Long of the Beast - system.demand.nanoTime() { 666L } - def instance - system.use { - instance = c.newInstance() - } - // may get false positives if multiple annotations with the same expireTime defined in test script - assert instance.dump().matches('.*timedInterrupt\\S+\\$expireTime=' + expireTime + ' .*') - system.demand.nanoTime() { expireTime } - system.use { - instance."$methodName"() + @Test // TODO: not sure all these tests are pulling their weight - testing Groovy annotation type handing not subject + void testErrorHandling() { + shouldFail shell, MultipleCompilationErrorsException, ''' + @TimedInterrupt(value = "5") + def myMethod() { } + ''' + + shouldFail shell, MultipleCompilationErrorsException, ''' + @TimedInterrupt(value = foo()) + def myMethod() { } + ''' + + shouldFail shell, MultipleCompilationErrorsException, ''' + @TimedInterrupt(value = 5L, applyToAllClasses = 5) + def myMethod() { } + ''' + + shouldFail shell, MultipleCompilationErrorsException, ''' + @TimedInterrupt(value = 5L, applyToAllClasses = foo()) + def myMethod() { } + ''' + + shouldFail shell, MultipleCompilationErrorsException, ''' + @TimedInterrupt(value = 5L, checkOnMethodStart = 5) + def myMethod() { } + ''' + + shouldFail shell, MultipleCompilationErrorsException, ''' + @TimedInterrupt(value = 5L, checkOnMethodStart = foo()) + def myMethod() { } + ''' + + shouldFail shell, MultipleCompilationErrorsException, ''' + @TimedInterrupt(value = 5L, unit = 5) + def myMethod() { } + ''' + + shouldFail shell, MultipleCompilationErrorsException, ''' + @TimedInterrupt(value = 5L, unit = foo()) + def myMethod() { } + ''' } - // one nanosecond too slow - system.demand.nanoTime() { expireTime + 1 } - system.use { - def e = shouldFail(exception) { - instance."$methodName"() - } - assert e.contains('Execution timed out after ' + units + ' ' + timeUnitName) + @Test + void testTimedInterruptOnAbstractClass() { + def script = ''' + @TimedInterrupt(value = 1L) + abstract class A { + abstract void m() + } + class C extends A { + void m() { + 99.times { + // do something + } + } + } + new C() + ''' + def system = new StubFor(System) + // start time initialized to the Long of the Beast + system.demand.nanoTime(4) { 666L } // 2 times to cover full instantiation + system.demand.nanoTime() { 1000000667L } + system.use { + def instance = shell.evaluate(script) + // may get false positives if multiple annotations with the same expireTime defined in test script + assert instance.dump().matches('.*timedInterrupt\\S+\\$expireTime=1000000666 .*') + + shouldFail(TimeoutException) { + instance.m() + } + } } - } - - private void assertPassesSlowExecution(c) { - def system = new StubFor(System) - // start time initialized to the Long of the Beast - system.demand.nanoTime() { 666L } - def instance - system.use { - instance = c.newInstance() + + //-------------------------------------------------------------------------- + + static void assertPassesNormalFailsSlowExecution(Map<String,?> args, Class type) { + def system = new StubFor(System) + // start time initialized to ... + system.demand.nanoTime() { 666L } + def instance + system.use { + instance = type.newInstance() + } + long expireTime = args.getOrDefault('expireTime', 1000000666L) + String methodName = args.getOrDefault('methodName', 'myMethod') + // may get false positives if multiple annotations with the same expireTime defined + assert instance.dump().matches('.*timedInterrupt\\S+\\$expireTime=' + expireTime + ' .*') + + system.demand.nanoTime() { expireTime } + system.use { + instance.(methodName)() + } + + // one nanosecond too slow + system.demand.nanoTime() { expireTime + 1 } + system.use { + def err = shouldFail(args.getOrDefault('exception', java.util.concurrent.TimeoutException)) { + instance.(methodName)() + } + assert err.message.contains('Execution timed out after ' + args.getOrDefault('units', '1') + ' ' + args.getOrDefault('timeUnitName', 'seconds')) + } } - system.demand.nanoTime() { 1000000667L } - system.use { - instance.myMethod() + + static void assertPassesSlowExecution(Class c) { + def system = new StubFor(System) + // start time initialized to the Long of the Beast + system.demand.nanoTime() { 666L } + def instance + system.use { + instance = c.newInstance() + } + system.demand.nanoTime() { 1000000667L } + system.use { + instance.myMethod() + } } - } } diff --git a/src/test/org/codehaus/groovy/classgen/asm/ClosureWriterGeneratedAnnotationTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/ClosureWriterGeneratedAnnotationTest.groovy index dba3b22467..9fbcabe848 100644 --- a/src/test/org/codehaus/groovy/classgen/asm/ClosureWriterGeneratedAnnotationTest.groovy +++ b/src/test/org/codehaus/groovy/classgen/asm/ClosureWriterGeneratedAnnotationTest.groovy @@ -19,7 +19,6 @@ package org.codehaus.groovy.classgen.asm import groovy.transform.Generated -import junit.framework.TestCase import org.codehaus.groovy.control.CompilationUnit import org.codehaus.groovy.control.Phases import org.junit.Test @@ -27,22 +26,21 @@ import org.junit.Test /** * Verifies if {@link Generated} annotations are added on {@code call} methods of generated closure classes. */ -class ClosureWriterGeneratedAnnotationTest extends TestCase { - private CompilationUnit compileScript(String scriptText) { - CompilationUnit compilationUnit = new CompilationUnit() - compilationUnit.addSource("script", scriptText) - compilationUnit.compile(Phases.ALL) +final class ClosureWriterGeneratedAnnotationTest { - compilationUnit + private CompilationUnit compileScript(String script) { + new CompilationUnit().tap { + addSource('script', script) + compile(Phases.CLASS_GENERATION) + } } private Collection<Class> findGeneratedClosureClasses(String outerClassName, CompilationUnit compilationUnit) { - Collection<Class> generatedClosureClasses = [] + List<Class> generatedClosureClasses = [] compilationUnit.classes.each { generatedClosureClasses.add(compilationUnit.classLoader.defineClass(it.name, it.bytes)) } - - return generatedClosureClasses.findAll({ it.name.matches(/.*${ outerClassName }\$\_.*\_closure.*/) }) + generatedClosureClasses.findAll { it.name =~ /${outerClassName}\$\_.*\_closure/ } } /** @@ -50,19 +48,18 @@ class ClosureWriterGeneratedAnnotationTest extends TestCase { */ @Test void testClosureWithNoParameters() { - String scriptText = """ - class MyClass { - void myMethod() { - [1..3].each { - println it + String scriptText = ''' + class MyClass { + void myMethod() { + [1..3].each { + println it + } } } - } - """ - + ''' CompilationUnit compilationUnit = compileScript(scriptText) - Class myClosureClassCompiled = findGeneratedClosureClasses("MyClass", compilationUnit)[0] - Collection callMethods = myClosureClassCompiled.declaredMethods.findAll { it.name == "call" } + Class myClosureClassCompiled = findGeneratedClosureClasses('MyClass', compilationUnit)[0] + Collection callMethods = myClosureClassCompiled.declaredMethods.findAll { it.name == 'call' } assert callMethods.size() == 0 } @@ -72,19 +69,18 @@ class ClosureWriterGeneratedAnnotationTest extends TestCase { */ @Test void testClosureWithSingleParameter() { - String scriptText = """ - class MyClass { - void myMethod() { - [1..3].each { Integer myInt -> - println myInt + String scriptText = ''' + class MyClass { + void myMethod() { + [1..3].each { Integer myInt -> + println myInt + } } } - } - """ - + ''' CompilationUnit compilationUnit = compileScript(scriptText) - Class myClosureClassCompiled = findGeneratedClosureClasses("MyClass", compilationUnit)[0] - Collection callMethodCollection = myClosureClassCompiled.declaredMethods.findAll { it.name == "call" } + Class myClosureClassCompiled = findGeneratedClosureClasses('MyClass', compilationUnit)[0] + Collection callMethodCollection = myClosureClassCompiled.declaredMethods.findAll { it.name == 'call' } assert callMethodCollection.size() == 1 assert callMethodCollection[0].getAnnotation(Generated) @@ -96,19 +92,18 @@ class ClosureWriterGeneratedAnnotationTest extends TestCase { */ @Test void testClosureWithMultipleParameters() { - String scriptText = """ - class MyClass { - void myMethod() { - [1..3].eachWithIndex { IntRange entry, Integer i -> - println entry[i] + String scriptText = ''' + class MyClass { + void myMethod() { + [1..3].eachWithIndex { IntRange entry, Integer i -> + println entry[i] + } } } - } - """ - + ''' CompilationUnit compilationUnit = compileScript(scriptText) - Class myClosureClassCompiled = findGeneratedClosureClasses("MyClass", compilationUnit)[0] - Collection callMethodCollection = myClosureClassCompiled.declaredMethods.findAll { it.name == "call" } + Class myClosureClassCompiled = findGeneratedClosureClasses('MyClass', compilationUnit)[0] + Collection callMethodCollection = myClosureClassCompiled.declaredMethods.findAll { it.name == 'call' } assert callMethodCollection.size() == 1 assert callMethodCollection[0].getAnnotation(Generated) diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy index 5576b16354..68181ad0c7 100644 --- a/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy +++ b/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy @@ -931,8 +931,7 @@ import groovy.transform.TypeCheckingMode // GROOVY-6095 void testServletError() { - def shell = new GroovyShell() - shell.evaluate ''' + assertScript ''' @Grab('javax.servlet:javax.servlet-api:3.0.1') import groovy.transform.CompileStatic diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileClosureGeneratedAnnotationTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileClosureGeneratedAnnotationTest.groovy index e9641a5c0f..634a6b10dc 100644 --- a/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileClosureGeneratedAnnotationTest.groovy +++ b/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileClosureGeneratedAnnotationTest.groovy @@ -19,7 +19,6 @@ package org.codehaus.groovy.classgen.asm.sc import groovy.transform.Generated -import junit.framework.TestCase import org.codehaus.groovy.control.CompilationUnit import org.codehaus.groovy.control.Phases import org.junit.Test @@ -29,22 +28,21 @@ import java.lang.reflect.Method /** * Verifies if {@link Generated} annotations are added on {@code call} methods of generated closure classes when static compilation is used. */ -class StaticCompileClosureGeneratedAnnotationTest extends TestCase { - private CompilationUnit compileScript(String scriptText) { - CompilationUnit compilationUnit = new CompilationUnit() - compilationUnit.addSource("script", scriptText) - compilationUnit.compile(Phases.ALL) +final class StaticCompileClosureGeneratedAnnotationTest { - compilationUnit + private CompilationUnit compileScript(String script) { + new CompilationUnit().tap { + addSource('script', script) + compile(Phases.CLASS_GENERATION) + } } private Collection<Class> findGeneratedClosureClasses(String outerClassName, CompilationUnit compilationUnit) { - Collection<Class> generatedClosureClasses = [] + List<Class> generatedClosureClasses = [] compilationUnit.classes.each { generatedClosureClasses.add(compilationUnit.classLoader.defineClass(it.name, it.bytes)) } - - return generatedClosureClasses.findAll({ it.name.matches(/.*${ outerClassName }\$\_.*\_closure.*/) }) + generatedClosureClasses.findAll { it.name =~ /${outerClassName}\$\_.*\_closure/ } } /** @@ -52,20 +50,19 @@ class StaticCompileClosureGeneratedAnnotationTest extends TestCase { */ @Test void testClosureWithNoParameters() { - String scriptText = """ - @groovy.transform.CompileStatic - class MyClass { - void myMethod() { - [1..3].each { - println it + String scriptText = ''' + @groovy.transform.CompileStatic + class MyClass { + void myMethod() { + [1..3].each { + println it + } } } - } - """ - + ''' CompilationUnit compilationUnit = compileScript(scriptText) - Class myClosureClassCompiled = findGeneratedClosureClasses("MyClass", compilationUnit)[0] - Collection callMethodCollection = myClosureClassCompiled.declaredMethods.findAll { it.name == "call" } + Class myClosureClassCompiled = findGeneratedClosureClasses('MyClass', compilationUnit)[0] + Collection callMethodCollection = myClosureClassCompiled.declaredMethods.findAll { it.name == 'call' } assert callMethodCollection.size() == 2 callMethodCollection.each { Method method -> @@ -80,20 +77,19 @@ class StaticCompileClosureGeneratedAnnotationTest extends TestCase { */ @Test void testClosureWithSingleParameter() { - String scriptText = """ - @groovy.transform.CompileStatic - class MyClass { - void myMethod() { - [1..3].each { IntRange myIntRange -> - println myIntRange + String scriptText = ''' + @groovy.transform.CompileStatic + class MyClass { + void myMethod() { + [1..3].each { IntRange myIntRange -> + println myIntRange + } } } - } - """ - + ''' CompilationUnit compilationUnit = compileScript(scriptText) - Class myClosureClassCompiled = findGeneratedClosureClasses("MyClass", compilationUnit)[0] - Collection callMethodCollection = myClosureClassCompiled.declaredMethods.findAll { it.name == "call" } + Class myClosureClassCompiled = findGeneratedClosureClasses('MyClass', compilationUnit)[0] + Collection callMethodCollection = myClosureClassCompiled.declaredMethods.findAll { it.name == 'call' } assert callMethodCollection.size() == 1 assert callMethodCollection[0].getAnnotation(Generated) @@ -105,20 +101,19 @@ class StaticCompileClosureGeneratedAnnotationTest extends TestCase { */ @Test void testClosureWithMultipleParameters() { - String scriptText = """ - @groovy.transform.CompileStatic - class MyClass { - void myMethod() { - [1..3].eachWithIndex { IntRange entry, Integer i -> - println entry[i] + String scriptText = ''' + @groovy.transform.CompileStatic + class MyClass { + void myMethod() { + [1..3].eachWithIndex { IntRange entry, Integer i -> + println entry[i] + } } } - } - """ - + ''' CompilationUnit compilationUnit = compileScript(scriptText) - Class myClosureClassCompiled = findGeneratedClosureClasses("MyClass", compilationUnit)[0] - Collection callMethodCollection = myClosureClassCompiled.declaredMethods.findAll { it.name == "call" } + Class myClosureClassCompiled = findGeneratedClosureClasses('MyClass', compilationUnit)[0] + Collection callMethodCollection = myClosureClassCompiled.declaredMethods.findAll { it.name == 'call' } assert callMethodCollection.size() == 1 assert callMethodCollection[0].getAnnotation(Generated) diff --git a/src/test/org/codehaus/groovy/transform/classloading/TransformsAndCustomClassLoadersTest.groovy b/src/test/org/codehaus/groovy/transform/classloading/TransformsAndCustomClassLoadersTest.groovy index e1884bbae1..bc8a0b66c0 100644 --- a/src/test/org/codehaus/groovy/transform/classloading/TransformsAndCustomClassLoadersTest.groovy +++ b/src/test/org/codehaus/groovy/transform/classloading/TransformsAndCustomClassLoadersTest.groovy @@ -113,7 +113,7 @@ final class TransformsAndCustomClassLoadersTest { try (def loader = new GroovyClassLoader(this.class.classLoader)) { def unit = new CompilationUnit(null, null, dependencyLoader, transformLoader) unit.addSource('Foo.groovy', source) - unit.compile() + unit.compile(CompilePhase.CLASS_GENERATION.phaseNumber) assert unit.classes.size() == 1 def classInfo = unit.classes[0]
