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 2f0a4f5108 Add tests to increase code coverage:
`CharSequenceReaderTest`, `NumberValueTest`, etc.
2f0a4f5108 is described below
commit 2f0a4f5108d815ce8b5f0f95555df86492545d11
Author: Daniel Sun <[email protected]>
AuthorDate: Sun Feb 1 20:08:22 2026 +0900
Add tests to increase code coverage: `CharSequenceReaderTest`,
`NumberValueTest`, etc.
---
src/test/java/groovy/lang/SpreadMapJUnit5Test.java | 240 ++++++++++++++
src/test/java/groovy/time/DurationJUnit5Test.java | 233 ++++++++++++++
.../time/TimeDatumDependentDurationJUnit5Test.java | 177 +++++++++++
.../java/groovy/time/TimeDurationJUnit5Test.java | 218 +++++++++++++
.../groovy/syntax/ReductionJUnit5Test.java | 263 ++++++++++++++++
.../codehaus/groovy/syntax/TokenJUnit5Test.java | 262 ++++++++++++++++
.../groovy/util/CharSequenceReaderTest.java | 189 ++++++++++++
.../java/groovy/json/StringEscapeUtilsTest.java | 343 +++++++++++++++++++++
.../json/internal/CharSequenceValueTest.java | 337 ++++++++++++++++++++
.../groovy/json/internal/NumberValueTest.java | 320 +++++++++++++++++++
10 files changed, 2582 insertions(+)
diff --git a/src/test/java/groovy/lang/SpreadMapJUnit5Test.java
b/src/test/java/groovy/lang/SpreadMapJUnit5Test.java
new file mode 100644
index 0000000000..39392f4340
--- /dev/null
+++ b/src/test/java/groovy/lang/SpreadMapJUnit5Test.java
@@ -0,0 +1,240 @@
+/*
+ * 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.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Unit tests for {@link SpreadMap}.
+ */
+class SpreadMapJUnit5Test {
+
+ private SpreadMap spreadMap;
+
+ @BeforeEach
+ void setUp() {
+ String[] entries = new String[]{"key1", "value1", "key2", "value2"};
+ spreadMap = new SpreadMap(entries);
+ }
+
+ @Test
+ void testConstructFromArray() {
+ Object[] entries = {"a", 1, "b", 2, "c", 3};
+ SpreadMap map = new SpreadMap(entries);
+ assertEquals(3, map.size());
+ assertEquals(1, map.get("a"));
+ assertEquals(2, map.get("b"));
+ assertEquals(3, map.get("c"));
+ }
+
+ @Test
+ void testConstructFromMap() {
+ Map<String, Integer> source = new HashMap<>();
+ source.put("x", 10);
+ source.put("y", 20);
+ SpreadMap map = new SpreadMap(source);
+ assertEquals(2, map.size());
+ assertEquals(10, map.get("x"));
+ assertEquals(20, map.get("y"));
+ }
+
+ @Test
+ void testConstructFromList() {
+ List<Object> list = Arrays.asList("first", 100, "second", 200);
+ SpreadMap map = new SpreadMap(list);
+ assertEquals(2, map.size());
+ assertEquals(100, map.get("first"));
+ assertEquals(200, map.get("second"));
+ }
+
+ @Test
+ void testPutThrowsException() {
+ assertThrows(RuntimeException.class, () -> spreadMap.put("newKey",
"newValue"));
+ }
+
+ @Test
+ void testRemoveThrowsException() {
+ assertThrows(RuntimeException.class, () -> spreadMap.remove("key1"));
+ }
+
+ @Test
+ void testPutAllThrowsException() {
+ Map<String, String> newMap = new HashMap<>();
+ newMap.put("a", "b");
+ assertThrows(RuntimeException.class, () -> spreadMap.putAll(newMap));
+ }
+
+ @Test
+ void testEqualsWithSameSpreadMap() {
+ SpreadMap map1 = new SpreadMap(new Object[]{"a", 1, "b", 2});
+ SpreadMap map2 = new SpreadMap(new Object[]{"a", 1, "b", 2});
+ assertEquals(map1, map2);
+ }
+
+ @Test
+ void testEqualsWithSelf() {
+ assertTrue(spreadMap.equals(spreadMap));
+ }
+
+ @Test
+ void testEqualsWithDifferentSize() {
+ SpreadMap map1 = new SpreadMap(new Object[]{"a", 1});
+ SpreadMap map2 = new SpreadMap(new Object[]{"a", 1, "b", 2});
+ assertNotEquals(map1, map2);
+ }
+
+ @Test
+ void testEqualsWithDifferentValues() {
+ SpreadMap map1 = new SpreadMap(new Object[]{"a", 1});
+ SpreadMap map2 = new SpreadMap(new Object[]{"a", 2});
+ assertNotEquals(map1, map2);
+ }
+
+ @Test
+ void testEqualsWithNonSpreadMap() {
+ Map<String, Integer> regularMap = new HashMap<>();
+ regularMap.put("key1", 1);
+ assertFalse(spreadMap.equals(regularMap));
+ }
+
+ @Test
+ void testEqualsWithNull() {
+ assertFalse(spreadMap.equals((SpreadMap) null));
+ }
+
+ @Test
+ void testHashCode() {
+ SpreadMap map1 = new SpreadMap(new Object[]{"a", 1, "b", 2});
+ SpreadMap map2 = new SpreadMap(new Object[]{"a", 1, "b", 2});
+ assertEquals(map1.hashCode(), map2.hashCode());
+ }
+
+ @Test
+ void testHashCodeWithNullKey() {
+ SpreadMap map = new SpreadMap(new Object[]{null, "value"});
+ // Should not throw, hashCode should handle null keys
+ int hash = map.hashCode();
+ // Just verify it doesn't throw and returns something
+ assertTrue(hash != 0 || hash == 0); // Always true, just checking no
exception
+ }
+
+ @Test
+ void testToStringEmpty() {
+ SpreadMap emptyMap = new SpreadMap(new Object[]{});
+ assertEquals("*:[:]", emptyMap.toString());
+ }
+
+ @Test
+ void testToStringWithEntries() {
+ SpreadMap map = new SpreadMap(new Object[]{"a", 1});
+ String result = map.toString();
+ assertTrue(result.startsWith("*:["));
+ assertTrue(result.endsWith("]"));
+ assertTrue(result.contains("a:1"));
+ }
+
+ @Test
+ void testToStringMultipleEntries() {
+ SpreadMap map = new SpreadMap(new Object[]{"a", 1, "b", 2});
+ String result = map.toString();
+ assertTrue(result.startsWith("*:["));
+ assertTrue(result.endsWith("]"));
+ assertTrue(result.contains(":"));
+ assertTrue(result.contains(", "));
+ }
+
+ @Test
+ void testContainsKey() {
+ assertTrue(spreadMap.containsKey("key1"));
+ assertTrue(spreadMap.containsKey("key2"));
+ assertFalse(spreadMap.containsKey("nonexistent"));
+ }
+
+ @Test
+ void testContainsValue() {
+ assertTrue(spreadMap.containsValue("value1"));
+ assertTrue(spreadMap.containsValue("value2"));
+ assertFalse(spreadMap.containsValue("nonexistent"));
+ }
+
+ @Test
+ void testKeySet() {
+ assertEquals(2, spreadMap.keySet().size());
+ assertTrue(spreadMap.keySet().contains("key1"));
+ assertTrue(spreadMap.keySet().contains("key2"));
+ }
+
+ @Test
+ void testValues() {
+ assertEquals(2, spreadMap.values().size());
+ assertTrue(spreadMap.values().contains("value1"));
+ assertTrue(spreadMap.values().contains("value2"));
+ }
+
+ @Test
+ void testEntrySet() {
+ assertEquals(2, spreadMap.entrySet().size());
+ }
+
+ @Test
+ void testIsEmpty() {
+ assertFalse(spreadMap.isEmpty());
+ SpreadMap emptyMap = new SpreadMap(new Object[]{});
+ assertTrue(emptyMap.isEmpty());
+ }
+
+ @Test
+ void testConstructFromEmptyList() {
+ List<Object> emptyList = new ArrayList<>();
+ SpreadMap map = new SpreadMap(emptyList);
+ assertTrue(map.isEmpty());
+ assertEquals("*:[:]", map.toString());
+ }
+
+ @Test
+ void testWithIntegerKeysAndValues() {
+ SpreadMap map = new SpreadMap(new Object[]{1, "one", 2, "two"});
+ assertEquals("one", map.get(1));
+ assertEquals("two", map.get(2));
+ }
+
+ @Test
+ void testWithMixedTypes() {
+ SpreadMap map = new SpreadMap(new Object[]{"string", 123, 456,
"number", null, "nullKey"});
+ assertEquals(123, map.get("string"));
+ assertEquals("number", map.get(456));
+ assertEquals("nullKey", map.get(null));
+ }
+
+ @Test
+ void testHashCodeConsistency() {
+ int hash1 = spreadMap.hashCode();
+ int hash2 = spreadMap.hashCode();
+ assertEquals(hash1, hash2);
+ }
+}
diff --git a/src/test/java/groovy/time/DurationJUnit5Test.java
b/src/test/java/groovy/time/DurationJUnit5Test.java
new file mode 100644
index 0000000000..85ea5801a2
--- /dev/null
+++ b/src/test/java/groovy/time/DurationJUnit5Test.java
@@ -0,0 +1,233 @@
+/*
+ * 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.time;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Unit tests for {@link Duration} class.
+ */
+class DurationJUnit5Test {
+
+ @Test
+ void testConstructor() {
+ Duration duration = new Duration(1, 2, 3, 4, 5);
+ assertEquals(1, duration.getDays());
+ assertEquals(2, duration.getHours());
+ assertEquals(3, duration.getMinutes());
+ assertEquals(4, duration.getSeconds());
+ assertEquals(5, duration.getMillis());
+ }
+
+ @Test
+ void testToMilliseconds() {
+ Duration duration = new Duration(1, 0, 0, 0, 0);
+ assertEquals(24 * 60 * 60 * 1000L, duration.toMilliseconds());
+ }
+
+ @Test
+ void testToMillisecondsComplex() {
+ Duration duration = new Duration(1, 2, 3, 4, 5);
+ long expected = ((((1L * 24 + 2) * 60 + 3) * 60 + 4) * 1000) + 5;
+ assertEquals(expected, duration.toMilliseconds());
+ }
+
+ @Test
+ void testPlusDuration() {
+ Duration d1 = new Duration(1, 2, 3, 4, 5);
+ Duration d2 = new Duration(1, 1, 1, 1, 1);
+ Duration result = d1.plus(d2);
+ assertEquals(2, result.getDays());
+ assertEquals(3, result.getHours());
+ assertEquals(4, result.getMinutes());
+ assertEquals(5, result.getSeconds());
+ assertEquals(6, result.getMillis());
+ }
+
+ @Test
+ void testPlusTimeDuration() {
+ Duration d = new Duration(1, 0, 0, 0, 0);
+ TimeDuration td = new TimeDuration(0, 2, 30, 0, 0);
+ Duration result = d.plus(td);
+ assertNotNull(result);
+ }
+
+ @Test
+ void testPlusDatumDependentDuration() {
+ Duration d = new Duration(1, 0, 0, 0, 0);
+ DatumDependentDuration ddd = new DatumDependentDuration(1, 2, 3, 4, 5,
6, 7);
+ DatumDependentDuration result = d.plus(ddd);
+ assertNotNull(result);
+ assertEquals(1, result.getYears());
+ assertEquals(2, result.getMonths());
+ }
+
+ @Test
+ void testMinusDuration() {
+ Duration d1 = new Duration(5, 10, 30, 45, 500);
+ Duration d2 = new Duration(2, 5, 15, 20, 200);
+ Duration result = d1.minus(d2);
+ assertEquals(3, result.getDays());
+ assertEquals(5, result.getHours());
+ assertEquals(15, result.getMinutes());
+ assertEquals(25, result.getSeconds());
+ assertEquals(300, result.getMillis());
+ }
+
+ @Test
+ void testMinusTimeDuration() {
+ Duration d = new Duration(2, 5, 30, 0, 0);
+ TimeDuration td = new TimeDuration(1, 2, 15, 0, 0);
+ TimeDuration result = d.minus(td);
+ assertEquals(1, result.getDays());
+ assertEquals(3, result.getHours());
+ assertEquals(15, result.getMinutes());
+ }
+
+ @Test
+ void testMinusDatumDependentDuration() {
+ Duration d = new Duration(10, 5, 30, 20, 100);
+ DatumDependentDuration ddd = new DatumDependentDuration(1, 2, 3, 1,
10, 5, 50);
+ DatumDependentDuration result = d.minus(ddd);
+ assertEquals(-1, result.getYears());
+ assertEquals(-2, result.getMonths());
+ assertEquals(7, result.getDays());
+ }
+
+ @Test
+ void testMinusTimeDatumDependentDuration() {
+ Duration d = new Duration(10, 5, 30, 20, 100);
+ TimeDatumDependentDuration tddd = new TimeDatumDependentDuration(1, 2,
3, 1, 10, 5, 50);
+ TimeDatumDependentDuration result = d.minus(tddd);
+ assertEquals(-1, result.getYears());
+ assertEquals(-2, result.getMonths());
+ }
+
+ @Test
+ void testGetAgo() {
+ Duration duration = new Duration(1, 0, 0, 0, 0);
+ Date ago = duration.getAgo();
+ assertNotNull(ago);
+
+ Calendar expected = Calendar.getInstance();
+ expected.add(Calendar.DAY_OF_YEAR, -1);
+ expected.set(Calendar.HOUR_OF_DAY, 0);
+ expected.set(Calendar.MINUTE, 0);
+ expected.set(Calendar.SECOND, 0);
+ expected.set(Calendar.MILLISECOND, 0);
+
+ assertEquals(expected.getTimeInMillis(), ago.getTime());
+ }
+
+ @Test
+ void testGetFrom() {
+ Duration duration = new Duration(3, 0, 0, 0, 0);
+ BaseDuration.From from = duration.getFrom();
+ assertNotNull(from);
+ Date now = from.getNow();
+ assertNotNull(now);
+
+ Calendar expected = Calendar.getInstance();
+ expected.add(Calendar.DAY_OF_YEAR, 3);
+ expected.set(Calendar.HOUR_OF_DAY, 0);
+ expected.set(Calendar.MINUTE, 0);
+ expected.set(Calendar.SECOND, 0);
+ expected.set(Calendar.MILLISECOND, 0);
+
+ assertEquals(expected.getTimeInMillis(), now.getTime());
+ }
+
+ @Test
+ void testZeroDuration() {
+ Duration zero = new Duration(0, 0, 0, 0, 0);
+ assertEquals(0, zero.toMilliseconds());
+ }
+
+ @Test
+ void testNegativeDuration() {
+ Duration negative = new Duration(-1, -2, -3, -4, -5);
+ assertTrue(negative.toMilliseconds() < 0);
+ }
+
+ @Test
+ void testMillisecondsOnly() {
+ Duration millis = new Duration(0, 0, 0, 0, 500);
+ assertEquals(500, millis.toMilliseconds());
+ }
+
+ @Test
+ void testSecondsOnly() {
+ Duration seconds = new Duration(0, 0, 0, 45, 0);
+ assertEquals(45 * 1000L, seconds.toMilliseconds());
+ }
+
+ @Test
+ void testMinutesOnly() {
+ Duration minutes = new Duration(0, 0, 30, 0, 0);
+ assertEquals(30 * 60 * 1000L, minutes.toMilliseconds());
+ }
+
+ @Test
+ void testHoursOnly() {
+ Duration hours = new Duration(0, 12, 0, 0, 0);
+ assertEquals(12 * 60 * 60 * 1000L, hours.toMilliseconds());
+ }
+
+ @Test
+ void testDaysOnly() {
+ Duration days = new Duration(7, 0, 0, 0, 0);
+ assertEquals(7 * 24 * 60 * 60 * 1000L, days.toMilliseconds());
+ }
+
+ @Test
+ void testPlusWithZero() {
+ Duration d = new Duration(1, 2, 3, 4, 5);
+ Duration zero = new Duration(0, 0, 0, 0, 0);
+ Duration result = d.plus(zero);
+ assertEquals(1, result.getDays());
+ assertEquals(2, result.getHours());
+ assertEquals(3, result.getMinutes());
+ assertEquals(4, result.getSeconds());
+ assertEquals(5, result.getMillis());
+ }
+
+ @Test
+ void testMinusWithZero() {
+ Duration d = new Duration(1, 2, 3, 4, 5);
+ Duration zero = new Duration(0, 0, 0, 0, 0);
+ Duration result = d.minus(zero);
+ assertEquals(1, result.getDays());
+ assertEquals(2, result.getHours());
+ assertEquals(3, result.getMinutes());
+ assertEquals(4, result.getSeconds());
+ assertEquals(5, result.getMillis());
+ }
+
+ @Test
+ void testLargeDuration() {
+ Duration large = new Duration(365, 23, 59, 59, 999);
+ long expected = ((((365L * 24 + 23) * 60 + 59) * 60 + 59) * 1000) +
999;
+ assertEquals(expected, large.toMilliseconds());
+ }
+}
diff --git
a/src/test/java/groovy/time/TimeDatumDependentDurationJUnit5Test.java
b/src/test/java/groovy/time/TimeDatumDependentDurationJUnit5Test.java
new file mode 100644
index 0000000000..b965f4ca13
--- /dev/null
+++ b/src/test/java/groovy/time/TimeDatumDependentDurationJUnit5Test.java
@@ -0,0 +1,177 @@
+/*
+ * 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.time;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Unit tests for {@link TimeDatumDependentDuration} class.
+ */
+class TimeDatumDependentDurationJUnit5Test {
+
+ @Test
+ void testConstructor() {
+ TimeDatumDependentDuration duration = new
TimeDatumDependentDuration(1, 2, 3, 4, 5, 6, 7);
+ assertEquals(1, duration.getYears());
+ assertEquals(2, duration.getMonths());
+ assertEquals(3, duration.getDays());
+ assertEquals(4, duration.getHours());
+ assertEquals(5, duration.getMinutes());
+ assertEquals(6, duration.getSeconds());
+ assertEquals(7, duration.getMillis());
+ }
+
+ @Test
+ void testPlusDuration() {
+ TimeDatumDependentDuration tddd = new TimeDatumDependentDuration(1, 2,
3, 4, 5, 6, 7);
+ Duration d = new Duration(1, 2, 3, 4, 5);
+ DatumDependentDuration result = tddd.plus(d);
+ assertTrue(result instanceof TimeDatumDependentDuration);
+ assertEquals(1, result.getYears());
+ assertEquals(2, result.getMonths());
+ assertEquals(4, result.getDays());
+ assertEquals(6, result.getHours());
+ assertEquals(8, result.getMinutes());
+ assertEquals(10, result.getSeconds());
+ assertEquals(12, result.getMillis());
+ }
+
+ @Test
+ void testPlusDatumDependentDuration() {
+ TimeDatumDependentDuration tddd1 = new TimeDatumDependentDuration(1,
2, 3, 4, 5, 6, 7);
+ DatumDependentDuration ddd2 = new DatumDependentDuration(2, 3, 4, 5,
6, 7, 8);
+ DatumDependentDuration result = tddd1.plus(ddd2);
+ assertTrue(result instanceof TimeDatumDependentDuration);
+ assertEquals(3, result.getYears());
+ assertEquals(5, result.getMonths());
+ assertEquals(7, result.getDays());
+ assertEquals(9, result.getHours());
+ assertEquals(11, result.getMinutes());
+ assertEquals(13, result.getSeconds());
+ assertEquals(15, result.getMillis());
+ }
+
+ @Test
+ void testMinusDuration() {
+ TimeDatumDependentDuration tddd = new TimeDatumDependentDuration(1, 2,
10, 10, 30, 45, 500);
+ Duration d = new Duration(3, 2, 10, 15, 100);
+ DatumDependentDuration result = tddd.minus(d);
+ assertTrue(result instanceof TimeDatumDependentDuration);
+ assertEquals(1, result.getYears());
+ assertEquals(2, result.getMonths());
+ assertEquals(7, result.getDays());
+ assertEquals(8, result.getHours());
+ assertEquals(20, result.getMinutes());
+ assertEquals(30, result.getSeconds());
+ assertEquals(400, result.getMillis());
+ }
+
+ @Test
+ void testMinusDatumDependentDuration() {
+ TimeDatumDependentDuration tddd1 = new TimeDatumDependentDuration(5,
6, 10, 10, 30, 45, 500);
+ DatumDependentDuration ddd2 = new DatumDependentDuration(2, 3, 4, 5,
15, 20, 100);
+ DatumDependentDuration result = tddd1.minus(ddd2);
+ assertTrue(result instanceof TimeDatumDependentDuration);
+ assertEquals(3, result.getYears());
+ assertEquals(3, result.getMonths());
+ assertEquals(6, result.getDays());
+ assertEquals(5, result.getHours());
+ assertEquals(15, result.getMinutes());
+ assertEquals(25, result.getSeconds());
+ assertEquals(400, result.getMillis());
+ }
+
+ @Test
+ void testGetFrom() {
+ TimeDatumDependentDuration duration = new
TimeDatumDependentDuration(1, 2, 3, 4, 5, 6, 7);
+ BaseDuration.From from = duration.getFrom();
+ assertNotNull(from);
+ Date now = from.getNow();
+ assertNotNull(now);
+
+ Calendar expected = Calendar.getInstance();
+ expected.add(Calendar.YEAR, 1);
+ expected.add(Calendar.MONTH, 2);
+ expected.add(Calendar.DAY_OF_YEAR, 3);
+ expected.add(Calendar.HOUR_OF_DAY, 4);
+ expected.add(Calendar.MINUTE, 5);
+ expected.add(Calendar.SECOND, 6);
+ expected.add(Calendar.MILLISECOND, 7);
+
+ long tolerance = 5000;
+ assertTrue(Math.abs(expected.getTimeInMillis() - now.getTime()) <
tolerance);
+ }
+
+ @Test
+ void testZeroDuration() {
+ TimeDatumDependentDuration zero = new TimeDatumDependentDuration(0, 0,
0, 0, 0, 0, 0);
+ assertEquals(0, zero.getYears());
+ assertEquals(0, zero.getMonths());
+ assertEquals(0, zero.getDays());
+ assertEquals(0, zero.getHours());
+ assertEquals(0, zero.getMinutes());
+ assertEquals(0, zero.getSeconds());
+ assertEquals(0, zero.getMillis());
+ }
+
+ @Test
+ void testNegativeDuration() {
+ TimeDatumDependentDuration negative = new
TimeDatumDependentDuration(-1, -2, -3, -4, -5, -6, -7);
+ assertEquals(-1, negative.getYears());
+ assertEquals(-2, negative.getMonths());
+ assertEquals(-3, negative.getDays());
+ assertEquals(-4, negative.getHours());
+ assertEquals(-5, negative.getMinutes());
+ assertEquals(-6, negative.getSeconds());
+ assertEquals(-7, negative.getMillis());
+ }
+
+ @Test
+ void testPlusTimeDatumDependentDuration() {
+ TimeDatumDependentDuration tddd1 = new TimeDatumDependentDuration(1,
2, 3, 4, 5, 6, 7);
+ TimeDatumDependentDuration tddd2 = new TimeDatumDependentDuration(1,
1, 1, 1, 1, 1, 1);
+ DatumDependentDuration result = tddd1.plus(tddd2);
+ assertEquals(2, result.getYears());
+ assertEquals(3, result.getMonths());
+ assertEquals(4, result.getDays());
+ assertEquals(5, result.getHours());
+ assertEquals(6, result.getMinutes());
+ assertEquals(7, result.getSeconds());
+ assertEquals(8, result.getMillis());
+ }
+
+ @Test
+ void testMinusTimeDatumDependentDuration() {
+ TimeDatumDependentDuration tddd1 = new TimeDatumDependentDuration(5,
6, 7, 8, 9, 10, 11);
+ TimeDatumDependentDuration tddd2 = new TimeDatumDependentDuration(1,
2, 3, 4, 5, 6, 7);
+ DatumDependentDuration result = tddd1.minus(tddd2);
+ assertEquals(4, result.getYears());
+ assertEquals(4, result.getMonths());
+ assertEquals(4, result.getDays());
+ assertEquals(4, result.getHours());
+ assertEquals(4, result.getMinutes());
+ assertEquals(4, result.getSeconds());
+ assertEquals(4, result.getMillis());
+ }
+}
diff --git a/src/test/java/groovy/time/TimeDurationJUnit5Test.java
b/src/test/java/groovy/time/TimeDurationJUnit5Test.java
new file mode 100644
index 0000000000..503b6563cc
--- /dev/null
+++ b/src/test/java/groovy/time/TimeDurationJUnit5Test.java
@@ -0,0 +1,218 @@
+/*
+ * 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.time;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Unit tests for {@link TimeDuration} class.
+ */
+class TimeDurationJUnit5Test {
+
+ @Test
+ void testConstructorWithoutDays() {
+ TimeDuration duration = new TimeDuration(2, 30, 45, 100);
+ assertEquals(0, duration.getDays());
+ assertEquals(2, duration.getHours());
+ assertEquals(30, duration.getMinutes());
+ assertEquals(45, duration.getSeconds());
+ assertEquals(100, duration.getMillis());
+ }
+
+ @Test
+ void testConstructorWithDays() {
+ TimeDuration duration = new TimeDuration(1, 2, 30, 45, 100);
+ assertEquals(1, duration.getDays());
+ assertEquals(2, duration.getHours());
+ assertEquals(30, duration.getMinutes());
+ assertEquals(45, duration.getSeconds());
+ assertEquals(100, duration.getMillis());
+ }
+
+ @Test
+ void testToMilliseconds() {
+ TimeDuration duration = new TimeDuration(1, 2, 3, 4);
+ long expected = (((1L * 60) + 2) * 60 + 3) * 1000 + 4;
+ assertEquals(expected, duration.toMilliseconds());
+ }
+
+ @Test
+ void testPlusDuration() {
+ // TimeDuration constructor: hours, minutes, seconds, millis
+ TimeDuration td1 = new TimeDuration(1, 2, 30, 100);
+ // Duration constructor: days, hours, minutes, seconds, millis
+ Duration d2 = new Duration(1, 1, 15, 20, 50);
+ Duration result = td1.plus(d2);
+ assertTrue(result instanceof TimeDuration);
+ // td1: days=0, hours=1, minutes=2, seconds=30, millis=100
+ // d2: days=1, hours=1, minutes=15, seconds=20, millis=50
+ // result: days=1, hours=2, minutes=17, seconds=50, millis=150 (no
normalization)
+ assertEquals(1, result.getDays());
+ assertEquals(2, result.getHours());
+ assertEquals(17, result.getMinutes());
+ assertEquals(50, result.getSeconds());
+ assertEquals(150, result.getMillis());
+ }
+
+ @Test
+ void testPlusDatumDependentDuration() {
+ TimeDuration td = new TimeDuration(1, 2, 30, 45, 100);
+ DatumDependentDuration ddd = new DatumDependentDuration(2, 3, 4, 5,
15, 10, 50);
+ DatumDependentDuration result = td.plus(ddd);
+ assertTrue(result instanceof TimeDatumDependentDuration);
+ assertEquals(2, result.getYears());
+ assertEquals(3, result.getMonths());
+ assertEquals(5, result.getDays());
+ assertEquals(7, result.getHours());
+ assertEquals(45, result.getMinutes());
+ assertEquals(55, result.getSeconds());
+ assertEquals(150, result.getMillis());
+ }
+
+ @Test
+ void testMinusDuration() {
+ TimeDuration td1 = new TimeDuration(2, 5, 30, 45, 100);
+ Duration d2 = new Duration(1, 2, 15, 20, 50);
+ Duration result = td1.minus(d2);
+ assertTrue(result instanceof TimeDuration);
+ assertEquals(1, result.getDays());
+ assertEquals(3, result.getHours());
+ assertEquals(15, result.getMinutes());
+ assertEquals(25, result.getSeconds());
+ assertEquals(50, result.getMillis());
+ }
+
+ @Test
+ void testMinusDatumDependentDuration() {
+ TimeDuration td = new TimeDuration(2, 5, 30, 45, 100);
+ DatumDependentDuration ddd = new DatumDependentDuration(1, 2, 1, 2,
15, 20, 50);
+ DatumDependentDuration result = td.minus(ddd);
+ assertTrue(result instanceof TimeDatumDependentDuration);
+ assertEquals(-1, result.getYears());
+ assertEquals(-2, result.getMonths());
+ assertEquals(1, result.getDays());
+ assertEquals(3, result.getHours());
+ assertEquals(15, result.getMinutes());
+ assertEquals(25, result.getSeconds());
+ assertEquals(50, result.getMillis());
+ }
+
+ @Test
+ void testGetAgo() {
+ TimeDuration duration = new TimeDuration(0, 1, 0, 0, 0);
+ Date ago = duration.getAgo();
+ assertNotNull(ago);
+
+ // The time should be approximately 1 hour ago (with some tolerance
for test execution)
+ long expectedTime = System.currentTimeMillis() - (60 * 60 * 1000L);
+ long actualTime = ago.getTime();
+ assertTrue(Math.abs(expectedTime - actualTime) < 5000, "Time should be
approximately 1 hour ago");
+ }
+
+ @Test
+ void testGetAgoWithAllComponents() {
+ TimeDuration duration = new TimeDuration(1, 2, 30, 45, 500);
+ Date ago = duration.getAgo();
+ assertNotNull(ago);
+
+ Calendar expected = Calendar.getInstance();
+ expected.add(Calendar.DAY_OF_YEAR, -1);
+ expected.add(Calendar.HOUR_OF_DAY, -2);
+ expected.add(Calendar.MINUTE, -30);
+ expected.add(Calendar.SECOND, -45);
+ expected.add(Calendar.MILLISECOND, -500);
+
+ // Allow some tolerance for test execution time
+ long tolerance = 5000;
+ assertTrue(Math.abs(expected.getTimeInMillis() - ago.getTime()) <
tolerance);
+ }
+
+ @Test
+ void testGetFrom() {
+ TimeDuration duration = new TimeDuration(0, 1, 0, 0, 0);
+ BaseDuration.From from = duration.getFrom();
+ assertNotNull(from);
+ Date now = from.getNow();
+ assertNotNull(now);
+
+ // The time should be approximately 1 hour from now
+ long expectedTime = System.currentTimeMillis() + (60 * 60 * 1000L);
+ long actualTime = now.getTime();
+ assertTrue(Math.abs(expectedTime - actualTime) < 5000, "Time should be
approximately 1 hour from now");
+ }
+
+ @Test
+ void testGetFromWithAllComponents() {
+ TimeDuration duration = new TimeDuration(1, 2, 30, 45, 500);
+ BaseDuration.From from = duration.getFrom();
+ Date now = from.getNow();
+ assertNotNull(now);
+
+ Calendar expected = Calendar.getInstance();
+ expected.add(Calendar.DAY_OF_YEAR, 1);
+ expected.add(Calendar.HOUR_OF_DAY, 2);
+ expected.add(Calendar.MINUTE, 30);
+ expected.add(Calendar.SECOND, 45);
+ expected.add(Calendar.MILLISECOND, 500);
+
+ long tolerance = 5000;
+ assertTrue(Math.abs(expected.getTimeInMillis() - now.getTime()) <
tolerance);
+ }
+
+ @Test
+ void testZeroTimeDuration() {
+ TimeDuration zero = new TimeDuration(0, 0, 0, 0);
+ assertEquals(0, zero.toMilliseconds());
+ }
+
+ @Test
+ void testNegativeTimeDuration() {
+ TimeDuration negative = new TimeDuration(-1, -30, -15, -500);
+ assertTrue(negative.toMilliseconds() < 0);
+ }
+
+ @Test
+ void testOnlyMillis() {
+ TimeDuration millis = new TimeDuration(0, 0, 0, 500);
+ assertEquals(500, millis.toMilliseconds());
+ }
+
+ @Test
+ void testOnlySeconds() {
+ TimeDuration seconds = new TimeDuration(0, 0, 30, 0);
+ assertEquals(30 * 1000L, seconds.toMilliseconds());
+ }
+
+ @Test
+ void testOnlyMinutes() {
+ TimeDuration minutes = new TimeDuration(0, 15, 0, 0);
+ assertEquals(15 * 60 * 1000L, minutes.toMilliseconds());
+ }
+
+ @Test
+ void testOnlyHours() {
+ TimeDuration hours = new TimeDuration(3, 0, 0, 0);
+ assertEquals(3 * 60 * 60 * 1000L, hours.toMilliseconds());
+ }
+}
diff --git a/src/test/java/org/codehaus/groovy/syntax/ReductionJUnit5Test.java
b/src/test/java/org/codehaus/groovy/syntax/ReductionJUnit5Test.java
new file mode 100644
index 0000000000..8389b80632
--- /dev/null
+++ b/src/test/java/org/codehaus/groovy/syntax/ReductionJUnit5Test.java
@@ -0,0 +1,263 @@
+/*
+ * 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 static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Unit tests for {@link Reduction}.
+ */
+class ReductionJUnit5Test {
+
+ @Test
+ void testConstructorWithToken() {
+ Token root = new Token(Types.PLUS, "+", 1, 1);
+ Reduction reduction = new Reduction(root);
+ assertEquals(1, reduction.size());
+ assertSame(root, reduction.getRoot());
+ }
+
+ @Test
+ void testEmptyReduction() {
+ assertTrue(Reduction.EMPTY.isEmpty());
+ assertEquals(0, Reduction.EMPTY.size());
+ assertNull(Reduction.EMPTY.getRoot());
+ }
+
+ @Test
+ void testNewContainer() {
+ Reduction container = Reduction.newContainer();
+ assertFalse(container.isEmpty());
+ assertEquals(1, container.size());
+ assertSame(Token.NULL, container.getRoot());
+ }
+
+ @Test
+ void testAdd() {
+ Token root = new Token(Types.PLUS, "+", 1, 1);
+ Reduction reduction = new Reduction(root);
+
+ Token child = new Token(Types.INTEGER_NUMBER, "5", 1, 3);
+ reduction.add(child);
+
+ assertEquals(2, reduction.size());
+ assertSame(child, reduction.get(1));
+ }
+
+ @Test
+ void testAddMultiple() {
+ Token root = new Token(Types.PLUS, "+", 1, 1);
+ Reduction reduction = new Reduction(root);
+
+ Token child1 = new Token(Types.INTEGER_NUMBER, "5", 1, 3);
+ Token child2 = new Token(Types.INTEGER_NUMBER, "3", 1, 5);
+ reduction.add(child1);
+ reduction.add(child2);
+
+ assertEquals(3, reduction.size());
+ assertSame(child1, reduction.get(1));
+ assertSame(child2, reduction.get(2));
+ }
+
+ @Test
+ void testSetAtIndex() {
+ Token root = new Token(Types.PLUS, "+", 1, 1);
+ Reduction reduction = new Reduction(root);
+
+ Token child = new Token(Types.INTEGER_NUMBER, "5", 1, 3);
+ reduction.set(1, child);
+
+ assertEquals(2, reduction.size());
+ assertSame(child, reduction.get(1));
+ }
+
+ @Test
+ void testSetAtGapIndex() {
+ Token root = new Token(Types.PLUS, "+", 1, 1);
+ Reduction reduction = new Reduction(root);
+
+ Token child = new Token(Types.INTEGER_NUMBER, "5", 1, 3);
+ reduction.set(5, child);
+
+ assertEquals(6, reduction.size());
+ assertNull(reduction.get(1));
+ assertNull(reduction.get(2));
+ assertNull(reduction.get(3));
+ assertNull(reduction.get(4));
+ assertSame(child, reduction.get(5));
+ }
+
+ @Test
+ void testSetNonTokenAsRootThrows() {
+ Token root = new Token(Types.PLUS, "+", 1, 1);
+ Reduction reduction = new Reduction(root);
+ Reduction nestedReduction = Reduction.newContainer();
+
+ assertThrows(GroovyBugError.class, () -> reduction.set(0,
nestedReduction));
+ }
+
+ @Test
+ void testRemove() {
+ Token root = new Token(Types.PLUS, "+", 1, 1);
+ Reduction reduction = new Reduction(root);
+
+ Token child1 = new Token(Types.INTEGER_NUMBER, "5", 1, 3);
+ Token child2 = new Token(Types.INTEGER_NUMBER, "3", 1, 5);
+ reduction.add(child1);
+ reduction.add(child2);
+
+ CSTNode removed = reduction.remove(1);
+
+ assertSame(child1, removed);
+ assertEquals(2, reduction.size());
+ assertSame(child2, reduction.get(1));
+ }
+
+ @Test
+ void testRemoveRootThrows() {
+ Token root = new Token(Types.PLUS, "+", 1, 1);
+ Reduction reduction = new Reduction(root);
+
+ assertThrows(GroovyBugError.class, () -> reduction.remove(0));
+ }
+
+ @Test
+ void testGetBeyondSize() {
+ Token root = new Token(Types.PLUS, "+", 1, 1);
+ Reduction reduction = new Reduction(root);
+
+ assertNull(reduction.get(10));
+ }
+
+ @Test
+ void testMarkAsExpression() {
+ // Use PLUS operator which is not a complex expression type by default
+ Token root = Token.newSymbol(Types.PLUS, 1, 1);
+ Reduction reduction = new Reduction(root);
+
+ assertFalse(reduction.isAnExpression());
+ reduction.markAsExpression();
+ assertTrue(reduction.isAnExpression());
+ }
+
+ @Test
+ void testIsAnExpressionWithComplexExpression() {
+ Token root = Token.newSymbol(Types.LEFT_PARENTHESIS, 1, 1);
+ Reduction reduction = new Reduction(root);
+ // Types.LEFT_PARENTHESIS is categorized as a complex expression type
+ // Test the behavior
+ assertFalse(reduction.isAnExpression());
+ }
+
+ @Test
+ void testAsReduction() {
+ Token root = new Token(Types.PLUS, "+", 1, 1);
+ Reduction reduction = new Reduction(root);
+
+ assertSame(reduction, reduction.asReduction());
+ }
+
+ @Test
+ void testSize() {
+ Token root = new Token(Types.PLUS, "+", 1, 1);
+ Reduction reduction = new Reduction(root);
+ assertEquals(1, reduction.size());
+
+ reduction.add(new Token(Types.INTEGER_NUMBER, "1", 1, 3));
+ assertEquals(2, reduction.size());
+
+ reduction.add(new Token(Types.INTEGER_NUMBER, "2", 1, 5));
+ assertEquals(3, reduction.size());
+ }
+
+ @Test
+ void testHasChildren() {
+ Token root = new Token(Types.PLUS, "+", 1, 1);
+ Reduction reduction = new Reduction(root);
+ assertFalse(reduction.hasChildren());
+
+ reduction.add(new Token(Types.INTEGER_NUMBER, "1", 1, 3));
+ assertTrue(reduction.hasChildren());
+ }
+
+ @Test
+ void testChildren() {
+ Token root = new Token(Types.PLUS, "+", 1, 1);
+ Reduction reduction = new Reduction(root);
+ assertEquals(0, reduction.children());
+
+ reduction.add(new Token(Types.INTEGER_NUMBER, "1", 1, 3));
+ assertEquals(1, reduction.children());
+
+ reduction.add(new Token(Types.INTEGER_NUMBER, "2", 1, 5));
+ assertEquals(2, reduction.children());
+ }
+
+ @Test
+ void testGetWithSafe() {
+ Token root = new Token(Types.PLUS, "+", 1, 1);
+ Reduction reduction = new Reduction(root);
+
+ // Safe mode returns Token.NULL for out of bounds
+ CSTNode result = reduction.get(10, true);
+ assertSame(Token.NULL, result);
+
+ // Regular mode returns null
+ assertNull(reduction.get(10, false));
+ }
+
+ @Test
+ void testGetRoot() {
+ Token root = new Token(Types.PLUS, "+", 1, 1);
+ Reduction reduction = new Reduction(root);
+
+ assertSame(root, reduction.getRoot());
+ assertSame(root, reduction.getRoot(false));
+ assertSame(root, reduction.getRoot(true));
+ }
+
+ @Test
+ void testEmptyReductionGetRootSafe() {
+ // Safe mode on empty reduction
+ Token result = Reduction.EMPTY.getRoot(true);
+ assertSame(Token.NULL, result);
+ }
+
+ @Test
+ void testAddChildrenOf() {
+ Token root1 = new Token(Types.PLUS, "+", 1, 1);
+ Reduction reduction1 = new Reduction(root1);
+
+ Token root2 = new Token(Types.MINUS, "-", 1, 1);
+ Reduction reduction2 = new Reduction(root2);
+ Token child1 = new Token(Types.INTEGER_NUMBER, "1", 1, 3);
+ Token child2 = new Token(Types.INTEGER_NUMBER, "2", 1, 5);
+ reduction2.add(child1);
+ reduction2.add(child2);
+
+ reduction1.addChildrenOf(reduction2);
+
+ assertEquals(3, reduction1.size());
+ assertSame(child1, reduction1.get(1));
+ assertSame(child2, reduction1.get(2));
+ }
+}
diff --git a/src/test/java/org/codehaus/groovy/syntax/TokenJUnit5Test.java
b/src/test/java/org/codehaus/groovy/syntax/TokenJUnit5Test.java
new file mode 100644
index 0000000000..d8301b7cf5
--- /dev/null
+++ b/src/test/java/org/codehaus/groovy/syntax/TokenJUnit5Test.java
@@ -0,0 +1,262 @@
+/*
+ * 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.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Unit tests for {@link Token}.
+ */
+class TokenJUnit5Test {
+
+ private static final int LINE = 11;
+ private static final int COLUMN = 33;
+
+ @Test
+ void testConstruct() {
+ Token token = new Token(42, "forty-two", LINE, COLUMN);
+
+ assertEquals(42, token.getType());
+ assertEquals("forty-two", token.getText());
+ assertEquals(LINE, token.getStartLine());
+ assertEquals(COLUMN, token.getStartColumn());
+ }
+
+ @Test
+ void testGetMeaning() {
+ Token token = new Token(Types.PLUS, "+", LINE, COLUMN);
+ assertEquals(Types.PLUS, token.getMeaning());
+ }
+
+ @Test
+ void testSetMeaning() {
+ Token token = new Token(Types.PLUS, "+", LINE, COLUMN);
+ token.setMeaning(Types.MINUS);
+ assertEquals(Types.MINUS, token.getMeaning());
+ assertEquals(Types.PLUS, token.getType()); // Type unchanged
+ }
+
+ @Test
+ void testEofTokenImmutable() {
+ Token eof = Token.EOF;
+ eof.setMeaning(Types.PLUS);
+ assertEquals(Types.EOF, eof.getMeaning()); // Should not change
+ eof.setText("changed");
+ assertEquals("", eof.getText()); // Should not change
+ }
+
+ @Test
+ void testNullTokenImmutable() {
+ Token nullToken = Token.NULL;
+ nullToken.setMeaning(Types.PLUS);
+ assertEquals(Types.UNKNOWN, nullToken.getMeaning()); // Should not
change
+ nullToken.setText("changed");
+ assertEquals("", nullToken.getText()); // Should not change
+ }
+
+ @Test
+ void testDup() {
+ Token original = new Token(Types.IDENTIFIER, "myVar", LINE, COLUMN);
+ original.setMeaning(Types.LEFT_SQUARE_BRACKET);
+
+ Token copy = original.dup();
+
+ assertEquals(original.getType(), copy.getType());
+ assertEquals(original.getText(), copy.getText());
+ assertEquals(original.getMeaning(), copy.getMeaning());
+ assertEquals(original.getStartLine(), copy.getStartLine());
+ assertEquals(original.getStartColumn(), copy.getStartColumn());
+
+ // Verify they are different objects
+ assertNotSame(original, copy);
+ }
+
+ @Test
+ void testSize() {
+ Token token = new Token(Types.PLUS, "+", LINE, COLUMN);
+ assertEquals(1, token.size());
+ }
+
+ @Test
+ void testGetReturnsThis() {
+ Token token = new Token(Types.PLUS, "+", LINE, COLUMN);
+ assertSame(token, token.get(0));
+ }
+
+ @Test
+ void testGetWithInvalidIndexThrows() {
+ Token token = new Token(Types.PLUS, "+", LINE, COLUMN);
+ assertThrows(org.codehaus.groovy.GroovyBugError.class, () ->
token.get(1));
+ }
+
+ @Test
+ void testGetRoot() {
+ Token token = new Token(Types.PLUS, "+", LINE, COLUMN);
+ assertSame(token, token.getRoot());
+ }
+
+ @Test
+ void testGetRootText() {
+ Token token = new Token(Types.PLUS, "+", LINE, COLUMN);
+ assertEquals("+", token.getRootText());
+ }
+
+ @Test
+ void testAsReduction() {
+ Token token = new Token(Types.PLUS, "+", LINE, COLUMN);
+ Reduction reduction = token.asReduction();
+ assertNotNull(reduction);
+ assertEquals(1, reduction.size());
+ assertSame(token, reduction.getRoot());
+ }
+
+ @Test
+ void testAsReductionWithSecond() {
+ Token token = new Token(Types.PLUS, "+", LINE, COLUMN);
+ Token second = new Token(Types.INTEGER_NUMBER, "5", LINE, COLUMN + 1);
+ Reduction reduction = token.asReduction(second);
+ assertEquals(2, reduction.size());
+ assertSame(token, reduction.getRoot());
+ assertSame(second, reduction.get(1));
+ }
+
+ @Test
+ void testAsReductionWithThird() {
+ Token first = new Token(Types.PLUS, "+", LINE, COLUMN);
+ Token second = new Token(Types.INTEGER_NUMBER, "5", LINE, COLUMN + 1);
+ Token third = new Token(Types.INTEGER_NUMBER, "3", LINE, COLUMN + 3);
+ Reduction reduction = first.asReduction(second, third);
+ assertEquals(3, reduction.size());
+ assertSame(first, reduction.getRoot());
+ assertSame(second, reduction.get(1));
+ assertSame(third, reduction.get(2));
+ }
+
+ @Test
+ void testAsReductionWithFourth() {
+ Token first = new Token(Types.PLUS, "+", LINE, COLUMN);
+ Token second = new Token(Types.INTEGER_NUMBER, "5", LINE, COLUMN + 1);
+ Token third = new Token(Types.INTEGER_NUMBER, "3", LINE, COLUMN + 3);
+ Token fourth = new Token(Types.INTEGER_NUMBER, "7", LINE, COLUMN + 5);
+ Reduction reduction = first.asReduction(second, third, fourth);
+ assertEquals(4, reduction.size());
+ }
+
+ @Test
+ void testNewKeyword() {
+ Token ifToken = Token.newKeyword("if", LINE, COLUMN);
+ assertNotNull(ifToken);
+ assertEquals(Types.KEYWORD_IF, ifToken.getType());
+ assertEquals("if", ifToken.getText());
+ }
+
+ @Test
+ void testNewKeywordNotKeyword() {
+ Token notKeyword = Token.newKeyword("notAKeyword", LINE, COLUMN);
+ assertNull(notKeyword);
+ }
+
+ @Test
+ void testNewString() {
+ Token stringToken = Token.newString("hello", LINE, COLUMN);
+ assertEquals(Types.STRING, stringToken.getType());
+ assertEquals("hello", stringToken.getText());
+ }
+
+ @Test
+ void testNewIdentifier() {
+ Token idToken = Token.newIdentifier("myVariable", LINE, COLUMN);
+ assertEquals(Types.IDENTIFIER, idToken.getType());
+ assertEquals("myVariable", idToken.getText());
+ }
+
+ @Test
+ void testNewInteger() {
+ Token intToken = Token.newInteger("42", LINE, COLUMN);
+ assertEquals(Types.INTEGER_NUMBER, intToken.getType());
+ assertEquals("42", intToken.getText());
+ }
+
+ @Test
+ void testNewDecimal() {
+ Token decimalToken = Token.newDecimal("3.14", LINE, COLUMN);
+ assertEquals(Types.DECIMAL_NUMBER, decimalToken.getType());
+ assertEquals("3.14", decimalToken.getText());
+ }
+
+ @Test
+ void testNewSymbolByType() {
+ Token plusToken = Token.newSymbol(Types.PLUS, LINE, COLUMN);
+ assertEquals(Types.PLUS, plusToken.getType());
+ assertEquals("+", plusToken.getText());
+ }
+
+ @Test
+ void testNewSymbolByText() {
+ Token plusToken = Token.newSymbol("+", LINE, COLUMN);
+ assertEquals(Types.PLUS, plusToken.getType());
+ assertEquals("+", plusToken.getText());
+ }
+
+ @Test
+ void testNewPlaceholder() {
+ Token placeholder = Token.newPlaceholder(Types.SYNTH_METHOD);
+ assertEquals(Types.UNKNOWN, placeholder.getType());
+ assertEquals(Types.SYNTH_METHOD, placeholder.getMeaning());
+ assertEquals("", placeholder.getText());
+ assertEquals(-1, placeholder.getStartLine());
+ assertEquals(-1, placeholder.getStartColumn());
+ }
+
+ @Test
+ void testSetText() {
+ Token token = new Token(Types.STRING, "original", LINE, COLUMN);
+ token.setText("changed");
+ assertEquals("changed", token.getText());
+ }
+
+ @Test
+ void testKeywords() {
+ // Test a selection of keywords
+ assertKeyword("class", Types.KEYWORD_CLASS);
+ assertKeyword("def", Types.KEYWORD_DEF);
+ assertKeyword("if", Types.KEYWORD_IF);
+ assertKeyword("else", Types.KEYWORD_ELSE);
+ assertKeyword("while", Types.KEYWORD_WHILE);
+ assertKeyword("for", Types.KEYWORD_FOR);
+ assertKeyword("return", Types.KEYWORD_RETURN);
+ assertKeyword("try", Types.KEYWORD_TRY);
+ assertKeyword("catch", Types.KEYWORD_CATCH);
+ assertKeyword("finally", Types.KEYWORD_FINALLY);
+ assertKeyword("throw", Types.KEYWORD_THROW);
+ assertKeyword("new", Types.KEYWORD_NEW);
+ assertKeyword("true", Types.KEYWORD_TRUE);
+ assertKeyword("false", Types.KEYWORD_FALSE);
+ assertKeyword("null", Types.KEYWORD_NULL);
+ }
+
+ private void assertKeyword(String text, int expectedType) {
+ Token token = Token.newKeyword(text, LINE, COLUMN);
+ assertNotNull(token, "Expected keyword for: " + text);
+ assertEquals(expectedType, token.getType());
+ assertEquals(text, token.getText());
+ }
+}
diff --git a/src/test/java/org/codehaus/groovy/util/CharSequenceReaderTest.java
b/src/test/java/org/codehaus/groovy/util/CharSequenceReaderTest.java
new file mode 100644
index 0000000000..01f5904c0d
--- /dev/null
+++ b/src/test/java/org/codehaus/groovy/util/CharSequenceReaderTest.java
@@ -0,0 +1,189 @@
+/*
+ * 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.*;
+
+/**
+ * Unit tests for {@link CharSequenceReader}.
+ */
+class CharSequenceReaderTest {
+
+ @Test
+ void testReadSingleCharacter() {
+ CharSequenceReader reader = new CharSequenceReader("abc");
+ assertEquals('a', reader.read());
+ assertEquals('b', reader.read());
+ assertEquals('c', reader.read());
+ assertEquals(-1, reader.read());
+ }
+
+ @Test
+ void testReadIntoArray() {
+ CharSequenceReader reader = new CharSequenceReader("hello world");
+ char[] buffer = new char[5];
+ int count = reader.read(buffer, 0, 5);
+ assertEquals(5, count);
+ assertArrayEquals("hello".toCharArray(), buffer);
+ }
+
+ @Test
+ void testReadIntoArrayWithOffset() {
+ CharSequenceReader reader = new CharSequenceReader("test");
+ char[] buffer = new char[10];
+ buffer[0] = 'x';
+ buffer[1] = 'y';
+ int count = reader.read(buffer, 2, 4);
+ assertEquals(4, count);
+ assertEquals('x', buffer[0]);
+ assertEquals('y', buffer[1]);
+ assertEquals('t', buffer[2]);
+ assertEquals('e', buffer[3]);
+ assertEquals('s', buffer[4]);
+ assertEquals('t', buffer[5]);
+ }
+
+ @Test
+ void testReadBeyondEnd() {
+ CharSequenceReader reader = new CharSequenceReader("ab");
+ char[] buffer = new char[5];
+ int count = reader.read(buffer, 0, 5);
+ assertEquals(2, count);
+ assertEquals('a', buffer[0]);
+ assertEquals('b', buffer[1]);
+
+ count = reader.read(buffer, 0, 5);
+ assertEquals(-1, count);
+ }
+
+ @Test
+ void testMarkAndReset() {
+ CharSequenceReader reader = new CharSequenceReader("abcdef");
+ assertEquals('a', reader.read());
+ assertEquals('b', reader.read());
+ reader.mark(100);
+ assertEquals('c', reader.read());
+ assertEquals('d', reader.read());
+ reader.reset();
+ assertEquals('c', reader.read());
+ assertEquals('d', reader.read());
+ }
+
+ @Test
+ void testMarkSupported() {
+ CharSequenceReader reader = new CharSequenceReader("test");
+ assertTrue(reader.markSupported());
+ }
+
+ @Test
+ void testSkip() {
+ CharSequenceReader reader = new CharSequenceReader("abcdefgh");
+ assertEquals('a', reader.read());
+ long skipped = reader.skip(3);
+ assertEquals(3, skipped);
+ assertEquals('e', reader.read());
+ }
+
+ @Test
+ void testSkipBeyondEnd() {
+ CharSequenceReader reader = new CharSequenceReader("abc");
+ long skipped = reader.skip(10);
+ assertEquals(3, skipped);
+ assertEquals(-1, reader.read());
+ }
+
+ @Test
+ void testSkipNegativeThrows() {
+ CharSequenceReader reader = new CharSequenceReader("test");
+ assertThrows(IllegalArgumentException.class, () -> reader.skip(-1));
+ }
+
+ @Test
+ void testSkipAtEnd() {
+ CharSequenceReader reader = new CharSequenceReader("a");
+ reader.read();
+ long skipped = reader.skip(5);
+ assertEquals(-1, skipped);
+ }
+
+ @Test
+ void testClose() {
+ CharSequenceReader reader = new CharSequenceReader("test");
+ reader.read();
+ reader.read();
+ reader.mark(10);
+ reader.close();
+ // After close, reader resets to start and mark is cleared
+ assertEquals('t', reader.read());
+ }
+
+ @Test
+ void testToString() {
+ String input = "hello world";
+ CharSequenceReader reader = new CharSequenceReader(input);
+ assertEquals(input, reader.toString());
+ }
+
+ @Test
+ void testNullInput() {
+ CharSequenceReader reader = new CharSequenceReader(null);
+ assertEquals(-1, reader.read());
+ assertEquals("", reader.toString());
+ }
+
+ @Test
+ void testEmptyInput() {
+ CharSequenceReader reader = new CharSequenceReader("");
+ assertEquals(-1, reader.read());
+ }
+
+ @Test
+ void testReadArrayWithNullThrows() {
+ CharSequenceReader reader = new CharSequenceReader("test");
+ assertThrows(NullPointerException.class, () -> reader.read(null, 0,
1));
+ }
+
+ @Test
+ void testReadArrayWithInvalidBoundsThrows() {
+ CharSequenceReader reader = new CharSequenceReader("test");
+ char[] buffer = new char[5];
+ assertThrows(IndexOutOfBoundsException.class, () ->
reader.read(buffer, -1, 1));
+ assertThrows(IndexOutOfBoundsException.class, () ->
reader.read(buffer, 0, -1));
+ assertThrows(IndexOutOfBoundsException.class, () ->
reader.read(buffer, 3, 5));
+ }
+
+ @Test
+ void testStringBuilder() {
+ StringBuilder sb = new StringBuilder("StringBuilder content");
+ CharSequenceReader reader = new CharSequenceReader(sb);
+ char[] buffer = new char[13];
+ reader.read(buffer, 0, 13);
+ assertEquals("StringBuilder", new String(buffer));
+ }
+
+ @Test
+ void testZeroLengthRead() {
+ CharSequenceReader reader = new CharSequenceReader("test");
+ char[] buffer = new char[5];
+ int count = reader.read(buffer, 0, 0);
+ assertEquals(0, count);
+ }
+}
diff --git
a/subprojects/groovy-json/src/test/java/groovy/json/StringEscapeUtilsTest.java
b/subprojects/groovy-json/src/test/java/groovy/json/StringEscapeUtilsTest.java
new file mode 100644
index 0000000000..0019edd428
--- /dev/null
+++
b/subprojects/groovy-json/src/test/java/groovy/json/StringEscapeUtilsTest.java
@@ -0,0 +1,343 @@
+/*
+ * 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.json;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Unit tests for {@link StringEscapeUtils}.
+ */
+class StringEscapeUtilsTest {
+
+ @Test
+ void testEscapeJavaNull() {
+ assertNull(StringEscapeUtils.escapeJava(null));
+ }
+
+ @Test
+ void testEscapeJavaEmpty() {
+ assertEquals("", StringEscapeUtils.escapeJava(""));
+ }
+
+ @Test
+ void testEscapeJavaBasicString() {
+ assertEquals("hello", StringEscapeUtils.escapeJava("hello"));
+ }
+
+ @Test
+ void testEscapeJavaDoubleQuote() {
+ assertEquals("\\\"hello\\\"",
StringEscapeUtils.escapeJava("\"hello\""));
+ }
+
+ @Test
+ void testEscapeJavaBackslash() {
+ assertEquals("path\\\\to\\\\file",
StringEscapeUtils.escapeJava("path\\to\\file"));
+ }
+
+ @Test
+ void testEscapeJavaNewline() {
+ assertEquals("line1\\nline2",
StringEscapeUtils.escapeJava("line1\nline2"));
+ }
+
+ @Test
+ void testEscapeJavaTab() {
+ assertEquals("col1\\tcol2",
StringEscapeUtils.escapeJava("col1\tcol2"));
+ }
+
+ @Test
+ void testEscapeJavaCarriageReturn() {
+ assertEquals("line1\\rline2",
StringEscapeUtils.escapeJava("line1\rline2"));
+ }
+
+ @Test
+ void testEscapeJavaFormFeed() {
+ assertEquals("page1\\fpage2",
StringEscapeUtils.escapeJava("page1\fpage2"));
+ }
+
+ @Test
+ void testEscapeJavaBackspace() {
+ assertEquals("back\\bspace",
StringEscapeUtils.escapeJava("back\bspace"));
+ }
+
+ @Test
+ void testEscapeJavaSingleQuoteNotEscaped() {
+ assertEquals("it's", StringEscapeUtils.escapeJava("it's"));
+ }
+
+ @Test
+ void testEscapeJavaForwardSlashNotEscaped() {
+ assertEquals("path/to/file",
StringEscapeUtils.escapeJava("path/to/file"));
+ }
+
+ @Test
+ void testEscapeJavaUnicodeHigh() {
+ // Characters > 0xfff
+ String input = "\u4e2d\u6587"; // Chinese characters
+ String result = StringEscapeUtils.escapeJava(input);
+ assertTrue(result.contains("\\u"));
+ }
+
+ @Test
+ void testEscapeJavaUnicodeMid() {
+ // Characters > 0xff and <= 0xfff
+ String input = "\u0100"; // Latin Extended-A
+ String result = StringEscapeUtils.escapeJava(input);
+ assertTrue(result.contains("\\u0100"));
+ }
+
+ @Test
+ void testEscapeJavaUnicodeLow() {
+ // Characters >= 0x7f and <= 0xff
+ String input = "\u007f"; // DEL character
+ String result = StringEscapeUtils.escapeJava(input);
+ assertTrue(result.contains("\\u007F") || result.contains("\\u007f"));
+ }
+
+ @Test
+ void testEscapeJavaControlCharacter() {
+ // Control characters < 32 (not the standard escape sequences)
+ String input = "\u0001"; // SOH
+ String result = StringEscapeUtils.escapeJava(input);
+ assertTrue(result.contains("\\u0001"));
+ }
+
+ @Test
+ void testEscapeJavaControlCharacterBetween15And31() {
+ String input = "\u0010"; // DLE
+ String result = StringEscapeUtils.escapeJava(input);
+ assertTrue(result.contains("\\u0010"));
+ }
+
+ @Test
+ void testEscapeJavaToWriter() throws IOException {
+ StringWriter writer = new StringWriter();
+ StringEscapeUtils.escapeJava(writer, "hello\"world");
+ assertEquals("hello\\\"world", writer.toString());
+ }
+
+ @Test
+ void testEscapeJavaToWriterNull() throws IOException {
+ StringWriter writer = new StringWriter();
+ StringEscapeUtils.escapeJava(writer, null);
+ assertEquals("", writer.toString());
+ }
+
+ @Test
+ void testEscapeJavaToWriterWithNullWriter() {
+ assertThrows(IllegalArgumentException.class, () ->
+ StringEscapeUtils.escapeJava(null, "test"));
+ }
+
+ @Test
+ void testEscapeJavaScript() {
+ assertEquals("it\\'s", StringEscapeUtils.escapeJavaScript("it's"));
+ }
+
+ @Test
+ void testEscapeJavaScriptForwardSlash() {
+ assertEquals("path\\/to\\/file",
StringEscapeUtils.escapeJavaScript("path/to/file"));
+ }
+
+ @Test
+ void testEscapeJavaScriptNull() {
+ assertNull(StringEscapeUtils.escapeJavaScript(null));
+ }
+
+ @Test
+ void testEscapeJavaScriptToWriter() throws IOException {
+ StringWriter writer = new StringWriter();
+ StringEscapeUtils.escapeJavaScript(writer, "it's \"quoted\"");
+ assertEquals("it\\'s \\\"quoted\\\"", writer.toString());
+ }
+
+ @Test
+ void testEscapeJavaScriptToWriterWithNullWriter() {
+ assertThrows(IllegalArgumentException.class, () ->
+ StringEscapeUtils.escapeJavaScript(null, "test"));
+ }
+
+ @Test
+ void testUnescapeJavaNull() {
+ assertNull(StringEscapeUtils.unescapeJava(null));
+ }
+
+ @Test
+ void testUnescapeJavaEmpty() {
+ assertEquals("", StringEscapeUtils.unescapeJava(""));
+ }
+
+ @Test
+ void testUnescapeJavaBasicString() {
+ assertEquals("hello", StringEscapeUtils.unescapeJava("hello"));
+ }
+
+ @Test
+ void testUnescapeJavaDoubleQuote() {
+ assertEquals("\"hello\"",
StringEscapeUtils.unescapeJava("\\\"hello\\\""));
+ }
+
+ @Test
+ void testUnescapeJavaSingleQuote() {
+ assertEquals("'hello'", StringEscapeUtils.unescapeJava("\\'hello\\'"));
+ }
+
+ @Test
+ void testUnescapeJavaBackslash() {
+ assertEquals("path\\to\\file",
StringEscapeUtils.unescapeJava("path\\\\to\\\\file"));
+ }
+
+ @Test
+ void testUnescapeJavaNewline() {
+ assertEquals("line1\nline2",
StringEscapeUtils.unescapeJava("line1\\nline2"));
+ }
+
+ @Test
+ void testUnescapeJavaTab() {
+ assertEquals("col1\tcol2",
StringEscapeUtils.unescapeJava("col1\\tcol2"));
+ }
+
+ @Test
+ void testUnescapeJavaCarriageReturn() {
+ assertEquals("line1\rline2",
StringEscapeUtils.unescapeJava("line1\\rline2"));
+ }
+
+ @Test
+ void testUnescapeJavaFormFeed() {
+ assertEquals("page1\fpage2",
StringEscapeUtils.unescapeJava("page1\\fpage2"));
+ }
+
+ @Test
+ void testUnescapeJavaBackspace() {
+ assertEquals("back\bspace",
StringEscapeUtils.unescapeJava("back\\bspace"));
+ }
+
+ @Test
+ void testUnescapeJavaUnicode() {
+ assertEquals("\u0041", StringEscapeUtils.unescapeJava("\\u0041"));
+ assertEquals("A", StringEscapeUtils.unescapeJava("\\u0041"));
+ }
+
+ @Test
+ void testUnescapeJavaUnicodeMultiple() {
+ assertEquals("AB", StringEscapeUtils.unescapeJava("\\u0041\\u0042"));
+ }
+
+ @Test
+ void testUnescapeJavaMixedContent() {
+ assertEquals("hello\nworld\ttab",
StringEscapeUtils.unescapeJava("hello\\nworld\\ttab"));
+ }
+
+ @Test
+ void testUnescapeJavaInvalidUnicode() {
+ assertThrows(RuntimeException.class, () ->
+ StringEscapeUtils.unescapeJava("\\uXYZQ"));
+ }
+
+ @Test
+ void testUnescapeJavaTrailingBackslash() {
+ assertEquals("trailing\\",
StringEscapeUtils.unescapeJava("trailing\\"));
+ }
+
+ @Test
+ void testUnescapeJavaUnknownEscape() {
+ // Unknown escape sequences should just return the character after
backslash
+ assertEquals("x", StringEscapeUtils.unescapeJava("\\x"));
+ }
+
+ @Test
+ void testUnescapeJavaToWriter() throws IOException {
+ StringWriter writer = new StringWriter();
+ StringEscapeUtils.unescapeJava(writer, "hello\\\"world");
+ assertEquals("hello\"world", writer.toString());
+ }
+
+ @Test
+ void testUnescapeJavaToWriterNull() throws IOException {
+ StringWriter writer = new StringWriter();
+ StringEscapeUtils.unescapeJava(writer, null);
+ assertEquals("", writer.toString());
+ }
+
+ @Test
+ void testUnescapeJavaToWriterWithNullWriter() {
+ assertThrows(IllegalArgumentException.class, () ->
+ StringEscapeUtils.unescapeJava(null, "test"));
+ }
+
+ @Test
+ void testUnescapeJavaScriptSameAsJava() {
+ String escaped = "hello\\\"world\\'test";
+ assertEquals(StringEscapeUtils.unescapeJava(escaped),
+ StringEscapeUtils.unescapeJavaScript(escaped));
+ }
+
+ @Test
+ void testUnescapeJavaScriptNull() {
+ assertNull(StringEscapeUtils.unescapeJavaScript(null));
+ }
+
+ @Test
+ void testUnescapeJavaScriptToWriter() throws IOException {
+ StringWriter writer = new StringWriter();
+ StringEscapeUtils.unescapeJavaScript(writer, "hello\\nworld");
+ assertEquals("hello\nworld", writer.toString());
+ }
+
+ @Test
+ void testRoundTripJava() {
+ String original = "Hello\n\"World\"\t\\Test/";
+ String escaped = StringEscapeUtils.escapeJava(original);
+ String unescaped = StringEscapeUtils.unescapeJava(escaped);
+ assertEquals(original, unescaped);
+ }
+
+ @Test
+ void testRoundTripJavaScript() {
+ String original = "Hello'World";
+ String escaped = StringEscapeUtils.escapeJavaScript(original);
+ String unescaped = StringEscapeUtils.unescapeJavaScript(escaped);
+ assertEquals(original, unescaped);
+ }
+
+ @Test
+ void testConstructor() {
+ // Test that constructor can be called (even though it's typically
used statically)
+ StringEscapeUtils utils = new StringEscapeUtils();
+ assertNotNull(utils);
+ }
+
+ @Test
+ void testAllControlCharacters() {
+ // Test all control characters (0x00 to 0x1f except standard escapes)
+ for (int i = 0; i < 32; i++) {
+ if (i != '\b' && i != '\t' && i != '\n' && i != '\f' && i != '\r')
{
+ String input = String.valueOf((char) i);
+ String escaped = StringEscapeUtils.escapeJava(input);
+ assertTrue(escaped.startsWith("\\u000") ||
escaped.startsWith("\\u00"),
+ "Control char " + i + " should be unicode escaped");
+ }
+ }
+ }
+}
diff --git
a/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/CharSequenceValueTest.java
b/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/CharSequenceValueTest.java
new file mode 100644
index 0000000000..3404710583
--- /dev/null
+++
b/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/CharSequenceValueTest.java
@@ -0,0 +1,337 @@
+/*
+ * 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.math.BigDecimal;
+import java.math.BigInteger;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Unit tests for {@link CharSequenceValue}.
+ */
+class CharSequenceValueTest {
+
+ @Test
+ void testStringValue() {
+ char[] buffer = "hello".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.STRING, 0,
buffer.length, buffer, false, false);
+ assertEquals("hello", value.stringValue());
+ }
+
+ @Test
+ void testStringValueWithOffset() {
+ char[] buffer = "___hello___".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.STRING, 3,
8, buffer, false, false);
+ assertEquals("hello", value.stringValue());
+ }
+
+ @Test
+ void testIntegerValue() {
+ char[] buffer = "42".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.INTEGER,
0, buffer.length, buffer, false, false);
+ assertEquals(42, value.intValue());
+ }
+
+ @Test
+ void testNegativeIntegerValue() {
+ char[] buffer = "-42".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.INTEGER,
0, buffer.length, buffer, false, false);
+ assertEquals(-42, value.intValue());
+ }
+
+ @Test
+ void testLongValue() {
+ char[] buffer = "9876543210".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.INTEGER,
0, buffer.length, buffer, false, false);
+ assertEquals(9876543210L, value.longValue());
+ }
+
+ @Test
+ void testShortLongValue() {
+ char[] buffer = "42".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.INTEGER,
0, buffer.length, buffer, false, false);
+ assertEquals(42L, value.longValue());
+ }
+
+ @Test
+ void testDoubleValue() {
+ char[] buffer = "3.14159".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.DOUBLE, 0,
buffer.length, buffer, false, false);
+ assertEquals(3.14159, value.doubleValue(), 0.00001);
+ }
+
+ @Test
+ void testFloatValue() {
+ char[] buffer = "2.5".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.DOUBLE, 0,
buffer.length, buffer, false, false);
+ assertEquals(2.5f, value.floatValue(), 0.0001f);
+ }
+
+ @Test
+ void testByteValue() {
+ char[] buffer = "127".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.INTEGER,
0, buffer.length, buffer, false, false);
+ assertEquals((byte) 127, value.byteValue());
+ }
+
+ @Test
+ void testShortValue() {
+ char[] buffer = "1000".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.INTEGER,
0, buffer.length, buffer, false, false);
+ assertEquals((short) 1000, value.shortValue());
+ }
+
+ @Test
+ void testBigDecimalValue() {
+ char[] buffer = "123.456789".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.DOUBLE, 0,
buffer.length, buffer, false, false);
+ BigDecimal expected = new BigDecimal("123.456789");
+ assertEquals(expected, value.bigDecimalValue());
+ }
+
+ @Test
+ void testBigIntegerValue() {
+ char[] buffer = "123456789012345678901234567890".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.INTEGER,
0, buffer.length, buffer, false, false);
+ BigInteger expected = new BigInteger("123456789012345678901234567890");
+ assertEquals(expected, value.bigIntegerValue());
+ }
+
+ @Test
+ void testToString() {
+ char[] buffer = "test string".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.STRING, 0,
buffer.length, buffer, false, false);
+ assertEquals("test string", value.toString());
+ }
+
+ @Test
+ void testToStringWithOffset() {
+ char[] buffer = "___test___".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.STRING, 3,
7, buffer, false, false);
+ assertEquals("test", value.toString());
+ }
+
+ @Test
+ void testToValue() {
+ char[] buffer = "42".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.INTEGER,
0, buffer.length, buffer, false, false);
+ Object result = value.toValue();
+ assertEquals(42, result);
+ }
+
+ @Test
+ void testToValueDouble() {
+ char[] buffer = "3.14".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.DOUBLE, 0,
buffer.length, buffer, false, false);
+ Object result = value.toValue();
+ assertEquals(3.14, (Double) result, 0.0001);
+ }
+
+ @Test
+ void testToValueString() {
+ char[] buffer = "hello".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.STRING, 0,
buffer.length, buffer, false, false);
+ Object result = value.toValue();
+ assertEquals("hello", result);
+ }
+
+ @Test
+ void testToValueCached() {
+ char[] buffer = "hello".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.STRING, 0,
buffer.length, buffer, false, false);
+ Object result1 = value.toValue();
+ Object result2 = value.toValue();
+ assertSame(result1, result2);
+ }
+
+ @Test
+ void testIsContainer() {
+ char[] buffer = "test".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.STRING, 0,
buffer.length, buffer, false, false);
+ assertFalse(value.isContainer());
+ }
+
+ @Test
+ void testLength() {
+ char[] buffer = "hello".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.STRING, 0,
buffer.length, buffer, false, false);
+ assertEquals(5, value.length());
+ }
+
+ @Test
+ void testCharAt() {
+ char[] buffer = "hello".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.STRING, 0,
buffer.length, buffer, false, false);
+ assertEquals('h', value.charAt(0));
+ assertEquals('e', value.charAt(1));
+ assertEquals('o', value.charAt(4));
+ }
+
+ @Test
+ void testSubSequence() {
+ char[] buffer = "hello world".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.STRING, 0,
buffer.length, buffer, false, false);
+ CharSequence sub = value.subSequence(0, 5);
+ assertEquals("hello", sub.toString());
+ }
+
+ @Test
+ void testChop() {
+ char[] buffer = "___hello___".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.STRING, 3,
8, buffer, false, false);
+ value.chop();
+ assertEquals("hello", value.toString());
+ // Call chop again to ensure idempotency
+ value.chop();
+ assertEquals("hello", value.toString());
+ }
+
+ @Test
+ void testChopOnConstruction() {
+ char[] buffer = "___hello___".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(true, Type.STRING, 3,
8, buffer, false, false);
+ assertEquals("hello", value.toString());
+ }
+
+ @Test
+ void testCharValue() {
+ char[] buffer = "A".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.STRING, 0,
buffer.length, buffer, false, false);
+ assertEquals('A', value.charValue());
+ }
+
+ @Test
+ void testBooleanValue() {
+ char[] buffer = "true".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.STRING, 0,
buffer.length, buffer, false, false);
+ assertTrue(value.booleanValue());
+ }
+
+ @Test
+ void testBooleanValueFalse() {
+ char[] buffer = "false".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.STRING, 0,
buffer.length, buffer, false, false);
+ assertFalse(value.booleanValue());
+ }
+
+ @Test
+ void testEquals() {
+ char[] buffer = "test".toCharArray();
+ CharSequenceValue value1 = new CharSequenceValue(false, Type.STRING,
0, buffer.length, buffer, false, false);
+ CharSequenceValue value2 = new CharSequenceValue(false, Type.STRING,
0, buffer.length, buffer, false, false);
+ assertEquals(value1, value2);
+ }
+
+ @Test
+ void testEqualsSameObject() {
+ char[] buffer = "test".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.STRING, 0,
buffer.length, buffer, false, false);
+ assertEquals(value, value);
+ }
+
+ @Test
+ void testNotEqualsDifferentIndex() {
+ char[] buffer = "test1234".toCharArray();
+ CharSequenceValue value1 = new CharSequenceValue(false, Type.STRING,
0, 4, buffer, false, false);
+ CharSequenceValue value2 = new CharSequenceValue(false, Type.STRING,
4, 8, buffer, false, false);
+ assertNotEquals(value1, value2);
+ }
+
+ @Test
+ void testNotEqualsDifferentType() {
+ char[] buffer = "42".toCharArray();
+ CharSequenceValue value1 = new CharSequenceValue(false, Type.STRING,
0, buffer.length, buffer, false, false);
+ CharSequenceValue value2 = new CharSequenceValue(false, Type.INTEGER,
0, buffer.length, buffer, false, false);
+ assertNotEquals(value1, value2);
+ }
+
+ @Test
+ void testHashCode() {
+ char[] buffer = "test".toCharArray();
+ CharSequenceValue value1 = new CharSequenceValue(false, Type.STRING,
0, buffer.length, buffer, false, false);
+ CharSequenceValue value2 = new CharSequenceValue(false, Type.STRING,
0, buffer.length, buffer, false, false);
+ assertEquals(value1.hashCode(), value2.hashCode());
+ }
+
+ @Test
+ void testStringValueEncoded() {
+ char[] buffer = "hello".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.STRING, 0,
buffer.length, buffer, false, false);
+ assertEquals("hello", value.stringValueEncoded());
+ }
+
+ @Test
+ void testToEnumWithString() {
+ char[] buffer = "TWO".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.STRING, 0,
buffer.length, buffer, false, false);
+ TestEnum result = value.toEnum(TestEnum.class);
+ assertEquals(TestEnum.TWO, result);
+ }
+
+ @Test
+ void testToEnumWithInteger() {
+ char[] buffer = "1".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.INTEGER,
0, buffer.length, buffer, false, false);
+ TestEnum result = value.toEnum(TestEnum.class);
+ assertEquals(TestEnum.TWO, result);
+ }
+
+ @Test
+ void testToEnumNull() {
+ char[] buffer = "null".toCharArray();
+ CharSequenceValue value = new CharSequenceValue(false, Type.NULL, 0,
buffer.length, buffer, false, false);
+ TestEnum result = value.toEnum(TestEnum.class);
+ assertNull(result);
+ }
+
+ @Test
+ void testStaticToEnumByValue() {
+ TestEnum result = CharSequenceValue.toEnum(TestEnum.class, "ONE");
+ assertEquals(TestEnum.ONE, result);
+ }
+
+ @Test
+ void testStaticToEnumByValueWithDash() {
+ // The implementation converts dashes to underscores and uses uppercase
+ TestEnum result = CharSequenceValue.toEnum(TestEnum.class, "one");
+ assertEquals(TestEnum.ONE, result);
+ }
+
+ @Test
+ void testStaticToEnumByOrdinal() {
+ TestEnum result = CharSequenceValue.toEnum(TestEnum.class, 2);
+ assertEquals(TestEnum.THREE, result);
+ }
+
+ @Test
+ void testIntegerWithLongRange() {
+ char[] buffer = "2147483648".toCharArray(); // Integer.MAX_VALUE + 1
+ CharSequenceValue value = new CharSequenceValue(false, Type.INTEGER,
0, buffer.length, buffer, false, false);
+ Object result = value.toValue();
+ assertEquals(2147483648L, result);
+ }
+
+ // Helper enum for testing
+ enum TestEnum {
+ ONE, TWO, THREE
+ }
+}
diff --git
a/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/NumberValueTest.java
b/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/NumberValueTest.java
new file mode 100644
index 0000000000..a1e3eeb3b9
--- /dev/null
+++
b/subprojects/groovy-json/src/test/java/org/apache/groovy/json/internal/NumberValueTest.java
@@ -0,0 +1,320 @@
+/*
+ * 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.math.BigDecimal;
+import java.math.BigInteger;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Unit tests for {@link NumberValue}.
+ */
+class NumberValueTest {
+
+ @Test
+ void testIntegerValue() {
+ char[] buffer = "42".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ assertEquals(42, value.intValue());
+ assertEquals(42L, value.longValue());
+ assertEquals(42.0, value.doubleValue(), 0.0001);
+ assertEquals(42.0f, value.floatValue(), 0.0001f);
+ }
+
+ @Test
+ void testLongValue() {
+ char[] buffer = "9876543210".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ assertEquals(9876543210L, value.longValue());
+ }
+
+ @Test
+ void testDoubleValue() {
+ char[] buffer = "3.14159".toCharArray();
+ NumberValue value = new NumberValue(false, Type.DOUBLE, 0,
buffer.length, buffer);
+ assertEquals(3.14159, value.doubleValue(), 0.00001);
+ }
+
+ @Test
+ void testFloatValue() {
+ char[] buffer = "2.5".toCharArray();
+ NumberValue value = new NumberValue(false, Type.DOUBLE, 0,
buffer.length, buffer);
+ assertEquals(2.5f, value.floatValue(), 0.0001f);
+ }
+
+ @Test
+ void testByteValue() {
+ char[] buffer = "127".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ assertEquals((byte) 127, value.byteValue());
+ }
+
+ @Test
+ void testShortValue() {
+ char[] buffer = "1000".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ assertEquals((short) 1000, value.shortValue());
+ }
+
+ @Test
+ void testBigDecimalValue() {
+ char[] buffer = "123.456789".toCharArray();
+ NumberValue value = new NumberValue(false, Type.DOUBLE, 0,
buffer.length, buffer);
+ BigDecimal expected = new BigDecimal("123.456789");
+ assertEquals(expected, value.bigDecimalValue());
+ }
+
+ @Test
+ void testBigIntegerValue() {
+ char[] buffer = "123456789012345678901234567890".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ BigInteger expected = new BigInteger("123456789012345678901234567890");
+ assertEquals(expected, value.bigIntegerValue());
+ }
+
+ @Test
+ void testStringValue() {
+ char[] buffer = "42".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ assertEquals("42", value.stringValue());
+ }
+
+ @Test
+ void testStringValueEncoded() {
+ char[] buffer = "123".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ assertEquals("123", value.stringValueEncoded());
+ }
+
+ @Test
+ void testToString() {
+ char[] buffer = "999".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ assertEquals("999", value.toString());
+ }
+
+ @Test
+ void testToStringWithOffset() {
+ char[] buffer = "___42___".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 3, 5, buffer);
+ assertEquals("42", value.toString());
+ }
+
+ @Test
+ void testToValue() {
+ char[] buffer = "42".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ Object result = value.toValue();
+ assertEquals(42, result);
+ }
+
+ @Test
+ void testToValueDouble() {
+ char[] buffer = "3.14".toCharArray();
+ NumberValue value = new NumberValue(false, Type.DOUBLE, 0,
buffer.length, buffer);
+ Object result = value.toValue();
+ assertTrue(result instanceof BigDecimal);
+ }
+
+ @Test
+ void testToValueLong() {
+ char[] buffer = "9876543210".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ Object result = value.toValue();
+ assertEquals(9876543210L, result);
+ }
+
+ @Test
+ void testToValueCached() {
+ char[] buffer = "42".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ Object result1 = value.toValue();
+ Object result2 = value.toValue();
+ assertSame(result1, result2);
+ }
+
+ @Test
+ void testIsContainer() {
+ char[] buffer = "42".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ assertFalse(value.isContainer());
+ }
+
+ @Test
+ void testChop() {
+ char[] buffer = "___42___".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 3, 5, buffer);
+ value.chop();
+ assertEquals("42", value.toString());
+ // Call chop again to test idempotency
+ value.chop();
+ assertEquals("42", value.toString());
+ }
+
+ @Test
+ void testChopOnConstruction() {
+ char[] buffer = "___42___".toCharArray();
+ NumberValue value = new NumberValue(true, Type.INTEGER, 3, 5, buffer);
+ assertEquals("42", value.toString());
+ assertEquals(42, value.intValue());
+ }
+
+ @Test
+ void testCharValue() {
+ char[] buffer = "5".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ assertEquals('5', value.charValue());
+ }
+
+ @Test
+ void testDateValue() {
+ char[] buffer = "1609459200000".toCharArray(); // 2021-01-01 00:00:00
UTC
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ assertNotNull(value.dateValue());
+ }
+
+ @Test
+ void testBooleanValue() {
+ char[] buffer = "true".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ // Boolean.parseBoolean("true") returns true
+ assertTrue(value.booleanValue());
+ }
+
+ @Test
+ void testBooleanValueFalse() {
+ char[] buffer = "0".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ // Boolean.parseBoolean("0") returns false
+ assertFalse(value.booleanValue());
+ }
+
+ @Test
+ void testEquals() {
+ char[] buffer = "42".toCharArray();
+ NumberValue value1 = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ NumberValue value2 = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ assertEquals(value1, value2);
+ }
+
+ @Test
+ void testEqualsSameObject() {
+ char[] buffer = "42".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ assertEquals(value, value);
+ }
+
+ @Test
+ void testEqualsNull() {
+ char[] buffer = "42".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ assertNotEquals(null, value);
+ }
+
+ @Test
+ void testNotEqualsDifferentIndex() {
+ char[] buffer = "1234".toCharArray();
+ NumberValue value1 = new NumberValue(false, Type.INTEGER, 0, 2,
buffer);
+ NumberValue value2 = new NumberValue(false, Type.INTEGER, 2, 4,
buffer);
+ assertNotEquals(value1, value2);
+ }
+
+ @Test
+ void testNotEqualsDifferentType() {
+ char[] buffer = "42".toCharArray();
+ NumberValue value1 = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ NumberValue value2 = new NumberValue(false, Type.DOUBLE, 0,
buffer.length, buffer);
+ assertNotEquals(value1, value2);
+ }
+
+ @Test
+ void testHashCode() {
+ char[] buffer = "42".toCharArray();
+ NumberValue value1 = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ NumberValue value2 = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ assertEquals(value1.hashCode(), value2.hashCode());
+ }
+
+ @Test
+ void testHashCodeDifferent() {
+ char[] buffer1 = "42".toCharArray();
+ char[] buffer2 = "43".toCharArray();
+ NumberValue value1 = new NumberValue(false, Type.INTEGER, 0,
buffer1.length, buffer1);
+ NumberValue value2 = new NumberValue(false, Type.INTEGER, 0,
buffer2.length, buffer2);
+ assertNotEquals(value1.hashCode(), value2.hashCode());
+ }
+
+ @Test
+ void testDefaultConstructor() {
+ NumberValue value = new NumberValue();
+ assertNotNull(value);
+ }
+
+ @Test
+ void testTypeConstructor() {
+ NumberValue value = new NumberValue(Type.INTEGER);
+ assertNotNull(value);
+ }
+
+ @Test
+ void testNegativeInteger() {
+ char[] buffer = "-42".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ assertEquals(-42, value.intValue());
+ }
+
+ @Test
+ void testNegativeDouble() {
+ char[] buffer = "-3.14".toCharArray();
+ NumberValue value = new NumberValue(false, Type.DOUBLE, 0,
buffer.length, buffer);
+ assertEquals(-3.14, value.doubleValue(), 0.0001);
+ }
+
+ @Test
+ void testZero() {
+ char[] buffer = "0".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ assertEquals(0, value.intValue());
+ assertEquals(0L, value.longValue());
+ assertEquals(0.0, value.doubleValue(), 0.0001);
+ }
+
+ @Test
+ void testSingleMinusThrows() {
+ char[] buffer = "-".toCharArray();
+ assertThrows(RuntimeException.class, () ->
+ new NumberValue(false, Type.INTEGER, 0, buffer.length, buffer));
+ }
+
+ @Test
+ void testToEnumByOrdinal() {
+ char[] buffer = "1".toCharArray();
+ NumberValue value = new NumberValue(false, Type.INTEGER, 0,
buffer.length, buffer);
+ TestEnum result = value.toEnum(TestEnum.class);
+ assertEquals(TestEnum.TWO, result);
+ }
+
+ // Helper enum for testing
+ enum TestEnum {
+ ONE, TWO, THREE
+ }
+}