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

sunlan 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 aa72d4fc16 Add tests to increase code coverage: `ChrTest`, 
`ExceptionsTest`, etc.
aa72d4fc16 is described below

commit aa72d4fc162cea141a57c8c2a3d60b6da235cc61
Author: Daniel Sun <[email protected]>
AuthorDate: Sun Feb 1 20:58:16 2026 +0900

    Add tests to increase code coverage: `ChrTest`, `ExceptionsTest`, etc.
---
 .../concurrentlinkedhashmap/LinkedDequeTest.java   | 624 +++++++++++++++++++++
 .../groovy/runtime/StackTraceUtilsJUnit5Test.java  | 225 ++++++++
 .../groovy/util/HashCodeHelperJUnit5Test.java      | 393 +++++++++++++
 .../org/apache/groovy/json/internal/ChrTest.java   | 347 ++++++++++++
 .../groovy/json/internal/ExceptionsTest.java       | 318 +++++++++++
 .../groovy/json/internal/ValueMapImplTest.java     | 278 +++++++++
 6 files changed, 2185 insertions(+)

diff --git 
a/src/test/java/org/apache/groovy/util/concurrent/concurrentlinkedhashmap/LinkedDequeTest.java
 
b/src/test/java/org/apache/groovy/util/concurrent/concurrentlinkedhashmap/LinkedDequeTest.java
new file mode 100644
index 0000000000..cabdcddc65
--- /dev/null
+++ 
b/src/test/java/org/apache/groovy/util/concurrent/concurrentlinkedhashmap/LinkedDequeTest.java
@@ -0,0 +1,624 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.util.concurrent.concurrentlinkedhashmap;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * JUnit 5 tests for LinkedDeque class.
+ */
+class LinkedDequeTest {
+
+    private LinkedDeque<TestNode> deque;
+
+    @BeforeEach
+    void setUp() {
+        deque = new LinkedDeque<>();
+    }
+
+    // Test node implementation
+    static class TestNode implements Linked<TestNode> {
+        private TestNode prev;
+        private TestNode next;
+        private final String value;
+
+        TestNode(String value) {
+            this.value = value;
+        }
+
+        @Override
+        public TestNode getPrevious() {
+            return prev;
+        }
+
+        @Override
+        public void setPrevious(TestNode prev) {
+            this.prev = prev;
+        }
+
+        @Override
+        public TestNode getNext() {
+            return next;
+        }
+
+        @Override
+        public void setNext(TestNode next) {
+            this.next = next;
+        }
+
+        @Override
+        public String toString() {
+            return value;
+        }
+    }
+
+    @Test
+    void testIsEmptyOnNew() {
+        assertTrue(deque.isEmpty());
+    }
+
+    @Test
+    void testSizeOnEmpty() {
+        assertEquals(0, deque.size());
+    }
+
+    @Test
+    void testAddFirst() {
+        TestNode node = new TestNode("first");
+        deque.addFirst(node);
+        
+        assertFalse(deque.isEmpty());
+        assertEquals(1, deque.size());
+        assertSame(node, deque.peekFirst());
+    }
+
+    @Test
+    void testAddLast() {
+        TestNode node = new TestNode("last");
+        deque.addLast(node);
+        
+        assertFalse(deque.isEmpty());
+        assertEquals(1, deque.size());
+        assertSame(node, deque.peekLast());
+    }
+
+    @Test
+    void testOfferFirst() {
+        TestNode node = new TestNode("offer-first");
+        assertTrue(deque.offerFirst(node));
+        assertSame(node, deque.peekFirst());
+    }
+
+    @Test
+    void testOfferLast() {
+        TestNode node = new TestNode("offer-last");
+        assertTrue(deque.offerLast(node));
+        assertSame(node, deque.peekLast());
+    }
+
+    @Test
+    void testOfferFirstDuplicateReturnsfalse() {
+        TestNode node = new TestNode("dup");
+        assertTrue(deque.offerFirst(node));
+        assertFalse(deque.offerFirst(node)); // Already in deque
+    }
+
+    @Test
+    void testOfferLastDuplicateReturnsFalse() {
+        TestNode node = new TestNode("dup");
+        assertTrue(deque.offerLast(node));
+        assertFalse(deque.offerLast(node)); // Already in deque
+    }
+
+    @Test
+    void testOffer() {
+        TestNode node = new TestNode("offer");
+        assertTrue(deque.offer(node));
+        assertSame(node, deque.peekLast());
+    }
+
+    @Test
+    void testAdd() {
+        TestNode node = new TestNode("add");
+        assertTrue(deque.add(node));
+        assertEquals(1, deque.size());
+    }
+
+    @Test
+    void testPeekOnEmpty() {
+        assertNull(deque.peek());
+    }
+
+    @Test
+    void testPeekFirstOnEmpty() {
+        assertNull(deque.peekFirst());
+    }
+
+    @Test
+    void testPeekLastOnEmpty() {
+        assertNull(deque.peekLast());
+    }
+
+    @Test
+    void testPeek() {
+        TestNode node = new TestNode("peek-test");
+        deque.addFirst(node);
+        assertSame(node, deque.peek());
+    }
+
+    @Test
+    void testGetFirstOnEmpty() {
+        assertThrows(NoSuchElementException.class, () -> deque.getFirst());
+    }
+
+    @Test
+    void testGetLastOnEmpty() {
+        assertThrows(NoSuchElementException.class, () -> deque.getLast());
+    }
+
+    @Test
+    void testElementOnEmpty() {
+        assertThrows(NoSuchElementException.class, () -> deque.element());
+    }
+
+    @Test
+    void testGetFirst() {
+        TestNode node = new TestNode("get-first");
+        deque.addFirst(node);
+        assertSame(node, deque.getFirst());
+    }
+
+    @Test
+    void testGetLast() {
+        TestNode node = new TestNode("get-last");
+        deque.addLast(node);
+        assertSame(node, deque.getLast());
+    }
+
+    @Test
+    void testElement() {
+        TestNode node = new TestNode("element");
+        deque.addFirst(node);
+        assertSame(node, deque.element());
+    }
+
+    @Test
+    void testPollOnEmpty() {
+        assertNull(deque.poll());
+    }
+
+    @Test
+    void testPollFirstOnEmpty() {
+        assertNull(deque.pollFirst());
+    }
+
+    @Test
+    void testPollLastOnEmpty() {
+        assertNull(deque.pollLast());
+    }
+
+    @Test
+    void testPoll() {
+        TestNode node = new TestNode("poll");
+        deque.addFirst(node);
+        assertSame(node, deque.poll());
+        assertTrue(deque.isEmpty());
+    }
+
+    @Test
+    void testPollFirst() {
+        TestNode first = new TestNode("first");
+        TestNode second = new TestNode("second");
+        deque.addLast(first);
+        deque.addLast(second);
+        
+        assertSame(first, deque.pollFirst());
+        assertEquals(1, deque.size());
+        assertSame(second, deque.peekFirst());
+    }
+
+    @Test
+    void testPollLast() {
+        TestNode first = new TestNode("first");
+        TestNode second = new TestNode("second");
+        deque.addLast(first);
+        deque.addLast(second);
+        
+        assertSame(second, deque.pollLast());
+        assertEquals(1, deque.size());
+        assertSame(first, deque.peekLast());
+    }
+
+    @Test
+    void testRemoveOnEmpty() {
+        assertThrows(NoSuchElementException.class, () -> deque.remove());
+    }
+
+    @Test
+    void testRemoveFirstOnEmpty() {
+        assertThrows(NoSuchElementException.class, () -> deque.removeFirst());
+    }
+
+    @Test
+    void testRemoveLastOnEmpty() {
+        assertThrows(NoSuchElementException.class, () -> deque.removeLast());
+    }
+
+    @Test
+    void testRemove() {
+        TestNode node = new TestNode("remove");
+        deque.addFirst(node);
+        assertSame(node, deque.remove());
+        assertTrue(deque.isEmpty());
+    }
+
+    @Test
+    void testRemoveFirst() {
+        TestNode first = new TestNode("first");
+        TestNode second = new TestNode("second");
+        deque.addLast(first);
+        deque.addLast(second);
+        
+        assertSame(first, deque.removeFirst());
+        assertEquals(1, deque.size());
+    }
+
+    @Test
+    void testRemoveLast() {
+        TestNode first = new TestNode("first");
+        TestNode second = new TestNode("second");
+        deque.addLast(first);
+        deque.addLast(second);
+        
+        assertSame(second, deque.removeLast());
+        assertEquals(1, deque.size());
+    }
+
+    @Test
+    void testRemoveObject() {
+        TestNode node = new TestNode("to-remove");
+        deque.addFirst(node);
+        
+        assertTrue(deque.remove(node));
+        assertTrue(deque.isEmpty());
+    }
+
+    @Test
+    void testRemoveObjectNotFound() {
+        TestNode node1 = new TestNode("in-deque");
+        TestNode node2 = new TestNode("not-in-deque");
+        deque.addFirst(node1);
+        
+        assertFalse(deque.remove(node2));
+        assertEquals(1, deque.size());
+    }
+
+    @Test
+    void testRemoveNonLinked() {
+        assertFalse(deque.remove("not a linked object"));
+    }
+
+    @Test
+    void testContains() {
+        TestNode node = new TestNode("contained");
+        deque.addFirst(node);
+        
+        assertTrue(deque.contains(node));
+    }
+
+    @Test
+    void testContainsNotFound() {
+        TestNode node1 = new TestNode("in-deque");
+        TestNode node2 = new TestNode("not-in-deque");
+        deque.addFirst(node1);
+        
+        assertFalse(deque.contains(node2));
+    }
+
+    @Test
+    void testContainsNonLinked() {
+        assertFalse(deque.contains("not a linked object"));
+    }
+
+    @Test
+    void testClear() {
+        deque.addLast(new TestNode("a"));
+        deque.addLast(new TestNode("b"));
+        deque.addLast(new TestNode("c"));
+        
+        assertEquals(3, deque.size());
+        
+        deque.clear();
+        
+        assertTrue(deque.isEmpty());
+        assertEquals(0, deque.size());
+    }
+
+    @Test
+    void testPush() {
+        TestNode node = new TestNode("pushed");
+        deque.push(node);
+        
+        assertSame(node, deque.peekFirst());
+    }
+
+    @Test
+    void testPop() {
+        TestNode node = new TestNode("popped");
+        deque.push(node);
+        
+        assertSame(node, deque.pop());
+        assertTrue(deque.isEmpty());
+    }
+
+    @Test
+    void testPopOnEmpty() {
+        assertThrows(NoSuchElementException.class, () -> deque.pop());
+    }
+
+    @Test
+    void testIterator() {
+        TestNode a = new TestNode("a");
+        TestNode b = new TestNode("b");
+        TestNode c = new TestNode("c");
+        
+        deque.addLast(a);
+        deque.addLast(b);
+        deque.addLast(c);
+        
+        List<String> values = new ArrayList<>();
+        for (TestNode node : deque) {
+            values.add(node.value);
+        }
+        
+        assertEquals(Arrays.asList("a", "b", "c"), values);
+    }
+
+    @Test
+    void testIteratorHasNextOnEmpty() {
+        Iterator<TestNode> it = deque.iterator();
+        assertFalse(it.hasNext());
+    }
+
+    @Test
+    void testIteratorNextOnEmpty() {
+        Iterator<TestNode> it = deque.iterator();
+        assertThrows(NoSuchElementException.class, it::next);
+    }
+
+    @Test
+    void testIteratorRemoveUnsupported() {
+        TestNode node = new TestNode("test");
+        deque.addFirst(node);
+        
+        Iterator<TestNode> it = deque.iterator();
+        it.next();
+        
+        assertThrows(UnsupportedOperationException.class, it::remove);
+    }
+
+    @Test
+    void testDescendingIterator() {
+        TestNode a = new TestNode("a");
+        TestNode b = new TestNode("b");
+        TestNode c = new TestNode("c");
+        
+        deque.addLast(a);
+        deque.addLast(b);
+        deque.addLast(c);
+        
+        List<String> values = new ArrayList<>();
+        Iterator<TestNode> it = deque.descendingIterator();
+        while (it.hasNext()) {
+            values.add(it.next().value);
+        }
+        
+        assertEquals(Arrays.asList("c", "b", "a"), values);
+    }
+
+    @Test
+    void testDescendingIteratorOnEmpty() {
+        Iterator<TestNode> it = deque.descendingIterator();
+        assertFalse(it.hasNext());
+    }
+
+    @Test
+    void testRemoveFirstOccurrence() {
+        TestNode node = new TestNode("target");
+        deque.addFirst(node);
+        
+        assertTrue(deque.removeFirstOccurrence(node));
+        assertTrue(deque.isEmpty());
+    }
+
+    @Test
+    void testRemoveFirstOccurrenceNotFound() {
+        TestNode node1 = new TestNode("in");
+        TestNode node2 = new TestNode("out");
+        deque.addFirst(node1);
+        
+        assertFalse(deque.removeFirstOccurrence(node2));
+    }
+
+    @Test
+    void testRemoveLastOccurrence() {
+        TestNode node = new TestNode("target");
+        deque.addLast(node);
+        
+        assertTrue(deque.removeLastOccurrence(node));
+        assertTrue(deque.isEmpty());
+    }
+
+    @Test
+    void testRemoveAll() {
+        TestNode a = new TestNode("a");
+        TestNode b = new TestNode("b");
+        TestNode c = new TestNode("c");
+        
+        deque.addLast(a);
+        deque.addLast(b);
+        deque.addLast(c);
+        
+        assertTrue(deque.removeAll(Arrays.asList(a, c)));
+        assertEquals(1, deque.size());
+        assertSame(b, deque.peekFirst());
+    }
+
+    @Test
+    void testRemoveAllEmpty() {
+        assertFalse(deque.removeAll(Arrays.asList()));
+    }
+
+    @Test
+    void testMoveToFront() {
+        TestNode a = new TestNode("a");
+        TestNode b = new TestNode("b");
+        TestNode c = new TestNode("c");
+        
+        deque.addLast(a);
+        deque.addLast(b);
+        deque.addLast(c);
+        
+        deque.moveToFront(c);
+        
+        assertSame(c, deque.peekFirst());
+        assertSame(b, deque.peekLast());
+    }
+
+    @Test
+    void testMoveToFrontAlreadyFirst() {
+        TestNode a = new TestNode("a");
+        TestNode b = new TestNode("b");
+        
+        deque.addLast(a);
+        deque.addLast(b);
+        
+        deque.moveToFront(a);
+        
+        assertSame(a, deque.peekFirst());
+        assertSame(b, deque.peekLast());
+    }
+
+    @Test
+    void testMoveToBack() {
+        TestNode a = new TestNode("a");
+        TestNode b = new TestNode("b");
+        TestNode c = new TestNode("c");
+        
+        deque.addLast(a);
+        deque.addLast(b);
+        deque.addLast(c);
+        
+        deque.moveToBack(a);
+        
+        assertSame(b, deque.peekFirst());
+        assertSame(a, deque.peekLast());
+    }
+
+    @Test
+    void testMoveToBackAlreadyLast() {
+        TestNode a = new TestNode("a");
+        TestNode b = new TestNode("b");
+        
+        deque.addLast(a);
+        deque.addLast(b);
+        
+        deque.moveToBack(b);
+        
+        assertSame(a, deque.peekFirst());
+        assertSame(b, deque.peekLast());
+    }
+
+    @Test
+    void testMultipleAddRemove() {
+        TestNode a = new TestNode("a");
+        TestNode b = new TestNode("b");
+        TestNode c = new TestNode("c");
+        
+        deque.addLast(a);
+        deque.addLast(b);
+        deque.addLast(c);
+        
+        assertEquals(3, deque.size());
+        
+        deque.removeFirst();
+        assertEquals(2, deque.size());
+        assertSame(b, deque.peekFirst());
+        
+        deque.removeLast();
+        assertEquals(1, deque.size());
+        assertSame(b, deque.peekFirst());
+        assertSame(b, deque.peekLast());
+        
+        deque.remove(b);
+        assertTrue(deque.isEmpty());
+    }
+
+    @Test
+    void testAddFirstDuplicateThrows() {
+        TestNode node = new TestNode("dup");
+        deque.addFirst(node);
+        
+        assertThrows(IllegalArgumentException.class, () -> 
deque.addFirst(node));
+    }
+
+    @Test
+    void testAddLastDuplicateThrows() {
+        TestNode node = new TestNode("dup");
+        deque.addLast(node);
+        
+        assertThrows(IllegalArgumentException.class, () -> 
deque.addLast(node));
+    }
+
+    @Test
+    void testSingleElement() {
+        TestNode single = new TestNode("single");
+        deque.addFirst(single);
+        
+        assertSame(single, deque.peekFirst());
+        assertSame(single, deque.peekLast());
+        assertSame(deque.removeFirst(), deque.peekFirst() == null ? single : 
null);
+    }
+
+    @Test
+    void testRemoveMiddleElement() {
+        TestNode a = new TestNode("a");
+        TestNode b = new TestNode("b");
+        TestNode c = new TestNode("c");
+        
+        deque.addLast(a);
+        deque.addLast(b);
+        deque.addLast(c);
+        
+        assertTrue(deque.remove(b));
+        
+        assertEquals(2, deque.size());
+        assertSame(a, deque.peekFirst());
+        assertSame(c, deque.peekLast());
+    }
+}
diff --git 
a/src/test/java/org/codehaus/groovy/runtime/StackTraceUtilsJUnit5Test.java 
b/src/test/java/org/codehaus/groovy/runtime/StackTraceUtilsJUnit5Test.java
new file mode 100644
index 0000000000..d144339591
--- /dev/null
+++ b/src/test/java/org/codehaus/groovy/runtime/StackTraceUtilsJUnit5Test.java
@@ -0,0 +1,225 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.Closure;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * JUnit 5 tests for StackTraceUtils class.
+ */
+class StackTraceUtilsJUnit5Test {
+
+    @AfterEach
+    void tearDown() {
+        // Clear any custom class tests that may have been added
+        // Note: There's no public method to clear tests, so we rely on test 
isolation
+    }
+
+    @Test
+    void testIsApplicationClassWithApplicationClass() {
+        // A class that doesn't start with groovy internal packages
+        assertTrue(StackTraceUtils.isApplicationClass("com.example.MyClass"));
+        
assertTrue(StackTraceUtils.isApplicationClass("org.example.SomeClass"));
+        assertTrue(StackTraceUtils.isApplicationClass("MyClass"));
+    }
+
+    @Test
+    void testIsApplicationClassWithGroovyInternalClasses() {
+        // Classes that start with groovy internal packages should not be 
application classes
+        
assertFalse(StackTraceUtils.isApplicationClass("groovy.lang.GroovyObject"));
+        
assertFalse(StackTraceUtils.isApplicationClass("org.codehaus.groovy.runtime.DefaultGroovyMethods"));
+        assertFalse(StackTraceUtils.isApplicationClass("java.lang.String"));
+        assertFalse(StackTraceUtils.isApplicationClass("javax.swing.JFrame"));
+        assertFalse(StackTraceUtils.isApplicationClass("sun.misc.Unsafe"));
+        assertFalse(StackTraceUtils.isApplicationClass("com.sun.proxy.Proxy"));
+        
assertFalse(StackTraceUtils.isApplicationClass("org.apache.groovy.util.Something"));
+        
assertFalse(StackTraceUtils.isApplicationClass("jdk.internal.misc.Unsafe"));
+    }
+
+    @Test
+    void testAddClassTest() {
+        // Add a custom test that returns true for a specific class
+        StackTraceUtils.addClassTest(new Closure<Boolean>(null) {
+            @Override
+            public Boolean call(Object... args) {
+                String className = (String) args[0];
+                if (className.equals("my.custom.TestClass")) {
+                    return true;
+                }
+                return null; // continue with other tests
+            }
+        });
+
+        // The custom class should now be considered an application class
+        assertTrue(StackTraceUtils.isApplicationClass("my.custom.TestClass"));
+    }
+
+    @Test
+    void testAddClassTestReturningFalse() {
+        // Add a custom test that returns false for a specific class
+        StackTraceUtils.addClassTest(new Closure<Boolean>(null) {
+            @Override
+            public Boolean call(Object... args) {
+                String className = (String) args[0];
+                if (className.startsWith("force.exclude.")) {
+                    return false;
+                }
+                return null;
+            }
+        });
+
+        // The class should be excluded
+        
assertFalse(StackTraceUtils.isApplicationClass("force.exclude.SomeClass"));
+    }
+
+    @Test
+    void testExtractRootCause() {
+        Exception root = new RuntimeException("root cause");
+        Exception middle = new Exception("middle", root);
+        Exception top = new Exception("top", middle);
+
+        Throwable extracted = StackTraceUtils.extractRootCause(top);
+        assertSame(root, extracted);
+        assertEquals("root cause", extracted.getMessage());
+    }
+
+    @Test
+    void testExtractRootCauseWithNoCause() {
+        Exception single = new RuntimeException("single exception");
+        Throwable extracted = StackTraceUtils.extractRootCause(single);
+        assertSame(single, extracted);
+    }
+
+    @Test
+    void testExtractRootCauseWithDeepNesting() {
+        Exception e1 = new RuntimeException("level 1");
+        Exception e2 = new Exception("level 2", e1);
+        Exception e3 = new Exception("level 3", e2);
+        Exception e4 = new Exception("level 4", e3);
+        Exception e5 = new Exception("level 5", e4);
+
+        Throwable extracted = StackTraceUtils.extractRootCause(e5);
+        assertSame(e1, extracted);
+    }
+
+    @Test
+    void testSanitize() {
+        // Create an exception with a stack trace
+        Exception ex = new RuntimeException("test exception");
+
+        // Sanitize it
+        Throwable sanitized = StackTraceUtils.sanitize(ex);
+
+        // Should return the same exception instance
+        assertSame(ex, sanitized);
+
+        // The stack trace should be modified (groovy internal classes removed)
+        // Note: The actual filtering depends on system property 
groovy.full.stacktrace
+        assertNotNull(sanitized.getStackTrace());
+    }
+
+    @Test
+    void testSanitizeRootCause() {
+        Exception root = new RuntimeException("root");
+        Exception wrapper = new Exception("wrapper", root);
+
+        Throwable sanitizedRoot = StackTraceUtils.sanitizeRootCause(wrapper);
+
+        // Should return the sanitized root cause
+        assertEquals("root", sanitizedRoot.getMessage());
+    }
+
+    @Test
+    void testDeepSanitize() {
+        Exception root = new RuntimeException("root");
+        Exception middle = new Exception("middle", root);
+        Exception top = new Exception("top", middle);
+
+        Throwable result = StackTraceUtils.deepSanitize(top);
+
+        // Should return the top exception (sanitized)
+        assertSame(top, result);
+        assertNotNull(result.getStackTrace());
+    }
+
+    @Test
+    void testDeepSanitizeWithSingleException() {
+        Exception single = new RuntimeException("single");
+
+        Throwable result = StackTraceUtils.deepSanitize(single);
+
+        assertSame(single, result);
+    }
+
+    @Test
+    void testPrintSanitizedStackTraceWithPrintWriter() {
+        Exception ex = new RuntimeException("test");
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+
+        StackTraceUtils.printSanitizedStackTrace(ex, pw);
+        pw.flush();
+
+        String output = sw.toString();
+        // Should contain "at" entries for the stack trace
+        assertTrue(output.contains("at ") || output.isEmpty());
+    }
+
+    @Test
+    void testStackLogName() {
+        assertEquals("StackTrace", StackTraceUtils.STACK_LOG_NAME);
+    }
+
+    @Test
+    void testIsApplicationClassWithGjdkClasses() {
+        
assertFalse(StackTraceUtils.isApplicationClass("gjdk.groovy.lang.SomeThing"));
+    }
+
+    @Test
+    void testIsApplicationClassWithGroovyJarJarClasses() {
+        
assertFalse(StackTraceUtils.isApplicationClass("groovyjarjar.asm.ClassVisitor"));
+    }
+
+    @Test
+    void testSanitizePreservesMessage() {
+        String message = "Original error message";
+        Exception ex = new RuntimeException(message);
+
+        Throwable sanitized = StackTraceUtils.sanitize(ex);
+
+        assertEquals(message, sanitized.getMessage());
+    }
+
+    @Test
+    void testSanitizePreservesCause() {
+        Exception cause = new IllegalArgumentException("cause");
+        Exception ex = new RuntimeException("wrapper", cause);
+
+        Throwable sanitized = StackTraceUtils.sanitize(ex);
+
+        assertSame(cause, sanitized.getCause());
+    }
+}
diff --git 
a/src/test/java/org/codehaus/groovy/util/HashCodeHelperJUnit5Test.java 
b/src/test/java/org/codehaus/groovy/util/HashCodeHelperJUnit5Test.java
new file mode 100644
index 0000000000..5e66e91f7a
--- /dev/null
+++ b/src/test/java/org/codehaus/groovy/util/HashCodeHelperJUnit5Test.java
@@ -0,0 +1,393 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.util;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * JUnit 5 tests for HashCodeHelper class.
+ */
+class HashCodeHelperJUnit5Test {
+
+    @Test
+    void testInitHash() {
+        int hash = HashCodeHelper.initHash();
+        // The initial hash should be the SEED value (127)
+        assertEquals(127, hash);
+    }
+
+    @Test
+    void testUpdateHashWithBoolean() {
+        int hash = HashCodeHelper.initHash();
+        
+        int hashTrue = HashCodeHelper.updateHash(hash, true);
+        int hashFalse = HashCodeHelper.updateHash(hash, false);
+        
+        // true and false should produce different hashes
+        assertNotEquals(hashTrue, hashFalse);
+        
+        // Same value should produce same hash
+        assertEquals(hashTrue, HashCodeHelper.updateHash(hash, true));
+        assertEquals(hashFalse, HashCodeHelper.updateHash(hash, false));
+    }
+
+    @Test
+    void testUpdateHashWithChar() {
+        int hash = HashCodeHelper.initHash();
+        
+        int hashA = HashCodeHelper.updateHash(hash, 'a');
+        int hashB = HashCodeHelper.updateHash(hash, 'b');
+        
+        assertNotEquals(hashA, hashB);
+        assertEquals(hashA, HashCodeHelper.updateHash(hash, 'a'));
+    }
+
+    @Test
+    void testUpdateHashWithCharacter() {
+        int hash = HashCodeHelper.initHash();
+        
+        int hashChar = HashCodeHelper.updateHash(hash, Character.valueOf('x'));
+        int hashNull = HashCodeHelper.updateHash(hash, (Character) null);
+        
+        assertNotEquals(hashChar, hashNull);
+        // Null should be treated as 0
+        assertEquals(HashCodeHelper.updateHash(hash, (char) 0), hashNull);
+    }
+
+    @Test
+    void testUpdateHashWithInt() {
+        int hash = HashCodeHelper.initHash();
+        
+        int hash1 = HashCodeHelper.updateHash(hash, 42);
+        int hash2 = HashCodeHelper.updateHash(hash, 43);
+        
+        assertNotEquals(hash1, hash2);
+        assertEquals(hash1, HashCodeHelper.updateHash(hash, 42));
+    }
+
+    @Test
+    void testUpdateHashWithInteger() {
+        int hash = HashCodeHelper.initHash();
+        
+        int hashInt = HashCodeHelper.updateHash(hash, Integer.valueOf(100));
+        int hashNull = HashCodeHelper.updateHash(hash, (Integer) null);
+        
+        assertNotEquals(hashInt, hashNull);
+        assertEquals(HashCodeHelper.updateHash(hash, 0), hashNull);
+    }
+
+    @Test
+    void testUpdateHashWithLong() {
+        int hash = HashCodeHelper.initHash();
+        
+        int hash1 = HashCodeHelper.updateHash(hash, 123456789L);
+        int hash2 = HashCodeHelper.updateHash(hash, 987654321L);
+        
+        assertNotEquals(hash1, hash2);
+        assertEquals(hash1, HashCodeHelper.updateHash(hash, 123456789L));
+    }
+
+    @Test
+    void testUpdateHashWithLongObject() {
+        int hash = HashCodeHelper.initHash();
+        
+        int hashLong = HashCodeHelper.updateHash(hash, Long.valueOf(999L));
+        int hashNull = HashCodeHelper.updateHash(hash, (Long) null);
+        
+        assertNotEquals(hashLong, hashNull);
+        assertEquals(HashCodeHelper.updateHash(hash, 0L), hashNull);
+    }
+
+    @Test
+    void testUpdateHashWithFloat() {
+        int hash = HashCodeHelper.initHash();
+        
+        int hash1 = HashCodeHelper.updateHash(hash, 3.14f);
+        int hash2 = HashCodeHelper.updateHash(hash, 2.71f);
+        
+        assertNotEquals(hash1, hash2);
+        assertEquals(hash1, HashCodeHelper.updateHash(hash, 3.14f));
+    }
+
+    @Test
+    void testUpdateHashWithFloatObject() {
+        int hash = HashCodeHelper.initHash();
+        
+        int hashFloat = HashCodeHelper.updateHash(hash, Float.valueOf(1.5f));
+        int hashNull = HashCodeHelper.updateHash(hash, (Float) null);
+        
+        assertNotEquals(hashFloat, hashNull);
+        assertEquals(HashCodeHelper.updateHash(hash, 0f), hashNull);
+    }
+
+    @Test
+    void testUpdateHashWithDouble() {
+        int hash = HashCodeHelper.initHash();
+        
+        int hash1 = HashCodeHelper.updateHash(hash, 3.14159265);
+        int hash2 = HashCodeHelper.updateHash(hash, 2.71828182);
+        
+        assertNotEquals(hash1, hash2);
+        assertEquals(hash1, HashCodeHelper.updateHash(hash, 3.14159265));
+    }
+
+    @Test
+    void testUpdateHashWithDoubleObject() {
+        int hash = HashCodeHelper.initHash();
+        
+        int hashDouble = HashCodeHelper.updateHash(hash, 
Double.valueOf(99.99));
+        int hashNull = HashCodeHelper.updateHash(hash, (Double) null);
+        
+        assertNotEquals(hashDouble, hashNull);
+        assertEquals(HashCodeHelper.updateHash(hash, 0d), hashNull);
+    }
+
+    @Test
+    void testUpdateHashWithObject() {
+        int hash = HashCodeHelper.initHash();
+        
+        int hashStr1 = HashCodeHelper.updateHash(hash, "hello");
+        int hashStr2 = HashCodeHelper.updateHash(hash, "world");
+        int hashNull = HashCodeHelper.updateHash(hash, (Object) null);
+        
+        assertNotEquals(hashStr1, hashStr2);
+        assertNotEquals(hashStr1, hashNull);
+    }
+
+    @Test
+    void testUpdateHashWithObjectArray() {
+        int hash = HashCodeHelper.initHash();
+        
+        Object[] arr1 = {"a", "b", "c"};
+        Object[] arr2 = {"x", "y", "z"};
+        
+        int hashArr1 = HashCodeHelper.updateHash(hash, (Object) arr1);
+        int hashArr2 = HashCodeHelper.updateHash(hash, (Object) arr2);
+        
+        assertNotEquals(hashArr1, hashArr2);
+    }
+
+    @Test
+    void testUpdateHashWithBooleanArray() {
+        int hash = HashCodeHelper.initHash();
+        
+        boolean[] arr1 = {true, false, true};
+        boolean[] arr2 = {false, true, false};
+        boolean[] nullArr = null;
+        
+        int hashArr1 = HashCodeHelper.updateHash(hash, arr1);
+        int hashArr2 = HashCodeHelper.updateHash(hash, arr2);
+        int hashNull = HashCodeHelper.updateHash(hash, nullArr);
+        
+        assertNotEquals(hashArr1, hashArr2);
+        assertNotEquals(hashArr1, hashNull);
+    }
+
+    @Test
+    void testUpdateHashWithCharArray() {
+        int hash = HashCodeHelper.initHash();
+        
+        char[] arr1 = {'a', 'b', 'c'};
+        char[] arr2 = {'x', 'y', 'z'};
+        char[] nullArr = null;
+        
+        int hashArr1 = HashCodeHelper.updateHash(hash, arr1);
+        int hashArr2 = HashCodeHelper.updateHash(hash, arr2);
+        int hashNull = HashCodeHelper.updateHash(hash, nullArr);
+        
+        assertNotEquals(hashArr1, hashArr2);
+        assertNotEquals(hashArr1, hashNull);
+    }
+
+    @Test
+    void testUpdateHashWithByteArray() {
+        int hash = HashCodeHelper.initHash();
+        
+        byte[] arr1 = {1, 2, 3};
+        byte[] arr2 = {4, 5, 6};
+        byte[] nullArr = null;
+        
+        int hashArr1 = HashCodeHelper.updateHash(hash, arr1);
+        int hashArr2 = HashCodeHelper.updateHash(hash, arr2);
+        int hashNull = HashCodeHelper.updateHash(hash, nullArr);
+        
+        assertNotEquals(hashArr1, hashArr2);
+        assertNotEquals(hashArr1, hashNull);
+    }
+
+    @Test
+    void testUpdateHashWithShortArray() {
+        int hash = HashCodeHelper.initHash();
+        
+        short[] arr1 = {100, 200, 300};
+        short[] arr2 = {400, 500, 600};
+        short[] nullArr = null;
+        
+        int hashArr1 = HashCodeHelper.updateHash(hash, arr1);
+        int hashArr2 = HashCodeHelper.updateHash(hash, arr2);
+        int hashNull = HashCodeHelper.updateHash(hash, nullArr);
+        
+        assertNotEquals(hashArr1, hashArr2);
+        assertNotEquals(hashArr1, hashNull);
+    }
+
+    @Test
+    void testUpdateHashWithIntArray() {
+        int hash = HashCodeHelper.initHash();
+        
+        int[] arr1 = {1, 2, 3};
+        int[] arr2 = {4, 5, 6};
+        int[] nullArr = null;
+        
+        int hashArr1 = HashCodeHelper.updateHash(hash, arr1);
+        int hashArr2 = HashCodeHelper.updateHash(hash, arr2);
+        int hashNull = HashCodeHelper.updateHash(hash, nullArr);
+        
+        assertNotEquals(hashArr1, hashArr2);
+        assertNotEquals(hashArr1, hashNull);
+    }
+
+    @Test
+    void testUpdateHashWithLongArray() {
+        int hash = HashCodeHelper.initHash();
+        
+        long[] arr1 = {1L, 2L, 3L};
+        long[] arr2 = {4L, 5L, 6L};
+        long[] nullArr = null;
+        
+        int hashArr1 = HashCodeHelper.updateHash(hash, arr1);
+        int hashArr2 = HashCodeHelper.updateHash(hash, arr2);
+        int hashNull = HashCodeHelper.updateHash(hash, nullArr);
+        
+        assertNotEquals(hashArr1, hashArr2);
+        assertNotEquals(hashArr1, hashNull);
+    }
+
+    @Test
+    void testUpdateHashWithFloatArray() {
+        int hash = HashCodeHelper.initHash();
+        
+        float[] arr1 = {1.0f, 2.0f, 3.0f};
+        float[] arr2 = {4.0f, 5.0f, 6.0f};
+        float[] nullArr = null;
+        
+        int hashArr1 = HashCodeHelper.updateHash(hash, arr1);
+        int hashArr2 = HashCodeHelper.updateHash(hash, arr2);
+        int hashNull = HashCodeHelper.updateHash(hash, nullArr);
+        
+        assertNotEquals(hashArr1, hashArr2);
+        assertNotEquals(hashArr1, hashNull);
+    }
+
+    @Test
+    void testUpdateHashWithDoubleArray() {
+        int hash = HashCodeHelper.initHash();
+        
+        double[] arr1 = {1.0, 2.0, 3.0};
+        double[] arr2 = {4.0, 5.0, 6.0};
+        double[] nullArr = null;
+        
+        int hashArr1 = HashCodeHelper.updateHash(hash, arr1);
+        int hashArr2 = HashCodeHelper.updateHash(hash, arr2);
+        int hashNull = HashCodeHelper.updateHash(hash, nullArr);
+        
+        assertNotEquals(hashArr1, hashArr2);
+        assertNotEquals(hashArr1, hashNull);
+    }
+
+    @Test
+    void testChainingHashUpdates() {
+        int hash = HashCodeHelper.initHash();
+        hash = HashCodeHelper.updateHash(hash, 42);
+        hash = HashCodeHelper.updateHash(hash, "test");
+        hash = HashCodeHelper.updateHash(hash, true);
+        hash = HashCodeHelper.updateHash(hash, 3.14);
+        
+        // Verify we get a valid hash value
+        assertNotEquals(0, hash);
+        
+        // Same sequence should produce same result
+        int hash2 = HashCodeHelper.initHash();
+        hash2 = HashCodeHelper.updateHash(hash2, 42);
+        hash2 = HashCodeHelper.updateHash(hash2, "test");
+        hash2 = HashCodeHelper.updateHash(hash2, true);
+        hash2 = HashCodeHelper.updateHash(hash2, 3.14);
+        
+        assertEquals(hash, hash2);
+    }
+
+    @Test
+    void testOrderMatters() {
+        int hash1 = HashCodeHelper.initHash();
+        hash1 = HashCodeHelper.updateHash(hash1, 1);
+        hash1 = HashCodeHelper.updateHash(hash1, 2);
+        
+        int hash2 = HashCodeHelper.initHash();
+        hash2 = HashCodeHelper.updateHash(hash2, 2);
+        hash2 = HashCodeHelper.updateHash(hash2, 1);
+        
+        // Order should matter
+        assertNotEquals(hash1, hash2);
+    }
+
+    @Test
+    void testEmptyArrays() {
+        int hash = HashCodeHelper.initHash();
+        
+        int hashEmptyInt = HashCodeHelper.updateHash(hash, new int[0]);
+        int hashEmptyLong = HashCodeHelper.updateHash(hash, new long[0]);
+        
+        // Empty arrays of same type should produce same hash
+        assertEquals(hashEmptyInt, HashCodeHelper.updateHash(hash, new 
int[0]));
+        
+        // Note: Empty arrays may produce the same hash code due to 
Arrays.hashCode() behavior
+        // Both int[0] and long[0] have hashCode of 1
+        assertEquals(hashEmptyInt, hashEmptyLong);
+    }
+
+    @Test
+    void testSpecialDoubleValues() {
+        int hash = HashCodeHelper.initHash();
+        
+        int hashNaN = HashCodeHelper.updateHash(hash, Double.NaN);
+        int hashPosInf = HashCodeHelper.updateHash(hash, 
Double.POSITIVE_INFINITY);
+        int hashNegInf = HashCodeHelper.updateHash(hash, 
Double.NEGATIVE_INFINITY);
+        
+        // All should be different
+        assertNotEquals(hashNaN, hashPosInf);
+        assertNotEquals(hashNaN, hashNegInf);
+        assertNotEquals(hashPosInf, hashNegInf);
+    }
+
+    @Test
+    void testSpecialFloatValues() {
+        int hash = HashCodeHelper.initHash();
+        
+        int hashNaN = HashCodeHelper.updateHash(hash, Float.NaN);
+        int hashPosInf = HashCodeHelper.updateHash(hash, 
Float.POSITIVE_INFINITY);
+        int hashNegInf = HashCodeHelper.updateHash(hash, 
Float.NEGATIVE_INFINITY);
+        
+        // All should be different
+        assertNotEquals(hashNaN, hashPosInf);
+        assertNotEquals(hashNaN, hashNegInf);
+        assertNotEquals(hashPosInf, hashNegInf);
+    }
+}
diff --git 
a/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/ChrTest.java
 
b/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/ChrTest.java
new file mode 100644
index 0000000000..997be23c76
--- /dev/null
+++ 
b/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/ChrTest.java
@@ -0,0 +1,347 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.json.internal;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * JUnit 5 tests for Chr class (char array utilities).
+ */
+class ChrTest {
+
+    @Test
+    void testArray() {
+        char[] result = Chr.array('a', 'b', 'c');
+        assertArrayEquals(new char[]{'a', 'b', 'c'}, result);
+    }
+
+    @Test
+    void testArrayEmpty() {
+        char[] result = Chr.array();
+        assertEquals(0, result.length);
+    }
+
+    @Test
+    void testArraySingleChar() {
+        char[] result = Chr.array('x');
+        assertArrayEquals(new char[]{'x'}, result);
+    }
+
+    @Test
+    void testChars() {
+        char[] result = Chr.chars("hello");
+        assertArrayEquals(new char[]{'h', 'e', 'l', 'l', 'o'}, result);
+    }
+
+    @Test
+    void testCharsEmpty() {
+        char[] result = Chr.chars("");
+        assertEquals(0, result.length);
+    }
+
+    @Test
+    void testInCharFound() {
+        char[] array = {'a', 'b', 'c', 'd'};
+        assertTrue(Chr.in('b', array));
+        assertTrue(Chr.in('a', array));
+        assertTrue(Chr.in('d', array));
+    }
+
+    @Test
+    void testInCharNotFound() {
+        char[] array = {'a', 'b', 'c'};
+        assertFalse(Chr.in('x', array));
+        assertFalse(Chr.in('z', array));
+    }
+
+    @Test
+    void testInIntFound() {
+        char[] array = {'a', 'b', 'c'};
+        assertTrue(Chr.in((int) 'a', array));
+        assertTrue(Chr.in((int) 'b', array));
+    }
+
+    @Test
+    void testInIntNotFound() {
+        char[] array = {'a', 'b', 'c'};
+        assertFalse(Chr.in((int) 'x', array));
+    }
+
+    @Test
+    void testInWithOffset() {
+        char[] array = {'a', 'b', 'c', 'd', 'e'};
+        // Search from offset 2 (starting at 'c')
+        assertTrue(Chr.in('c', 2, array));
+        assertTrue(Chr.in('d', 2, array));
+        assertTrue(Chr.in('e', 2, array));
+        assertFalse(Chr.in('a', 2, array));
+        assertFalse(Chr.in('b', 2, array));
+    }
+
+    @Test
+    void testInWithOffsetAndEnd() {
+        char[] array = {'a', 'b', 'c', 'd', 'e'};
+        // Search from offset 1 to 3 (searching 'b', 'c')
+        assertTrue(Chr.in('b', 1, 3, array));
+        assertTrue(Chr.in('c', 1, 3, array));
+        assertFalse(Chr.in('a', 1, 3, array));
+        assertFalse(Chr.in('d', 1, 3, array));
+    }
+
+    @Test
+    void testGrowWithSize() {
+        char[] array = {'a', 'b', 'c'};
+        char[] grown = Chr.grow(array, 5);
+        
+        assertEquals(8, grown.length);
+        assertEquals('a', grown[0]);
+        assertEquals('b', grown[1]);
+        assertEquals('c', grown[2]);
+    }
+
+    @Test
+    void testGrowDouble() {
+        char[] array = {'a', 'b', 'c'};
+        char[] grown = Chr.grow(array);
+        
+        assertEquals(6, grown.length);
+        assertEquals('a', grown[0]);
+        assertEquals('b', grown[1]);
+        assertEquals('c', grown[2]);
+    }
+
+    @Test
+    void testCopy() {
+        char[] array = {'h', 'e', 'l', 'l', 'o'};
+        char[] copy = Chr.copy(array);
+        
+        assertArrayEquals(array, copy);
+        assertNotSame(array, copy);
+    }
+
+    @Test
+    void testCopyWithOffsetAndLength() {
+        char[] array = {'h', 'e', 'l', 'l', 'o'};
+        char[] copy = Chr.copy(array, 1, 3);
+        
+        assertArrayEquals(new char[]{'e', 'l', 'l'}, copy);
+    }
+
+    @Test
+    void testAddChar() {
+        char[] array = {'a', 'b'};
+        char[] result = Chr.add(array, 'c');
+        
+        assertArrayEquals(new char[]{'a', 'b', 'c'}, result);
+    }
+
+    @Test
+    void testAddString() {
+        char[] array = {'h', 'i'};
+        char[] result = Chr.add(array, " there");
+        
+        assertArrayEquals(new char[]{'h', 'i', ' ', 't', 'h', 'e', 'r', 'e'}, 
result);
+    }
+
+    @Test
+    void testAddStringBuilder() {
+        char[] array = {'a', 'b'};
+        StringBuilder sb = new StringBuilder("cd");
+        char[] result = Chr.add(array, sb);
+        
+        assertArrayEquals(new char[]{'a', 'b', 'c', 'd'}, result);
+    }
+
+    @Test
+    void testAddTwoArrays() {
+        char[] array1 = {'a', 'b'};
+        char[] array2 = {'c', 'd', 'e'};
+        char[] result = Chr.add(array1, array2);
+        
+        assertArrayEquals(new char[]{'a', 'b', 'c', 'd', 'e'}, result);
+    }
+
+    @Test
+    void testAddMultipleArrays() {
+        char[] arr1 = {'a', 'b'};
+        char[] arr2 = {'c'};
+        char[] arr3 = {'d', 'e', 'f'};
+        
+        char[] result = Chr.add(arr1, arr2, arr3);
+        assertArrayEquals(new char[]{'a', 'b', 'c', 'd', 'e', 'f'}, result);
+    }
+
+    @Test
+    void testAddMultipleArraysWithNull() {
+        char[] arr1 = {'a', 'b'};
+        char[] arr2 = null;
+        char[] arr3 = {'c', 'd'};
+        
+        char[] result = Chr.add(arr1, arr2, arr3);
+        assertArrayEquals(new char[]{'a', 'b', 'c', 'd'}, result);
+    }
+
+    @Test
+    void testLpad() {
+        char[] input = {'1', '2', '3'};
+        char[] result = Chr.lpad(input, 6, '0');
+        
+        assertArrayEquals(new char[]{'0', '0', '0', '1', '2', '3'}, result);
+    }
+
+    @Test
+    void testLpadNoChange() {
+        char[] input = {'1', '2', '3'};
+        char[] result = Chr.lpad(input, 3, '0');
+        
+        assertSame(input, result);
+    }
+
+    @Test
+    void testLpadSmallerSize() {
+        char[] input = {'1', '2', '3'};
+        char[] result = Chr.lpad(input, 2, '0');
+        
+        assertSame(input, result);
+    }
+
+    @Test
+    void testContains() {
+        char[] chars = {'a', 'b', 'c', 'd', 'e'};
+        
+        assertTrue(Chr.contains(chars, 'b', 0, 5));
+        assertTrue(Chr.contains(chars, 'a', 0, 5));
+        assertTrue(Chr.contains(chars, 'e', 0, 5));
+        assertFalse(Chr.contains(chars, 'x', 0, 5));
+    }
+
+    @Test
+    void testContainsWithStartAndLength() {
+        char[] chars = {'a', 'b', 'c', 'd', 'e'};
+        
+        // Only search within 'b', 'c', 'd' (start=1, length=3)
+        assertTrue(Chr.contains(chars, 'b', 1, 3));
+        assertTrue(Chr.contains(chars, 'c', 1, 3));
+        assertTrue(Chr.contains(chars, 'd', 1, 3));
+        assertFalse(Chr.contains(chars, 'a', 1, 3));
+        assertFalse(Chr.contains(chars, 'e', 1, 3));
+    }
+
+    @Test
+    void testIdxWithByteArray() {
+        char[] buffer = new char[10];
+        byte[] bytes = {65, 66, 67}; // A, B, C
+        
+        Chr._idx(buffer, 2, bytes);
+        
+        assertEquals('A', buffer[2]);
+        assertEquals('B', buffer[3]);
+        assertEquals('C', buffer[4]);
+    }
+
+    @Test
+    void testIdxWithCharArray() {
+        char[] buffer = new char[10];
+        char[] input = {'x', 'y', 'z'};
+        
+        Chr._idx(buffer, 3, input);
+        
+        assertEquals('x', buffer[3]);
+        assertEquals('y', buffer[4]);
+        assertEquals('z', buffer[5]);
+    }
+
+    @Test
+    void testIdxWithCharArrayAndLength() {
+        char[] buffer = new char[10];
+        char[] input = {'x', 'y', 'z', 'w'};
+        
+        Chr._idx(buffer, 1, input, 2);
+        
+        assertEquals('x', buffer[1]);
+        assertEquals('y', buffer[2]);
+        assertEquals(0, buffer[3]); // Not written
+    }
+
+    @Test
+    void testIdxWithByteArrayRange() {
+        char[] buffer = new char[10];
+        byte[] bytes = {65, 66, 67, 68, 69}; // A, B, C, D, E
+        
+        // Copy bytes[1] to bytes[3] (B, C, D) starting at buffer[0]
+        Chr._idx(buffer, 0, bytes, 1, 4);
+        
+        assertEquals('B', buffer[0]);
+        assertEquals('C', buffer[1]);
+        assertEquals('D', buffer[2]);
+    }
+
+    @Test
+    void testInEmptyArray() {
+        char[] array = {};
+        assertFalse(Chr.in('a', array));
+    }
+
+    @Test
+    void testGrowEmptyArray() {
+        char[] array = {};
+        char[] grown = Chr.grow(array, 5);
+        assertEquals(5, grown.length);
+    }
+
+    @Test
+    void testCopyEmptyArray() {
+        char[] array = {};
+        char[] copy = Chr.copy(array);
+        assertEquals(0, copy.length);
+    }
+
+    @Test
+    void testAddToEmptyArray() {
+        char[] array = {};
+        char[] result = Chr.add(array, 'a');
+        assertArrayEquals(new char[]{'a'}, result);
+    }
+
+    @Test
+    void testAddEmptyString() {
+        char[] array = {'a', 'b'};
+        char[] result = Chr.add(array, "");
+        assertArrayEquals(new char[]{'a', 'b'}, result);
+    }
+
+    @Test
+    void testCharsUnicode() {
+        char[] result = Chr.chars("日本語");
+        assertEquals(3, result.length);
+        assertEquals('日', result[0]);
+        assertEquals('本', result[1]);
+        assertEquals('語', result[2]);
+    }
+
+    @Test
+    void testAddEmptyArrays() {
+        char[] arr1 = {};
+        char[] arr2 = {};
+        char[] result = Chr.add(arr1, arr2);
+        assertEquals(0, result.length);
+    }
+}
diff --git 
a/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/ExceptionsTest.java
 
b/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/ExceptionsTest.java
new file mode 100644
index 0000000000..b21fbe482d
--- /dev/null
+++ 
b/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/ExceptionsTest.java
@@ -0,0 +1,318 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.json.internal;
+
+import groovy.json.JsonException;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * JUnit 5 tests for Exceptions class (JSON internal exception utilities).
+ */
+class ExceptionsTest {
+
+    @Test
+    void testDieNoMessage() {
+        assertThrows(Exceptions.JsonInternalException.class, () -> {
+            Exceptions.die();
+        });
+    }
+
+    @Test
+    void testDieWithMessage() {
+        Exceptions.JsonInternalException ex = assertThrows(
+            Exceptions.JsonInternalException.class,
+            () -> Exceptions.die("custom message")
+        );
+        assertTrue(ex.getMessage().contains("custom message"));
+    }
+
+    @Test
+    void testDieWithClassAndMessage() {
+        assertThrows(Exceptions.JsonInternalException.class, () -> {
+            Exceptions.die(String.class, "test error");
+        });
+    }
+
+    @Test
+    void testHandle() {
+        Exception original = new RuntimeException("original");
+        
+        Exceptions.JsonInternalException ex = assertThrows(
+            Exceptions.JsonInternalException.class,
+            () -> Exceptions.handle(original)
+        );
+        
+        assertSame(original, ex.getCause());
+    }
+
+    @Test
+    void testHandleWithClass() {
+        Exception original = new IllegalArgumentException("arg error");
+        
+        Exceptions.JsonInternalException ex = assertThrows(
+            Exceptions.JsonInternalException.class,
+            () -> Exceptions.handle(String.class, original)
+        );
+        
+        assertSame(original, ex.getCause());
+    }
+
+    @Test
+    void testHandleWithClassAlreadyJsonInternalException() {
+        Exceptions.JsonInternalException original = new 
Exceptions.JsonInternalException("already wrapped");
+        
+        Exceptions.JsonInternalException ex = assertThrows(
+            Exceptions.JsonInternalException.class,
+            () -> Exceptions.handle(String.class, original)
+        );
+        
+        assertSame(original, ex);
+    }
+
+    @Test
+    void testHandleWithClassMessageAndThrowable() {
+        Throwable original = new RuntimeException("cause");
+        
+        Exceptions.JsonInternalException ex = assertThrows(
+            Exceptions.JsonInternalException.class,
+            () -> Exceptions.handle(String.class, "wrapper message", original)
+        );
+        
+        assertTrue(ex.getMessage().contains("wrapper message"));
+        assertSame(original, ex.getCause());
+    }
+
+    @Test
+    void testHandleWithMessageAndThrowable() {
+        Throwable original = new RuntimeException("cause");
+        
+        Exceptions.JsonInternalException ex = assertThrows(
+            Exceptions.JsonInternalException.class,
+            () -> Exceptions.handle("error occurred", original)
+        );
+        
+        assertTrue(ex.getMessage().contains("error occurred"));
+        assertSame(original, ex.getCause());
+    }
+
+    @Test
+    void testJsonInternalExceptionMessage() {
+        Exceptions.JsonInternalException ex = new 
Exceptions.JsonInternalException("test message");
+        assertEquals("test message", ex.getMessage());
+    }
+
+    @Test
+    void testJsonInternalExceptionWithCause() {
+        RuntimeException cause = new RuntimeException("cause message");
+        Exceptions.JsonInternalException ex = new 
Exceptions.JsonInternalException("wrapper", cause);
+        
+        assertTrue(ex.getMessage().contains("wrapper"));
+        assertTrue(ex.getMessage().contains("cause message"));
+        assertSame(cause, ex.getCause());
+    }
+
+    @Test
+    void testJsonInternalExceptionWrappingCause() {
+        RuntimeException cause = new RuntimeException("original error");
+        Exceptions.JsonInternalException ex = new 
Exceptions.JsonInternalException(cause);
+        
+        assertEquals("Wrapped Exception", 
ex.getMessage().split("\n")[0].trim());
+        assertSame(cause, ex.getCause());
+    }
+
+    @Test
+    void testJsonInternalExceptionGetLocalizedMessage() {
+        Exceptions.JsonInternalException ex = new 
Exceptions.JsonInternalException("localized test");
+        assertEquals(ex.getMessage(), ex.getLocalizedMessage());
+    }
+
+    @Test
+    void testJsonInternalExceptionGetStackTraceWithCause() {
+        RuntimeException cause = new RuntimeException("cause");
+        Exceptions.JsonInternalException ex = new 
Exceptions.JsonInternalException(cause);
+        
+        // Should return the cause's stack trace elements (equivalent content)
+        StackTraceElement[] causeTrace = cause.getStackTrace();
+        StackTraceElement[] exTrace = ex.getStackTrace();
+        
+        assertEquals(causeTrace.length, exTrace.length);
+        for (int i = 0; i < causeTrace.length; i++) {
+            assertEquals(causeTrace[i], exTrace[i]);
+        }
+    }
+
+    @Test
+    void testJsonInternalExceptionGetStackTraceWithoutCause() {
+        Exceptions.JsonInternalException ex = new 
Exceptions.JsonInternalException("no cause");
+        
+        // Should return its own stack trace
+        assertNotNull(ex.getStackTrace());
+        assertTrue(ex.getStackTrace().length > 0);
+    }
+
+    @Test
+    void testJsonInternalExceptionGetCause() {
+        RuntimeException cause = new RuntimeException("the cause");
+        Exceptions.JsonInternalException ex = new 
Exceptions.JsonInternalException(cause);
+        
+        assertSame(cause, ex.getCause());
+    }
+
+    @Test
+    void testJsonInternalExceptionPrintStackTracePrintStream() {
+        RuntimeException cause = new RuntimeException("cause error");
+        Exceptions.JsonInternalException ex = new 
Exceptions.JsonInternalException(cause);
+        
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintStream ps = new PrintStream(baos);
+        
+        ex.printStackTrace(ps);
+        ps.flush();
+        
+        String output = baos.toString();
+        assertTrue(output.contains("Wrapped Exception"));
+        assertTrue(output.contains("original exception"));
+    }
+
+    @Test
+    void testJsonInternalExceptionPrintStackTracePrintStreamNoCause() {
+        Exceptions.JsonInternalException ex = new 
Exceptions.JsonInternalException("direct error");
+        
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintStream ps = new PrintStream(baos);
+        
+        ex.printStackTrace(ps);
+        ps.flush();
+        
+        String output = baos.toString();
+        assertTrue(output.contains("direct error"));
+    }
+
+    @Test
+    void testJsonInternalExceptionPrintStackTracePrintWriter() {
+        RuntimeException cause = new RuntimeException("writer cause");
+        Exceptions.JsonInternalException ex = new 
Exceptions.JsonInternalException(cause);
+        
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        
+        ex.printStackTrace(pw);
+        pw.flush();
+        
+        String output = sw.toString();
+        assertTrue(output.contains("Wrapped Exception"));
+    }
+
+    @Test
+    void testJsonInternalExceptionPrintStackTracePrintWriterNoCause() {
+        Exceptions.JsonInternalException ex = new 
Exceptions.JsonInternalException("writer direct");
+        
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        
+        ex.printStackTrace(pw);
+        pw.flush();
+        
+        String output = sw.toString();
+        assertTrue(output.contains("writer direct"));
+    }
+
+    @Test
+    void testJsonInternalExceptionIsJsonException() {
+        Exceptions.JsonInternalException ex = new 
Exceptions.JsonInternalException("test");
+        assertTrue(ex instanceof JsonException);
+    }
+
+    @Test
+    void testToString() {
+        Exception ex = new RuntimeException("test error for toString");
+        String result = Exceptions.toString(ex);
+        
+        assertNotNull(result);
+        assertTrue(result.contains("test error for toString"));
+    }
+
+    @Test
+    void testSputsWithBuffer() {
+        CharBuf buf = CharBuf.create(100);
+        String result = Exceptions.sputs(buf, "hello", "world", 42);
+        
+        assertTrue(result.contains("hello"));
+        assertTrue(result.contains("world"));
+        assertTrue(result.contains("42"));
+    }
+
+    @Test
+    void testSputsWithoutBuffer() {
+        String result = Exceptions.sputs("one", "two", "three");
+        
+        assertTrue(result.contains("one"));
+        assertTrue(result.contains("two"));
+        assertTrue(result.contains("three"));
+    }
+
+    @Test
+    void testSputsWithNullValue() {
+        String result = Exceptions.sputs("value", null, "other");
+        
+        assertTrue(result.contains("value"));
+        assertTrue(result.contains("<NULL>"));
+        assertTrue(result.contains("other"));
+    }
+
+    @Test
+    void testSputsWithArray() {
+        Object[] arr = {"a", "b"};
+        String result = Exceptions.sputs("prefix", arr);
+        
+        assertTrue(result.contains("prefix"));
+        // Arrays are printed using Collections.singletonList().toString()
+        assertNotNull(result);
+    }
+
+    @Test
+    void testSputsEmpty() {
+        String result = Exceptions.sputs();
+        assertNotNull(result);
+        assertTrue(result.endsWith("\n"));
+    }
+
+    @Test
+    void testSputsSingleValue() {
+        String result = Exceptions.sputs("single");
+        assertTrue(result.contains("single"));
+    }
+
+    @Test
+    void testToStringWithNestedCause() {
+        RuntimeException cause = new RuntimeException("root cause");
+        Exception wrapper = new Exception("wrapper", cause);
+        
+        String result = Exceptions.toString(wrapper);
+        assertNotNull(result);
+        assertTrue(result.contains("wrapper"));
+    }
+}
diff --git 
a/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/ValueMapImplTest.java
 
b/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/ValueMapImplTest.java
new file mode 100644
index 0000000000..b6e8e92be8
--- /dev/null
+++ 
b/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/ValueMapImplTest.java
@@ -0,0 +1,278 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.json.internal;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * JUnit 5 tests for ValueMapImpl class.
+ */
+class ValueMapImplTest {
+
+    private ValueMapImpl valueMap;
+
+    @BeforeEach
+    void setUp() {
+        valueMap = new ValueMapImpl();
+    }
+
+    // Simple Value implementation for testing
+    private static Value createStringValue(final String str) {
+        return new Value() {
+            @Override
+            public byte byteValue() { return 0; }
+            @Override
+            public short shortValue() { return 0; }
+            @Override
+            public int intValue() { return 0; }
+            @Override
+            public long longValue() { return 0; }
+            @Override
+            public BigDecimal bigDecimalValue() { return null; }
+            @Override
+            public BigInteger bigIntegerValue() { return null; }
+            @Override
+            public float floatValue() { return 0; }
+            @Override
+            public double doubleValue() { return 0; }
+            @Override
+            public boolean booleanValue() { return false; }
+            @Override
+            public Date dateValue() { return null; }
+            @Override
+            public String stringValue() { return str; }
+            @Override
+            public String stringValueEncoded() { return str; }
+            @Override
+            public Object toValue() { return str; }
+            @Override
+            public <T extends Enum> T toEnum(Class<T> cls) { return null; }
+            @Override
+            public boolean isContainer() { return false; }
+            @Override
+            public void chop() { }
+            @Override
+            public char charValue() { return str != null && str.length() > 0 ? 
str.charAt(0) : 0; }
+            @Override
+            public String toString() { return str; }
+        };
+    }
+
+    private MapItemValue createEntry(String key, String value) {
+        return new MapItemValue(createStringValue(key), 
createStringValue(value));
+    }
+
+    @Test
+    void testInitialState() {
+        assertEquals(0, valueMap.len());
+        assertFalse(valueMap.hydrated());
+    }
+
+    @Test
+    void testAdd() {
+        MapItemValue entry = createEntry("key1", "value1");
+        valueMap.add(entry);
+
+        assertEquals(1, valueMap.len());
+    }
+
+    @Test
+    void testAddMultiple() {
+        valueMap.add(createEntry("key1", "value1"));
+        valueMap.add(createEntry("key2", "value2"));
+        valueMap.add(createEntry("key3", "value3"));
+
+        assertEquals(3, valueMap.len());
+    }
+
+    @Test
+    void testItems() {
+        MapItemValue entry1 = createEntry("a", "1");
+        MapItemValue entry2 = createEntry("b", "2");
+
+        valueMap.add(entry1);
+        valueMap.add(entry2);
+
+        Map.Entry<String, Value>[] items = valueMap.items();
+        assertNotNull(items);
+        assertEquals(entry1, items[0]);
+        assertEquals(entry2, items[1]);
+    }
+
+    @Test
+    void testGetBeforeHydration() {
+        MapItemValue entry = createEntry("mykey", "myvalue");
+        valueMap.add(entry);
+
+        Value result = valueMap.get("mykey");
+        assertNotNull(result);
+        assertEquals("myvalue", result.toString());
+    }
+
+    @Test
+    void testGetNotFound() {
+        valueMap.add(createEntry("key1", "value1"));
+
+        Value result = valueMap.get("nonexistent");
+        assertNull(result);
+    }
+
+    @Test
+    void testHydratedAfterEntrySet() {
+        valueMap.add(createEntry("key1", "value1"));
+        assertFalse(valueMap.hydrated());
+
+        valueMap.entrySet();
+        assertTrue(valueMap.hydrated());
+    }
+
+    @Test
+    void testEntrySet() {
+        valueMap.add(createEntry("a", "1"));
+        valueMap.add(createEntry("b", "2"));
+
+        Set<Map.Entry<String, Value>> entrySet = valueMap.entrySet();
+        assertEquals(2, entrySet.size());
+    }
+
+    @Test
+    void testValues() {
+        valueMap.add(createEntry("key1", "val1"));
+        valueMap.add(createEntry("key2", "val2"));
+
+        Collection<Value> values = valueMap.values();
+        assertEquals(2, values.size());
+    }
+
+    @Test
+    void testSize() {
+        valueMap.add(createEntry("a", "1"));
+        valueMap.add(createEntry("b", "2"));
+        valueMap.add(createEntry("c", "3"));
+
+        assertEquals(3, valueMap.size());
+    }
+
+    @Test
+    void testSizeEmpty() {
+        assertEquals(0, valueMap.size());
+    }
+
+    @Test
+    void testPutThrowsException() {
+        assertThrows(Exceptions.JsonInternalException.class, () -> {
+            valueMap.put("key", createStringValue("value"));
+        });
+    }
+
+    @Test
+    void testGetAfterHydration() {
+        valueMap.add(createEntry("key1", "value1"));
+        valueMap.add(createEntry("key2", "value2"));
+
+        // Force hydration by calling entrySet
+        valueMap.entrySet();
+
+        // Now get should use the internal map
+        Value result = valueMap.get("key1");
+        assertNotNull(result);
+        assertEquals("value1", result.toString());
+    }
+
+    @Test
+    void testAddMoreThanInitialCapacity() {
+        // The initial capacity is 20, test adding more
+        for (int i = 0; i < 25; i++) {
+            valueMap.add(createEntry("key" + i, "value" + i));
+        }
+
+        assertEquals(25, valueMap.len());
+        assertEquals(25, valueMap.size());
+    }
+
+    @Test
+    void testGetMultipleKeys() {
+        valueMap.add(createEntry("first", "1st"));
+        valueMap.add(createEntry("second", "2nd"));
+        valueMap.add(createEntry("third", "3rd"));
+
+        assertEquals("1st", valueMap.get("first").toString());
+        assertEquals("2nd", valueMap.get("second").toString());
+        assertEquals("3rd", valueMap.get("third").toString());
+    }
+
+    @Test
+    void testEntrySetMultipleCalls() {
+        valueMap.add(createEntry("x", "y"));
+
+        Set<Map.Entry<String, Value>> set1 = valueMap.entrySet();
+        Set<Map.Entry<String, Value>> set2 = valueMap.entrySet();
+
+        // Both should return the same map's entry set
+        assertEquals(set1.size(), set2.size());
+    }
+
+    @Test
+    void testValuesContents() {
+        valueMap.add(createEntry("a", "alpha"));
+        valueMap.add(createEntry("b", "beta"));
+
+        Collection<Value> values = valueMap.values();
+
+        boolean foundAlpha = false;
+        boolean foundBeta = false;
+        for (Value v : values) {
+            if ("alpha".equals(v.toString())) foundAlpha = true;
+            if ("beta".equals(v.toString())) foundBeta = true;
+        }
+
+        assertTrue(foundAlpha);
+        assertTrue(foundBeta);
+    }
+
+    @Test
+    void testEmptyEntrySet() {
+        Set<Map.Entry<String, Value>> entrySet = valueMap.entrySet();
+        assertTrue(entrySet.isEmpty());
+    }
+
+    @Test
+    void testEmptyValues() {
+        Collection<Value> values = valueMap.values();
+        assertTrue(values.isEmpty());
+    }
+
+    @Test
+    void testItemsArray() {
+        Map.Entry<String, Value>[] items = valueMap.items();
+        assertNotNull(items);
+        // Initial array has capacity 20
+        assertEquals(20, items.length);
+    }
+}

Reply via email to