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 75d71af80 Include 0x7E (~) in RandomStringUtils nextGraph and
nextPrint (#1808)
75d71af80 is described below
commit 75d71af80ace2d75dab3f6bb82ac2ef91650634e
Author: alhuda <[email protected]>
AuthorDate: Thu Oct 1 17:51:12 2026 +0530
Include 0x7E (~) in RandomStringUtils nextGraph and nextPrint (#1808)
* include 0x7e in RandomStringUtils nextGraph and nextPrint
* Update tests for RandomStringUtils to verify character ranges
Co-authored-by: Copilot Autofix powered by AI
<[email protected]>
* Assert both range ends in the nextGraph and nextPrint tests
The range-only checks passed whether or not nextGraph and nextPrint were
fixed, so they did not guard the off-by-one. Fold the range check and the
boundary check into one helper that asserts the lowest and highest
documented characters are both generated and that nothing outside the
inclusive range is, and drop the hard-coded probability formula from the
Javadoc in favour of referencing LOOP_COUNT.
---------
Co-authored-by: Gary Gregory <[email protected]>
Co-authored-by: Copilot Autofix powered by AI
<[email protected]>
---
.../apache/commons/lang3/RandomStringUtils.java | 4 +-
.../commons/lang3/RandomStringUtilsTest.java | 59 ++++++++++++++++++++++
2 files changed, 61 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/apache/commons/lang3/RandomStringUtils.java
b/src/main/java/org/apache/commons/lang3/RandomStringUtils.java
index 1baa97a0d..97874e7d3 100644
--- a/src/main/java/org/apache/commons/lang3/RandomStringUtils.java
+++ b/src/main/java/org/apache/commons/lang3/RandomStringUtils.java
@@ -969,7 +969,7 @@ public String nextAscii(final int minLengthInclusive, final
int maxLengthExclusi
* @since 3.5
*/
public String nextGraph(final int count) {
- return next(count, 33, 126, false, false);
+ return next(count, 33, 127, false, false);
}
/**
@@ -1034,7 +1034,7 @@ public String nextNumeric(final int minLengthInclusive,
final int maxLengthExclu
* @since 3.16.0
*/
public String nextPrint(final int count) {
- return next(count, 32, 126, false, false);
+ return next(count, 32, 127, false, false);
}
/**
diff --git a/src/test/java/org/apache/commons/lang3/RandomStringUtilsTest.java
b/src/test/java/org/apache/commons/lang3/RandomStringUtilsTest.java
index 9d4c1daa5..52507302f 100644
--- a/src/test/java/org/apache/commons/lang3/RandomStringUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/RandomStringUtilsTest.java
@@ -30,6 +30,7 @@
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Random;
+import java.util.function.IntFunction;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
@@ -46,10 +47,42 @@ class RandomStringUtilsTest extends AbstractLangTest {
private static final int LOOP_COUNT = 1_000;
+ /** Characters generated per iteration when checking that a documented
range boundary is reachable. */
+ private static final int BOUNDARY_SAMPLE_LENGTH = 100;
+
/** Maximum safe value for count to avoid overflow: (21x + 3) / 5 + 10 <
0x0FFF_FFFF */
private static final int MAX_SAFE_COUNT = 63_913_201;
+ /**
+ * Asserts that a generator produces both ends of an inclusive character
range and nothing outside it.
+ * <p>
+ * The random number generator of a {@link RandomStringUtils} instance
cannot be injected, so reaching a boundary is
+ * sampled rather than forced: {@value #LOOP_COUNT} strings of {@value
#BOUNDARY_SAMPLE_LENGTH} characters. Over a sample
+ * that size, a correct generator missing a given boundary character is
not a failure mode that can be observed in
+ * practice.
+ * </p>
+ *
+ * @param generator Generator under test, called with a character count.
+ * @param minInclusive Lowest character the generator is documented to
produce.
+ * @param maxInclusive Highest character the generator is documented to
produce.
+ */
+ private static void assertInclusiveRange(final IntFunction<String>
generator, final char minInclusive, final char maxInclusive) {
+ boolean minFound = false;
+ boolean maxFound = false;
+ for (int i = 0; i < LOOP_COUNT; i++) {
+ final String randString = generator.apply(BOUNDARY_SAMPLE_LENGTH);
+ for (int j = 0; j < randString.length(); j++) {
+ final char ch = randString.charAt(j);
+ assertTrue(ch >= minInclusive && ch <= maxInclusive, () ->
"character out of range: " + (int) ch);
+ minFound |= ch == minInclusive;
+ maxFound |= ch == maxInclusive;
+ }
+ }
+ assertTrue(minFound, () -> "character not generated in " + LOOP_COUNT
+ " attempts: " + (int) minInclusive);
+ assertTrue(maxFound, () -> "character not generated in " + LOOP_COUNT
+ " attempts: " + (int) maxInclusive);
+ }
+
static Stream<RandomStringUtils> randomProvider() {
return Stream.of(RandomStringUtils.secure(),
RandomStringUtils.secureStrong(), RandomStringUtils.insecure());
}
@@ -746,6 +779,32 @@ void testRandomAscii(final RandomStringUtils rsu) {
}
}
+ /**
+ * Verifies that {@link RandomStringUtils#nextGraph(int)} generates both
ends of the POSIX {@code [:graph:]} class,
+ * {@code '!'} (0x21) and {@code '~'} (0x7E), and nothing outside it.
+ *
+ * @param rsu The instance to test
+ * @see #assertInclusiveRange(IntFunction, char, char)
+ */
+ @ParameterizedTest
+ @MethodSource("randomProvider")
+ void testRandomGraphIncludesTilde(final RandomStringUtils rsu) {
+ assertInclusiveRange(rsu::nextGraph, '!', '~');
+ }
+
+ /**
+ * Verifies that {@link RandomStringUtils#nextPrint(int)} generates both
ends of the POSIX {@code [:print:]} class,
+ * space (0x20) and {@code '~'} (0x7E), and nothing outside it.
+ *
+ * @param rsu The instance to test
+ * @see #assertInclusiveRange(IntFunction, char, char)
+ */
+ @ParameterizedTest
+ @MethodSource("randomProvider")
+ void testRandomPrintIncludesTilde(final RandomStringUtils rsu) {
+ assertInclusiveRange(rsu::nextPrint, ' ', '~');
+ }
+
@ParameterizedTest
@MethodSource("randomProvider")
void testRandomAsciiRange(final RandomStringUtils rsu) {