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 '''