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 01e52d1a11 Add tests to increase code coverage: `CharBufTest`,
`SysTest`, etc.
01e52d1a11 is described below
commit 01e52d1a11b2d99021b04818a2041889153fc4e5
Author: Daniel Sun <[email protected]>
AuthorDate: Sun Feb 1 21:34:24 2026 +0900
Add tests to increase code coverage: `CharBufTest`, `SysTest`, etc.
---
.../java/groovy/lang/GroovySystemJUnit5Test.java | 125 +++++++
src/test/java/groovy/lang/ReferenceJUnit5Test.java | 156 ++++++++
.../groovy/runtime/FormatHelperJUnit5Test.java | 349 ++++++++++++++++++
.../runtime/NumberAwareComparatorJUnit5Test.java | 201 +++++++++++
.../codehaus/groovy/syntax/CSTNodeJUnit5Test.java | 368 +++++++++++++++++++
.../groovy/tools/ErrorReporterJUnit5Test.java | 226 ++++++++++++
.../codehaus/groovy/util/FastArrayJUnit5Test.java | 375 +++++++++++++++++++
.../apache/groovy/json/internal/CharBufTest.java | 402 +++++++++++++++++++++
.../groovy/json/internal/SimpleCacheTest.java | 213 +++++++++++
.../org/apache/groovy/json/internal/SysTest.java | 103 ++++++
10 files changed, 2518 insertions(+)
diff --git a/src/test/java/groovy/lang/GroovySystemJUnit5Test.java
b/src/test/java/groovy/lang/GroovySystemJUnit5Test.java
new file mode 100644
index 0000000000..6af9351d71
--- /dev/null
+++ b/src/test/java/groovy/lang/GroovySystemJUnit5Test.java
@@ -0,0 +1,125 @@
+/*
+ * 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 groovy.lang;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * JUnit 5 tests for GroovySystem class.
+ */
+class GroovySystemJUnit5Test {
+
+ @Test
+ void testGetMetaClassRegistry() {
+ MetaClassRegistry registry = GroovySystem.getMetaClassRegistry();
+ assertNotNull(registry);
+ }
+
+ @Test
+ void testGetMetaClassRegistryReturnsSameInstance() {
+ MetaClassRegistry registry1 = GroovySystem.getMetaClassRegistry();
+ MetaClassRegistry registry2 = GroovySystem.getMetaClassRegistry();
+ assertSame(registry1, registry2);
+ }
+
+ @Test
+ void testGetVersion() {
+ String version = GroovySystem.getVersion();
+ assertNotNull(version);
+ assertFalse(version.isEmpty());
+ }
+
+ @Test
+ void testGetShortVersion() {
+ String shortVersion = GroovySystem.getShortVersion();
+ assertNotNull(shortVersion);
+
+ // Short version should contain exactly one dot
+ int dotCount = shortVersion.length() - shortVersion.replace(".",
"").length();
+ assertEquals(1, dotCount);
+
+ // Should start with a number
+ assertTrue(Character.isDigit(shortVersion.charAt(0)));
+ }
+
+ @Test
+ void testShortVersionIsPartOfFullVersion() {
+ String fullVersion = GroovySystem.getVersion();
+ String shortVersion = GroovySystem.getShortVersion();
+
+ assertTrue(fullVersion.startsWith(shortVersion));
+ }
+
+ @Test
+ void testIsKeepJavaMetaClassesDefault() {
+ // Default should be false
+ boolean original = GroovySystem.isKeepJavaMetaClasses();
+ try {
+ // Test the default behavior
+ assertFalse(original);
+ } finally {
+ // Restore original value
+ GroovySystem.setKeepJavaMetaClasses(original);
+ }
+ }
+
+ @Test
+ void testSetKeepJavaMetaClasses() {
+ boolean original = GroovySystem.isKeepJavaMetaClasses();
+ try {
+ GroovySystem.setKeepJavaMetaClasses(true);
+ assertTrue(GroovySystem.isKeepJavaMetaClasses());
+
+ GroovySystem.setKeepJavaMetaClasses(false);
+ assertFalse(GroovySystem.isKeepJavaMetaClasses());
+ } finally {
+ GroovySystem.setKeepJavaMetaClasses(original);
+ }
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
+ void testIsUseReflection() {
+ // This is deprecated but we can still test it
+ assertTrue(GroovySystem.isUseReflection());
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
+ void testRunnerRegistry() {
+ // RUNNER_REGISTRY is deprecated but we can verify it exists
+ assertNotNull(GroovySystem.RUNNER_REGISTRY);
+ }
+
+ @Test
+ void testVersionFormat() {
+ String version = GroovySystem.getVersion();
+ // Version should match pattern like "4.0.0", "4.0.1-SNAPSHOT",
"4.0.0-rc-1", etc.
+ assertTrue(version.matches("\\d+\\.\\d+\\.\\d+.*"),
+ "Version should match pattern X.Y.Z[suffix], got: " + version);
+ }
+
+ @Test
+ void testStopThreadedReferenceManager() {
+ // This should not throw any exception
+ assertDoesNotThrow(() -> GroovySystem.stopThreadedReferenceManager());
+ }
+}
diff --git a/src/test/java/groovy/lang/ReferenceJUnit5Test.java
b/src/test/java/groovy/lang/ReferenceJUnit5Test.java
new file mode 100644
index 0000000000..2af9c2f081
--- /dev/null
+++ b/src/test/java/groovy/lang/ReferenceJUnit5Test.java
@@ -0,0 +1,156 @@
+/*
+ * 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 groovy.lang;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * JUnit 5 tests for Reference class.
+ */
+class ReferenceJUnit5Test {
+
+ @Test
+ void testDefaultConstructor() {
+ Reference<String> ref = new Reference<>();
+ assertNull(ref.get());
+ }
+
+ @Test
+ void testConstructorWithValue() {
+ Reference<String> ref = new Reference<>("hello");
+ assertEquals("hello", ref.get());
+ }
+
+ @Test
+ void testGetAndSet() {
+ Reference<Integer> ref = new Reference<>();
+ assertNull(ref.get());
+
+ ref.set(42);
+ assertEquals(42, ref.get());
+
+ ref.set(100);
+ assertEquals(100, ref.get());
+ }
+
+ @Test
+ void testSetNull() {
+ Reference<String> ref = new Reference<>("initial");
+ assertEquals("initial", ref.get());
+
+ ref.set(null);
+ assertNull(ref.get());
+ }
+
+ @Test
+ void testWithDifferentTypes() {
+ Reference<Double> doubleRef = new Reference<>(3.14);
+ assertEquals(3.14, doubleRef.get());
+
+ Reference<Boolean> boolRef = new Reference<>(true);
+ assertTrue(boolRef.get());
+
+ Reference<Object> objRef = new Reference<>(new int[]{1, 2, 3});
+ assertArrayEquals(new int[]{1, 2, 3}, (int[]) objRef.get());
+ }
+
+ @Test
+ void testGetPropertyOnNullValue() {
+ Reference<String> ref = new Reference<>();
+ // When value is null, getProperty should delegate to super
+ assertThrows(MissingPropertyException.class, () -> {
+ ref.getProperty("length");
+ });
+ }
+
+ @Test
+ void testSetPropertyOnNullValue() {
+ Reference<Object> ref = new Reference<>();
+ // When value is null, setProperty should delegate to super
+ assertThrows(MissingPropertyException.class, () -> {
+ ref.setProperty("someProp", "value");
+ });
+ }
+
+ @Test
+ void testInvokeMethodOnNullValue() {
+ Reference<String> ref = new Reference<>();
+ // When value is null, invokeMethod delegates to super
(GroovyObjectSupport)
+ // It throws MissingMethodException for undefined methods
+ assertThrows(MissingMethodException.class, () -> {
+ ref.invokeMethod("someUndefinedMethod", null);
+ });
+ }
+
+ @Test
+ void testReferenceIsSerializable() {
+ Reference<String> ref = new Reference<>("test");
+ assertTrue(ref instanceof java.io.Serializable);
+ }
+
+ @Test
+ void testReferenceExtendsGroovyObjectSupport() {
+ Reference<String> ref = new Reference<>();
+ assertTrue(ref instanceof GroovyObjectSupport);
+ }
+
+ @Test
+ void testMultipleReferences() {
+ Reference<String> ref1 = new Reference<>("one");
+ Reference<String> ref2 = new Reference<>("two");
+
+ assertNotEquals(ref1.get(), ref2.get());
+
+ ref1.set("same");
+ ref2.set("same");
+ assertEquals(ref1.get(), ref2.get());
+ }
+
+ @Test
+ void testReferenceWithComplexObject() {
+ class Person {
+ String name;
+ int age;
+ Person(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+ }
+
+ Person person = new Person("John", 30);
+ Reference<Person> ref = new Reference<>(person);
+
+ assertSame(person, ref.get());
+ assertEquals("John", ref.get().name);
+ assertEquals(30, ref.get().age);
+ }
+
+ @Test
+ void testReferenceValueMutation() {
+ StringBuilder sb = new StringBuilder("initial");
+ Reference<StringBuilder> ref = new Reference<>(sb);
+
+ sb.append(" modified");
+
+ // Since Reference holds the same object, mutation is visible
+ assertEquals("initial modified", ref.get().toString());
+ }
+}
diff --git
a/src/test/java/org/codehaus/groovy/runtime/FormatHelperJUnit5Test.java
b/src/test/java/org/codehaus/groovy/runtime/FormatHelperJUnit5Test.java
new file mode 100644
index 0000000000..88b54fcb9b
--- /dev/null
+++ b/src/test/java/org/codehaus/groovy/runtime/FormatHelperJUnit5Test.java
@@ -0,0 +1,349 @@
+/*
+ * 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 org.junit.jupiter.api.Test;
+
+import java.util.*;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * JUnit 5 tests for FormatHelper class.
+ */
+class FormatHelperJUnit5Test {
+
+ @Test
+ void testToStringNull() {
+ String result = FormatHelper.toString(null);
+ assertEquals("null", result);
+ }
+
+ @Test
+ void testToStringString() {
+ String result = FormatHelper.toString("hello");
+ assertEquals("hello", result);
+ }
+
+ @Test
+ void testToStringInteger() {
+ String result = FormatHelper.toString(42);
+ assertEquals("42", result);
+ }
+
+ @Test
+ void testToStringCollection() {
+ List<String> list = Arrays.asList("a", "b", "c");
+ String result = FormatHelper.toString(list);
+ assertEquals("[a, b, c]", result);
+ }
+
+ @Test
+ void testToStringMap() {
+ Map<String, Integer> map = new LinkedHashMap<>();
+ map.put("one", 1);
+ map.put("two", 2);
+ String result = FormatHelper.toString(map);
+ assertTrue(result.contains("one") && result.contains("1"));
+ }
+
+ @Test
+ void testToStringObjectArray() {
+ Object[] arr = {"a", "b", "c"};
+ String result = FormatHelper.toString(arr);
+ assertEquals("[a, b, c]", result);
+ }
+
+ @Test
+ void testToStringCharArray() {
+ char[] chars = {'h', 'i'};
+ String result = FormatHelper.toString(chars);
+ assertEquals("hi", result);
+ }
+
+ @Test
+ void testToStringPrimitiveIntArray() {
+ int[] arr = {1, 2, 3};
+ String result = FormatHelper.toString(arr);
+ assertEquals("[1, 2, 3]", result);
+ }
+
+ @Test
+ void testInspectString() {
+ String result = FormatHelper.inspect("hello");
+ assertTrue(result.contains("hello"));
+ }
+
+ @Test
+ void testInspectNull() {
+ String result = FormatHelper.inspect(null);
+ assertEquals("null", result);
+ }
+
+ @Test
+ void testFormatVerbose() {
+ String result = FormatHelper.format("hello", true);
+ assertTrue(result.contains("hello"));
+ }
+
+ @Test
+ void testFormatWithMaxSize() {
+ String longString = "This is a very long string that should be
truncated";
+ String result = FormatHelper.format(longString, false, 10);
+ // Format doesn't truncate simple strings, just collections/maps
+ assertNotNull(result);
+ }
+
+ @Test
+ void testFormatWithMaxSizeOnCollection() {
+ List<String> list = Arrays.asList("item1", "item2", "item3", "item4",
"item5");
+ String result = FormatHelper.format(list, false, 20);
+ assertNotNull(result);
+ }
+
+ @Test
+ void testFormatEmptyCollection() {
+ List<String> list = Collections.emptyList();
+ String result = FormatHelper.format(list, false);
+ assertEquals("[]", result);
+ }
+
+ @Test
+ void testFormatEmptyMap() {
+ Map<String, String> map = Collections.emptyMap();
+ String result = FormatHelper.format(map, false);
+ assertEquals("[:]", result);
+ }
+
+ @Test
+ void testFormatMapWithInspect() {
+ Map<String, String> map = new LinkedHashMap<>();
+ map.put("key", "value");
+ String result = FormatHelper.format(map, true);
+ assertTrue(result.contains("key") && result.contains("value"));
+ }
+
+ @Test
+ void testToStringWithOptions() {
+ Map<String, Object> options = new HashMap<>();
+ options.put("safe", true);
+ options.put("maxSize", 100);
+
+ String result = FormatHelper.toString(options, "hello");
+ assertEquals("hello", result);
+ }
+
+ @Test
+ void testToStringWithOptionsVerbose() {
+ Map<String, Object> options = new HashMap<>();
+ options.put("verbose", true);
+
+ String result = FormatHelper.toString(options, "hello");
+ assertNotNull(result);
+ }
+
+ @Test
+ void testToStringWithOptionsInspect() {
+ Map<String, Object> options = new HashMap<>();
+ options.put("inspect", true);
+
+ String result = FormatHelper.toString(options, "hello");
+ assertTrue(result.contains("hello"));
+ }
+
+ @Test
+ void testToStringWithStringBuilder() {
+ StringBuilder sb = new StringBuilder("test");
+ String result = FormatHelper.toString(sb);
+ assertEquals("test", result);
+ }
+
+ @Test
+ void testToStringWithNestedCollection() {
+ List<List<Integer>> nested = Arrays.asList(
+ Arrays.asList(1, 2),
+ Arrays.asList(3, 4)
+ );
+ String result = FormatHelper.toString(nested);
+ assertEquals("[[1, 2], [3, 4]]", result);
+ }
+
+ @Test
+ void testWriteToBuffer() throws java.io.IOException {
+ java.io.StringWriter sw = new java.io.StringWriter();
+ FormatHelper.write(sw, "hello");
+ assertEquals("hello", sw.toString());
+ }
+
+ @Test
+ void testWriteNullToBuffer() throws java.io.IOException {
+ java.io.StringWriter sw = new java.io.StringWriter();
+ FormatHelper.write(sw, null);
+ assertEquals("null", sw.toString());
+ }
+
+ @Test
+ void testWriteArrayToBuffer() throws java.io.IOException {
+ java.io.StringWriter sw = new java.io.StringWriter();
+ Object[] arr = {"a", "b"};
+ FormatHelper.write(sw, arr);
+ assertEquals("[a, b]", sw.toString());
+ }
+
+ @Test
+ void testFormatSafeMode() {
+ // Object that throws exception on toString
+ Object problematic = new Object() {
+ @Override
+ public String toString() {
+ throw new RuntimeException("Cannot convert to string");
+ }
+ };
+
+ // Safe mode should not throw
+ String result = FormatHelper.format(problematic, false, -1, true);
+ assertNotNull(result);
+ }
+
+ @Test
+ void testFormatUnsafeMode() {
+ Object problematic = new Object() {
+ @Override
+ public String toString() {
+ throw new RuntimeException("Cannot convert to string");
+ }
+ };
+
+ // Unsafe mode should throw
+ assertThrows(RuntimeException.class, () ->
+ FormatHelper.format(problematic, false, -1, false)
+ );
+ }
+
+ @Test
+ void testEscapeBackslashes() {
+ String input = "hello\tworld\n";
+ String result = FormatHelper.escapeBackslashes(input);
+ assertTrue(result.contains("\\t") || result.contains("\\n"));
+ }
+
+ @Test
+ void testFormatWithEscapeBackslashes() {
+ String input = "line1\nline2";
+ String result = FormatHelper.format(input, true, true, -1, false);
+ assertNotNull(result);
+ }
+
+ @Test
+ void testToArrayString() {
+ Object[] arr = {1, 2, 3};
+ String result = FormatHelper.toArrayString(arr);
+ assertEquals("[1, 2, 3]", result);
+ }
+
+ @Test
+ void testToArrayStringWithInspect() {
+ Object[] arr = {"a", "b"};
+ // toArrayString with params is private, test via format
+ String result = FormatHelper.format(arr, true, -1);
+ assertTrue(result.contains("a") && result.contains("b"));
+ }
+
+ @Test
+ void testToMapString() {
+ Map<String, Integer> map = new LinkedHashMap<>();
+ map.put("a", 1);
+ String result = FormatHelper.toMapString(map);
+ assertTrue(result.contains("a") && result.contains("1"));
+ }
+
+ @Test
+ void testToMapStringMaxSize() {
+ Map<String, Integer> map = new LinkedHashMap<>();
+ for (int i = 0; i < 100; i++) {
+ map.put("key" + i, i);
+ }
+ String result = FormatHelper.toMapString(map, 50);
+ assertNotNull(result);
+ }
+
+ @Test
+ void testToListString() {
+ List<String> list = Arrays.asList("one", "two", "three");
+ String result = FormatHelper.toListString(list);
+ assertEquals("[one, two, three]", result);
+ }
+
+ @Test
+ void testToListStringWithMaxSize() {
+ List<String> list = Arrays.asList("one", "two", "three", "four",
"five");
+ String result = FormatHelper.toListString(list, 15);
+ assertNotNull(result);
+ }
+
+ @Test
+ void testToTypeString() {
+ Object[] args = {"hello", 42, true};
+ String result = FormatHelper.toTypeString(args);
+ assertTrue(result.contains("String"));
+ assertTrue(result.contains("Integer"));
+ assertTrue(result.contains("Boolean"));
+ }
+
+ @Test
+ void testToTypeStringWithMaxSize() {
+ Object[] args = {"hello", 42, true, 3.14, "world"};
+ String result = FormatHelper.toTypeString(args, 20);
+ assertNotNull(result);
+ }
+
+ @Test
+ void testToTypeStringNull() {
+ String result = FormatHelper.toTypeString(null);
+ assertEquals("null", result);
+ }
+
+ @Test
+ void testToTypeStringEmpty() {
+ String result = FormatHelper.toTypeString(new Object[0]);
+ assertEquals("", result);
+ }
+
+ @Test
+ void testFormatBoolean() {
+ assertEquals("true", FormatHelper.toString(true));
+ assertEquals("false", FormatHelper.toString(false));
+ }
+
+ @Test
+ void testFormatDouble() {
+ String result = FormatHelper.toString(3.14159);
+ assertTrue(result.startsWith("3.14"));
+ }
+
+ @Test
+ void testFormatLong() {
+ assertEquals("9999999999", FormatHelper.toString(9999999999L));
+ }
+
+ @Test
+ void testMetaRegistryNotNull() {
+ assertNotNull(FormatHelper.metaRegistry);
+ }
+}
diff --git
a/src/test/java/org/codehaus/groovy/runtime/NumberAwareComparatorJUnit5Test.java
b/src/test/java/org/codehaus/groovy/runtime/NumberAwareComparatorJUnit5Test.java
new file mode 100644
index 0000000000..20c8f56243
--- /dev/null
+++
b/src/test/java/org/codehaus/groovy/runtime/NumberAwareComparatorJUnit5Test.java
@@ -0,0 +1,201 @@
+/*
+ * 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 org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * JUnit 5 tests for NumberAwareComparator class.
+ */
+class NumberAwareComparatorJUnit5Test {
+
+ @Test
+ void testCompareEqualIntegers() {
+ NumberAwareComparator<Integer> comparator = new
NumberAwareComparator<>();
+ assertEquals(0, comparator.compare(5, 5));
+ }
+
+ @Test
+ void testCompareDifferentIntegers() {
+ NumberAwareComparator<Integer> comparator = new
NumberAwareComparator<>();
+ assertTrue(comparator.compare(3, 5) < 0);
+ assertTrue(comparator.compare(5, 3) > 0);
+ }
+
+ @Test
+ void testCompareEqualStrings() {
+ NumberAwareComparator<String> comparator = new
NumberAwareComparator<>();
+ assertEquals(0, comparator.compare("hello", "hello"));
+ }
+
+ @Test
+ void testCompareDifferentStrings() {
+ NumberAwareComparator<String> comparator = new
NumberAwareComparator<>();
+ assertTrue(comparator.compare("apple", "banana") < 0);
+ assertTrue(comparator.compare("banana", "apple") > 0);
+ }
+
+ @Test
+ void testCompareNullFirst() {
+ NumberAwareComparator<String> comparator = new
NumberAwareComparator<>();
+ assertTrue(comparator.compare(null, "hello") < 0);
+ }
+
+ @Test
+ void testCompareNullSecond() {
+ NumberAwareComparator<String> comparator = new
NumberAwareComparator<>();
+ assertTrue(comparator.compare("hello", null) > 0);
+ }
+
+ @Test
+ void testCompareBothNull() {
+ NumberAwareComparator<String> comparator = new
NumberAwareComparator<>();
+ assertEquals(0, comparator.compare(null, null));
+ }
+
+ @Test
+ void testCompareMixedNumberTypes() {
+ NumberAwareComparator<Number> comparator = new
NumberAwareComparator<>();
+ // Different numeric types should be comparable
+ assertEquals(0, comparator.compare(5, 5L));
+ assertEquals(0, comparator.compare(5.0, 5));
+ assertTrue(comparator.compare(3, 5.0) < 0);
+ }
+
+ @Test
+ void testCompareDoubles() {
+ NumberAwareComparator<Double> comparator = new
NumberAwareComparator<>();
+ assertEquals(0, comparator.compare(3.14, 3.14));
+ assertTrue(comparator.compare(2.71, 3.14) < 0);
+ }
+
+ @Test
+ void testCompareBigDecimal() {
+ NumberAwareComparator<BigDecimal> comparator = new
NumberAwareComparator<>();
+ BigDecimal a = new BigDecimal("100.50");
+ BigDecimal b = new BigDecimal("100.50");
+ BigDecimal c = new BigDecimal("200.00");
+
+ assertEquals(0, comparator.compare(a, b));
+ assertTrue(comparator.compare(a, c) < 0);
+ }
+
+ @Test
+ void testCompareBigInteger() {
+ NumberAwareComparator<BigInteger> comparator = new
NumberAwareComparator<>();
+ BigInteger a = new BigInteger("12345678901234567890");
+ BigInteger b = new BigInteger("12345678901234567890");
+ BigInteger c = new BigInteger("98765432109876543210");
+
+ assertEquals(0, comparator.compare(a, b));
+ assertTrue(comparator.compare(a, c) < 0);
+ }
+
+ @Test
+ void testIgnoreZeroSignFalse() {
+ NumberAwareComparator<Float> comparator = new
NumberAwareComparator<>(false);
+ // +0.0f and -0.0f should be different
+ int result = comparator.compare(0.0f, -0.0f);
+ // They may or may not be equal depending on compareTo implementation
+ assertNotNull(result);
+ }
+
+ @Test
+ void testIgnoreZeroSignTrueForFloat() {
+ NumberAwareComparator<Float> comparator = new
NumberAwareComparator<>(true);
+ // With ignoreZeroSign=true, +0.0f and -0.0f should be equal
+ assertEquals(0, comparator.compare(0.0f, -0.0f));
+ assertEquals(0, comparator.compare(-0.0f, 0.0f));
+ }
+
+ @Test
+ void testIgnoreZeroSignTrueForDouble() {
+ NumberAwareComparator<Double> comparator = new
NumberAwareComparator<>(true);
+ // With ignoreZeroSign=true, +0.0 and -0.0 should be equal
+ assertEquals(0, comparator.compare(0.0d, -0.0d));
+ assertEquals(0, comparator.compare(-0.0d, 0.0d));
+ }
+
+ @Test
+ void testIgnoreZeroSignOnlyAffectsZero() {
+ NumberAwareComparator<Double> comparator = new
NumberAwareComparator<>(true);
+ // Non-zero values should still compare normally
+ assertTrue(comparator.compare(1.0, 2.0) < 0);
+ assertTrue(comparator.compare(-1.0, 0.0) < 0);
+ }
+
+ @Test
+ void testSortingWithComparator() {
+ NumberAwareComparator<Integer> comparator = new
NumberAwareComparator<>();
+ List<Integer> list = Arrays.asList(5, 2, 8, 1, 9);
+ Collections.sort(list, comparator);
+
+ assertEquals(Arrays.asList(1, 2, 5, 8, 9), list);
+ }
+
+ @Test
+ void testSortingWithNulls() {
+ NumberAwareComparator<Integer> comparator = new
NumberAwareComparator<>();
+ List<Integer> list = Arrays.asList(5, null, 2, null, 1);
+ Collections.sort(list, comparator);
+
+ // Nulls should be first (less than any non-null)
+ assertNull(list.get(0));
+ assertNull(list.get(1));
+ assertEquals(1, list.get(2));
+ }
+
+ @Test
+ void testIsSerializable() {
+ NumberAwareComparator<Integer> comparator = new
NumberAwareComparator<>();
+ assertTrue(comparator instanceof java.io.Serializable);
+ }
+
+ @Test
+ void testCompareIncompatibleTypes() {
+ NumberAwareComparator<Object> comparator = new
NumberAwareComparator<>();
+ // Incompatible types should not throw but use hashCode comparison
+ int result = comparator.compare("string", 42);
+ // Result should be non-zero since they are different
+ assertNotEquals(0, result);
+ }
+
+ @Test
+ void testCompareSameObjectReference() {
+ NumberAwareComparator<Object> comparator = new
NumberAwareComparator<>();
+ Object obj = new Object();
+ assertEquals(0, comparator.compare(obj, obj));
+ }
+
+ @Test
+ void testCompareEqualObjects() {
+ NumberAwareComparator<String> comparator = new
NumberAwareComparator<>();
+ String s1 = new String("test");
+ String s2 = new String("test");
+ assertEquals(0, comparator.compare(s1, s2));
+ }
+}
diff --git a/src/test/java/org/codehaus/groovy/syntax/CSTNodeJUnit5Test.java
b/src/test/java/org/codehaus/groovy/syntax/CSTNodeJUnit5Test.java
new file mode 100644
index 0000000000..95afc38920
--- /dev/null
+++ b/src/test/java/org/codehaus/groovy/syntax/CSTNodeJUnit5Test.java
@@ -0,0 +1,368 @@
+/*
+ * 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.syntax;
+
+import org.codehaus.groovy.GroovyBugError;
+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 CSTNode class (via Token and Reduction implementations).
+ */
+class CSTNodeJUnit5Test {
+
+ @Test
+ void testTokenAsCSTNode() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+
+ assertNotNull(token);
+ assertEquals(Types.PLUS, token.getType());
+ assertEquals(Types.PLUS, token.getMeaning());
+ }
+
+ @Test
+ void testGetMeaning() {
+ Token token = Token.newSymbol(Types.STAR, 1, 1);
+ assertEquals(Types.STAR, token.getMeaning());
+ }
+
+ @Test
+ void testSetMeaning() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+ CSTNode result = token.setMeaning(Types.MINUS);
+
+ assertEquals(Types.MINUS, token.getMeaning());
+ assertSame(token, result);
+ }
+
+ @Test
+ void testGetType() {
+ Token token = Token.newIdentifier("test", 1, 1);
+ assertEquals(Types.IDENTIFIER, token.getType());
+ }
+
+ @Test
+ void testCanMean() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+
+ assertTrue(token.canMean(Types.PLUS));
+ }
+
+ @Test
+ void testIsA() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+ assertTrue(token.isA(Types.PLUS));
+ }
+
+ @Test
+ void testIsOneOf() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+
+ int[] types = {Types.MINUS, Types.PLUS, Types.STAR};
+ assertTrue(token.isOneOf(types));
+
+ int[] otherTypes = {Types.MINUS, Types.STAR};
+ assertFalse(token.isOneOf(otherTypes));
+ }
+
+ @Test
+ void testIsAllOf() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+
+ int[] types = {Types.PLUS};
+ assertTrue(token.isAllOf(types));
+ }
+
+ @Test
+ void testGetMeaningAs() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+
+ int[] types = {Types.MINUS, Types.PLUS};
+ assertEquals(Types.PLUS, token.getMeaningAs(types));
+
+ int[] otherTypes = {Types.MINUS, Types.STAR};
+ assertEquals(Types.UNKNOWN, token.getMeaningAs(otherTypes));
+ }
+
+ @Test
+ void testIsEmpty() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+ assertFalse(token.isEmpty());
+
+ // Token.NULL is not actually "empty" - it's a valid token with type
UNKNOWN
+ assertFalse(Token.NULL.isEmpty());
+ }
+
+ @Test
+ void testSize() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+ assertEquals(1, token.size());
+ }
+
+ @Test
+ void testHasChildren() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+ assertFalse(token.hasChildren());
+
+ Reduction reduction = new Reduction(token);
+ assertFalse(reduction.hasChildren());
+
+ reduction.add(Token.newSymbol(Types.MINUS, 2, 2));
+ assertTrue(reduction.hasChildren());
+ }
+
+ @Test
+ void testChildren() {
+ Reduction reduction = new Reduction(Token.newSymbol(Types.PLUS, 1, 1));
+ assertEquals(0, reduction.children());
+
+ reduction.add(Token.newSymbol(Types.MINUS, 2, 2));
+ assertEquals(1, reduction.children());
+ }
+
+ @Test
+ void testGet() {
+ Reduction reduction = new Reduction(Token.newSymbol(Types.PLUS, 1, 1));
+ Token child = Token.newSymbol(Types.MINUS, 2, 2);
+ reduction.add(child);
+
+ assertNotNull(reduction.get(0));
+ assertSame(child, reduction.get(1));
+ assertNull(reduction.get(99));
+ }
+
+ @Test
+ void testGetSafe() {
+ Reduction reduction = new Reduction(Token.newSymbol(Types.PLUS, 1, 1));
+
+ CSTNode safeMissing = reduction.get(99, true);
+ assertSame(Token.NULL, safeMissing);
+
+ CSTNode unsafeMissing = reduction.get(99, false);
+ assertNull(unsafeMissing);
+ }
+
+ @Test
+ void testGetRoot() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+ assertSame(token, token.getRoot());
+
+ Reduction reduction = new Reduction(token);
+ assertSame(token, reduction.getRoot());
+ }
+
+ @Test
+ void testGetRootSafe() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+ assertSame(token, token.getRoot(true));
+ assertSame(token, token.getRoot(false));
+ }
+
+ @Test
+ void testGetRootText() {
+ Token token = Token.newIdentifier("myvar", 1, 1);
+ assertEquals("myvar", token.getRootText());
+ }
+
+ @Test
+ void testGetDescription() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+ String desc = token.getDescription();
+ assertNotNull(desc);
+ }
+
+ @Test
+ void testGetStartLine() {
+ Token token = Token.newSymbol(Types.PLUS, 5, 10);
+ assertEquals(5, token.getStartLine());
+ }
+
+ @Test
+ void testGetStartColumn() {
+ Token token = Token.newSymbol(Types.PLUS, 5, 10);
+ assertEquals(10, token.getStartColumn());
+ }
+
+ @Test
+ void testMarkAsExpressionOnToken() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+ assertThrows(GroovyBugError.class, () -> token.markAsExpression());
+ }
+
+ @Test
+ void testIsAnExpression() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+ assertFalse(token.isAnExpression());
+ }
+
+ @Test
+ void testAddOnToken() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+ assertThrows(GroovyBugError.class, () -> token.add(Token.NULL));
+ }
+
+ @Test
+ void testSetOnToken() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+ assertThrows(GroovyBugError.class, () -> token.set(0, Token.NULL));
+ }
+
+ @Test
+ void testReductionAdd() {
+ Reduction reduction = new Reduction(Token.newSymbol(Types.PLUS, 1, 1));
+ Token child = Token.newSymbol(Types.MINUS, 2, 2);
+
+ CSTNode result = reduction.add(child);
+ assertSame(child, result);
+ assertEquals(2, reduction.size());
+ }
+
+ @Test
+ void testReductionSet() {
+ Reduction reduction = new Reduction(Token.newSymbol(Types.PLUS, 1, 1));
+ Token child1 = Token.newSymbol(Types.MINUS, 2, 2);
+ Token child2 = Token.newSymbol(Types.STAR, 3, 3);
+
+ reduction.add(child1);
+ reduction.set(1, child2);
+
+ assertSame(child2, reduction.get(1));
+ }
+
+ @Test
+ void testAddChildrenOf() {
+ Reduction source = new Reduction(Token.newSymbol(Types.PLUS, 1, 1));
+ source.add(Token.newSymbol(Types.MINUS, 2, 2));
+ source.add(Token.newSymbol(Types.STAR, 3, 3));
+
+ Reduction target = new Reduction(Token.newSymbol(Types.DIVIDE, 4, 4));
+ target.addChildrenOf(source);
+
+ assertEquals(3, target.size()); // root + 2 children
+ }
+
+ @Test
+ void testAsReduction() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+ Reduction reduction = token.asReduction();
+
+ assertNotNull(reduction);
+ assertSame(token, reduction.getRoot());
+
+ // Calling asReduction on Reduction returns self
+ Reduction existingReduction = new Reduction(token);
+ assertSame(existingReduction, existingReduction.asReduction());
+ }
+
+ @Test
+ void testToString() {
+ Token token = Token.newIdentifier("test", 1, 5);
+ String str = token.toString();
+ assertNotNull(str);
+ assertTrue(str.contains("IDENTIFIER") || str.contains("test"));
+ }
+
+ @Test
+ void testWriteToWriter() {
+ Token token = Token.newIdentifier("test", 1, 5);
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+
+ token.write(pw);
+ pw.flush();
+
+ String output = sw.toString();
+ assertNotNull(output);
+ assertTrue(output.length() > 0);
+ }
+
+ @Test
+ void testWriteWithChildren() {
+ Reduction reduction = new Reduction(Token.newSymbol(Types.PLUS, 1, 1));
+ reduction.add(Token.newSymbol(Types.MINUS, 2, 2));
+
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+
+ reduction.write(pw);
+ pw.flush();
+
+ String output = sw.toString();
+ assertNotNull(output);
+ }
+
+ @Test
+ void testTokenNullConstant() {
+ Token nullToken = Token.NULL;
+
+ // Token.NULL is not empty - it's a valid token with type UNKNOWN
+ assertFalse(nullToken.isEmpty());
+ assertEquals(Types.UNKNOWN, nullToken.getType());
+ }
+
+ @Test
+ void testMatches() {
+ Token token = Token.newSymbol(Types.PLUS, 1, 1);
+ assertTrue(token.matches(Types.PLUS));
+ assertFalse(token.matches(Types.MINUS));
+ }
+
+ @Test
+ void testMatchesWithChildren() {
+ Reduction reduction = new Reduction(Token.newSymbol(Types.PLUS, 1, 1));
+ reduction.add(Token.newSymbol(Types.MINUS, 2, 2));
+
+ assertTrue(reduction.matches(Types.PLUS, Types.MINUS));
+ assertFalse(reduction.matches(Types.PLUS, Types.STAR));
+ }
+
+ @Test
+ void testMatchesTwoChildren() {
+ Reduction reduction = new Reduction(Token.newSymbol(Types.PLUS, 1, 1));
+ reduction.add(Token.newSymbol(Types.MINUS, 2, 2));
+ reduction.add(Token.newSymbol(Types.STAR, 3, 3));
+
+ assertTrue(reduction.matches(Types.PLUS, Types.MINUS, Types.STAR));
+ }
+
+ @Test
+ void testMatchesThreeChildren() {
+ Reduction reduction = new Reduction(Token.newSymbol(Types.PLUS, 1, 1));
+ reduction.add(Token.newSymbol(Types.MINUS, 2, 2));
+ reduction.add(Token.newSymbol(Types.STAR, 3, 3));
+ reduction.add(Token.newSymbol(Types.DIVIDE, 4, 4));
+
+ assertTrue(reduction.matches(Types.PLUS, Types.MINUS, Types.STAR,
Types.DIVIDE));
+ }
+
+ @Test
+ void testMatchesFourChildren() {
+ Reduction reduction = new Reduction(Token.newSymbol(Types.PLUS, 1, 1));
+ reduction.add(Token.newSymbol(Types.MINUS, 2, 2));
+ reduction.add(Token.newSymbol(Types.STAR, 3, 3));
+ reduction.add(Token.newSymbol(Types.DIVIDE, 4, 4));
+ reduction.add(Token.newSymbol(Types.MOD, 5, 5));
+
+ assertTrue(reduction.matches(Types.PLUS, Types.MINUS, Types.STAR,
Types.DIVIDE, Types.MOD));
+ }
+}
diff --git
a/src/test/java/org/codehaus/groovy/tools/ErrorReporterJUnit5Test.java
b/src/test/java/org/codehaus/groovy/tools/ErrorReporterJUnit5Test.java
new file mode 100644
index 0000000000..e0d6024094
--- /dev/null
+++ b/src/test/java/org/codehaus/groovy/tools/ErrorReporterJUnit5Test.java
@@ -0,0 +1,226 @@
+/*
+ * 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.tools;
+
+import groovy.lang.GroovyRuntimeException;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.junit.jupiter.api.Test;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.ByteArrayOutputStream;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * JUnit 5 tests for ErrorReporter class.
+ */
+class ErrorReporterJUnit5Test {
+
+ @Test
+ void testConstructorWithException() {
+ Exception e = new RuntimeException("test error");
+ ErrorReporter reporter = new ErrorReporter(e);
+ assertNotNull(reporter);
+ }
+
+ @Test
+ void testConstructorWithExceptionAndDebug() {
+ Exception e = new RuntimeException("test error");
+ ErrorReporter reporter = new ErrorReporter(e, true);
+ assertNotNull(reporter);
+ }
+
+ @Test
+ void testWriteToPrintStream() {
+ Exception e = new RuntimeException("test message");
+ ErrorReporter reporter = new ErrorReporter(e);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos);
+
+ reporter.write(ps);
+
+ String output = baos.toString();
+ assertTrue(output.contains("test message"));
+ }
+
+ @Test
+ void testWriteToPrintWriter() {
+ Exception e = new RuntimeException("writer test");
+ ErrorReporter reporter = new ErrorReporter(e);
+
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+
+ reporter.write(pw);
+
+ String output = sw.toString();
+ assertTrue(output.contains("writer test"));
+ }
+
+ @Test
+ void testWriteWithDebugMode() {
+ Exception e = new RuntimeException("debug error");
+ ErrorReporter reporter = new ErrorReporter(e, true);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos);
+
+ reporter.write(ps);
+
+ String output = baos.toString();
+ assertTrue(output.contains("debug error"));
+ assertTrue(output.contains("stacktrace"));
+ }
+
+ @Test
+ void testReportCompilationFailedException() {
+ Exception cause = new Exception("compile error");
+ CompilationFailedException e = new CompilationFailedException(0, null,
cause);
+ ErrorReporter reporter = new ErrorReporter(e);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos);
+
+ reporter.write(ps);
+
+ String output = baos.toString();
+ assertNotNull(output);
+ }
+
+ @Test
+ void testReportGroovyRuntimeException() {
+ GroovyRuntimeException e = new GroovyRuntimeException("runtime error");
+ ErrorReporter reporter = new ErrorReporter(e);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos);
+
+ reporter.write(ps);
+
+ String output = baos.toString();
+ assertTrue(output.contains("runtime error"));
+ }
+
+ @Test
+ void testReportGenericException() {
+ Exception e = new IllegalArgumentException("generic error");
+ ErrorReporter reporter = new ErrorReporter(e);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos);
+
+ reporter.write(ps);
+
+ String output = baos.toString();
+ assertTrue(output.contains("generic error"));
+ }
+
+ @Test
+ void testReportThrowable() {
+ Throwable t = new Error("serious error");
+ ErrorReporter reporter = new ErrorReporter(t);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos);
+
+ reporter.write(ps);
+
+ String output = baos.toString();
+ assertTrue(output.contains("serious error"));
+ assertTrue(output.contains("a serious error occurred"));
+ }
+
+ @Test
+ void testReportWithNullMessage() {
+ Exception e = new RuntimeException((String) null);
+ ErrorReporter reporter = new ErrorReporter(e);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos);
+
+ // Should not throw
+ assertDoesNotThrow(() -> reporter.write(ps));
+ }
+
+ @Test
+ void testDebugModeAlwaysShowsStackTrace() {
+ Exception e = new RuntimeException("test");
+ ErrorReporter reporter = new ErrorReporter(e, true);
+
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+
+ reporter.write(pw);
+
+ String output = sw.toString();
+ assertTrue(output.contains("stacktrace"));
+ }
+
+ @Test
+ void testNonDebugModeForRegularException() {
+ Exception e = new RuntimeException("simple error");
+ ErrorReporter reporter = new ErrorReporter(e, false);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos);
+
+ reporter.write(ps);
+
+ String output = baos.toString();
+ assertTrue(output.contains("simple error"));
+ // In non-debug mode, regular exceptions don't show stacktrace by
default
+ }
+
+ @Test
+ void testFlushIsCalled() {
+ Exception e = new RuntimeException("flush test");
+ ErrorReporter reporter = new ErrorReporter(e);
+
+ final boolean[] flushed = {false};
+ PrintStream ps = new PrintStream(new ByteArrayOutputStream()) {
+ @Override
+ public void flush() {
+ flushed[0] = true;
+ super.flush();
+ }
+ };
+
+ reporter.write(ps);
+ assertTrue(flushed[0]);
+ }
+
+ @Test
+ void testExceptionChain() {
+ Exception cause = new RuntimeException("root cause");
+ Exception wrapper = new RuntimeException("wrapper", cause);
+ ErrorReporter reporter = new ErrorReporter(wrapper);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos);
+
+ reporter.write(ps);
+
+ String output = baos.toString();
+ assertTrue(output.contains("wrapper"));
+ }
+}
diff --git a/src/test/java/org/codehaus/groovy/util/FastArrayJUnit5Test.java
b/src/test/java/org/codehaus/groovy/util/FastArrayJUnit5Test.java
new file mode 100644
index 0000000000..39320ef50e
--- /dev/null
+++ b/src/test/java/org/codehaus/groovy/util/FastArrayJUnit5Test.java
@@ -0,0 +1,375 @@
+/*
+ * 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 java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * JUnit 5 tests for FastArray class.
+ */
+class FastArrayJUnit5Test {
+
+ @Test
+ void testDefaultConstructor() {
+ FastArray fa = new FastArray();
+ assertEquals(0, fa.size());
+ assertTrue(fa.isEmpty());
+ }
+
+ @Test
+ void testConstructorWithCapacity() {
+ FastArray fa = new FastArray(100);
+ assertEquals(0, fa.size());
+ assertTrue(fa.isEmpty());
+ }
+
+ @Test
+ void testConstructorWithCollection() {
+ List<String> list = Arrays.asList("a", "b", "c");
+ FastArray fa = new FastArray(list);
+ assertEquals(3, fa.size());
+ assertEquals("a", fa.get(0));
+ assertEquals("b", fa.get(1));
+ assertEquals("c", fa.get(2));
+ }
+
+ @Test
+ void testConstructorWithArray() {
+ Object[] array = {"x", "y", "z"};
+ FastArray fa = new FastArray(array);
+ assertEquals(3, fa.size());
+ assertEquals("x", fa.get(0));
+ }
+
+ @Test
+ void testAdd() {
+ FastArray fa = new FastArray();
+ fa.add("first");
+ assertEquals(1, fa.size());
+ assertEquals("first", fa.get(0));
+
+ fa.add("second");
+ assertEquals(2, fa.size());
+ assertEquals("second", fa.get(1));
+ }
+
+ @Test
+ void testAddWithGrowth() {
+ FastArray fa = new FastArray(2);
+ fa.add("1");
+ fa.add("2");
+ fa.add("3"); // This should trigger growth
+
+ assertEquals(3, fa.size());
+ assertEquals("3", fa.get(2));
+ }
+
+ @Test
+ void testAddFromEmptyArray() {
+ FastArray fa = new FastArray(0);
+ fa.add("item");
+ assertEquals(1, fa.size());
+ assertEquals("item", fa.get(0));
+ }
+
+ @Test
+ void testGet() {
+ FastArray fa = new FastArray();
+ fa.add("zero");
+ fa.add("one");
+ fa.add("two");
+
+ assertEquals("zero", fa.get(0));
+ assertEquals("one", fa.get(1));
+ assertEquals("two", fa.get(2));
+ }
+
+ @Test
+ void testSet() {
+ FastArray fa = new FastArray();
+ fa.add("original");
+
+ fa.set(0, "modified");
+ assertEquals("modified", fa.get(0));
+ }
+
+ @Test
+ void testSize() {
+ FastArray fa = new FastArray();
+ assertEquals(0, fa.size());
+
+ fa.add("a");
+ assertEquals(1, fa.size());
+
+ fa.add("b");
+ fa.add("c");
+ assertEquals(3, fa.size());
+ }
+
+ @Test
+ void testClear() {
+ FastArray fa = new FastArray();
+ fa.add("a");
+ fa.add("b");
+ fa.add("c");
+ assertEquals(3, fa.size());
+
+ fa.clear();
+ assertEquals(0, fa.size());
+ assertTrue(fa.isEmpty());
+ }
+
+ @Test
+ void testIsEmpty() {
+ FastArray fa = new FastArray();
+ assertTrue(fa.isEmpty());
+
+ fa.add("item");
+ assertFalse(fa.isEmpty());
+
+ fa.clear();
+ assertTrue(fa.isEmpty());
+ }
+
+ @Test
+ void testAddAllFastArray() {
+ FastArray fa1 = new FastArray();
+ fa1.add("a");
+ fa1.add("b");
+
+ FastArray fa2 = new FastArray();
+ fa2.add("c");
+ fa2.add("d");
+
+ fa1.addAll(fa2);
+
+ assertEquals(4, fa1.size());
+ assertEquals("a", fa1.get(0));
+ assertEquals("b", fa1.get(1));
+ assertEquals("c", fa1.get(2));
+ assertEquals("d", fa1.get(3));
+ }
+
+ @Test
+ void testAddAllEmptyFastArray() {
+ FastArray fa1 = new FastArray();
+ fa1.add("a");
+
+ FastArray fa2 = new FastArray();
+
+ fa1.addAll(fa2);
+ assertEquals(1, fa1.size());
+ }
+
+ @Test
+ void testAddAllList() {
+ FastArray fa = new FastArray();
+ fa.add("first");
+
+ List<String> list = Arrays.asList("second", "third");
+ fa.addAll(list);
+
+ assertEquals(3, fa.size());
+ assertEquals("second", fa.get(1));
+ assertEquals("third", fa.get(2));
+ }
+
+ @Test
+ void testAddAllObjectArray() {
+ FastArray fa = new FastArray();
+ fa.add("start");
+
+ Object[] array = {"middle", "end"};
+ fa.addAll(array, array.length);
+
+ assertEquals(3, fa.size());
+ }
+
+ @Test
+ void testCopy() {
+ FastArray original = new FastArray();
+ original.add("a");
+ original.add("b");
+ original.add("c");
+
+ FastArray copy = original.copy();
+
+ assertEquals(original.size(), copy.size());
+ assertEquals(original.get(0), copy.get(0));
+ assertEquals(original.get(1), copy.get(1));
+ assertEquals(original.get(2), copy.get(2));
+
+ // Verify it's a real copy, not the same reference
+ copy.set(0, "modified");
+ assertEquals("a", original.get(0));
+ assertEquals("modified", copy.get(0));
+ }
+
+ @Test
+ void testRemove() {
+ FastArray fa = new FastArray();
+ fa.add("a");
+ fa.add("b");
+ fa.add("c");
+
+ fa.remove(1);
+
+ assertEquals(2, fa.size());
+ assertEquals("a", fa.get(0));
+ assertEquals("c", fa.get(1));
+ }
+
+ @Test
+ void testRemoveFirst() {
+ FastArray fa = new FastArray();
+ fa.add("a");
+ fa.add("b");
+ fa.add("c");
+
+ fa.remove(0);
+
+ assertEquals(2, fa.size());
+ assertEquals("b", fa.get(0));
+ }
+
+ @Test
+ void testRemoveLast() {
+ FastArray fa = new FastArray();
+ fa.add("a");
+ fa.add("b");
+ fa.add("c");
+
+ fa.remove(2);
+
+ assertEquals(2, fa.size());
+ assertEquals("b", fa.get(1));
+ }
+
+ @Test
+ void testToListEmpty() {
+ FastArray fa = new FastArray();
+ List<?> list = fa.toList();
+ assertTrue(list.isEmpty());
+ }
+
+ @Test
+ void testToListSingle() {
+ FastArray fa = new FastArray();
+ fa.add("only");
+
+ List<?> list = fa.toList();
+ assertEquals(1, list.size());
+ assertEquals("only", list.get(0));
+ }
+
+ @Test
+ void testToListMultiple() {
+ FastArray fa = new FastArray();
+ fa.add("a");
+ fa.add("b");
+ fa.add("c");
+
+ List<?> list = fa.toList();
+ assertEquals(3, list.size());
+ assertEquals("a", list.get(0));
+ assertEquals("b", list.get(1));
+ assertEquals("c", list.get(2));
+ }
+
+ @Test
+ void testGetArray() {
+ FastArray fa = new FastArray();
+ fa.add("x");
+ fa.add("y");
+
+ Object[] array = fa.getArray();
+ assertNotNull(array);
+ assertEquals("x", array[0]);
+ assertEquals("y", array[1]);
+ }
+
+ @Test
+ void testToStringEmpty() {
+ FastArray fa = new FastArray();
+ assertEquals("[]", fa.toString());
+ }
+
+ @Test
+ void testToStringNonEmpty() {
+ FastArray fa = new FastArray();
+ fa.add("a");
+ fa.add("b");
+
+ String str = fa.toString();
+ assertTrue(str.contains("a"));
+ assertTrue(str.contains("b"));
+ }
+
+ @Test
+ void testClone() {
+ FastArray original = new FastArray();
+ original.add("1");
+ original.add("2");
+
+ FastArray clone = original.clone();
+
+ assertEquals(original.size(), clone.size());
+ assertEquals(original.get(0), clone.get(0));
+
+ // Verify it's independent
+ clone.add("3");
+ assertEquals(2, original.size());
+ assertEquals(3, clone.size());
+ }
+
+ @Test
+ void testEmptyList() {
+ assertNotNull(FastArray.EMPTY_LIST);
+ assertEquals(0, FastArray.EMPTY_LIST.size());
+ assertTrue(FastArray.EMPTY_LIST.isEmpty());
+ }
+
+ @Test
+ void testAddNull() {
+ FastArray fa = new FastArray();
+ fa.add(null);
+ assertEquals(1, fa.size());
+ assertNull(fa.get(0));
+ }
+
+ @Test
+ void testMixedTypes() {
+ FastArray fa = new FastArray();
+ fa.add("string");
+ fa.add(42);
+ fa.add(3.14);
+ fa.add(true);
+
+ assertEquals(4, fa.size());
+ assertEquals("string", fa.get(0));
+ assertEquals(42, fa.get(1));
+ assertEquals(3.14, fa.get(2));
+ assertEquals(true, fa.get(3));
+ }
+}
diff --git
a/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/CharBufTest.java
b/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/CharBufTest.java
new file mode 100644
index 0000000000..b701be60da
--- /dev/null
+++
b/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/CharBufTest.java
@@ -0,0 +1,402 @@
+/*
+ * 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 java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * JUnit 5 tests for CharBuf class.
+ */
+class CharBufTest {
+
+ @Test
+ void testCreateWithCapacity() {
+ CharBuf buf = CharBuf.create(100);
+ assertNotNull(buf);
+ assertEquals(0, buf.length());
+ }
+
+ @Test
+ void testCreateWithCharArray() {
+ char[] chars = "hello".toCharArray();
+ CharBuf buf = CharBuf.create(chars);
+ assertNotNull(buf);
+ // create() with char[] sets the capacity but location is still 0
+ // Content must be added separately
+ assertEquals(0, buf.length());
+ }
+
+ @Test
+ void testCreateExact() {
+ CharBuf buf = CharBuf.createExact(100);
+ assertNotNull(buf);
+ }
+
+ @Test
+ void testAddString() {
+ CharBuf buf = CharBuf.create(16);
+ buf.add("hello");
+ assertEquals(5, buf.length());
+ assertEquals("hello", buf.toString());
+ }
+
+ @Test
+ void testAddStringChain() {
+ CharBuf buf = CharBuf.create(16);
+ buf.add("hello").add(" ").add("world");
+ assertEquals("hello world", buf.toString());
+ }
+
+ @Test
+ void testAddStringMethod() {
+ CharBuf buf = CharBuf.create(16);
+ buf.addString("test");
+ assertEquals("test", buf.toString());
+ }
+
+ @Test
+ void testAddInt() {
+ CharBuf buf = CharBuf.create(16);
+ buf.add(42);
+ assertEquals("42", buf.toString());
+ }
+
+ @Test
+ void testAddIntSpecialCases() {
+ CharBuf buf1 = CharBuf.create(16);
+ buf1.addInt(0);
+ assertEquals("0", buf1.toString());
+
+ CharBuf buf2 = CharBuf.create(16);
+ buf2.addInt(1);
+ assertEquals("1", buf2.toString());
+
+ CharBuf buf3 = CharBuf.create(16);
+ buf3.addInt(-1);
+ assertEquals("-1", buf3.toString());
+ }
+
+ @Test
+ void testAddIntCaching() {
+ CharBuf buf = CharBuf.create(16);
+ buf.addInt(42);
+ buf.addInt(42); // Should use cached value
+ assertEquals("4242", buf.toString());
+ }
+
+ @Test
+ void testAddBoolean() {
+ CharBuf bufTrue = CharBuf.create(16);
+ bufTrue.add(true);
+ assertEquals("true", bufTrue.toString());
+
+ CharBuf bufFalse = CharBuf.create(16);
+ bufFalse.add(false);
+ assertEquals("false", bufFalse.toString());
+ }
+
+ @Test
+ void testAddBooleanMethod() {
+ CharBuf buf = CharBuf.create(16);
+ buf.addBoolean(true);
+ assertEquals("true", buf.toString());
+ }
+
+ @Test
+ void testAddByte() {
+ CharBuf buf = CharBuf.create(16);
+ buf.add((byte) 42);
+ assertEquals("42", buf.toString());
+ }
+
+ @Test
+ void testAddByteMethod() {
+ CharBuf buf = CharBuf.create(16);
+ buf.addByte((byte) 10);
+ assertEquals("10", buf.toString());
+ }
+
+ @Test
+ void testAddShort() {
+ CharBuf buf = CharBuf.create(16);
+ buf.add((short) 1000);
+ assertEquals("1000", buf.toString());
+ }
+
+ @Test
+ void testAddShortMethod() {
+ CharBuf buf = CharBuf.create(16);
+ buf.addShort((short) 500);
+ assertEquals("500", buf.toString());
+ }
+
+ @Test
+ void testAddLong() {
+ CharBuf buf = CharBuf.create(16);
+ buf.add(9999999999L);
+ assertEquals("9999999999", buf.toString());
+ }
+
+ @Test
+ void testAddDouble() {
+ CharBuf buf = CharBuf.create(16);
+ buf.add(3.14);
+ assertTrue(buf.toString().startsWith("3.14"));
+ }
+
+ @Test
+ void testAddDoubleMethod() {
+ CharBuf buf = CharBuf.create(16);
+ buf.addDouble(2.5);
+ buf.addDouble(2.5); // Should use caching
+ String result = buf.toString();
+ assertTrue(result.contains("2.5"));
+ }
+
+ @Test
+ void testAddFloat() {
+ CharBuf buf = CharBuf.create(16);
+ buf.add(1.5f);
+ assertTrue(buf.toString().contains("1.5"));
+ }
+
+ @Test
+ void testAddFloatMethod() {
+ CharBuf buf = CharBuf.create(16);
+ buf.addFloat(2.5f);
+ buf.addFloat(2.5f); // Should use caching
+ assertTrue(buf.toString().contains("2.5"));
+ }
+
+ @Test
+ void testAddChar() {
+ CharBuf buf = CharBuf.create(16);
+ buf.addChar('A');
+ assertEquals("A", buf.toString());
+ }
+
+ @Test
+ void testAddCharFromByte() {
+ CharBuf buf = CharBuf.create(16);
+ buf.addChar((byte) 65);
+ assertEquals("A", buf.toString());
+ }
+
+ @Test
+ void testAddCharFromInt() {
+ CharBuf buf = CharBuf.create(16);
+ buf.addChar(65);
+ assertEquals("A", buf.toString());
+ }
+
+ @Test
+ void testAddCharFromShort() {
+ CharBuf buf = CharBuf.create(16);
+ buf.addChar((short) 65);
+ assertEquals("A", buf.toString());
+ }
+
+ @Test
+ void testAddLine() {
+ CharBuf buf = CharBuf.create(32);
+ buf.addLine("hello");
+ assertEquals("hello\n", buf.toString());
+ }
+
+ @Test
+ void testAddLineCharSequence() {
+ CharBuf buf = CharBuf.create(32);
+ CharSequence cs = "world";
+ buf.addLine(cs);
+ assertEquals("world\n", buf.toString());
+ }
+
+ @Test
+ void testAddCharArray() {
+ CharBuf buf = CharBuf.create(16);
+ buf.add("test".toCharArray());
+ assertEquals("test", buf.toString());
+ }
+
+ @Test
+ void testAddChars() {
+ CharBuf buf = CharBuf.create(16);
+ buf.addChars("data".toCharArray());
+ assertEquals("data", buf.toString());
+ }
+
+ @Test
+ void testAddQuoted() {
+ CharBuf buf = CharBuf.create(16);
+ buf.addQuoted("hello".toCharArray());
+ assertEquals("\"hello\"", buf.toString());
+ }
+
+ @Test
+ void testAddJsonEscapedString() {
+ CharBuf buf = CharBuf.create(32);
+ buf.addJsonEscapedString("hello");
+ assertEquals("\"hello\"", buf.toString());
+ }
+
+ @Test
+ void testAddJsonEscapedStringWithSpecialChars() {
+ CharBuf buf = CharBuf.create(64);
+ buf.addJsonEscapedString("hello\nworld\t!");
+ String result = buf.toString();
+ assertTrue(result.contains("\\n"));
+ assertTrue(result.contains("\\t"));
+ }
+
+ @Test
+ void testAddJsonEscapedStringWithUnicodeDisabled() {
+ CharBuf buf = CharBuf.create(32);
+ buf.addJsonEscapedString("hello", true);
+ assertEquals("\"hello\"", buf.toString());
+ }
+
+ @Test
+ void testCharSequenceInterface() {
+ CharBuf buf = CharBuf.create(16);
+ buf.add("hello");
+
+ assertEquals(5, buf.length());
+ assertEquals('h', buf.charAt(0));
+ assertEquals('e', buf.charAt(1));
+ assertEquals('o', buf.charAt(4));
+ }
+
+ @Test
+ void testSubSequence() {
+ CharBuf buf = CharBuf.create(16);
+ buf.add("hello world");
+ CharSequence sub = buf.subSequence(0, 5);
+ assertEquals("hello", sub.toString());
+ }
+
+ @Test
+ void testWriterWrite() throws IOException {
+ CharBuf buf = CharBuf.create(16);
+ buf.write("test".toCharArray(), 0, 4);
+ assertEquals("test", buf.toString());
+ }
+
+ @Test
+ void testWriterWritePartial() throws IOException {
+ CharBuf buf = CharBuf.create(16);
+ buf.write("hello world".toCharArray(), 6, 5);
+ assertEquals("world", buf.toString());
+ }
+
+ @Test
+ void testWriterFlush() throws IOException {
+ CharBuf buf = CharBuf.create(16);
+ buf.add("test");
+ buf.flush(); // Should not throw
+ assertEquals("test", buf.toString());
+ }
+
+ @Test
+ void testWriterClose() throws IOException {
+ CharBuf buf = CharBuf.create(16);
+ buf.add("test");
+ buf.close(); // Should not throw
+ assertEquals("test", buf.toString());
+ }
+
+ @Test
+ void testToCharArray() {
+ CharBuf buf = CharBuf.create(16);
+ buf.add("hello");
+ // toCharArray returns the underlying buffer which is larger than
content
+ char[] chars = buf.toCharArray();
+ assertNotNull(chars);
+ assertTrue(chars.length >= 5);
+ }
+
+ @Test
+ void testAutoExpansion() {
+ CharBuf buf = CharBuf.create(4);
+ buf.add("this is a much longer string that exceeds initial capacity");
+ assertEquals("this is a much longer string that exceeds initial
capacity", buf.toString());
+ }
+
+ @Test
+ void testAutoExpansionWithChars() {
+ CharBuf buf = CharBuf.create(4);
+ for (int i = 0; i < 100; i++) {
+ buf.addChar('x');
+ }
+ assertEquals(100, buf.length());
+ }
+
+ @Test
+ void testReadForRecycleWithSmallLength() {
+ CharBuf buf = CharBuf.create(16);
+ buf.add("hello");
+ char[] result = buf.readForRecycle();
+ assertNotNull(result);
+ // Should return the underlying buffer for recycling
+ }
+
+ @Test
+ void testEmptyBuffer() {
+ CharBuf buf = CharBuf.create(16);
+ assertEquals(0, buf.length());
+ assertEquals("", buf.toString());
+ }
+
+ @Test
+ void testToStringWithBuffer() {
+ char[] initial = "initial".toCharArray();
+ CharBuf buf = new CharBuf(initial);
+ // Constructor sets capacity from buffer but location is 0
+ assertEquals("", buf.toString());
+ // Add content to see it
+ buf.add("test");
+ assertEquals("test", buf.toString());
+ }
+
+ @Test
+ void testConstructorWithBytes() {
+ byte[] bytes = "hello".getBytes();
+ CharBuf buf = new CharBuf(bytes);
+ // Constructor sets the buffer but location is 0
+ assertEquals("", buf.toString());
+ // Add content after construction
+ buf.add("world");
+ assertEquals("world", buf.toString());
+ }
+
+ @Test
+ void testMultipleAdditions() {
+ CharBuf buf = CharBuf.create(16);
+ buf.add("one")
+ .add(2)
+ .add(true)
+ .addChar('-')
+ .add(3.14);
+ String result = buf.toString();
+ assertTrue(result.startsWith("one2true-3.14"));
+ }
+}
diff --git
a/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/SimpleCacheTest.java
b/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/SimpleCacheTest.java
new file mode 100644
index 0000000000..cb283541f2
--- /dev/null
+++
b/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/SimpleCacheTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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 SimpleCache class.
+ */
+class SimpleCacheTest {
+
+ @Test
+ void testConstructorWithLimit() {
+ SimpleCache<String, String> cache = new SimpleCache<>(10);
+ assertNotNull(cache);
+ assertEquals(0, cache.size());
+ }
+
+ @Test
+ void testConstructorWithLimitAndLRUType() {
+ SimpleCache<String, String> cache = new SimpleCache<>(10,
CacheType.LRU);
+ assertNotNull(cache);
+ }
+
+ @Test
+ void testConstructorWithLimitAndFIFOType() {
+ SimpleCache<String, String> cache = new SimpleCache<>(10,
CacheType.FIFO);
+ assertNotNull(cache);
+ }
+
+ @Test
+ void testPutAndGet() {
+ SimpleCache<String, String> cache = new SimpleCache<>(10);
+
+ cache.put("key1", "value1");
+ assertEquals("value1", cache.get("key1"));
+ }
+
+ @Test
+ void testGetNonExistent() {
+ SimpleCache<String, String> cache = new SimpleCache<>(10);
+ assertNull(cache.get("nonexistent"));
+ }
+
+ @Test
+ void testPutMultiple() {
+ SimpleCache<String, Integer> cache = new SimpleCache<>(10);
+
+ cache.put("one", 1);
+ cache.put("two", 2);
+ cache.put("three", 3);
+
+ assertEquals(3, cache.size());
+ assertEquals(1, cache.get("one"));
+ assertEquals(2, cache.get("two"));
+ assertEquals(3, cache.get("three"));
+ }
+
+ @Test
+ void testPutOverwrite() {
+ SimpleCache<String, String> cache = new SimpleCache<>(10);
+
+ cache.put("key", "original");
+ assertEquals("original", cache.get("key"));
+
+ cache.put("key", "updated");
+ assertEquals("updated", cache.get("key"));
+ }
+
+ @Test
+ void testRemove() {
+ SimpleCache<String, String> cache = new SimpleCache<>(10);
+
+ cache.put("key", "value");
+ assertEquals(1, cache.size());
+
+ cache.remove("key");
+ assertNull(cache.get("key"));
+ }
+
+ @Test
+ void testRemoveNonExistent() {
+ SimpleCache<String, String> cache = new SimpleCache<>(10);
+ // Should not throw
+ assertDoesNotThrow(() -> cache.remove("nonexistent"));
+ }
+
+ @Test
+ void testSize() {
+ SimpleCache<String, String> cache = new SimpleCache<>(10);
+ assertEquals(0, cache.size());
+
+ cache.put("a", "1");
+ assertEquals(1, cache.size());
+
+ cache.put("b", "2");
+ cache.put("c", "3");
+ assertEquals(3, cache.size());
+ }
+
+ @Test
+ void testGetSilent() {
+ SimpleCache<String, String> cache = new SimpleCache<>(10);
+
+ cache.put("key", "value");
+
+ // getSilent should return the value without affecting LRU order
+ String value = cache.getSilent("key");
+ assertEquals("value", value);
+ }
+
+ @Test
+ void testGetSilentNonExistent() {
+ SimpleCache<String, String> cache = new SimpleCache<>(10);
+ assertNull(cache.getSilent("nonexistent"));
+ }
+
+ @Test
+ void testToString() {
+ SimpleCache<String, String> cache = new SimpleCache<>(10);
+ cache.put("key", "value");
+
+ String str = cache.toString();
+ assertNotNull(str);
+ }
+
+ @Test
+ void testEvictionLRU() {
+ // Small cache that will evict
+ SimpleCache<Integer, String> cache = new SimpleCache<>(3,
CacheType.LRU);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+
+ // Access 1 to make it recently used
+ cache.get(1);
+
+ // Add 4, should evict least recently used (2)
+ cache.put(4, "four");
+
+ assertEquals(3, cache.size());
+ assertNotNull(cache.get(1)); // 1 should still be there
+ assertNotNull(cache.get(4)); // 4 should be there
+ }
+
+ @Test
+ void testEvictionFIFO() {
+ // Small cache that will evict
+ SimpleCache<Integer, String> cache = new SimpleCache<>(3,
CacheType.FIFO);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+
+ // Add 4, should evict first in (1)
+ cache.put(4, "four");
+
+ assertEquals(3, cache.size());
+ assertNotNull(cache.get(4)); // 4 should be there
+ }
+
+ @Test
+ void testWithNullValue() {
+ SimpleCache<String, String> cache = new SimpleCache<>(10);
+
+ // Null values are not supported - throws NullPointerException
+ assertThrows(NullPointerException.class, () -> {
+ cache.put("key", null);
+ });
+ }
+
+ @Test
+ void testCacheTypeLRU() {
+ assertEquals(CacheType.LRU, CacheType.valueOf("LRU"));
+ }
+
+ @Test
+ void testCacheTypeFIFO() {
+ assertEquals(CacheType.FIFO, CacheType.valueOf("FIFO"));
+ }
+
+ @Test
+ void testLargeCache() {
+ SimpleCache<Integer, Integer> cache = new SimpleCache<>(1000);
+
+ for (int i = 0; i < 500; i++) {
+ cache.put(i, i * 2);
+ }
+
+ assertEquals(500, cache.size());
+ assertEquals(0, cache.get(0));
+ assertEquals(998, cache.get(499));
+ }
+}
diff --git
a/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/SysTest.java
b/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/SysTest.java
new file mode 100644
index 0000000000..bf232d0b6c
--- /dev/null
+++
b/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/SysTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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 Sys class (Java version detection utilities).
+ */
+class SysTest {
+
+ @Test
+ void testIs1_7OrLater() {
+ // Since we're running on modern Java, this should always be true
+ assertTrue(Sys.is1_7OrLater());
+ }
+
+ @Test
+ void testIs1_8OrLater() {
+ // Since we're running on Java 8+, this should be true
+ assertTrue(Sys.is1_8OrLater());
+ }
+
+ @Test
+ void testIs1_7() {
+ // If we're on Java 7, this is true, otherwise false
+ // On modern JDKs (8+), this should be false
+ boolean result = Sys.is1_7();
+ // Just verify it returns a boolean without error
+ assertTrue(result || !result);
+ }
+
+ @Test
+ void testIs1_8() {
+ // Returns true only if we're on exactly Java 8
+ boolean result = Sys.is1_8();
+ // Just verify it returns a boolean without error
+ assertTrue(result || !result);
+ }
+
+ @Test
+ void testVersionDetectionConsistency() {
+ // If is1_8 is true, is1_8OrLater must also be true
+ if (Sys.is1_8()) {
+ assertTrue(Sys.is1_8OrLater());
+ }
+
+ // If is1_7 is true, is1_7OrLater must be true
+ if (Sys.is1_7()) {
+ assertTrue(Sys.is1_7OrLater());
+ }
+ }
+
+ @Test
+ void testNotBothJava7And8() {
+ // Cannot be both Java 7 and Java 8
+ assertFalse(Sys.is1_7() && Sys.is1_8());
+ }
+
+ @Test
+ void testJava7ImpliesNotJava8() {
+ if (Sys.is1_7()) {
+ assertFalse(Sys.is1_8());
+ }
+ }
+
+ @Test
+ void testJava8ImpliesNotJava7() {
+ if (Sys.is1_8()) {
+ assertFalse(Sys.is1_7());
+ }
+ }
+
+ @Test
+ void testModernJavaVersion() {
+ // On Java 9+, both is1_7() and is1_8() should be false
+ String javaVersion = System.getProperty("java.version");
+ if (javaVersion.startsWith("9") || !javaVersion.startsWith("1")) {
+ // We're on Java 9 or later (version strings like "11", "17", "21")
+ assertFalse(Sys.is1_7());
+ assertFalse(Sys.is1_8());
+ assertTrue(Sys.is1_8OrLater());
+ }
+ }
+}