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
commit 725e8b73f2d3b183fb8a5581f8bc96941a20fd47 Author: Gary Gregory <[email protected]> AuthorDate: Wed Sep 9 02:44:25 2026 -0400 Sort members --- .../org/apache/commons/lang3/ClassUtilsTest.java | 36 ++--- .../org/apache/commons/lang3/ConversionTest.java | 90 +++++------ .../org/apache/commons/lang3/DoubleRangeTest.java | 26 ++-- .../apache/commons/lang3/event/EventUtilsTest.java | 16 +- .../lang3/exception/ExceptionUtilsTest.java | 92 +++++------ .../apache/commons/lang3/math/FractionTest.java | 172 ++++++++++----------- .../commons/lang3/reflect/MethodUtilsTest.java | 162 +++++++++---------- .../commons/lang3/reflect/TypeUtilsTest.java | 100 ++++++------ .../commons/lang3/text/StrSubstitutorTest.java | 48 +++--- 9 files changed, 371 insertions(+), 371 deletions(-) diff --git a/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java b/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java index 90c54fddd..436e188bc 100644 --- a/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java @@ -1276,24 +1276,6 @@ void testGetClassInner() throws ClassNotFoundException { assertEquals(Inner.DeeplyNested.class, ClassUtils.getClass(classLoader, "org.apache.commons.lang3.ClassUtilsTest$Inner.DeeplyNested")); } - @Test - void testGetClassStrict() throws Exception { - // Same resolution as getClass for exact binary names. - assertEquals(String.class, ClassUtils.getClassStrict("java.lang.String")); - assertEquals(Inner.DeeplyNested.class, ClassUtils.getClassStrict("org.apache.commons.lang3.ClassUtilsTest.Inner.DeeplyNested")); - final ClassLoader classLoader = Inner.DeeplyNested.class.getClassLoader(); - assertEquals(String.class, ClassUtils.getClassStrict(classLoader, "java.lang.String", true)); - // No whitespace normalization: whitespace-bearing spellings fail, matching Class.forName. - assertThrows(ClassNotFoundException.class, () -> ClassUtils.getClassStrict(" java.lang.String")); - assertThrows(ClassNotFoundException.class, () -> ClassUtils.getClassStrict("java .lang.String")); - assertThrows(ClassNotFoundException.class, () -> ClassUtils.getClassStrict("java.lang\t.String")); - assertThrows(ClassNotFoundException.class, () -> ClassUtils.getClassStrict(classLoader, "java.lang.String ", true)); - assertThrows(NullPointerException.class, () -> ClassUtils.getClassStrict(null)); - // The normalizing overloads keep their documented behavior. - assertEquals(String.class, ClassUtils.getClass(" java.lang.String")); - assertEquals(String.class, ClassUtils.getClass("java .lang.String")); - } - @Test void testGetClassInvalidArguments() throws Exception { assertGetClassThrowsNullPointerException(null); @@ -1338,6 +1320,24 @@ void testGetClassRawPrimitives() throws ClassNotFoundException { assertEquals(void.class, ClassUtils.getClass("void")); } + @Test + void testGetClassStrict() throws Exception { + // Same resolution as getClass for exact binary names. + assertEquals(String.class, ClassUtils.getClassStrict("java.lang.String")); + assertEquals(Inner.DeeplyNested.class, ClassUtils.getClassStrict("org.apache.commons.lang3.ClassUtilsTest.Inner.DeeplyNested")); + final ClassLoader classLoader = Inner.DeeplyNested.class.getClassLoader(); + assertEquals(String.class, ClassUtils.getClassStrict(classLoader, "java.lang.String", true)); + // No whitespace normalization: whitespace-bearing spellings fail, matching Class.forName. + assertThrows(ClassNotFoundException.class, () -> ClassUtils.getClassStrict(" java.lang.String")); + assertThrows(ClassNotFoundException.class, () -> ClassUtils.getClassStrict("java .lang.String")); + assertThrows(ClassNotFoundException.class, () -> ClassUtils.getClassStrict("java.lang\t.String")); + assertThrows(ClassNotFoundException.class, () -> ClassUtils.getClassStrict(classLoader, "java.lang.String ", true)); + assertThrows(NullPointerException.class, () -> ClassUtils.getClassStrict(null)); + // The normalizing overloads keep their documented behavior. + assertEquals(String.class, ClassUtils.getClass(" java.lang.String")); + assertEquals(String.class, ClassUtils.getClass("java .lang.String")); + } + /** * Pre-patch: getClass("java.lang.String[]junk[]") silently returns String[][][][] (4 dims, because (24 - 16)/2 = 4 — junk is 4 chars). Post-patch: must * throw IllegalArgumentException. diff --git a/src/test/java/org/apache/commons/lang3/ConversionTest.java b/src/test/java/org/apache/commons/lang3/ConversionTest.java index 9b475a9b3..3fe8d64f5 100644 --- a/src/test/java/org/apache/commons/lang3/ConversionTest.java +++ b/src/test/java/org/apache/commons/lang3/ConversionTest.java @@ -1476,6 +1476,51 @@ void testLongToShortArray() { assertIllegalArgumentException(() -> Conversion.longToShortArray(0x1234567890ABCDEFL, 33, new short[]{0}, 0, Long.SIZE / Short.SIZE)); } + /** + * Each converter documents an {@link IllegalArgumentException} when the requested count and position exceed the destination width, but the guard was + * evaluated in {@code int} arithmetic and silently overflowed for a large count or position, so the documented exception never fired. + */ + @Test + void testOverflowingCountOrPositionThrowsIllegalArgumentException() { + final int big = Integer.MAX_VALUE; + // nBools - 1 + dstPos + assertIllegalArgumentException(() -> Conversion.binaryToByte(new boolean[]{true}, 0, (byte) 0, big, 2)); + assertIllegalArgumentException(() -> Conversion.binaryToInt(new boolean[]{true}, 0, 0, big, 2)); + assertIllegalArgumentException(() -> Conversion.binaryToLong(new boolean[]{true}, 0, 0L, big, 2)); + assertIllegalArgumentException(() -> Conversion.binaryToShort(new boolean[]{true}, 0, (short) 0, big, 2)); + // nBools - 1 + srcPos + assertIllegalArgumentException(() -> Conversion.byteToBinary((byte) 1, big, new boolean[8], 0, 2)); + assertIllegalArgumentException(() -> Conversion.intToBinary(0, big, new boolean[32], 0, 2)); + assertIllegalArgumentException(() -> Conversion.longToBinary(0L, big, new boolean[64], 0, 2)); + assertIllegalArgumentException(() -> Conversion.shortToBinary((short) 0, big, new boolean[16], 0, 2)); + // (nBytes - 1) * 8 + dstPos + assertIllegalArgumentException(() -> Conversion.byteArrayToInt(new byte[]{1}, 0, 0, 0, big)); + assertIllegalArgumentException(() -> Conversion.byteArrayToLong(new byte[]{1}, 0, 0L, 0, big)); + assertIllegalArgumentException(() -> Conversion.byteArrayToShort(new byte[]{1}, 0, (short) 0, 0, big)); + // (nBytes - 1) * 8 + srcPos + assertIllegalArgumentException(() -> Conversion.intToByteArray(0, 0, new byte[4], 0, big)); + assertIllegalArgumentException(() -> Conversion.longToByteArray(0L, 0, new byte[8], 0, big)); + assertIllegalArgumentException(() -> Conversion.shortToByteArray((short) 0, 0, new byte[2], 0, big)); + // (nHex - 1) * 4 + dstPos + assertIllegalArgumentException(() -> Conversion.hexToByte("f", 0, (byte) 0, 0, big)); + assertIllegalArgumentException(() -> Conversion.hexToInt("f", 0, 0, 0, big)); + assertIllegalArgumentException(() -> Conversion.hexToLong("f", 0, 0L, 0, big)); + assertIllegalArgumentException(() -> Conversion.hexToShort("f", 0, (short) 0, 0, big)); + // (nHexs - 1) * 4 + srcPos + assertIllegalArgumentException(() -> Conversion.byteToHex((byte) 0, 0, "", 0, big)); + assertIllegalArgumentException(() -> Conversion.intToHex(0, 0, "", 0, big)); + assertIllegalArgumentException(() -> Conversion.longToHex(0L, 0, "", 0, big)); + assertIllegalArgumentException(() -> Conversion.shortToHex((short) 0, 0, "", 0, big)); + // (nInts - 1) * 32 + pos + assertIllegalArgumentException(() -> Conversion.intArrayToLong(new int[]{0}, 0, 0L, 0, big)); + assertIllegalArgumentException(() -> Conversion.longToIntArray(0L, 0, new int[2], 0, big)); + // (nShorts - 1) * 16 + pos + assertIllegalArgumentException(() -> Conversion.shortArrayToInt(new short[]{0}, 0, 0, 0, big)); + assertIllegalArgumentException(() -> Conversion.shortArrayToLong(new short[]{0}, 0, 0L, 0, big)); + assertIllegalArgumentException(() -> Conversion.intToShortArray(0, 0, new short[2], 0, big)); + assertIllegalArgumentException(() -> Conversion.longToShortArray(0L, 0, new short[4], 0, big)); + } + /** * Tests {@link Conversion#shortArrayToInt(short[], int, int, int, int)}. */ @@ -1729,49 +1774,4 @@ void testUuidToByteArray() { assertIllegalArgumentException(() -> Conversion.uuidToByteArray(new UUID( 0xFFEEDDCCBBAA9988L, 0x7766554433221100L), new byte[16], 2, 17)); } - - /** - * Each converter documents an {@link IllegalArgumentException} when the requested count and position exceed the destination width, but the guard was - * evaluated in {@code int} arithmetic and silently overflowed for a large count or position, so the documented exception never fired. - */ - @Test - void testOverflowingCountOrPositionThrowsIllegalArgumentException() { - final int big = Integer.MAX_VALUE; - // nBools - 1 + dstPos - assertIllegalArgumentException(() -> Conversion.binaryToByte(new boolean[]{true}, 0, (byte) 0, big, 2)); - assertIllegalArgumentException(() -> Conversion.binaryToInt(new boolean[]{true}, 0, 0, big, 2)); - assertIllegalArgumentException(() -> Conversion.binaryToLong(new boolean[]{true}, 0, 0L, big, 2)); - assertIllegalArgumentException(() -> Conversion.binaryToShort(new boolean[]{true}, 0, (short) 0, big, 2)); - // nBools - 1 + srcPos - assertIllegalArgumentException(() -> Conversion.byteToBinary((byte) 1, big, new boolean[8], 0, 2)); - assertIllegalArgumentException(() -> Conversion.intToBinary(0, big, new boolean[32], 0, 2)); - assertIllegalArgumentException(() -> Conversion.longToBinary(0L, big, new boolean[64], 0, 2)); - assertIllegalArgumentException(() -> Conversion.shortToBinary((short) 0, big, new boolean[16], 0, 2)); - // (nBytes - 1) * 8 + dstPos - assertIllegalArgumentException(() -> Conversion.byteArrayToInt(new byte[]{1}, 0, 0, 0, big)); - assertIllegalArgumentException(() -> Conversion.byteArrayToLong(new byte[]{1}, 0, 0L, 0, big)); - assertIllegalArgumentException(() -> Conversion.byteArrayToShort(new byte[]{1}, 0, (short) 0, 0, big)); - // (nBytes - 1) * 8 + srcPos - assertIllegalArgumentException(() -> Conversion.intToByteArray(0, 0, new byte[4], 0, big)); - assertIllegalArgumentException(() -> Conversion.longToByteArray(0L, 0, new byte[8], 0, big)); - assertIllegalArgumentException(() -> Conversion.shortToByteArray((short) 0, 0, new byte[2], 0, big)); - // (nHex - 1) * 4 + dstPos - assertIllegalArgumentException(() -> Conversion.hexToByte("f", 0, (byte) 0, 0, big)); - assertIllegalArgumentException(() -> Conversion.hexToInt("f", 0, 0, 0, big)); - assertIllegalArgumentException(() -> Conversion.hexToLong("f", 0, 0L, 0, big)); - assertIllegalArgumentException(() -> Conversion.hexToShort("f", 0, (short) 0, 0, big)); - // (nHexs - 1) * 4 + srcPos - assertIllegalArgumentException(() -> Conversion.byteToHex((byte) 0, 0, "", 0, big)); - assertIllegalArgumentException(() -> Conversion.intToHex(0, 0, "", 0, big)); - assertIllegalArgumentException(() -> Conversion.longToHex(0L, 0, "", 0, big)); - assertIllegalArgumentException(() -> Conversion.shortToHex((short) 0, 0, "", 0, big)); - // (nInts - 1) * 32 + pos - assertIllegalArgumentException(() -> Conversion.intArrayToLong(new int[]{0}, 0, 0L, 0, big)); - assertIllegalArgumentException(() -> Conversion.longToIntArray(0L, 0, new int[2], 0, big)); - // (nShorts - 1) * 16 + pos - assertIllegalArgumentException(() -> Conversion.shortArrayToInt(new short[]{0}, 0, 0, 0, big)); - assertIllegalArgumentException(() -> Conversion.shortArrayToLong(new short[]{0}, 0, 0L, 0, big)); - assertIllegalArgumentException(() -> Conversion.intToShortArray(0, 0, new short[2], 0, big)); - assertIllegalArgumentException(() -> Conversion.longToShortArray(0L, 0, new short[4], 0, big)); - } } diff --git a/src/test/java/org/apache/commons/lang3/DoubleRangeTest.java b/src/test/java/org/apache/commons/lang3/DoubleRangeTest.java index ab74c8b59..e53b816a4 100644 --- a/src/test/java/org/apache/commons/lang3/DoubleRangeTest.java +++ b/src/test/java/org/apache/commons/lang3/DoubleRangeTest.java @@ -372,19 +372,6 @@ void testIsWithCompareRange() { assertTrue(ri.contains(11), "should contain 11"); } - @Test - void testOfWithContains() { - // all integers are equal - final DoubleRange rb = of(-10, 20); - assertFalse(rb.contains(null), "should not contain null"); - assertTrue(rb.contains(10d), "should contain 10"); - assertTrue(rb.contains(-10d), "should contain -10"); - assertFalse(rb.contains(21d), "should not contain 21"); - assertFalse(rb.contains(-11d), "should not contain -11"); - - assertNullPointerException(() -> of(null, null)); - } - /** * A NaN endpoint sorts above every double under Double.compareTo's total order, so it used to construct a * half-unbounded range whose contains()/fit() accepted every value above the minimum. Construction must fail @@ -403,6 +390,19 @@ void testNaNEndpointsRejected() { assertTrue(of(0.0, Double.POSITIVE_INFINITY).contains(Double.MAX_VALUE)); } + @Test + void testOfWithContains() { + // all integers are equal + final DoubleRange rb = of(-10, 20); + assertFalse(rb.contains(null), "should not contain null"); + assertTrue(rb.contains(10d), "should contain 10"); + assertTrue(rb.contains(-10d), "should contain -10"); + assertFalse(rb.contains(21d), "should not contain 21"); + assertFalse(rb.contains(-11d), "should not contain -11"); + + assertNullPointerException(() -> of(null, null)); + } + @Test void testRangeOfChars() { final DoubleRange chars = of('a', 'z'); diff --git a/src/test/java/org/apache/commons/lang3/event/EventUtilsTest.java b/src/test/java/org/apache/commons/lang3/event/EventUtilsTest.java index adaf59851..98b2787e4 100644 --- a/src/test/java/org/apache/commons/lang3/event/EventUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/event/EventUtilsTest.java @@ -98,6 +98,14 @@ public void addPropertyChangeListener(final PropertyChangeListener listener) { } } + public static class ListenerCapturingSource { + PropertyChangeListener listener; + + public void addPropertyChangeListener(final PropertyChangeListener listener) { + this.listener = listener; + } + } + public interface MultipleEventListener { void event1(PropertyChangeEvent e); @@ -112,14 +120,6 @@ public void addMultipleEventListener(final MultipleEventListener listener) { } } - public static class ListenerCapturingSource { - PropertyChangeListener listener; - - public void addPropertyChangeListener(final PropertyChangeListener listener) { - this.listener = listener; - } - } - public static class PropertyChangeSource { private final EventListenerSupport<PropertyChangeListener> listeners = EventListenerSupport.create(PropertyChangeListener.class); diff --git a/src/test/java/org/apache/commons/lang3/exception/ExceptionUtilsTest.java b/src/test/java/org/apache/commons/lang3/exception/ExceptionUtilsTest.java index 6668b6856..533acce5b 100644 --- a/src/test/java/org/apache/commons/lang3/exception/ExceptionUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/exception/ExceptionUtilsTest.java @@ -565,6 +565,52 @@ void testGetThrowableCount_Throwable() { assertEquals(3, ExceptionUtils.getThrowableCount(cyclicCause)); } + @Test + void testGetThrowableList_Throwable_jdkNoCause() { + final List<?> throwables = ExceptionUtils.getThrowableList(jdkNoCause); + assertEquals(1, throwables.size()); + assertSame(jdkNoCause, throwables.get(0)); + } + + @Test + void testGetThrowableList_Throwable_nested() { + final List<?> throwables = ExceptionUtils.getThrowableList(nested); + assertEquals(2, throwables.size()); + assertSame(nested, throwables.get(0)); + assertSame(withoutCause, throwables.get(1)); + } + + @Test + void testGetThrowableList_Throwable_null() { + final List<?> throwables = ExceptionUtils.getThrowableList(null); + assertEquals(0, throwables.size()); + } + + @Test + void testGetThrowableList_Throwable_recursiveCause() { + final List<?> throwables = ExceptionUtils.getThrowableList(cyclicCause); + assertEquals(3, throwables.size()); + assertSame(cyclicCause, throwables.get(0)); + assertSame(cyclicCause.getCause(), throwables.get(1)); + assertSame(cyclicCause.getCause().getCause(), throwables.get(2)); + } + + @Test + void testGetThrowableList_Throwable_withCause() { + final List<?> throwables = ExceptionUtils.getThrowableList(withCause); + assertEquals(3, throwables.size()); + assertSame(withCause, throwables.get(0)); + assertSame(nested, throwables.get(1)); + assertSame(withoutCause, throwables.get(2)); + } + + @Test + void testGetThrowableList_Throwable_withoutCause() { + final List<?> throwables = ExceptionUtils.getThrowableList(withoutCause); + assertEquals(1, throwables.size()); + assertSame(withoutCause, throwables.get(0)); + } + @Test void testGetThrowableListDeepChain() { final CountingException[] chain = new CountingException[10_000]; @@ -618,52 +664,6 @@ void testGetThrowableListSelfCause() { assertSame(exception, throwables.get(0)); } - @Test - void testGetThrowableList_Throwable_jdkNoCause() { - final List<?> throwables = ExceptionUtils.getThrowableList(jdkNoCause); - assertEquals(1, throwables.size()); - assertSame(jdkNoCause, throwables.get(0)); - } - - @Test - void testGetThrowableList_Throwable_nested() { - final List<?> throwables = ExceptionUtils.getThrowableList(nested); - assertEquals(2, throwables.size()); - assertSame(nested, throwables.get(0)); - assertSame(withoutCause, throwables.get(1)); - } - - @Test - void testGetThrowableList_Throwable_null() { - final List<?> throwables = ExceptionUtils.getThrowableList(null); - assertEquals(0, throwables.size()); - } - - @Test - void testGetThrowableList_Throwable_recursiveCause() { - final List<?> throwables = ExceptionUtils.getThrowableList(cyclicCause); - assertEquals(3, throwables.size()); - assertSame(cyclicCause, throwables.get(0)); - assertSame(cyclicCause.getCause(), throwables.get(1)); - assertSame(cyclicCause.getCause().getCause(), throwables.get(2)); - } - - @Test - void testGetThrowableList_Throwable_withCause() { - final List<?> throwables = ExceptionUtils.getThrowableList(withCause); - assertEquals(3, throwables.size()); - assertSame(withCause, throwables.get(0)); - assertSame(nested, throwables.get(1)); - assertSame(withoutCause, throwables.get(2)); - } - - @Test - void testGetThrowableList_Throwable_withoutCause() { - final List<?> throwables = ExceptionUtils.getThrowableList(withoutCause); - assertEquals(1, throwables.size()); - assertSame(withoutCause, throwables.get(0)); - } - @Test void testGetThrowables_Throwable_jdkNoCause() { final Throwable[] throwables = ExceptionUtils.getThrowables(jdkNoCause); diff --git a/src/test/java/org/apache/commons/lang3/math/FractionTest.java b/src/test/java/org/apache/commons/lang3/math/FractionTest.java index 6060a926b..752b6eda0 100644 --- a/src/test/java/org/apache/commons/lang3/math/FractionTest.java +++ b/src/test/java/org/apache/commons/lang3/math/FractionTest.java @@ -64,92 +64,6 @@ void testAbs() { assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Integer.MIN_VALUE, 1).abs()); } - @Test - void testAddSubtractUnreducedOperands() { - // 1073741823/2147483646 is 1/2, and 1/2 + 3/5 is 11/10. - Fraction f = Fraction.getFraction(1073741823, 2147483646).add(Fraction.getFraction(3, 5)); - assertEquals(11, f.getNumerator()); - assertEquals(10, f.getDenominator()); - - f = Fraction.getFraction(1073741823, 2147483646).subtract(Fraction.getFraction(3, 5)); - assertEquals(-1, f.getNumerator()); - assertEquals(10, f.getDenominator()); - - // 2147483646/2147483646 is 1, and 1 + -11 is -10. - f = Fraction.getFraction(2147483646, 2147483646).add(Fraction.getFraction(-11, 1)); - assertEquals(-10, f.getNumerator()); - assertEquals(1, f.getDenominator()); - - // add() returns the result in reduced form. - f = Fraction.getFraction(50, 100).add(Fraction.getFraction(1, 3)); - assertEquals(5, f.getNumerator()); - assertEquals(6, f.getDenominator()); - - f = Fraction.getFraction(2, 4).add(Fraction.getFraction(1, 2)); - assertEquals(1, f.getNumerator()); - assertEquals(1, f.getDenominator()); - - // Reducing the operands by hand must not change the answer. - assertEquals(Fraction.getFraction(7, 13).reduce().add(Fraction.getFraction(46341, 1073741823).reduce()), - Fraction.getFraction(7, 13).add(Fraction.getFraction(46341, 1073741823))); - - // Both operands unreduced: 2/4 + 2/6 is 1/2 + 1/3. - f = Fraction.getFraction(2, 4).add(Fraction.getFraction(2, 6)); - assertEquals(5, f.getNumerator()); - assertEquals(6, f.getDenominator()); - - // Reduced denominators share a factor: 2/4 - 2/12 is 1/2 - 1/6. - f = Fraction.getFraction(2, 4).subtract(Fraction.getFraction(2, 12)); - assertEquals(1, f.getNumerator()); - assertEquals(3, f.getDenominator()); - - // Equal values cancel to 0/1. - f = Fraction.getFraction(2, 4).subtract(Fraction.getFraction(3, 6)); - assertEquals(0, f.getNumerator()); - assertEquals(1, f.getDenominator()); - - // Integer.MIN_VALUE/2 reduces to -1073741824/1 without overflowing. - f = Fraction.getFraction(Integer.MIN_VALUE, 2).add(Fraction.getFraction(2, 4)); - assertEquals(-Integer.MAX_VALUE, f.getNumerator()); - assertEquals(2, f.getDenominator()); - - // A result that genuinely does not fit an int still overflows. - final Fraction maxValue = Fraction.getFraction(-Integer.MAX_VALUE, 1); - assertThrows(ArithmeticException.class, () -> maxValue.add(maxValue)); - } - - @Test - void testAddSubtractZeroOperand() { - // A zero operand returns the other operand in reduced form. - Fraction f = Fraction.ZERO.add(Fraction.getFraction(2, 4)); - assertEquals(1, f.getNumerator()); - assertEquals(2, f.getDenominator()); - - f = Fraction.getFraction(2, 4).add(Fraction.ZERO); - assertEquals(1, f.getNumerator()); - assertEquals(2, f.getDenominator()); - - f = Fraction.ZERO.subtract(Fraction.getFraction(2, 4)); - assertEquals(-1, f.getNumerator()); - assertEquals(2, f.getDenominator()); - - f = Fraction.getFraction(2, 4).subtract(Fraction.ZERO); - assertEquals(1, f.getNumerator()); - assertEquals(2, f.getDenominator()); - - // Integer.MIN_VALUE/2 reduces to -1073741824/1, whose negation fits an int. - f = Fraction.ZERO.subtract(Fraction.getFraction(Integer.MIN_VALUE, 2)); - assertEquals(1073741824, f.getNumerator()); - assertEquals(1, f.getDenominator()); - - // Integer.MIN_VALUE/1 is in lowest terms and still cannot be negated. - assertThrows(ArithmeticException.class, () -> Fraction.ZERO.subtract(Fraction.getFraction(Integer.MIN_VALUE, 1))); - - // both operands being unreduced zeros - assertEquals(Fraction.ZERO, Fraction.getFraction(0, 2).add(Fraction.getFraction(0, 3))); - assertEquals(Fraction.ZERO, Fraction.getFraction(0, 2).subtract(Fraction.getFraction(0, 3))); - } - @Test void testAdd() { Fraction f; @@ -263,6 +177,92 @@ void testAdd() { assertEquals(2114962910, f.getDenominator()); } + @Test + void testAddSubtractUnreducedOperands() { + // 1073741823/2147483646 is 1/2, and 1/2 + 3/5 is 11/10. + Fraction f = Fraction.getFraction(1073741823, 2147483646).add(Fraction.getFraction(3, 5)); + assertEquals(11, f.getNumerator()); + assertEquals(10, f.getDenominator()); + + f = Fraction.getFraction(1073741823, 2147483646).subtract(Fraction.getFraction(3, 5)); + assertEquals(-1, f.getNumerator()); + assertEquals(10, f.getDenominator()); + + // 2147483646/2147483646 is 1, and 1 + -11 is -10. + f = Fraction.getFraction(2147483646, 2147483646).add(Fraction.getFraction(-11, 1)); + assertEquals(-10, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + // add() returns the result in reduced form. + f = Fraction.getFraction(50, 100).add(Fraction.getFraction(1, 3)); + assertEquals(5, f.getNumerator()); + assertEquals(6, f.getDenominator()); + + f = Fraction.getFraction(2, 4).add(Fraction.getFraction(1, 2)); + assertEquals(1, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + // Reducing the operands by hand must not change the answer. + assertEquals(Fraction.getFraction(7, 13).reduce().add(Fraction.getFraction(46341, 1073741823).reduce()), + Fraction.getFraction(7, 13).add(Fraction.getFraction(46341, 1073741823))); + + // Both operands unreduced: 2/4 + 2/6 is 1/2 + 1/3. + f = Fraction.getFraction(2, 4).add(Fraction.getFraction(2, 6)); + assertEquals(5, f.getNumerator()); + assertEquals(6, f.getDenominator()); + + // Reduced denominators share a factor: 2/4 - 2/12 is 1/2 - 1/6. + f = Fraction.getFraction(2, 4).subtract(Fraction.getFraction(2, 12)); + assertEquals(1, f.getNumerator()); + assertEquals(3, f.getDenominator()); + + // Equal values cancel to 0/1. + f = Fraction.getFraction(2, 4).subtract(Fraction.getFraction(3, 6)); + assertEquals(0, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + // Integer.MIN_VALUE/2 reduces to -1073741824/1 without overflowing. + f = Fraction.getFraction(Integer.MIN_VALUE, 2).add(Fraction.getFraction(2, 4)); + assertEquals(-Integer.MAX_VALUE, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + // A result that genuinely does not fit an int still overflows. + final Fraction maxValue = Fraction.getFraction(-Integer.MAX_VALUE, 1); + assertThrows(ArithmeticException.class, () -> maxValue.add(maxValue)); + } + + @Test + void testAddSubtractZeroOperand() { + // A zero operand returns the other operand in reduced form. + Fraction f = Fraction.ZERO.add(Fraction.getFraction(2, 4)); + assertEquals(1, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + f = Fraction.getFraction(2, 4).add(Fraction.ZERO); + assertEquals(1, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + f = Fraction.ZERO.subtract(Fraction.getFraction(2, 4)); + assertEquals(-1, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + f = Fraction.getFraction(2, 4).subtract(Fraction.ZERO); + assertEquals(1, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + // Integer.MIN_VALUE/2 reduces to -1073741824/1, whose negation fits an int. + f = Fraction.ZERO.subtract(Fraction.getFraction(Integer.MIN_VALUE, 2)); + assertEquals(1073741824, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + // Integer.MIN_VALUE/1 is in lowest terms and still cannot be negated. + assertThrows(ArithmeticException.class, () -> Fraction.ZERO.subtract(Fraction.getFraction(Integer.MIN_VALUE, 1))); + + // both operands being unreduced zeros + assertEquals(Fraction.ZERO, Fraction.getFraction(0, 2).add(Fraction.getFraction(0, 3))); + assertEquals(Fraction.ZERO, Fraction.getFraction(0, 2).subtract(Fraction.getFraction(0, 3))); + } + @Test void testCompareTo() { final Fraction f1; diff --git a/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java b/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java index 475413286..405c73191 100644 --- a/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java @@ -159,10 +159,16 @@ public void testTwo(final PackagePrivateEmptyInterface obj) { } } + static class InstanceLabel implements StaticLabel { + public String label() { + return "instance"; + } + } interface InterfaceGetMatchingMethod { default void testMethod6() { } } + private static final class MethodDescriptor { final Class<?> declaringClass; final String name; @@ -195,6 +201,24 @@ public static class PublicImpl2OfPackagePrivateEmptyInterface implements Package // empty } + static class StaticChild extends StaticParent { + public static String who() { + return "child"; + } + } + + public interface StaticLabel { + static String label() { + return "interface"; + } + } + + public static class StaticParent { + public static String who() { + return "parent"; + } + } + public static class TestBean { public static String bar() { @@ -528,30 +552,6 @@ public String foo() { } } - public static class StaticParent { - public static String who() { - return "parent"; - } - } - - static class StaticChild extends StaticParent { - public static String who() { - return "child"; - } - } - - public interface StaticLabel { - static String label() { - return "interface"; - } - } - - static class InstanceLabel implements StaticLabel { - public String label() { - return "instance"; - } - } - private static class TestMutable implements Mutable<Object> { @Override public Object getValue() { @@ -634,63 +634,6 @@ void testGetAccessibleInterfaceMethodFromDescription(final Class<?> clazz) { } } - @ParameterizedTest - @ValueSource(classes = {TestMutable.class, TestMutableSubclass.class}) - void testGetMatchingAccessibleMethodOnNonPublicClass(final Class<?> clazz) { - assertSame(Mutable.class, MethodUtils.getMatchingAccessibleMethod(clazz, "getValue").getDeclaringClass()); - assertSame(Mutable.class, - MethodUtils.getMatchingAccessibleMethod(clazz, "setValue", Object.class).getDeclaringClass()); - } - - @Test - void testGetMatchingAccessibleMethodOnNonPublicJdkClass() { - assertSame(List.class, - MethodUtils.getMatchingAccessibleMethod(Collections.emptyList().getClass(), "size").getDeclaringClass()); - assertSame(List.class, - MethodUtils.getMatchingAccessibleMethod(Arrays.asList(1, 2).getClass(), "size").getDeclaringClass()); - assertSame(Map.class, - MethodUtils.getMatchingAccessibleMethod(Collections.emptyMap().getClass(), "size").getDeclaringClass()); - } - - @Test - void testGetMatchingAccessibleMethodWithNoPublicDeclaration() { - assertSame(TestBeanWithInterfaces.class, - MethodUtils.getMatchingAccessibleMethod(TestBeanWithInterfaces.class, "foo").getDeclaringClass()); - } - - @Test - void testInvokeMethodOnNonPublicClass() throws Exception { - assertEquals(0, MethodUtils.invokeMethod(Collections.emptyList(), "size")); - assertEquals(2, MethodUtils.invokeMethod(Arrays.asList(1, 2), "size")); - assertEquals(0, MethodUtils.invokeMethod(Collections.emptyMap(), "size")); - assertEquals(0, MethodUtils.invokeMethod(Collections.unmodifiableList(new ArrayList<>()), "size")); - assertNull(MethodUtils.invokeMethod(new TestMutable(), "getValue")); - } - - @Test - void testInvokeStaticMethodOnNonPublicSubclass() throws Exception { - // A static method hides rather than overrides, so the subclass's own declaration is invoked. - assertSame(StaticChild.class, MethodUtils.getMatchingAccessibleMethod(StaticChild.class, "who").getDeclaringClass()); - assertEquals("child", MethodUtils.invokeStaticMethod(StaticChild.class, "who")); - } - - @Test - void testInvokeMethodIgnoresStaticInterfaceMethod() throws Exception { - assertSame(InstanceLabel.class, MethodUtils.getMatchingAccessibleMethod(InstanceLabel.class, "label").getDeclaringClass()); - assertEquals("instance", MethodUtils.invokeMethod(new InstanceLabel(), "label")); - assertNull(MethodUtils.getAccessibleMethod(InstanceLabel.class, "label")); - } - - @Test - void testInvokeMethodIgnoresPrivateInterfaceMethod() throws Exception { - // A private interface method needs Java 9, so the pair is precompiled under src/test/resources. - assumeTrue(SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_9)); - final Class<?> labels = Class.forName("org.apache.commons.lang3.reflect.testbed9.PrivateInterfaceLabels"); - final Object bean = labels.getMethod("newBean").invoke(null); - assertEquals("bean", MethodUtils.invokeMethod(bean, "label")); - assertNull(MethodUtils.getAccessibleMethod(bean.getClass(), "label")); - } - @Test void testGetAccessibleMethodInaccessible() throws Exception { assertNull(MethodUtils.getAccessibleMethod(TestBean.class.getDeclaredMethod("privateStuff"))); @@ -886,6 +829,30 @@ void testGetMatchingAccessibleMethod() { expectMatchingAccessibleMethodParameterTypes(Files.class, "exists", singletonArray(Path.class), new Class[] { Path.class, LinkOption[].class }); } + @ParameterizedTest + @ValueSource(classes = {TestMutable.class, TestMutableSubclass.class}) + void testGetMatchingAccessibleMethodOnNonPublicClass(final Class<?> clazz) { + assertSame(Mutable.class, MethodUtils.getMatchingAccessibleMethod(clazz, "getValue").getDeclaringClass()); + assertSame(Mutable.class, + MethodUtils.getMatchingAccessibleMethod(clazz, "setValue", Object.class).getDeclaringClass()); + } + + @Test + void testGetMatchingAccessibleMethodOnNonPublicJdkClass() { + assertSame(List.class, + MethodUtils.getMatchingAccessibleMethod(Collections.emptyList().getClass(), "size").getDeclaringClass()); + assertSame(List.class, + MethodUtils.getMatchingAccessibleMethod(Arrays.asList(1, 2).getClass(), "size").getDeclaringClass()); + assertSame(Map.class, + MethodUtils.getMatchingAccessibleMethod(Collections.emptyMap().getClass(), "size").getDeclaringClass()); + } + + @Test + void testGetMatchingAccessibleMethodWithNoPublicDeclaration() { + assertSame(TestBeanWithInterfaces.class, + MethodUtils.getMatchingAccessibleMethod(TestBeanWithInterfaces.class, "foo").getDeclaringClass()); + } + @Test void testGetMatchingMethod() throws NoSuchMethodException { assertEquals(MethodUtils.getMatchingMethod(GetMatchingMethodClass.class, "testMethod"), GetMatchingMethodClass.class.getMethod("testMethod")); @@ -1218,6 +1185,32 @@ void testInvokeMethodForceAccessWithArgs() throws Exception { assertNullPointerException(() -> MethodUtils.invokeMethod(testBean, true, null, "Hi There")); } + @Test + void testInvokeMethodIgnoresPrivateInterfaceMethod() throws Exception { + // A private interface method needs Java 9, so the pair is precompiled under src/test/resources. + assumeTrue(SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_9)); + final Class<?> labels = Class.forName("org.apache.commons.lang3.reflect.testbed9.PrivateInterfaceLabels"); + final Object bean = labels.getMethod("newBean").invoke(null); + assertEquals("bean", MethodUtils.invokeMethod(bean, "label")); + assertNull(MethodUtils.getAccessibleMethod(bean.getClass(), "label")); + } + + @Test + void testInvokeMethodIgnoresStaticInterfaceMethod() throws Exception { + assertSame(InstanceLabel.class, MethodUtils.getMatchingAccessibleMethod(InstanceLabel.class, "label").getDeclaringClass()); + assertEquals("instance", MethodUtils.invokeMethod(new InstanceLabel(), "label")); + assertNull(MethodUtils.getAccessibleMethod(InstanceLabel.class, "label")); + } + + @Test + void testInvokeMethodOnNonPublicClass() throws Exception { + assertEquals(0, MethodUtils.invokeMethod(Collections.emptyList(), "size")); + assertEquals(2, MethodUtils.invokeMethod(Arrays.asList(1, 2), "size")); + assertEquals(0, MethodUtils.invokeMethod(Collections.emptyMap(), "size")); + assertEquals(0, MethodUtils.invokeMethod(Collections.unmodifiableList(new ArrayList<>()), "size")); + assertNull(MethodUtils.invokeMethod(new TestMutable(), "getValue")); + } + @Test void testInvokeMethodVarArgsNotUniqueResolvable() throws Exception { assertEquals("Boolean...", MethodUtils.invokeMethod(testBean, "varOverload", new Object[] { null })); @@ -1355,6 +1348,13 @@ void testInvokeStaticMethod1PlusVarArgs() throws Exception { assertThrows(NoSuchMethodException.class, () -> MethodUtils.invokeMethod(testBean, "staticIntIntVarArg", 1, "s1", 5)); } + @Test + void testInvokeStaticMethodOnNonPublicSubclass() throws Exception { + // A static method hides rather than overrides, so the subclass's own declaration is invoked. + assertSame(StaticChild.class, MethodUtils.getMatchingAccessibleMethod(StaticChild.class, "who").getDeclaringClass()); + assertEquals("child", MethodUtils.invokeStaticMethod(StaticChild.class, "who")); + } + @Test void testInvokeStaticMethodVarArgsOfInterface() throws Exception { // staticPackagePrivateEmptyInterface diff --git a/src/test/java/org/apache/commons/lang3/reflect/TypeUtilsTest.java b/src/test/java/org/apache/commons/lang3/reflect/TypeUtilsTest.java index 04a2087eb..5dc6d15f9 100644 --- a/src/test/java/org/apache/commons/lang3/reflect/TypeUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/reflect/TypeUtilsTest.java @@ -1045,6 +1045,56 @@ void testIsAssignableGenericListTypes() throws NoSuchFieldException { assertTrue(TypeUtils.isAssignable(superStringListType, superStringListType)); } + @Test + void testIsAssignableWildcardWithMultipleUpperBounds() { + // ? extends Serializable & Cloneable + final WildcardType subject = TypeUtils.wildcardType() + .withUpperBounds(Serializable.class, Cloneable.class) + .build(); + + // ? extends Serializable + final WildcardType targetSerializable = TypeUtils.wildcardType() + .withUpperBounds(Serializable.class) + .build(); + + // ? extends Cloneable + final WildcardType targetCloneable = TypeUtils.wildcardType() + .withUpperBounds(Cloneable.class) + .build(); + + // ? extends CharSequence + final WildcardType targetCharSequence = TypeUtils.wildcardType() + .withUpperBounds(CharSequence.class) + .build(); + + // ? extends Serializable & Cloneable + final WildcardType targetSerializableAndCloneable = TypeUtils.wildcardType() + .withUpperBounds(Serializable.class, Cloneable.class) + .build(); + + // ? extends Serializable & CharSequence + final WildcardType targetSerializableAndCharSequence = TypeUtils.wildcardType() + .withUpperBounds(Serializable.class, CharSequence.class) + .build(); + + // Single target bound satisfied + assertTrue(TypeUtils.isAssignable(subject, targetSerializable)); + assertTrue(TypeUtils.isAssignable(subject, targetCloneable)); + assertTrue(TypeUtils.isAssignable(subject, TypeUtils.wildcardType().withUpperBounds(Object.class).build())); + assertFalse(TypeUtils.isAssignable(subject, targetCharSequence)); + + // Multiple target bounds where all are satisfied + assertTrue(TypeUtils.isAssignable(subject, targetSerializableAndCloneable)); + assertTrue(TypeUtils.isAssignable(subject, TypeUtils.wildcardType().withUpperBounds(Object.class, Serializable.class).build())); + + // Multiple target bounds where only one is satisfied + assertFalse(TypeUtils.isAssignable(subject, targetSerializableAndCharSequence)); + + // Reverse direction: single bound cannot satisfy multiple bounds + assertFalse(TypeUtils.isAssignable(targetSerializable, subject)); + assertFalse(TypeUtils.isAssignable(targetCloneable, subject)); + } + @SuppressWarnings("boxing") // boxing is deliberate here @Test void testIsInstance() throws NoSuchFieldException { @@ -1246,54 +1296,4 @@ void testWrap() { assertEquals(String.class, TypeUtils.wrap(String.class).getType()); } - @Test - void testIsAssignableWildcardWithMultipleUpperBounds() { - // ? extends Serializable & Cloneable - final WildcardType subject = TypeUtils.wildcardType() - .withUpperBounds(Serializable.class, Cloneable.class) - .build(); - - // ? extends Serializable - final WildcardType targetSerializable = TypeUtils.wildcardType() - .withUpperBounds(Serializable.class) - .build(); - - // ? extends Cloneable - final WildcardType targetCloneable = TypeUtils.wildcardType() - .withUpperBounds(Cloneable.class) - .build(); - - // ? extends CharSequence - final WildcardType targetCharSequence = TypeUtils.wildcardType() - .withUpperBounds(CharSequence.class) - .build(); - - // ? extends Serializable & Cloneable - final WildcardType targetSerializableAndCloneable = TypeUtils.wildcardType() - .withUpperBounds(Serializable.class, Cloneable.class) - .build(); - - // ? extends Serializable & CharSequence - final WildcardType targetSerializableAndCharSequence = TypeUtils.wildcardType() - .withUpperBounds(Serializable.class, CharSequence.class) - .build(); - - // Single target bound satisfied - assertTrue(TypeUtils.isAssignable(subject, targetSerializable)); - assertTrue(TypeUtils.isAssignable(subject, targetCloneable)); - assertTrue(TypeUtils.isAssignable(subject, TypeUtils.wildcardType().withUpperBounds(Object.class).build())); - assertFalse(TypeUtils.isAssignable(subject, targetCharSequence)); - - // Multiple target bounds where all are satisfied - assertTrue(TypeUtils.isAssignable(subject, targetSerializableAndCloneable)); - assertTrue(TypeUtils.isAssignable(subject, TypeUtils.wildcardType().withUpperBounds(Object.class, Serializable.class).build())); - - // Multiple target bounds where only one is satisfied - assertFalse(TypeUtils.isAssignable(subject, targetSerializableAndCharSequence)); - - // Reverse direction: single bound cannot satisfy multiple bounds - assertFalse(TypeUtils.isAssignable(targetSerializable, subject)); - assertFalse(TypeUtils.isAssignable(targetCloneable, subject)); - } - } diff --git a/src/test/java/org/apache/commons/lang3/text/StrSubstitutorTest.java b/src/test/java/org/apache/commons/lang3/text/StrSubstitutorTest.java index c7e3b407f..8c3f02274 100644 --- a/src/test/java/org/apache/commons/lang3/text/StrSubstitutorTest.java +++ b/src/test/java/org/apache/commons/lang3/text/StrSubstitutorTest.java @@ -228,30 +228,6 @@ void testDeeplyNestedReplacementThrowsIllegalStateException() { assertEquals("x", sub.replace("${v398}")); } - /** - * Tests that exponential acyclic fan-out (each variable expanding to many more) hits the - * total-output-size budget with an {@link IllegalStateException} instead of consuming - * unbounded CPU and memory. The cycle check cannot detect this shape (no variable repeats - * on the substitution stack). - */ - @Test - void testExponentialFanOutReplacementThrowsIllegalStateException() { - final Map<String, String> map = new HashMap<>(); - final char[] leafChars = new char[8192]; - java.util.Arrays.fill(leafChars, 'x'); - map.put("a6", new String(leafChars)); - for (int level = 5; level >= 0; level--) { - final StringBuilder value = new StringBuilder(); - for (int i = 0; i < 10; i++) { - value.append("${a").append(level + 1).append("}"); - } - map.put("a" + level, value.toString()); - } - // full expansion would be 10^6 leaves * 8 KiB = ~8 GiB - final StrSubstitutor sub = new StrSubstitutor(map); - assertThrows(IllegalStateException.class, () -> sub.replace("${a0}"), "Size budget was not enforced."); - } - @Test void testDefaultValueDelimiters() { final Map<String, String> map = new HashMap<>(); @@ -285,6 +261,30 @@ void testDefaultValueDelimiters() { sub.replace("The ${animal} jumps over the lazy ${target}. ${undefined.number!1234567890}.")); } + /** + * Tests that exponential acyclic fan-out (each variable expanding to many more) hits the + * total-output-size budget with an {@link IllegalStateException} instead of consuming + * unbounded CPU and memory. The cycle check cannot detect this shape (no variable repeats + * on the substitution stack). + */ + @Test + void testExponentialFanOutReplacementThrowsIllegalStateException() { + final Map<String, String> map = new HashMap<>(); + final char[] leafChars = new char[8192]; + java.util.Arrays.fill(leafChars, 'x'); + map.put("a6", new String(leafChars)); + for (int level = 5; level >= 0; level--) { + final StringBuilder value = new StringBuilder(); + for (int i = 0; i < 10; i++) { + value.append("${a").append(level + 1).append("}"); + } + map.put("a" + level, value.toString()); + } + // full expansion would be 10^6 leaves * 8 KiB = ~8 GiB + final StrSubstitutor sub = new StrSubstitutor(map); + assertThrows(IllegalStateException.class, () -> sub.replace("${a0}"), "Size budget was not enforced."); + } + /** * Tests get set. */
