This is an automated email from the ASF dual-hosted git repository. paulk pushed a commit to branch GROOVY_4_0_X in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 8efeaf26d5b693d20cafee54bc25df7048afbc87 Author: Eric Milles <[email protected]> AuthorDate: Fri Jan 21 10:04:00 2022 -0600 GROOVY-6363: add test case --- src/main/java/groovy/lang/MetaClassImpl.java | 29 +-- src/test/groovy/lang/CategoryAnnotationTest.groovy | 273 +++++++++++---------- src/test/groovy/lang/MixinAnnotationTest.groovy | 24 +- 3 files changed, 185 insertions(+), 141 deletions(-) diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java index aa9fdf8..7600e7a 100644 --- a/src/main/java/groovy/lang/MetaClassImpl.java +++ b/src/main/java/groovy/lang/MetaClassImpl.java @@ -3252,21 +3252,22 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass { * {@code null} : ignore method * {@code true} : replace */ - private static Boolean getMatchKindForCategory(MetaMethod aMethod, MetaMethod categoryMethod) { - CachedClass[] params1 = aMethod.getParameterTypes(); - CachedClass[] params2 = categoryMethod.getParameterTypes(); - if (params1.length != params2.length) return Boolean.FALSE; + private static Boolean getMatchKindForCategory(final MetaMethod aMethod, final MetaMethod categoryMethod) { + CachedClass[] paramTypes1 = aMethod.getParameterTypes(); + CachedClass[] paramTypes2 = categoryMethod.getParameterTypes(); + int n = paramTypes1.length; + if (n != paramTypes2.length) return Boolean.FALSE; + for (int i = 0; i < n; i += 1) { + if (paramTypes1[i] != paramTypes2[i]) return Boolean.FALSE; + } + + Class selfType1 = aMethod.getDeclaringClass().getTheClass(); + Class selfType2 = categoryMethod.getDeclaringClass().getTheClass(); + // replace if self type is the same or the category self type is more specific + if (selfType1 == selfType2 || selfType1.isAssignableFrom(selfType2)) return Boolean.TRUE; + // GROOVY-6363: replace if the private method self type is more specific + // if (aMethod.isPrivate() && selfType2.isAssignableFrom(selfType1)) return Boolean.TRUE; - for (int i = 0; i < params1.length; i++) { - if (params1[i] != params2[i]) return Boolean.FALSE; - } - - Class aMethodClass = aMethod.getDeclaringClass().getTheClass(); - Class categoryMethodClass = categoryMethod.getDeclaringClass().getTheClass(); - - if (aMethodClass == categoryMethodClass) return Boolean.TRUE; - boolean match = aMethodClass.isAssignableFrom(categoryMethodClass); - if (match) return Boolean.TRUE; return null; } diff --git a/src/test/groovy/lang/CategoryAnnotationTest.groovy b/src/test/groovy/lang/CategoryAnnotationTest.groovy index 098783b..0a0fde2 100644 --- a/src/test/groovy/lang/CategoryAnnotationTest.groovy +++ b/src/test/groovy/lang/CategoryAnnotationTest.groovy @@ -18,98 +18,92 @@ */ package groovy.lang -import groovy.test.GroovyTestCase +import groovy.test.NotYetImplemented +import org.junit.Test -class CategoryAnnotationTest extends GroovyTestCase { - void testTransformationOfPropertyInvokedOnThis() { - //Test the fix for GROOVY-3367 - assertScript """ - @Category(Distance3367) - class DistanceCategory3367 { - Distance3367 plus(Distance3367 increment) { - new Distance3367(number: this.number + increment.number) - } - } +import static groovy.test.GroovyAssert.assertScript +import static groovy.test.GroovyAssert.shouldFail + +final class CategoryAnnotationTest { - class Distance3367 { + @Test // GROOVY-3367 + void testTransformationOfPropertyInvokedOnThis() { + assertScript ''' + class Distance { def number } + @Category(Distance) + class DistanceCategory { + Distance plus(Distance increment) { + new Distance(number: this.number + increment.number) + } + } - use(DistanceCategory3367) { - def d1 = new Distance3367(number: 5) - def d2 = new Distance3367(number: 10) + use(DistanceCategory) { + def d1 = new Distance(number: 5) + def d2 = new Distance(number: 10) def d3 = d1 + d2 assert d3.number == 15 } - """ + ''' } - void testTransformationWithCatchClause() { - //Test the fix for GROOVY-4801 - assertScript """ - class ExceptionHandler { - static def handled(Object self, Closure block) { - try { block.call() } - catch (Throwable t) { t.message } - } + @Test // GROOVY-3543 + void testCategoryMethodsHavingDeclarationStatements() { + // Declaration statements in category class' methods were not being visited by + // CategoryASTTransformation's expressionTransformer resulting in declaration + // variables not being defined on varStack resulting in compilation errors later + assertScript ''' + interface Test { + String getName() } - - @Mixin(ExceptionHandler) - class Caller { - def thrower() { handled { 1/0 } } + class MyTest implements Test { + String getName() { + return "Pre-" + } } - - assert new Caller().thrower() == 'Division by zero' - """ - } - - void testCategoryMethodsHavingDeclarationStatements() { - // GROOVY-3543: Declaration statements in category class' methods were not being visited by - // CategoryASTTransformation's expressionTransformer resulting in declaration variables not being - // defined on varStack resulting in compilation errors later - assertScript """ @Category(Test) class TestCategory { - String getSuperName1() { - String myname = "" - return myname + "hi from category" + String getSuperName1() { + String myname = "" + return myname + "hi from category" } // 2nd test case of JIRA - String getSuperName2() { - String myname = this.getName() - for(int i = 0; i < 5; i++) myname += i + String getSuperName2() { + String myname = this.getName() + for(int i = 0; i < 5; i++) myname += i return myname + "-Post" } // 3rd test case of JIRA - String getSuperName3() { - String myname = this.getName() + String getSuperName3() { + String myname = this.getName() for(i in 0..4) myname += i return myname + "-Post" } } - interface Test { - String getName() - } - - class MyTest implements Test { - String getName() { - return "Pre-" - } + def test = new MyTest() + use(TestCategory) { + assert test.getSuperName1() == "hi from category" + assert test.getSuperName2() == "Pre-01234-Post" + assert test.getSuperName3() == "Pre-01234-Post" } - - def onetest = new MyTest() - use(TestCategory) { - assert onetest.getSuperName1() == "hi from category" - assert onetest.getSuperName2() == "Pre-01234-Post" - assert onetest.getSuperName3() == "Pre-01234-Post" - } - """ + ''' } + @Test // GROOVY-3543 void testPropertyNameExpandingToGetterInsideCategoryMethod() { - //GROOVY-3543: Inside the category method, this.getType().name was failing but this.getType().getName() was not. - assertScript """ + // Inside the category method, this.getType().name was failing but this.getType().getName() was not. + assertScript ''' + class Type { + String name + } + interface Guy { + Type getType() + } + class MyGuyver implements Guy { + Type type + } @Category(Guy) class Naming { String getTypeName() { @@ -120,59 +114,46 @@ class CategoryAnnotationTest extends GroovyTestCase { } } - interface Guy { - Type getType() - } - - class Type { - String name - } - - class MyGuyver implements Guy { - Type type - } - def atype = new Type(name: 'String') def onetest = new MyGuyver(type:atype) use(Naming) { assert onetest.getTypeName() == onetest.getType().getName() } - """ + ''' } + @Test void testClosureUsingThis() { - assertScript """ - @Category(Guy) - class Filtering { - List process() { - this.messages.findAll{it.name != this.getName()} - } - } - + assertScript ''' interface Guy { String getName() List getMessages() } - class MyGuyver implements Guy { List messages String name } + @Category(Guy) + class Filtering { + List process() { + this.messages.findAll{it.name != this.getName()} + } + } - def onetest = new MyGuyver( - name: 'coucou', - messages : [['name':'coucou'], ['name':'test'], ['name':'salut']]) + def onetest = new MyGuyver(name: 'coucou', + messages: [['name':'coucou'], ['name':'test'], ['name':'salut']] + ) - Guy.mixin Filtering + Guy.mixin Filtering - assert onetest.process() == onetest.messages.findAll{it.name != onetest.getName()} - """ + assert onetest.process() == onetest.messages.findAll{it.name != onetest.getName()} + ''' } + @Test // GROOVY-4546 void testClosureWithinDeclarationExpressionAndMultipleAssignment() { - // GROOVY-4546 - assertScript """ + assertScript ''' @Category(Integer) class MyOps { def multiplesUpTo4() { [this * 2, this * 3, this * 4] } @@ -195,16 +176,12 @@ class CategoryAnnotationTest extends GroovyTestCase { assert 5.alsoMultiplesUpTo(6) == [10, 15, 20, 25, 30] assert 5.twice() == 10 } - """ + ''' } - // GROOVY-6120 + @Test // GROOVY-6120 void testFieldShouldNotBeAllowedInCategory() { - def message = shouldFail(RuntimeException) { - assertScript ''' - @Mixin(Foo) - class Bar { } - + def err = shouldFail RuntimeException, ''' @Category(Bar) class Foo { public x = 5 @@ -212,20 +189,18 @@ class CategoryAnnotationTest extends GroovyTestCase { x } } + @Mixin(Foo) + class Bar { + } assert new Bar().foo() == 5 - ''' - } - assert message.contains('The @Category transformation does not support instance fields') + ''' + assert err =~ /The @Category transformation does not support instance fields/ } - // GROOVY-6120 + @Test // GROOVY-6120 void testPropertyShouldNotBeAllowedInCategory() { - def message = shouldFail(RuntimeException) { - assertScript ''' - @Mixin(Foo) - class Bar { } - + def err = shouldFail RuntimeException, ''' @Category(Bar) class Foo { int x = 5 @@ -233,56 +208,67 @@ class CategoryAnnotationTest extends GroovyTestCase { x } } + @Mixin(Foo) + class Bar { + } assert new Bar().foo() == 5 - ''' - } - assert message.contains('The @Category transformation does not support instance properties') + ''' + assert err =~ /The @Category transformation does not support instance properties/ } - // GROOVY-6120 + @Test // GROOVY-6120 void testShouldNotThrowVerifyError() { assertScript ''' - @Mixin(Foo) - class Bar { int x = 5 } - @Category(Bar) class Foo { def foo() { x } } + @Mixin(Foo) + class Bar { + int x = 5 + } assert new Bar().foo() == 5 ''' } - // GROOVY-6120 + @Test // GROOVY-6120 void testCategoryShouldBeCompatibleWithCompileStatic() { assertScript ''' - @Mixin(Foo) - class Bar { int x = 5 } + import groovy.transform.CompileStatic + @CompileStatic @Category(Bar) - @groovy.transform.CompileStatic class Foo { def foo() { x } } + @Mixin(Foo) + class Bar { + int x = 5 + } assert new Bar().foo() == 5 ''' } - void testCategoryShouldBeCompatibleWithCompileStatic_GROOVY6917() { + @Test // GROOVY-6917 + void testCategoryShouldBeCompatibleWithCompileStatic2() { assertScript ''' - @groovy.transform.CompileStatic + import groovy.transform.CompileStatic + + @CompileStatic @Category(Integer) class IntegerCategory { - Integer twice() { this * 2 } + Integer twice() { + this * 2 + } List<Integer> multiplesUpTo(Integer num) { - (2..num).collect{ j -> this * j } + (2..num).collect{ n -> this * n } } List<Integer> multiplyAll(List<Integer> nums) { nums.collect{ it * this } @@ -296,5 +282,40 @@ class CategoryAnnotationTest extends GroovyTestCase { } ''' } -} + @NotYetImplemented @Test // GROOVY-6363 + void testCategoryMethodOverridesPrivateMethod() { + assertScript ''' + import groovy.transform.CompileStatic + + interface Path { + def m() + } + @Category(Path) + class PathExtension { + boolean isEmpty() { + return true + } + } + class UnixPath implements Path { + private boolean isEmpty() { + return false + } + @CompileStatic + @Override m() { + isEmpty().toString() + } + } + + Path getPath(String path) { + return new UnixPath() + } + + use (PathExtension) { + def path = getPath('/foo') + assert path.isEmpty() : 'private method used' + assert path.m() == 'false' : 'category method used' + } + ''' + } +} diff --git a/src/test/groovy/lang/MixinAnnotationTest.groovy b/src/test/groovy/lang/MixinAnnotationTest.groovy index 49df14f..6591a32 100644 --- a/src/test/groovy/lang/MixinAnnotationTest.groovy +++ b/src/test/groovy/lang/MixinAnnotationTest.groovy @@ -58,7 +58,7 @@ final class MixinAnnotationTest { } @Test - void testMultipleMixinAnnotation () { + void testMultipleMixinAnnotation() { assertScript ''' @Category(Object) class CategoryToUse1 { @@ -86,6 +86,28 @@ final class MixinAnnotationTest { ''' } + @Test // GROOVY-4801 + void testMixinWithTryCatchClause() { + assertScript ''' + class ExceptionHandler { + static tryCatch(Object self, Closure block) { + try { + block.call() + } catch (Throwable t) { + return t.message + } + } + } + + @Mixin(ExceptionHandler) + class C { + def m() { tryCatch { 1/0 } } + } + + assert new C().m() == 'Division by zero' + ''' + } + @Test // GROOVY-10200 void testStaticInnerMixinAnnotation() { assertScript '''
