You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/01/21 16:04:08 UTC

[groovy] branch master updated: GROOVY-6363: add test case

This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new f774766  GROOVY-6363: add test case
f774766 is described below

commit f77476675a20946595b9d4188acda3af60d7328f
Author: Eric Milles <er...@thomsonreuters.com>
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 '''