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 2780ad699014d6ce9c7141b15c45bb2c806ef173
Author: Gary Gregory <[email protected]>
AuthorDate: Mon Jun 15 12:00:37 2026 +0000

    Fix RandomStringUtils.random() false rejection of letters and digits
    (#1703).
    
    Sort members
---
 src/changes/changes.xml                            |  1 +
 .../commons/lang3/RandomStringUtilsTest.java       | 60 +++++++++++-----------
 2 files changed, 31 insertions(+), 30 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index f76c6f675..dc87f2991 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -187,6 +187,7 @@ java.lang.NullPointerException: Cannot invoke
     <action                   type="fix" dev="ggregory" due-to="alhudz">Fix 
int overflow in StringUtils.midString() and StrBuilder.midString() length 
handling (#1699).</action>
     <action                   type="fix" dev="ggregory" due-to="Gary Gregory, 
Javid Khan">Throw IllegalArgumentException for trailing whitespace in 
ExtendedMessageFormat argument index (#1701).</action>
     <action                   type="fix" dev="ggregory" due-to="alhudz, Gary 
Gregory">Reject doubled leading sign in NumberUtils.createBigInteger 
(#1702).</action>
+    <action                   type="fix" dev="ggregory" due-to="alhudz, Gary 
Gregory">Fix RandomStringUtils.random() false rejection of letters and digits 
(#1703).</action>
     <!-- ADD -->
     <action                   type="add" dev="ggregory" due-to="Gary 
Gregory">Add JavaVersion.JAVA_27.</action>
     <action                   type="add" dev="ggregory" due-to="Gary 
Gregory">Add SystemUtils.IS_JAVA_27.</action>
diff --git a/src/test/java/org/apache/commons/lang3/RandomStringUtilsTest.java 
b/src/test/java/org/apache/commons/lang3/RandomStringUtilsTest.java
index 2c6a29181..ab0c3717f 100644
--- a/src/test/java/org/apache/commons/lang3/RandomStringUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/RandomStringUtilsTest.java
@@ -119,36 +119,6 @@ public void testCustomLetterCharsArrayDoesNotThrowIAE() {
         }, "RandomStringUtils.random() threw IAE for valid letter chars array 
- pre-patch behavior");
     }
 
-    /**
-     * Asking for {@code letters && digits} must never be stricter than asking 
for {@code digits} alone. The range
-     * {@code ['0', 'A')} holds the digits but no letters, so {@code 
random(count, '0', 'A', true, true, ...)} must
-     * generate digits like the digits-only call over the same range, not 
throw IllegalArgumentException.
-     */
-    @Test
-    void testLettersAndDigitsOverDigitOnlyRange() {
-        final String both = RandomStringUtils.random(100, '0', 'A', true, 
true, null, new Random(42));
-        assertEquals(100, both.length());
-        for (final char c : both.toCharArray()) {
-            assertTrue(c >= '0' && c <= '9', () -> "Expected a digit but got: 
" + c);
-        }
-        // digits alone already works over this range, so letters && digits 
must not reject it
-        assertDoesNotThrow(() -> RandomStringUtils.random(100, '0', 'A', 
false, true, null, new Random(42)));
-    }
-
-    /**
-     * The {@code letters && digits} ASCII fast path clamps {@code start} up 
to {@code '0'} and {@code end} down to
-     * {@code 'z' + 1}. A range sitting entirely above the alphanumerics, e.g. 
{@code ['z' + 1, 0x7f)}, collapses to
-     * {@code start >= end} after that clamp. It must throw a clear range 
IllegalArgumentException, not fall through to
-     * {@code nextBits(0)} which reports the unrelated "number of bits must be 
between 1 and 32".
-     */
-    @Test
-    void testLettersAndDigitsOverEmptyAsciiRange() {
-        final IllegalArgumentException e = 
assertThrows(IllegalArgumentException.class,
-                () -> RandomStringUtils.random(10, 'z' + 1, 0x7f, true, true, 
null, new Random(42)));
-        assertTrue(e.getMessage() != null && !e.getMessage().contains("number 
of bits"),
-                () -> "Expected a range-validation message but got: " + 
e.getMessage());
-    }
-
     @Test
     void testExceptionsRandom() {
         assertIllegalArgumentException(() -> RandomStringUtils.random(-1));
@@ -367,6 +337,36 @@ void testLANG807(final RandomStringUtils rsu) {
         assertTrue(msg.contains("end"), "Message (" + msg + ") must contain 
'end'");
     }
 
+    /**
+     * Asking for {@code letters && digits} must never be stricter than asking 
for {@code digits} alone. The range
+     * {@code ['0', 'A')} holds the digits but no letters, so {@code 
random(count, '0', 'A', true, true, ...)} must
+     * generate digits like the digits-only call over the same range, not 
throw IllegalArgumentException.
+     */
+    @Test
+    void testLettersAndDigitsOverDigitOnlyRange() {
+        final String both = RandomStringUtils.random(100, '0', 'A', true, 
true, null, new Random(42));
+        assertEquals(100, both.length());
+        for (final char c : both.toCharArray()) {
+            assertTrue(c >= '0' && c <= '9', () -> "Expected a digit but got: 
" + c);
+        }
+        // digits alone already works over this range, so letters && digits 
must not reject it
+        assertDoesNotThrow(() -> RandomStringUtils.random(100, '0', 'A', 
false, true, null, new Random(42)));
+    }
+
+    /**
+     * The {@code letters && digits} ASCII fast path clamps {@code start} up 
to {@code '0'} and {@code end} down to
+     * {@code 'z' + 1}. A range sitting entirely above the alphanumerics, e.g. 
{@code ['z' + 1, 0x7f)}, collapses to
+     * {@code start >= end} after that clamp. It must throw a clear range 
IllegalArgumentException, not fall through to
+     * {@code nextBits(0)} which reports the unrelated "number of bits must be 
between 1 and 32".
+     */
+    @Test
+    void testLettersAndDigitsOverEmptyAsciiRange() {
+        final IllegalArgumentException e = 
assertThrows(IllegalArgumentException.class,
+                () -> RandomStringUtils.random(10, 'z' + 1, 0x7f, true, true, 
null, new Random(42)));
+        assertTrue(e.getMessage() != null && !e.getMessage().contains("number 
of bits"),
+                () -> "Expected a range-validation message but got: " + 
e.getMessage());
+    }
+
     /**
      * Test {@code RandomStringUtils.random} works appropriately when 
letters=true
      * and the range does not only include ASCII letters.

Reply via email to