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

paulk-asert 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 293ebc5000 GROOVY-12017: Tuple equals should follow Groovy's List 
equals semantics (cont'd)
293ebc5000 is described below

commit 293ebc5000f8875fb61cefa4ea65e6602a5cdf1c
Author: Paul King <[email protected]>
AuthorDate: Mon May 18 14:53:07 2026 +1000

    GROOVY-12017: Tuple equals should follow Groovy's List equals semantics 
(cont'd)
---
 .../groovy/runtime/DefaultGroovyMethods.java       |  8 +++++-
 src/test/groovy/groovy/lang/TupleTest.java         | 31 ++++++++++++++++++++++
 2 files changed, 38 insertions(+), 1 deletion(-)

diff --git 
a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java 
b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
index 2de1cca996..5817e8b0dd 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -5141,7 +5141,13 @@ public class DefaultGroovyMethods extends 
DefaultGroovyMethodsSupport {
     }
 
     static boolean coercedEquals(Object o1, Object o2) {
-        if (o1 instanceof Comparable) {
+        // An object that is both Comparable and a List (e.g. Tuple) must be
+        // compared as a List here, consistent with how a plain 
(non-Comparable)
+        // List is compared by DefaultTypeTransformation.compareEqual, rather
+        // than taking the Comparable/numberAwareCompareTo short-circuit below;
+        // otherwise nested Tuple/List equality (element-wise list equals)
+        // would be inconsistent with `==` and with Tuple.equals(List).
+        if (o1 instanceof Comparable && !(o1 instanceof List && o2 instanceof 
List)) {
             if (!(o2 instanceof Comparable && 
numberAwareCompareTo((Comparable) o1, (Comparable) o2) == 0)) {
                 return false;
             }
diff --git a/src/test/groovy/groovy/lang/TupleTest.java 
b/src/test/groovy/groovy/lang/TupleTest.java
index 12d39a3d13..f87d88301c 100644
--- a/src/test/groovy/groovy/lang/TupleTest.java
+++ b/src/test/groovy/groovy/lang/TupleTest.java
@@ -330,4 +330,35 @@ public class TupleTest extends GroovyTestCase {
         assertNotEquals(t, Arrays.asList(1, 2, 3), "size mismatch");
         assertNotEquals(t, Arrays.asList(1, 9), "content mismatch");
     }
+
+    // Nested Tuple/List equality: the element comparator (coercedEquals) used
+    // by list/set/map equals and unique() must treat a Tuple like a List,
+    // consistent with == / compareEqual / Tuple.equals(List).
+    public void testNestedTupleEquality() throws Exception {
+        assertEquals("[Tuple2].equals([[..]])",
+                Arrays.asList(new Tuple2<>(1, 2), new Tuple2<>(2, 3)),
+                Arrays.asList(Arrays.asList(1, 2), Arrays.asList(2, 3)));
+        assertEquals("[[..]].equals([Tuple2])",
+                Arrays.asList(Arrays.asList(1, 2), Arrays.asList(2, 3)),
+                Arrays.asList(new Tuple2<>(1, 2), new Tuple2<>(2, 3)));
+
+        // Groovy == nested, both operand orders, number-aware
+        assertScript(
+                "assert [new Tuple2(1, 2), new Tuple2(2, 3)] == [[1, 2], [2, 
3]]\n" +
+                "assert [[1, 2], [2, 3]] == [new Tuple2(1, 2), new Tuple2(2, 
3)]\n" +
+                "assert [new Tuple2(1, 2)] == [[1L, 2L]]\n" +
+                "assert new Tuple2(1, 2) == new Tuple2(1L, 2L)\n" +
+                "assert [new Tuple2(1, 2)] == [new Tuple2(1L, 2L)]\n");
+
+        // number-heterogeneous tuple .equals() (previously risked CCE via 
compareTo)
+        assertEquals("Tuple2(1,2).equals(Tuple2(1L,2L))",
+                new Tuple2<>(1, 2), new Tuple2<>(1L, 2L));
+
+        // unique()/toUnique() must dedupe a Tuple and its equivalent List
+        assertScript(
+                "assert [new Tuple2(1, 2), [1, 2]].unique().size() == 1\n" +
+                "assert [new Tuple2(1, 2), new Tuple2(1, 2)].unique().size() 
== 1\n" +
+                "assert [[1, 2], [1, 2]].unique().size() == 1\n" +
+                "assert [new Tuple2(1, 2), new Tuple2(3, 4)].unique().size() 
== 2\n");
+    }
 }

Reply via email to