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

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


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

commit d61ed6956cb1f4fcbe620ac02a5aef4376fb0817
Author: Eric Milles <[email protected]>
AuthorDate: Wed Nov 19 09:52:08 2025 -0600

    GROOVY-11775: add test case
---
 .../groovy/reflection/MixinInMetaClass.java        |  96 +++----
 src/test/groovy/groovy/lang/MixinTest.groovy       | 296 ++++++++++++---------
 2 files changed, 214 insertions(+), 178 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java 
b/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java
index 90fc73d1d2..8bb1c12c03 100644
--- a/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java
+++ b/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java
@@ -34,33 +34,33 @@ import 
org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaMethod;
 import org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaProperty;
 import org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod;
 
+import java.lang.annotation.Annotation;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Objects;
+import java.util.Map;
+
+import static java.util.Objects.hash;
 
 public class MixinInMetaClass {
     final ExpandoMetaClass emc;
     final CachedClass mixinClass;
     final CachedConstructor constructor;
-    private final ManagedIdentityConcurrentMap managedIdentityConcurrentMap =
-            new 
ManagedIdentityConcurrentMap(ManagedIdentityConcurrentMap.ReferenceType.SOFT);
+    private final Map<Object, Object> managedIdentityConcurrentMap =
+        new 
ManagedIdentityConcurrentMap<>(ManagedIdentityConcurrentMap.ReferenceType.SOFT);
 
     public MixinInMetaClass(ExpandoMetaClass emc, CachedClass mixinClass) {
         this.emc = emc;
         this.mixinClass = mixinClass;
-
         this.constructor = findDefaultConstructor(mixinClass);
+
         emc.addMixinClass(this);
     }
 
     private static CachedConstructor findDefaultConstructor(CachedClass 
mixinClass) {
-        for (CachedConstructor constr : mixinClass.getConstructors()) {
-            if (!constr.isPublic())
-                continue;
-
-            CachedClass[] classes = constr.getParameterTypes();
-            if (classes.length == 0)
-                return constr;
+        for (CachedConstructor cc : mixinClass.getConstructors()) {
+            if (cc.isPublic() && cc.getParameterTypes().length == 0) {
+                return cc;
+            }
         }
 
         throw new GroovyRuntimeException("No default constructor for class " + 
mixinClass.getName() + "! Can't be mixed in.");
@@ -92,8 +92,8 @@ public class MixinInMetaClass {
         return mixinClass;
     }
 
-    public static void mixinClassesToMetaClass(MetaClass self, List<Class> 
categoryClasses) {
-        final Class selfClass = self.getTheClass();
+    public static void mixinClassesToMetaClass(MetaClass self, final 
List<Class> categoryClasses) {
+        final Class<?> selfClass = self.getTheClass();
 
         if (self instanceof HandleMetaClass) {
             self = (MetaClass) ((HandleMetaClass) self).replaceDelegate();
@@ -107,26 +107,23 @@ public class MixinInMetaClass {
             }
         }
 
-        ExpandoMetaClass mc = (ExpandoMetaClass) self;
-
-        List<MetaMethod> arr = new ArrayList<>();
-        for (Class categoryClass : categoryClasses) {
-
+        ExpandoMetaClass emc = (ExpandoMetaClass) self;
+        List<MetaMethod> toRegister = new ArrayList<>();
+        for (Class<?> categoryClass : categoryClasses) {
             final CachedClass cachedCategoryClass = 
ReflectionCache.getCachedClass(categoryClass);
-            final MixinInMetaClass mixin = new MixinInMetaClass(mc, 
cachedCategoryClass);
+            final MixinInMetaClass mixin = new MixinInMetaClass(emc, 
cachedCategoryClass);
 
             final MetaClass metaClass = 
GroovySystem.getMetaClassRegistry().getMetaClass(categoryClass);
-            final List<MetaProperty> propList = metaClass.getProperties();
-            for (MetaProperty prop : propList)
-                if (self.getMetaProperty(prop.getName()) == null) {
-                    mc.registerBeanProperty(prop.getName(), new 
MixinInstanceMetaProperty(prop, mixin));
+            for (MetaProperty mp : metaClass.getProperties()) {
+                if (emc.getMetaProperty(mp.getName()) == null) {
+                    emc.registerBeanProperty(mp.getName(), new 
MixinInstanceMetaProperty(mp, mixin));
                 }
-
-            for (MetaProperty prop : cachedCategoryClass.getFields())
-                if (self.getMetaProperty(prop.getName()) == null) {
-                    mc.registerBeanProperty(prop.getName(), new 
MixinInstanceMetaProperty(prop, mixin));
+            }
+            for (MetaProperty mp : cachedCategoryClass.getFields()) {
+                if (emc.getMetaProperty(mp.getName()) == null) {
+                    emc.registerBeanProperty(mp.getName(), new 
MixinInstanceMetaProperty(mp, mixin));
                 }
-
+            }
             for (MetaMethod method : metaClass.getMethods()) {
                 if (!method.isPublic())
                     continue;
@@ -139,30 +136,29 @@ public class MixinInMetaClass {
 
                 if (method.isStatic()) {
                     if (method instanceof CachedMethod)
-                        staticMethod(self, arr, (CachedMethod) method);
+                        staticMethod(self, toRegister, (CachedMethod) method);
                 } else if (method.getDeclaringClass().getTheClass() != 
Object.class || "toString".equals(method.getName())) {
-                  //if (self.pickMethod(method.getName(), 
method.getNativeParameterTypes()) == null) {
-                        arr.add(new MixinInstanceMetaMethod(method, mixin));
+                  //if (emc.pickMethod(method.getName(), 
method.getNativeParameterTypes()) == null) {
+                        toRegister.add(new MixinInstanceMetaMethod(method, 
mixin));
                   //}
                 }
             }
         }
 
-        for (MetaMethod res : arr) {
-            final MetaMethod metaMethod = res;
-            if (metaMethod.getDeclaringClass().isAssignableFrom(selfClass))
-                mc.registerInstanceMethod(metaMethod);
-            else {
-                mc.registerSubclassInstanceMethod(metaMethod);
+        for (MetaMethod mm : toRegister) {
+            if (mm.getDeclaringClass().isAssignableFrom(selfClass)) {
+                emc.registerInstanceMethod(mm);
+            } else {
+                emc.registerSubclassInstanceMethod(mm);
             }
         }
     }
 
-    private static boolean hasAnnotation(CachedMethod method, Class<Internal> 
annotationClass) {
+    private static boolean hasAnnotation(final CachedMethod method, final 
Class<? extends Annotation> annotationClass) {
         return method.getAnnotation(annotationClass) != null;
     }
 
-    private static void staticMethod(final MetaClass self, List<MetaMethod> 
arr, final CachedMethod method) {
+    private static void staticMethod(final MetaClass self, final 
List<MetaMethod> arr, final CachedMethod method) {
         CachedClass[] paramTypes = method.getParameterTypes();
 
         if (paramTypes.length == 0)
@@ -189,24 +185,16 @@ public class MixinInMetaClass {
     }
 
     @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof MixinInMetaClass)) return false;
-        if (!super.equals(o)) return false;
-
-        MixinInMetaClass that = (MixinInMetaClass) o;
-
-        if (!Objects.equals(mixinClass, that.mixinClass)) return false;
-
-        return true;
+    public boolean equals(final Object that) {
+        return (that == this)
+            || (that instanceof MixinInMetaClass
+                && emc.equals(((MixinInMetaClass) that).emc)
+                && mixinClass.equals(((MixinInMetaClass) that).mixinClass)
+                && constructor.equals(((MixinInMetaClass) that).constructor));
     }
 
     @Override
     public int hashCode() {
-        int result = super.hashCode();
-        result = 31 * result + (emc != null ? emc.hashCode() : 0);
-        result = 31 * result + (mixinClass != null ? mixinClass.hashCode() : 
0);
-        result = 31 * result + (constructor != null ? constructor.hashCode() : 
0);
-        return result;
+        return hash(emc, mixinClass, constructor);
     }
 }
diff --git a/src/test/groovy/groovy/lang/MixinTest.groovy 
b/src/test/groovy/groovy/lang/MixinTest.groovy
index 3694da2252..7746767962 100644
--- a/src/test/groovy/groovy/lang/MixinTest.groovy
+++ b/src/test/groovy/groovy/lang/MixinTest.groovy
@@ -18,24 +18,34 @@
  */
 package groovy.lang
 
-import groovy.test.GroovyTestCase
+import groovy.test.NotYetImplemented
+import groovy.transform.CompileStatic
+import org.junit.jupiter.api.AfterEach
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
 
 import java.util.concurrent.locks.ReentrantLock
-import org.codehaus.groovy.reflection.ClassInfo
 
-class MixinTest extends GroovyTestCase {
+import static groovy.test.GroovyAssert.assertScript
+import static org.junit.jupiter.api.Assertions.assertEquals
+import static org.junit.jupiter.api.Assertions.assertTrue
 
-    @groovy.transform.CompileStatic
-    protected void setUp() {
-        ClassInfo.clearModifiedExpandos()
+final class MixinTest {
+
+    @BeforeEach
+    @CompileStatic
+    void setUp() {
+        org.codehaus.groovy.reflection.ClassInfo.clearModifiedExpandos()
     }
 
-    protected void tearDown() {
+    @AfterEach
+    void tearDown() {
         ArrayList.metaClass = null
-        List.metaClass = null
+             List.metaClass = null
         ObjToTest.metaClass = null
     }
 
+    @Test
     void testOneClass() {
         List.mixin ListExt
         ArrayList.mixin ArrayListExt
@@ -45,6 +55,7 @@ class MixinTest extends GroovyTestCase {
         assertEquals 1, [0, 1].swap().unswap()[1]
     }
 
+    @Test
     void testWithList() {
         ArrayList.mixin ArrayListExt, ListExt
         assertEquals 1, [0, 1].swap()[0]
@@ -53,6 +64,7 @@ class MixinTest extends GroovyTestCase {
         assertEquals 1, [0, 1].swap().unswap()[1]
     }
 
+    @Test
     void testCombined() {
         ArrayList.mixin Combined
         assertEquals 1, [0, 1].swap()[0]
@@ -61,6 +73,7 @@ class MixinTest extends GroovyTestCase {
         assertEquals 1, [0, 1].swap().unswap()[1]
     }
 
+    @Test
     void testWithEmc() {
         ArrayList.metaClass.unswap = {
             [delegate[1], delegate[0]]
@@ -72,25 +85,28 @@ class MixinTest extends GroovyTestCase {
         assertEquals 1, [0, 1].swap().unswap()[1]
     }
 
+    @Test
     void testGroovyObject() {
         def obj = new ObjToTest()
-        assertEquals "original", obj.value
+        assertEquals 'original', obj.value
         obj.metaClass.mixin ObjToTestCategory
-        assertEquals "changed by category", obj.value
-        assertEquals "original", new ObjToTest().value
+        assertEquals 'changed by category', obj.value
+        assertEquals 'original', new ObjToTest().value
     }
 
+    @Test
     void testGroovyObjectWithEmc() {
         ObjToTest.metaClass.getValue = {->
-            "emc changed"
+            'emc changed'
         }
         ObjToTest obj = new ObjToTest()
-        assertEquals "emc changed", obj.getValue()
+        assertEquals 'emc changed', obj.getValue()
         obj.metaClass.mixin ObjToTestCategory
-        assertEquals "changed by category", obj.value
-        assertEquals "emc changed", new ObjToTest().value
+        assertEquals 'changed by category', obj.value
+        assertEquals 'emc changed', new ObjToTest().value
     }
 
+    @Test
     void testFlatten() {
         Object.metaClass.mixin DeepFlattenToCategory
         assertEquals([8, 9, 3, 2, 1, 4], [[8, 9] as Object[], [3, 2, [2: 1, 3: 
4]], [2, 3]].flattenTo() as List)
@@ -110,11 +126,11 @@ class MixinTest extends GroovyTestCase {
             return set
         }
         Object.metaClass.flattenTo(ArrayList) {Set set ->
-            set << "oops"
-            return Collection.metaClass.invokeMethod(delegate, "flattenTo", 
set)
+            set << 'oops'
+            return Collection.metaClass.invokeMethod(delegate, 'flattenTo', 
set)
         }
         ArrayList.metaClass = null
-        assertEquals(["oops", -2, -3, 8, 9, 3, 2, 1, 4], [x, [8, 9] as 
Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo() as List)
+        assertEquals(['oops', -2, -3, 8, 9, 3, 2, 1, 4], [x, [8, 9] as 
Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo() as List)
 
         ArrayList.metaClass = null
         Object.metaClass {
@@ -125,15 +141,15 @@ class MixinTest extends GroovyTestCase {
             }
 
             flattenTo(ArrayList) {Set set ->
-                set << "oopsssss"
-                return Collection.metaClass.invokeMethod(delegate, 
"flattenTo", set)
+                set << 'oopsssss'
+                return Collection.metaClass.invokeMethod(delegate, 
'flattenTo', set)
             }
 
             asList {->
                 delegate as List
             }
         }
-        assertEquals(["oopsssss", -2, -3, 8, 9, 3, 2, 1, 4], [x, [8, 9] as 
Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo().asList())
+        assertEquals(['oopsssss', -2, -3, 8, 9, 3, 2, 1, 4], [x, [8, 9] as 
Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo().asList())
 
         ArrayList.metaClass = null
         Object.metaClass {
@@ -145,8 +161,8 @@ class MixinTest extends GroovyTestCase {
                 }
 
                 flattenTo {Set set ->
-                    set << "ssoops"
-                    return Collection.metaClass.invokeMethod(delegate, 
"flattenTo", set)
+                    set << 'ssoops'
+                    return Collection.metaClass.invokeMethod(delegate, 
'flattenTo', set)
                 }
             }
 
@@ -154,14 +170,15 @@ class MixinTest extends GroovyTestCase {
                 delegate as List
             }
         }
-        assertEquals(["ssoops", -2, -3, 8, 9, 3, 2, 1, 4], [x, [8, 9] as 
Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo().asList())
+        assertEquals(['ssoops', -2, -3, 8, 9, 3, 2, 1, 4], [x, [8, 9] as 
Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo().asList())
 
         Object.metaClass = null
     }
 
+    @Test
     void testMixingLockable() {
         Object.metaClass.mixin ReentrantLock
-        def name = "abcdef"
+        def name = 'abcdef'
         name.lock()
         try {
             assertTrue name.isLocked()
@@ -172,6 +189,7 @@ class MixinTest extends GroovyTestCase {
         Object.metaClass = null
     }
 
+    @Test
     void testConcurrentQueue() {
         ReentrantLock.metaClass {
             withLock {Closure operation ->
@@ -196,6 +214,7 @@ class MixinTest extends GroovyTestCase {
         ReentrantLock.metaClass = null
     }
 
+    @Test
     void testDynamicConcurrentQueue() {
         ReentrantLock.metaClass {
             withLock {Closure operation ->
@@ -264,6 +283,7 @@ class MixinTest extends GroovyTestCase {
         ReentrantLock.metaClass = null
     }
 
+    @Test
     void testNoDupCollection() {
         def list = new Object()
         list.metaClass {
@@ -287,6 +307,7 @@ class MixinTest extends GroovyTestCase {
         assertEquals 3, list[2]
     }
 
+    @Test
     void testList() {
         def u = []
         u.metaClass {
@@ -311,8 +332,8 @@ class MixinTest extends GroovyTestCase {
         assertEquals 2, ((Set) u).size()
     }
 
+    @Test
     void testWPM() {
-
         new WPM_B().foo()
 
         WPM_C.metaClass { mixin WPM_B }
@@ -322,6 +343,7 @@ class MixinTest extends GroovyTestCase {
         c.foobar()
     }
 
+    @Test
     void testStackOverflow() {
         Overflow_B.metaClass {
             mixin Overflow_A
@@ -338,15 +360,17 @@ class MixinTest extends GroovyTestCase {
         b.foo()
     }
 
+    // GROOVY-3474
+    @Test
     void testStackOverflowErrorWithMixinsAndClosure() {
-        assertScript """
+        assertScript '''
             class Groovy3474A {
                 int counter = 1
                 protected final foo() {
                     bar { counter }
                 }
-                private final String bar(Closure code) { 
-                    return "Bar " + code() 
+                private final String bar(Closure code) {
+                    return "Bar " + code()
                 }
             }
 
@@ -357,11 +381,12 @@ class MixinTest extends GroovyTestCase {
             def c = new Groovy3474C()
             assert c.foo() == 'Bar 1'
             println "testStackOverflowErrorWithMixinsAndClosure() Done"
-        """
+        '''
     }
 
+    @Test
     void testMixinWithVarargs() {
-        assertScript """
+        assertScript '''
             class Dsl {
                 static novarargs(java.util.List s) { "novarargs" + s.size() }
                 static plainVarargs(Object... s) { "plainVarargs" + s.size() }
@@ -371,143 +396,166 @@ class MixinTest extends GroovyTestCase {
             assert novarargs(["a", "b"]) == "novarargs2"
             assert plainVarargs("a", "b", 35) == "plainVarargs3"
             assert mixedVarargs(3, "a", "b", "c", "d") == "mixedVarargs4"
-        """
+        '''
     }
-}
 
-class ArrayListExt {
-    static def swap(ArrayList self) {
-        [self[1], self[0]]
+    // GROOVY-11775
+    @NotYetImplemented @Test
+    void testRepeatMixing() {
+        assertScript '''
+            class Foo {
+                int value = new Random().nextInt()
+            }
+            class Bar {
+            }
+            Bar.mixin(Foo)
+            def bar = new Bar()
+            int val = bar.value
+            assert bar.value == val
+            Bar.mixin(Foo)
+            assert bar.value == val
+            assert bar.value == val
+        '''
     }
-}
 
-class ListExt {
-    static def unswap(List self) {
-        [self[1], self[0]]
-    }
-}
+    
//--------------------------------------------------------------------------
 
-class Combined {
-    static def swap(ArrayList self) {
-        [self[1], self[0]]
+    static class ArrayListExt {
+        static def swap(ArrayList self) {
+            [self[1], self[0]]
+        }
     }
 
-    static def unswap(List self) {
-        [self[1], self[0]]
+    static class ListExt {
+        static def unswap(List self) {
+            [self[1], self[0]]
+        }
     }
-}
 
-class ObjToTest {
-    def getValue() {
-        "original"
-    }
-}
+    static class Combined {
+        static def swap(ArrayList self) {
+            [self[1], self[0]]
+        }
 
-class ObjToTestCategory {
-    static getValue(ObjToTest self) {
-        "changed by category"
+        static def unswap(List self) {
+            [self[1], self[0]]
+        }
     }
-}
 
-class DeepFlattenToCategory {
-    static Set flattenTo(element) {
-        LinkedHashSet set = new LinkedHashSet()
-        element.flattenTo(set)
-        return set
+    static class ObjToTest {
+        def getValue() {
+            'original'
+        }
     }
 
-    // Object - put to result set
-    static void flattenTo(element, Set addTo) {
-        addTo << element
+    static class ObjToTestCategory {
+        static getValue(ObjToTest self) {
+            'changed by category'
+        }
     }
 
-    // Collection - flatten each element
-    static void flattenTo(Collection elements, Set addTo) {
-        elements.each {element ->
-            element.flattenTo(addTo)
+    static class DeepFlattenToCategory {
+        static Set flattenTo(element) {
+            LinkedHashSet set = new LinkedHashSet()
+            element.flattenTo(set)
+            return set
         }
-    }
 
-    // Map - flatten each value
-    static void flattenTo(Map elements, Set addTo) {
-        elements.values().flattenTo(addTo)
-    }
+        // Object - put to result set
+        static void flattenTo(element, Set addTo) {
+            addTo << element
+        }
 
-    // Array - flatten each element
-    static void flattenTo(Object[] elements, Set addTo) {
-        elements.each {element ->
-            element.flattenTo(addTo)
+        // Collection - flatten each element
+        static void flattenTo(Collection elements, Set addTo) {
+            elements.each {element ->
+                element.flattenTo(addTo)
+            }
         }
-    }
-}
 
-class NoFlattenArrayListCategory {
-    // Object - put to result set
+        // Map - flatten each value
+        static void flattenTo(Map elements, Set addTo) {
+            elements.values().flattenTo(addTo)
+        }
 
-    static void flattenTo(ArrayList element, Set addTo) {
-        addTo << element
+        // Array - flatten each element
+        static void flattenTo(Object[] elements, Set addTo) {
+            elements.each {element ->
+                element.flattenTo(addTo)
+            }
+        }
     }
-}
 
-class ConcurrentQueue {
-    static {
-        ConcurrentQueue.metaClass.mixin LinkedList, ReentrantLock
-    }
+    static class NoFlattenArrayListCategory {
+        // Object - put to result set
 
-    def get() {
-        withLock {
-            removeFirst()
+        static void flattenTo(ArrayList element, Set addTo) {
+            addTo << element
         }
     }
 
-    void put(def obj) {
-        withLock {
-            addLast(obj)
+    static class ConcurrentQueue {
+        static {
+            ConcurrentQueue.metaClass.mixin LinkedList, ReentrantLock
         }
-    }
-}
 
-class NoDuplicateCollection {
-    void put(def obj) {
-        def clone = find {
-            it == obj
+        def get() {
+            withLock {
+                removeFirst()
+            }
         }
 
-        if (!clone)
-            add obj
+        void put(def obj) {
+            withLock {
+                addLast(obj)
+            }
+        }
     }
-}
 
-class WPM_A {
+    static class NoDuplicateCollection {
+        void put(def obj) {
+            def clone = find {
+                it == obj
+            }
 
-    final foo() {
-        bar()
+            if (!clone)
+                add obj
+        }
     }
 
-    private final String bar() { return "Bar" }
-}
+    static class WPM_A {
+        final foo() {
+            bar()
+        }
 
-class WPM_B extends WPM_A {
-    def foobar() {
-        super.foo()
+        private final String bar() {
+            return 'Bar'
+        }
     }
-}
 
-class WPM_C {}
+    static class WPM_B extends WPM_A {
+        def foobar() {
+            super.foo()
+        }
+    }
 
-class Overflow_A {
-    public void foo() {
-        println 'Original foo ' + receive('')
+    static class WPM_C {
     }
 
-    protected Object receive() {
-        return "Message"
+    static class Overflow_A {
+        public void foo() {
+            println 'Original foo ' + receive('')
+        }
+
+        protected Object receive() {
+            return 'Message'
+        }
+
+        protected Object receive(param) {
+            receive() + param
+        }
     }
 
-    protected Object receive(Object param) {
-        receive() + param
+    static class Overflow_B {
     }
 }
-
-class Overflow_B {}
-

Reply via email to