This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git
The following commit(s) were added to refs/heads/master by this push:
new 07fe35dae Sort members
07fe35dae is described below
commit 07fe35dae2c1dbeb943ab6a95fc015925e3500c1
Author: Gary Gregory <[email protected]>
AuthorDate: Sun Jun 7 07:31:37 2026 -0400
Sort members
---
.../lang3/builder/CompareToBuilderTest.java | 58 +++++------
.../EqualsBuilderReflectionEqualsCycleTest.java | 20 ++--
.../lang3/builder/HashCodeBuilderCycleTest.java | 24 ++---
.../concurrent/AtomicSafeInitializerInitTest.java | 10 +-
.../text/translate/NumericEntityEscaperTest.java | 20 ++--
.../apache/commons/lang3/time/DateUtilsTest.java | 108 ++++++++++-----------
.../commons/lang3/time/FastDateFormatTest.java | 60 ++++++------
.../commons/lang3/time/TimeZonesGmtTest.java | 36 +++----
8 files changed, 168 insertions(+), 168 deletions(-)
diff --git
a/src/test/java/org/apache/commons/lang3/builder/CompareToBuilderTest.java
b/src/test/java/org/apache/commons/lang3/builder/CompareToBuilderTest.java
index c49d0e217..cc8c36e60 100644
--- a/src/test/java/org/apache/commons/lang3/builder/CompareToBuilderTest.java
+++ b/src/test/java/org/apache/commons/lang3/builder/CompareToBuilderTest.java
@@ -96,35 +96,6 @@ public int hashCode() {
}
}
- /**
- * Mutually-referential {@code Object[]}s: {@code a[0] = b}, {@code b[0] =
a}, with {@code a != b}. The recursion is
- * {@code append(a,b) -> append(a[0]=b, b[0]=a) -> append(b,a) ->
- * append(b[0]=a, a[0]=b) -> append(a,b)}, never terminating.
- */
- @Test
- void testCycleMutuallyReferentialObjectArrays() {
- final Object[] a = new Object[1];
- final Object[] b = new Object[1];
- a[0] = b;
- b[0] = a;
- assertEquals(0, new CompareToBuilder().append(a, b,
null).toComparison());
- assertEquals(0, new CompareToBuilder().append((Object) a, (Object) b,
null).toComparison());
- }
-
- /**
- * Two distinct self-referential {@code Object[]}s: {@code a[0] = a},
{@code b[0] = b}, with {@code a != b}. The recursion is
- * {@code append(a,b) -> append(a[0]=a, b[0]=b) -> append(a,b)}, never
terminating.
- */
- @Test
- void testCycleTwoDistinctSelfReferentialObjectArrays() {
- final Object[] a = new Object[1];
- final Object[] b = new Object[1];
- a[0] = a;
- b[0] = b;
- assertEquals(0, new CompareToBuilder().append(a, b,
null).toComparison());
- assertEquals(0, new CompareToBuilder().append((Object) a, (Object) b,
null).toComparison());
- }
-
static class TestTransientSubObject extends TestObject {
@SuppressWarnings("unused")
private final transient int t;
@@ -406,6 +377,35 @@ void testCharArrayHiddenByObject() {
assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() <
0);
}
+ /**
+ * Mutually-referential {@code Object[]}s: {@code a[0] = b}, {@code b[0] =
a}, with {@code a != b}. The recursion is
+ * {@code append(a,b) -> append(a[0]=b, b[0]=a) -> append(b,a) ->
+ * append(b[0]=a, a[0]=b) -> append(a,b)}, never terminating.
+ */
+ @Test
+ void testCycleMutuallyReferentialObjectArrays() {
+ final Object[] a = new Object[1];
+ final Object[] b = new Object[1];
+ a[0] = b;
+ b[0] = a;
+ assertEquals(0, new CompareToBuilder().append(a, b,
null).toComparison());
+ assertEquals(0, new CompareToBuilder().append((Object) a, (Object) b,
null).toComparison());
+ }
+
+ /**
+ * Two distinct self-referential {@code Object[]}s: {@code a[0] = a},
{@code b[0] = b}, with {@code a != b}. The recursion is
+ * {@code append(a,b) -> append(a[0]=a, b[0]=b) -> append(a,b)}, never
terminating.
+ */
+ @Test
+ void testCycleTwoDistinctSelfReferentialObjectArrays() {
+ final Object[] a = new Object[1];
+ final Object[] b = new Object[1];
+ a[0] = a;
+ b[0] = b;
+ assertEquals(0, new CompareToBuilder().append(a, b,
null).toComparison());
+ assertEquals(0, new CompareToBuilder().append((Object) a, (Object) b,
null).toComparison());
+ }
+
@Test
void testDouble() {
final double o1 = 1;
diff --git
a/src/test/java/org/apache/commons/lang3/builder/EqualsBuilderReflectionEqualsCycleTest.java
b/src/test/java/org/apache/commons/lang3/builder/EqualsBuilderReflectionEqualsCycleTest.java
index d0192c4e4..bbf536658 100644
---
a/src/test/java/org/apache/commons/lang3/builder/EqualsBuilderReflectionEqualsCycleTest.java
+++
b/src/test/java/org/apache/commons/lang3/builder/EqualsBuilderReflectionEqualsCycleTest.java
@@ -36,6 +36,16 @@
*/
class EqualsBuilderReflectionEqualsCycleTest {
+ @Test
+ void testCrossReferentialObjectArrays() {
+ final Object[] a = new Object[1];
+ final Object[] b = new Object[1];
+ // a[0] -> b, b[0] -> a: mutual cycle
+ a[0] = b;
+ b[0] = a;
+ assertTrue(EqualsBuilder.reflectionEquals(a, b));
+ }
+
@Test
void testSelfReferentialObjectArrays() {
final Object[] a = new Object[1];
@@ -48,14 +58,4 @@ void testSelfReferentialObjectArrays() {
// The key assertion is that NO StackOverflowError is thrown.
assertTrue(EqualsBuilder.reflectionEquals(a, b));
}
-
- @Test
- void testCrossReferentialObjectArrays() {
- final Object[] a = new Object[1];
- final Object[] b = new Object[1];
- // a[0] -> b, b[0] -> a: mutual cycle
- a[0] = b;
- b[0] = a;
- assertTrue(EqualsBuilder.reflectionEquals(a, b));
- }
}
diff --git
a/src/test/java/org/apache/commons/lang3/builder/HashCodeBuilderCycleTest.java
b/src/test/java/org/apache/commons/lang3/builder/HashCodeBuilderCycleTest.java
index c4440f220..4664c8b59 100644
---
a/src/test/java/org/apache/commons/lang3/builder/HashCodeBuilderCycleTest.java
+++
b/src/test/java/org/apache/commons/lang3/builder/HashCodeBuilderCycleTest.java
@@ -48,16 +48,24 @@ static class CyclicNode {
}
@Override
- public int hashCode() {
- return new HashCodeBuilder(17,
37).append(label).append(peer).toHashCode();
+ public boolean equals(final Object o) {
+ return o instanceof CyclicNode && label.equals(((CyclicNode)
o).label);
}
@Override
- public boolean equals(final Object o) {
- return o instanceof CyclicNode && label.equals(((CyclicNode)
o).label);
+ public int hashCode() {
+ return new HashCodeBuilder(17,
37).append(label).append(peer).toHashCode();
}
}
+ @Test
+ void acyclicChainProducesValue() {
+ final CyclicNode a = new CyclicNode("a");
+ final CyclicNode b = new CyclicNode("b");
+ a.peer = b; // b.peer is null, no cycle
+ assertNotEquals(0, a.hashCode());
+ }
+
@Test
void cyclicPeerDoesNotOverflowStack() {
final CyclicNode a = new CyclicNode("a");
@@ -73,12 +81,4 @@ void selfReferentialDoesNotOverflowStack() {
self.peer = self;
assertNotEquals(0, self.hashCode());
}
-
- @Test
- void acyclicChainProducesValue() {
- final CyclicNode a = new CyclicNode("a");
- final CyclicNode b = new CyclicNode("b");
- a.peer = b; // b.peer is null, no cycle
- assertNotEquals(0, a.hashCode());
- }
}
diff --git
a/src/test/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializerInitTest.java
b/src/test/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializerInitTest.java
index 0c73c7d72..2387c4787 100644
---
a/src/test/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializerInitTest.java
+++
b/src/test/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializerInitTest.java
@@ -53,6 +53,11 @@ class AtomicSafeInitializerInitTest {
private static final int INIT_MS = 100;
private static final int SPINNER_THREADS = 8;
+ private static long threadCpuTimeNanos() {
+ final ThreadMXBean mx = ManagementFactory.getThreadMXBean();
+ return mx.isCurrentThreadCpuTimeSupported() ?
mx.getCurrentThreadCpuTime() : 0;
+ }
+
@Test
void testSpinningThreadsYieldDuringSlowInit() throws Exception {
final CountDownLatch startLatch = new CountDownLatch(1);
@@ -93,9 +98,4 @@ void testSpinningThreadsYieldDuringSlowInit() throws
Exception {
assertTimeout(Duration.ofMillis(INIT_MS * SPINNER_THREADS / 4), () ->
assertFalse(cpuMs > INIT_MS * SPINNER_THREADS / 4,
() -> "Spinner threads consumed " + cpuMs + " ms CPU during "
+ INIT_MS + " ms init — missing Thread.yield() in get() spin loop"));
}
-
- private static long threadCpuTimeNanos() {
- final ThreadMXBean mx = ManagementFactory.getThreadMXBean();
- return mx.isCurrentThreadCpuTimeSupported() ?
mx.getCurrentThreadCpuTime() : 0;
- }
}
diff --git
a/src/test/java/org/apache/commons/lang3/text/translate/NumericEntityEscaperTest.java
b/src/test/java/org/apache/commons/lang3/text/translate/NumericEntityEscaperTest.java
index dab4c884a..30a589bc8 100644
---
a/src/test/java/org/apache/commons/lang3/text/translate/NumericEntityEscaperTest.java
+++
b/src/test/java/org/apache/commons/lang3/text/translate/NumericEntityEscaperTest.java
@@ -54,16 +54,6 @@ void testBetween() {
assertEquals("ADFGZ", result, "Failed to escape numeric
entities via the between method");
}
- // See LANG-617
- @Test
- void testSupplementary() {
- final NumericEntityEscaper nee = new NumericEntityEscaper();
- final String input = "\uD803\uDC22";
- final String expected = "𐰢";
- final String result = nee.translate(input);
- assertEquals(expected, result, "Failed to escape numeric entities
supplementary characters");
- }
-
@Test
void testNumericEntityOverflow() throws Exception {
// cp = 1234567890 > Character.MAX_CODE_POINT (0x10FFFF = 1114111).
@@ -79,6 +69,16 @@ void testNumericEntityOverflow() throws Exception {
assertEquals("", sw.toString());
}
+ // See LANG-617
+ @Test
+ void testSupplementary() {
+ final NumericEntityEscaper nee = new NumericEntityEscaper();
+ final String input = "\uD803\uDC22";
+ final String expected = "𐰢";
+ final String result = nee.translate(input);
+ assertEquals(expected, result, "Failed to escape numeric entities
supplementary characters");
+ }
+
@Test
void testValidCodePoint() throws Exception {
// Negative control: 'A' = 'A' must translate successfully.
diff --git a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java
b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java
index 14bb9d9e5..00f6bb1f2 100644
--- a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java
@@ -726,6 +726,60 @@ void testConstructor() {
assertFalse(Modifier.isFinal(DateUtils.class.getModifiers()));
}
+ /**
+ * Failed date parse can poison the calendar's time zone for the next
pattern.
+ *
+ * <p>
+ * {@link DateUtils#parseDateWithLeniency} reuses a single {@link
Calendar} across pattern attempts. The reset between iterations is
+ * {@link Calendar#clear()}, which does NOT reset the calendar's time
zone. A TZ-aware pattern (e.g. starts with {@code "X"}, {@code "Z"}, {@code
"z"}) that
+ * successfully consumes its TZ token (mutating the calendar via {@link
Calendar#setTimeZone}) before failing on later tokens leaves the calendar
carrying
+ * the parsed (foreign) zone. The next pattern in the list then interprets
its date fields against that zone, producing a wrong instant.
+ * </p>
+ *
+ * <p>
+ * <b>Construction:</b> input {@code "+0500 01/01/2024"} is parsed against
two patterns:
+ * </p>
+ * <ol>
+ * <li>{@code "Z 'X' MM/dd/yyyy"}: the {@code Z} strategy consumes {@code
"+0500"} and calls {@code calendar.setTimeZone(GMT+05:00)}; the next literal
+ * {@code 'X'} fails to match {@code " 01"}, so the overall parse returns
false. The leaked TZ remains on the shared calendar.</li>
+ * <li>{@code "'+0500 'MM/dd/yyyy"}: a TZ-less pattern that absorbs the
literal prefix and parses the date. With the bug, the calendar still carries
+ * GMT+05:00, so the date is interpreted as midnight in GMT+05:00 (5 hours
earlier as an instant) than the same date interpreted in the system default
+ * zone.</li>
+ * </ol>
+ *
+ * <p>
+ * The assertion compares {@link Date#getTime()} against a baseline parse
using only the second pattern. A naive PoC that only checked calendar fields
+ * (year/month/day) would not detect the bug because those equal in both
runs.
+ * </p>
+ *
+ * <p>
+ * Pre-patch: the bug shifts the resulting instant by 5 hours.<br>
+ * Post-patch: {@code parseDateWithLeniency} restores {@code
calendar.setTimeZone(TimeZone.getDefault())} before each iteration, so the
leaked zone never
+ * reaches the second attempt and both instants match.
+ * </p>
+ */
+ @Test
+ public void testFailedTimeZonePatternDoesNotPoisonNextPatternInstant()
throws Exception {
+ final String input = "+0500 01/01/2024";
+ // Baseline: a single TZ-less pattern; no leak source. The calendar
uses
+ // TimeZone.getDefault(), so the result is midnight 2024-01-01 in the
default zone.
+ final Date baseline = DateUtils.parseDate(input, "'+0500 'MM/dd/yyyy");
+ assertNotNull(baseline, "baseline parse must succeed");
+ // Poisoning sequence:
+ // pattern 1 = "Z 'X' MM/dd/yyyy" -> Z consumes "+0500" then
setTimeZone(GMT+05:00),
+ // 'X' literal mismatches " 01",
returns false.
+ // pattern 2 = "'+0500 'MM/dd/yyyy"-> matches the whole input via
literal prefix +
+ // MM/dd/yyyy. With the bug,
calendar still has
+ // GMT+05:00 leaked from pattern
1, so the date
+ // is interpreted in GMT+05:00
instead of default.
+ final Date poisoned = DateUtils.parseDate(input, "Z 'X' MM/dd/yyyy",
"'+0500 'MM/dd/yyyy");
+ assertNotNull(poisoned, "poisoning sequence parse must succeed via the
second pattern");
+ // Compare INSTANTS, not Y/M/D. Calendar fields equal in both runs by
coincidence
+ // even when the bug shifts the underlying instant.
+ assertEquals(baseline.getTime(), poisoned.getTime(),
+ "Calendar TZ leaked from a failed prior pattern must not
affect the instant produced by a subsequent TZ-less pattern");
+ }
+
@Test
void testIsSameDay_Cal() {
final GregorianCalendar cala = new GregorianCalendar(2004, 6, 9, 13,
45);
@@ -1679,59 +1733,5 @@ void testWeekIterator() {
now.add(Calendar.DATE, 1);
}
}
-
- /**
- * Failed date parse can poison the calendar's time zone for the next
pattern.
- *
- * <p>
- * {@link DateUtils#parseDateWithLeniency} reuses a single {@link
Calendar} across pattern attempts. The reset between iterations is
- * {@link Calendar#clear()}, which does NOT reset the calendar's time
zone. A TZ-aware pattern (e.g. starts with {@code "X"}, {@code "Z"}, {@code
"z"}) that
- * successfully consumes its TZ token (mutating the calendar via {@link
Calendar#setTimeZone}) before failing on later tokens leaves the calendar
carrying
- * the parsed (foreign) zone. The next pattern in the list then interprets
its date fields against that zone, producing a wrong instant.
- * </p>
- *
- * <p>
- * <b>Construction:</b> input {@code "+0500 01/01/2024"} is parsed against
two patterns:
- * </p>
- * <ol>
- * <li>{@code "Z 'X' MM/dd/yyyy"}: the {@code Z} strategy consumes {@code
"+0500"} and calls {@code calendar.setTimeZone(GMT+05:00)}; the next literal
- * {@code 'X'} fails to match {@code " 01"}, so the overall parse returns
false. The leaked TZ remains on the shared calendar.</li>
- * <li>{@code "'+0500 'MM/dd/yyyy"}: a TZ-less pattern that absorbs the
literal prefix and parses the date. With the bug, the calendar still carries
- * GMT+05:00, so the date is interpreted as midnight in GMT+05:00 (5 hours
earlier as an instant) than the same date interpreted in the system default
- * zone.</li>
- * </ol>
- *
- * <p>
- * The assertion compares {@link Date#getTime()} against a baseline parse
using only the second pattern. A naive PoC that only checked calendar fields
- * (year/month/day) would not detect the bug because those equal in both
runs.
- * </p>
- *
- * <p>
- * Pre-patch: the bug shifts the resulting instant by 5 hours.<br>
- * Post-patch: {@code parseDateWithLeniency} restores {@code
calendar.setTimeZone(TimeZone.getDefault())} before each iteration, so the
leaked zone never
- * reaches the second attempt and both instants match.
- * </p>
- */
- @Test
- public void testFailedTimeZonePatternDoesNotPoisonNextPatternInstant()
throws Exception {
- final String input = "+0500 01/01/2024";
- // Baseline: a single TZ-less pattern; no leak source. The calendar
uses
- // TimeZone.getDefault(), so the result is midnight 2024-01-01 in the
default zone.
- final Date baseline = DateUtils.parseDate(input, "'+0500 'MM/dd/yyyy");
- assertNotNull(baseline, "baseline parse must succeed");
- // Poisoning sequence:
- // pattern 1 = "Z 'X' MM/dd/yyyy" -> Z consumes "+0500" then
setTimeZone(GMT+05:00),
- // 'X' literal mismatches " 01",
returns false.
- // pattern 2 = "'+0500 'MM/dd/yyyy"-> matches the whole input via
literal prefix +
- // MM/dd/yyyy. With the bug,
calendar still has
- // GMT+05:00 leaked from pattern
1, so the date
- // is interpreted in GMT+05:00
instead of default.
- final Date poisoned = DateUtils.parseDate(input, "Z 'X' MM/dd/yyyy",
"'+0500 'MM/dd/yyyy");
- assertNotNull(poisoned, "poisoning sequence parse must succeed via the
second pattern");
- // Compare INSTANTS, not Y/M/D. Calendar fields equal in both runs by
coincidence
- // even when the bug shifts the underlying instant.
- assertEquals(baseline.getTime(), poisoned.getTime(),
- "Calendar TZ leaked from a failed prior pattern must not
affect the instant produced by a subsequent TZ-less pattern");
- }
}
diff --git
a/src/test/java/org/apache/commons/lang3/time/FastDateFormatTest.java
b/src/test/java/org/apache/commons/lang3/time/FastDateFormatTest.java
index 6c084b634..15895c363 100644
--- a/src/test/java/org/apache/commons/lang3/time/FastDateFormatTest.java
+++ b/src/test/java/org/apache/commons/lang3/time/FastDateFormatTest.java
@@ -110,36 +110,6 @@ private AtomicLongArray measureTime(final Format printer,
final Format parser) t
return totalElapsed;
}
- /**
- * Pre-patch: UnsupportedOperationException when formatting a Japanese
Imperial Calendar with 'Y' (week-year) pattern.
- * <p>
- * Post-patch: falls back to Calendar.YEAR and formats successfully.
- * </p>
- */
- @Test
- void testJapaneseImperialCalendarWeekYearDoesNotThrow() {
- final Locale japaneseImperial = new Locale("ja", "JP", "JP");
- final Calendar japaneseCal = Calendar.getInstance(japaneseImperial);
- final FastDateFormat fdp = FastDateFormat.getInstance("YYYY-MM-dd");
- assertNotNull(fdp.format(japaneseCal), "Formatting
JapaneseImperialCalendar with YYYY pattern must not throw
UnsupportedOperationException");
- }
-
- /**
- * Pre-patch: UnsupportedOperationException when formatting a Japanese
Imperial
- * <p>
- * Calendar with 'Y' (week-year) pattern. Post-patch: falls back to
Calendar.YEAR and formats successfully.
- * </p>
- * <p>
- * Also test that a regular Gregorian calendar works fine with YYYY.
- * </p>
- */
- @Test
- void testGregorianCalendarWeekYearWorks() {
- final Calendar cal = Calendar.getInstance();
- final FastDateFormat fdp = FastDateFormat.getInstance("YYYY-MM-dd");
- assertNotNull(fdp.format(cal), "Formatting Gregorian Calendar with
YYYY pattern should always work");
- }
-
@DefaultLocale(language = "en", country = "US")
@Test
void test_changeDefault_Locale_DateInstance() {
@@ -287,6 +257,36 @@ void testDateDefaults() {
FastDateFormat.getDateInstance(FastDateFormat.LONG,
TimeZone.getDefault(), Locale.getDefault()));
}
+ /**
+ * Pre-patch: UnsupportedOperationException when formatting a Japanese
Imperial
+ * <p>
+ * Calendar with 'Y' (week-year) pattern. Post-patch: falls back to
Calendar.YEAR and formats successfully.
+ * </p>
+ * <p>
+ * Also test that a regular Gregorian calendar works fine with YYYY.
+ * </p>
+ */
+ @Test
+ void testGregorianCalendarWeekYearWorks() {
+ final Calendar cal = Calendar.getInstance();
+ final FastDateFormat fdp = FastDateFormat.getInstance("YYYY-MM-dd");
+ assertNotNull(fdp.format(cal), "Formatting Gregorian Calendar with
YYYY pattern should always work");
+ }
+
+ /**
+ * Pre-patch: UnsupportedOperationException when formatting a Japanese
Imperial Calendar with 'Y' (week-year) pattern.
+ * <p>
+ * Post-patch: falls back to Calendar.YEAR and formats successfully.
+ * </p>
+ */
+ @Test
+ void testJapaneseImperialCalendarWeekYearDoesNotThrow() {
+ final Locale japaneseImperial = new Locale("ja", "JP", "JP");
+ final Calendar japaneseCal = Calendar.getInstance(japaneseImperial);
+ final FastDateFormat fdp = FastDateFormat.getInstance("YYYY-MM-dd");
+ assertNotNull(fdp.format(japaneseCal), "Formatting
JapaneseImperialCalendar with YYYY pattern must not throw
UnsupportedOperationException");
+ }
+
@Test
void testLang1152() {
final TimeZone utc = FastTimeZone.getGmtTimeZone();
diff --git a/src/test/java/org/apache/commons/lang3/time/TimeZonesGmtTest.java
b/src/test/java/org/apache/commons/lang3/time/TimeZonesGmtTest.java
index 800d281e6..820d1251e 100644
--- a/src/test/java/org/apache/commons/lang3/time/TimeZonesGmtTest.java
+++ b/src/test/java/org/apache/commons/lang3/time/TimeZonesGmtTest.java
@@ -42,13 +42,17 @@
public class TimeZonesGmtTest {
@Test
- public void testGmtTimeZoneIsImmutableSetRawOffset() {
- assertThrows(UnsupportedOperationException.class, () ->
TimeZones.GMT.setRawOffset(3600000));
+ public void testCloneReturnsSelfForImmutableSingleton() {
+ // For an immutable singleton, clone() should return the same instance,
+ // and there must be no need to defensively copy the value.
+ final Object copy = TimeZones.GMT.clone();
+ assertSame(TimeZones.GMT, copy, "clone() of an immutable singleton
should return the same instance");
}
@Test
- public void testGmtTimeZoneIsImmutableSetId() {
+ public void testGmtIdRemainsGmtAfterAttemptedMutation() {
assertThrows(UnsupportedOperationException.class, () ->
TimeZones.GMT.setID("Etc/UTC"));
+ assertEquals("GMT", TimeZones.GMT.getID());
}
@Test
@@ -58,25 +62,13 @@ public void
testGmtOffsetRemainsZeroAfterAttemptedMutation() {
}
@Test
- public void testGmtIdRemainsGmtAfterAttemptedMutation() {
+ public void testGmtTimeZoneIsImmutableSetId() {
assertThrows(UnsupportedOperationException.class, () ->
TimeZones.GMT.setID("Etc/UTC"));
- assertEquals("GMT", TimeZones.GMT.getID());
- }
-
- @Test
- public void testCloneReturnsSelfForImmutableSingleton() {
- // For an immutable singleton, clone() should return the same instance,
- // and there must be no need to defensively copy the value.
- final Object copy = TimeZones.GMT.clone();
- assertSame(TimeZones.GMT, copy, "clone() of an immutable singleton
should return the same instance");
}
@Test
- public void testSerializedFormUsesNamedClass() throws Exception {
- // Wire-format hazard guard: ensure the implementation class is a
named static
- // nested type, not an anonymous inner class whose synthetic name
(TimeZones$1)
- // is brittle across non-functional code reorderings.
- assertEquals(ImmutableTimeZone.class.getName(),
TimeZones.GMT.getClass().getName());
+ public void testGmtTimeZoneIsImmutableSetRawOffset() {
+ assertThrows(UnsupportedOperationException.class, () ->
TimeZones.GMT.setRawOffset(3600000));
}
@Test
@@ -87,4 +79,12 @@ public void testSerializationRoundTripPreservesId() throws
Exception {
assertEquals("GMT", roundtrip.getID(), "Round-tripped TimeZone id must
remain \"GMT\"");
assertEquals(0, roundtrip.getRawOffset(), "Round-tripped TimeZone
offset must remain 0");
}
+
+ @Test
+ public void testSerializedFormUsesNamedClass() throws Exception {
+ // Wire-format hazard guard: ensure the implementation class is a
named static
+ // nested type, not an anonymous inner class whose synthetic name
(TimeZones$1)
+ // is brittle across non-functional code reorderings.
+ assertEquals(ImmutableTimeZone.class.getName(),
TimeZones.GMT.getClass().getName());
+ }
}