View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3;
18  
19  import static org.hamcrest.MatcherAssert.assertThat;
20  import static org.hamcrest.Matchers.allOf;
21  import static org.hamcrest.Matchers.greaterThanOrEqualTo;
22  import static org.hamcrest.Matchers.is;
23  import static org.hamcrest.Matchers.lessThan;
24  import static org.hamcrest.Matchers.lessThanOrEqualTo;
25  import static org.junit.jupiter.api.Assertions.assertEquals;
26  import static org.junit.jupiter.api.Assertions.assertFalse;
27  import static org.junit.jupiter.api.Assertions.assertNotEquals;
28  import static org.junit.jupiter.api.Assertions.assertNotNull;
29  import static org.junit.jupiter.api.Assertions.assertThrows;
30  import static org.junit.jupiter.api.Assertions.assertTrue;
31  import static org.junit.jupiter.api.Assertions.fail;
32  
33  import java.nio.charset.Charset;
34  import java.nio.charset.StandardCharsets;
35  import java.util.Random;
36  import java.util.stream.Stream;
37  
38  import org.junit.jupiter.api.Test;
39  import org.junit.jupiter.params.ParameterizedTest;
40  import org.junit.jupiter.params.provider.MethodSource;
41  
42  /**
43   * Tests {@link RandomStringUtils}.
44   */
45  public class RandomStringUtilsTest extends AbstractLangTest {
46  
47      private static final int LOOP_COUNT = 1_000;
48  
49      static Stream<RandomStringUtils> randomProvider() {
50          return Stream.of(RandomStringUtils.secure(), RandomStringUtils.secureStrong(), RandomStringUtils.insecure());
51      }
52  
53      /**
54       * Computes Chi-Square statistic given observed and expected counts
55       *
56       * @param observed array of observed frequency counts
57       * @param expected array of expected frequency counts
58       */
59      private double chiSquare(final int[] expected, final int[] observed) {
60          double sumSq = 0.0d;
61          for (int i = 0; i < observed.length; i++) {
62              final double dev = observed[i] - expected[i];
63              sumSq += dev * dev / expected[i];
64          }
65          return sumSq;
66      }
67  
68      /**
69       * Test for LANG-1286. Creates situation where old code would overflow a char and result in a code point outside the specified range.
70       */
71      @Test
72      public void testCharOverflow() {
73          final int start = Character.MAX_VALUE;
74          final int end = Integer.MAX_VALUE;
75  
76          @SuppressWarnings("serial")
77          final Random fixedRandom = new Random() {
78              @Override
79              public int nextInt(final int n) {
80                  // Prevents selection of 'start' as the character
81                  return super.nextInt(n - 1) + 1;
82              }
83          };
84  
85          final String result = RandomStringUtils.random(2, start, end, false, false, null, fixedRandom);
86          final int c = result.codePointAt(0);
87          assertTrue(c >= start && c < end, String.format("Character '%d' not in range [%d,%d).", c, start, end));
88      }
89  
90      @Test
91      public void testConstructor() {
92          assertNotNull(new RandomStringUtils());
93      }
94  
95      @Test
96      public void testExceptionsRandom() {
97          assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.random(-1));
98          assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.random(-1, true, true));
99          assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.random(-1, new char[] { 'a' }));
100         assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.random(1, new char[0]));
101         assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.random(-1, ""));
102         assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.random(-1, (String) null));
103         assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.random(-1, 'a', 'z', false, false));
104         assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.random(-1, 'a', 'z', false, false, new char[] { 'a' }));
105         assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.random(-1, 'a', 'z', false, false, new char[] { 'a' }, new Random()));
106         assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.random(8, 32, 48, false, true));
107         assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.random(8, 32, 65, true, false));
108         assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.random(1, Integer.MIN_VALUE, -10, false, false, null));
109     }
110 
111     @ParameterizedTest
112     @MethodSource("randomProvider")
113     public void testExceptionsRandom(final RandomStringUtils rsu) {
114         assertThrows(IllegalArgumentException.class, () -> rsu.next(-1));
115         assertThrows(IllegalArgumentException.class, () -> rsu.next(-1, true, true));
116         assertThrows(IllegalArgumentException.class, () -> rsu.next(-1, new char[] { 'a' }));
117         assertThrows(IllegalArgumentException.class, () -> rsu.next(1, new char[0]));
118         assertThrows(IllegalArgumentException.class, () -> rsu.next(-1, ""));
119         assertThrows(IllegalArgumentException.class, () -> rsu.next(-1, (String) null));
120         assertThrows(IllegalArgumentException.class, () -> rsu.next(-1, 'a', 'z', false, false));
121         assertThrows(IllegalArgumentException.class, () -> rsu.next(-1, 'a', 'z', false, false, new char[] { 'a' }));
122         assertThrows(IllegalArgumentException.class, () -> rsu.next(8, 32, 48, false, true));
123         assertThrows(IllegalArgumentException.class, () -> rsu.next(8, 32, 65, true, false));
124         assertThrows(IllegalArgumentException.class, () -> rsu.next(1, Integer.MIN_VALUE, -10, false, false, null));
125     }
126 
127     @Test
128     public void testExceptionsRandomAlphabetic() {
129         assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.randomAlphabetic(-1));
130     }
131 
132     @ParameterizedTest
133     @MethodSource("randomProvider")
134     public void testExceptionsRandomAlphabetic(final RandomStringUtils rsu) {
135         assertThrows(IllegalArgumentException.class, () -> rsu.nextAlphabetic(-1));
136     }
137 
138     @Test
139     public void testExceptionsRandomAscii() {
140         assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.randomAscii(-1));
141     }
142 
143     @ParameterizedTest
144     @MethodSource("randomProvider")
145     public void testExceptionsRandomAscii(final RandomStringUtils rsu) {
146         assertThrows(IllegalArgumentException.class, () -> rsu.nextAscii(-1));
147     }
148 
149     @Test
150     public void testExceptionsRandomGraph() {
151         assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.randomGraph(-1));
152     }
153 
154     @ParameterizedTest
155     @MethodSource("randomProvider")
156     public void testExceptionsRandomGraph(final RandomStringUtils rsu) {
157         assertThrows(IllegalArgumentException.class, () -> rsu.nextGraph(-1));
158     }
159 
160     @Test
161     public void testExceptionsRandomNumeric() {
162         assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.randomNumeric(-1));
163     }
164 
165     @ParameterizedTest
166     @MethodSource("randomProvider")
167     public void testExceptionsRandomNumeric(final RandomStringUtils rsu) {
168         assertThrows(IllegalArgumentException.class, () -> rsu.nextNumeric(-1));
169     }
170 
171     @Test
172     public void testExceptionsRandomPrint() {
173         assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.randomPrint(-1));
174     }
175 
176     @ParameterizedTest
177     @MethodSource("randomProvider")
178     public void testExceptionsRandomPrint(final RandomStringUtils rsu) {
179         assertThrows(IllegalArgumentException.class, () -> rsu.nextPrint(-1));
180     }
181 
182     /**
183      * Test homogeneity of random strings generated -- i.e., test that characters show up with expected frequencies in generated strings. Will fail randomly
184      * about 1 in 100,000 times. Repeated failures indicate a problem.
185      *
186      * @param rsu the instance to test.
187      */
188     @ParameterizedTest
189     @MethodSource("randomProvider")
190     public void testHomogeneity(final RandomStringUtils rsu) {
191         final String set = "abc";
192         final char[] chars = set.toCharArray();
193         final int[] counts = { 0, 0, 0 };
194         final int[] expected = { 200, 200, 200 };
195         for (int i = 0; i < 100; i++) {
196             final String gen = rsu.next(6, chars);
197             for (int j = 0; j < 6; j++) {
198                 switch (gen.charAt(j)) {
199                 case 'a': {
200                     counts[0]++;
201                     break;
202                 }
203                 case 'b': {
204                     counts[1]++;
205                     break;
206                 }
207                 case 'c': {
208                     counts[2]++;
209                     break;
210                 }
211                 default: {
212                     fail("generated character not in set");
213                 }
214                 }
215             }
216         }
217         // Perform chi-square test with degrees of freedom = 3-1 = 2, testing at 1e-5 level.
218         // This expects a failure rate of 1 in 100,000.
219         // critical value: from scipy.stats import chi2; chi2(2).isf(1e-5)
220         assertThat("test homogeneity -- will fail about 1 in 100,000 times", chiSquare(expected, counts), lessThan(23.025850929940457d));
221     }
222 
223     /**
224      * Checks if the string got by {@link RandomStringUtils#random(int)} can be converted to UTF-8 and back without loss.
225      *
226      * @see <a href="https://issues.apache.org/jira/browse/LANG-100">LANG-100</a>
227      */
228     @Test
229     public void testLang100() {
230         final int size = 5000;
231         final Charset charset = StandardCharsets.UTF_8;
232         final String orig = RandomStringUtils.random(size);
233         final byte[] bytes = orig.getBytes(charset);
234         final String copy = new String(bytes, charset);
235 
236         // for a verbose compare:
237         for (int i = 0; i < orig.length() && i < copy.length(); i++) {
238             final char o = orig.charAt(i);
239             final char c = copy.charAt(i);
240             assertEquals(o, c, "differs at " + i + "(" + Integer.toHexString(Character.valueOf(o).hashCode()) + ","
241                     + Integer.toHexString(Character.valueOf(c).hashCode()) + ")");
242         }
243         // compare length also
244         assertEquals(orig.length(), copy.length());
245         // just to be complete
246         assertEquals(orig, copy);
247     }
248 
249     /**
250      * Checks if the string got by {@link RandomStringUtils#random(int)} can be converted to UTF-8 and back without loss.
251      *
252      * @param rsu the instance to test
253      * @see <a href="https://issues.apache.org/jira/browse/LANG-100">LANG-100</a>
254      */
255     @ParameterizedTest
256     @MethodSource("randomProvider")
257     public void testLang100(final RandomStringUtils rsu) {
258         final int size = 5000;
259         final Charset charset = StandardCharsets.UTF_8;
260         final String orig = rsu.next(size);
261         final byte[] bytes = orig.getBytes(charset);
262         final String copy = new String(bytes, charset);
263 
264         // for a verbose compare:
265         for (int i = 0; i < orig.length() && i < copy.length(); i++) {
266             final char o = orig.charAt(i);
267             final char c = copy.charAt(i);
268             assertEquals(o, c, "differs at " + i + "(" + Integer.toHexString(Character.valueOf(o).hashCode()) + ","
269                     + Integer.toHexString(Character.valueOf(c).hashCode()) + ")");
270         }
271         // compare length also
272         assertEquals(orig.length(), copy.length());
273         // just to be complete
274         assertEquals(orig, copy);
275     }
276 
277     @Test
278     public void testLANG805() {
279         final long seedMillis = System.currentTimeMillis();
280         assertEquals("aaa", RandomStringUtils.random(3, 0, 0, false, false, new char[] { 'a' }, new Random(seedMillis)));
281     }
282 
283     @ParameterizedTest
284     @MethodSource("randomProvider")
285     public void testLANG807(final RandomStringUtils rsu) {
286         final IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> rsu.next(3, 5, 5, false, false));
287         final String msg = ex.getMessage();
288         assertTrue(msg.contains("start"), "Message (" + msg + ") must contain 'start'");
289         assertTrue(msg.contains("end"), "Message (" + msg + ") must contain 'end'");
290     }
291 
292     /**
293      * Make sure boundary alpha characters are generated by randomAlphabetic This test will fail randomly with probability = 4 * (51/52)**1000 ~ 1.58E-8
294      */
295     @Test
296     public void testRandomAlphabetic() {
297         final char[] testChars = { 'a', 'z', 'A', 'Z' };
298         final boolean[] found = { false, false, false, false };
299         for (int i = 0; i < LOOP_COUNT; i++) {
300             final String randString = RandomStringUtils.randomAlphabetic(10);
301             for (int j = 0; j < testChars.length; j++) {
302                 if (randString.indexOf(testChars[j]) > 0) {
303                     found[j] = true;
304                 }
305             }
306         }
307         for (int i = 0; i < testChars.length; i++) {
308             assertTrue(found[i], "alphanumeric character not generated in 1000 attempts: " + testChars[i] + " -- repeated failures indicate a problem ");
309         }
310     }
311 
312     /**
313      * Make sure boundary alpha characters are generated by randomAlphabetic This test will fail randomly with probability = 4 * (51/52)**1000 ~ 1.58E-8
314      *
315      * @param rsu the instance to test
316      */
317     @ParameterizedTest
318     @MethodSource("randomProvider")
319     public void testRandomAlphabetic(final RandomStringUtils rsu) {
320         final char[] testChars = { 'a', 'z', 'A', 'Z' };
321         final boolean[] found = { false, false, false, false };
322         for (int i = 0; i < LOOP_COUNT; i++) {
323             final String randString = rsu.nextAlphabetic(10);
324             for (int j = 0; j < testChars.length; j++) {
325                 if (randString.indexOf(testChars[j]) > 0) {
326                     found[j] = true;
327                 }
328             }
329         }
330         for (int i = 0; i < testChars.length; i++) {
331             assertTrue(found[i], "alphanumeric character not generated in 1000 attempts: " + testChars[i] + " -- repeated failures indicate a problem ");
332         }
333     }
334 
335     @Test
336     public void testRandomAlphabeticRange() {
337         final int expectedMinLengthInclusive = 1;
338         final int expectedMaxLengthExclusive = 11;
339         final String pattern = "^\\p{Alpha}{" + expectedMinLengthInclusive + ',' + expectedMaxLengthExclusive + "}$";
340 
341         int maxCreatedLength = expectedMinLengthInclusive;
342         int minCreatedLength = expectedMaxLengthExclusive - 1;
343         for (int i = 0; i < LOOP_COUNT; i++) {
344             final String s = RandomStringUtils.randomAlphabetic(expectedMinLengthInclusive, expectedMaxLengthExclusive);
345             assertThat("within range", s.length(), allOf(greaterThanOrEqualTo(expectedMinLengthInclusive), lessThanOrEqualTo(expectedMaxLengthExclusive - 1)));
346             assertTrue(s.matches(pattern), s);
347 
348             if (s.length() < minCreatedLength) {
349                 minCreatedLength = s.length();
350             }
351 
352             if (s.length() > maxCreatedLength) {
353                 maxCreatedLength = s.length();
354             }
355         }
356         assertThat("min generated, may fail randomly rarely", minCreatedLength, is(expectedMinLengthInclusive));
357         assertThat("max generated, may fail randomly rarely", maxCreatedLength, is(expectedMaxLengthExclusive - 1));
358     }
359 
360     @ParameterizedTest
361     @MethodSource("randomProvider")
362     public void testRandomAlphabeticRange(final RandomStringUtils rsu) {
363         final int expectedMinLengthInclusive = 1;
364         final int expectedMaxLengthExclusive = 11;
365         final String pattern = "^\\p{Alpha}{" + expectedMinLengthInclusive + ',' + expectedMaxLengthExclusive + "}$";
366 
367         int maxCreatedLength = expectedMinLengthInclusive;
368         int minCreatedLength = expectedMaxLengthExclusive - 1;
369         for (int i = 0; i < LOOP_COUNT; i++) {
370             final String s = rsu.nextAlphabetic(expectedMinLengthInclusive, expectedMaxLengthExclusive);
371             assertThat("within range", s.length(), allOf(greaterThanOrEqualTo(expectedMinLengthInclusive), lessThanOrEqualTo(expectedMaxLengthExclusive - 1)));
372             assertTrue(s.matches(pattern), s);
373 
374             if (s.length() < minCreatedLength) {
375                 minCreatedLength = s.length();
376             }
377 
378             if (s.length() > maxCreatedLength) {
379                 maxCreatedLength = s.length();
380             }
381         }
382         assertThat("min generated, may fail randomly rarely", minCreatedLength, is(expectedMinLengthInclusive));
383         assertThat("max generated, may fail randomly rarely", maxCreatedLength, is(expectedMaxLengthExclusive - 1));
384     }
385 
386     /**
387      * Make sure boundary alphanumeric characters are generated by randomAlphaNumeric This test will fail randomly with probability = 6 * (61/62)**1000 ~ 5.2E-7
388      */
389     @Test
390     public void testRandomAlphaNumeric() {
391         final char[] testChars = { 'a', 'z', 'A', 'Z', '0', '9' };
392         final boolean[] found = { false, false, false, false, false, false };
393         for (int i = 0; i < LOOP_COUNT; i++) {
394             final String randString = RandomStringUtils.randomAlphanumeric(10);
395             for (int j = 0; j < testChars.length; j++) {
396                 if (randString.indexOf(testChars[j]) > 0) {
397                     found[j] = true;
398                 }
399             }
400         }
401         for (int i = 0; i < testChars.length; i++) {
402             assertTrue(found[i], "alphanumeric character not generated in 1000 attempts: " + testChars[i] + " -- repeated failures indicate a problem ");
403         }
404     }
405 
406     /**
407      * Make sure boundary alphanumeric characters are generated by randomAlphaNumeric This test will fail randomly with probability = 6 * (61/62)**1000 ~ 5.2E-7
408      *
409      * @param rsu the instance to test
410      */
411     @ParameterizedTest
412     @MethodSource("randomProvider")
413     public void testRandomAlphaNumeric(final RandomStringUtils rsu) {
414         final char[] testChars = { 'a', 'z', 'A', 'Z', '0', '9' };
415         final boolean[] found = { false, false, false, false, false, false };
416         for (int i = 0; i < LOOP_COUNT; i++) {
417             final String randString = rsu.nextAlphanumeric(10);
418             for (int j = 0; j < testChars.length; j++) {
419                 if (randString.indexOf(testChars[j]) > 0) {
420                     found[j] = true;
421                 }
422             }
423         }
424         for (int i = 0; i < testChars.length; i++) {
425             assertTrue(found[i], "alphanumeric character not generated in 1000 attempts: " + testChars[i] + " -- repeated failures indicate a problem ");
426         }
427     }
428 
429     @Test
430     public void testRandomAlphanumericRange() {
431         final int expectedMinLengthInclusive = 1;
432         final int expectedMaxLengthExclusive = 11;
433         final String pattern = "^\\p{Alnum}{" + expectedMinLengthInclusive + ',' + expectedMaxLengthExclusive + "}$";
434 
435         int maxCreatedLength = expectedMinLengthInclusive;
436         int minCreatedLength = expectedMaxLengthExclusive - 1;
437         for (int i = 0; i < LOOP_COUNT; i++) {
438             final String s = RandomStringUtils.randomAlphanumeric(expectedMinLengthInclusive, expectedMaxLengthExclusive);
439             assertThat("within range", s.length(), allOf(greaterThanOrEqualTo(expectedMinLengthInclusive), lessThanOrEqualTo(expectedMaxLengthExclusive - 1)));
440             assertTrue(s.matches(pattern), s);
441 
442             if (s.length() < minCreatedLength) {
443                 minCreatedLength = s.length();
444             }
445 
446             if (s.length() > maxCreatedLength) {
447                 maxCreatedLength = s.length();
448             }
449         }
450         assertThat("min generated, may fail randomly rarely", minCreatedLength, is(expectedMinLengthInclusive));
451         assertThat("max generated, may fail randomly rarely", maxCreatedLength, is(expectedMaxLengthExclusive - 1));
452     }
453 
454     /**
455      * Test the implementation
456      *
457      * @param rsu the instance to test.
458      */
459     @ParameterizedTest
460     @MethodSource("randomProvider")
461     public void testRandomApis(final RandomStringUtils rsu) {
462         String r1 = rsu.next(50);
463         assertEquals(50, r1.length(), "random(50) length");
464         String r2 = rsu.next(50);
465         assertEquals(50, r2.length(), "random(50) length");
466         assertFalse(r1.equals(r2), "!r1.equals(r2)");
467 
468         r1 = rsu.nextAscii(50);
469         assertEquals(50, r1.length(), "randomAscii(50) length");
470         for (int i = 0; i < r1.length(); i++) {
471             assertThat("char >= 32 && <= 127", (int) r1.charAt(i), allOf(greaterThanOrEqualTo(32), lessThanOrEqualTo(127)));
472         }
473         r2 = rsu.nextAscii(50);
474         assertFalse(r1.equals(r2), "!r1.equals(r2)");
475 
476         r1 = rsu.nextAlphabetic(50);
477         assertEquals(50, r1.length(), "randomAlphabetic(50)");
478         for (int i = 0; i < r1.length(); i++) {
479             assertTrue(Character.isLetter(r1.charAt(i)) && !Character.isDigit(r1.charAt(i)), "r1 contains alphabetic");
480         }
481         r2 = rsu.nextAlphabetic(50);
482         assertFalse(r1.equals(r2), "!r1.equals(r2)");
483 
484         r1 = rsu.nextAlphanumeric(50);
485         assertEquals(50, r1.length(), "randomAlphanumeric(50)");
486         for (int i = 0; i < r1.length(); i++) {
487             assertTrue(Character.isLetterOrDigit(r1.charAt(i)), "r1 contains alphanumeric");
488         }
489         r2 = rsu.nextAlphabetic(50);
490         assertFalse(r1.equals(r2), "!r1.equals(r2)");
491 
492         r1 = rsu.nextGraph(50);
493         assertEquals(50, r1.length(), "randomGraph(50) length");
494         for (int i = 0; i < r1.length(); i++) {
495             assertTrue(r1.charAt(i) >= 33 && r1.charAt(i) <= 126, "char between 33 and 126");
496         }
497         r2 = rsu.nextGraph(50);
498         assertFalse(r1.equals(r2), "!r1.equals(r2)");
499 
500         r1 = rsu.nextNumeric(50);
501         assertEquals(50, r1.length(), "randomNumeric(50)");
502         for (int i = 0; i < r1.length(); i++) {
503             assertTrue(Character.isDigit(r1.charAt(i)) && !Character.isLetter(r1.charAt(i)), "r1 contains numeric");
504         }
505         r2 = rsu.nextNumeric(50);
506         assertFalse(r1.equals(r2), "!r1.equals(r2)");
507 
508         r1 = rsu.nextPrint(50);
509         assertEquals(50, r1.length(), "randomPrint(50) length");
510         for (int i = 0; i < r1.length(); i++) {
511             assertTrue(r1.charAt(i) >= 32 && r1.charAt(i) <= 126, "char between 32 and 126");
512         }
513         r2 = rsu.nextPrint(50);
514         assertFalse(r1.equals(r2), "!r1.equals(r2)");
515 
516         String set = "abcdefg";
517         r1 = rsu.next(50, set);
518         assertEquals(50, r1.length(), "random(50, \"abcdefg\")");
519         for (int i = 0; i < r1.length(); i++) {
520             assertTrue(set.indexOf(r1.charAt(i)) > -1, "random char in set");
521         }
522         r2 = rsu.next(50, set);
523         assertFalse(r1.equals(r2), "!r1.equals(r2)");
524 
525         r1 = rsu.next(50, (String) null);
526         assertEquals(50, r1.length(), "random(50) length");
527         r2 = rsu.next(50, (String) null);
528         assertEquals(50, r2.length(), "random(50) length");
529         assertFalse(r1.equals(r2), "!r1.equals(r2)");
530 
531         set = "stuvwxyz";
532         r1 = rsu.next(50, set.toCharArray());
533         assertEquals(50, r1.length(), "random(50, \"stuvwxyz\")");
534         for (int i = 0; i < r1.length(); i++) {
535             assertTrue(set.indexOf(r1.charAt(i)) > -1, "random char in set");
536         }
537         r2 = rsu.next(50, set);
538         assertFalse(r1.equals(r2), "!r1.equals(r2)");
539 
540         r1 = rsu.next(50, (char[]) null);
541         assertEquals(50, r1.length(), "random(50) length");
542         r2 = rsu.next(50, (char[]) null);
543         assertEquals(50, r2.length(), "random(50) length");
544         assertFalse(r1.equals(r2), "!r1.equals(r2)");
545 
546         r1 = rsu.next(0);
547         assertEquals("", r1, "random(0).equals(\"\")");
548     }
549 
550     /**
551      * Make sure 32 and 127 are generated by randomNumeric This test will fail randomly with probability = 2*(95/96)**1000 ~ 5.7E-5
552      *
553      * @param rsu the instance to test
554      */
555     @ParameterizedTest
556     @MethodSource("randomProvider")
557     public void testRandomAscii(final RandomStringUtils rsu) {
558         final char[] testChars = { (char) 32, (char) 126 };
559         final boolean[] found = { false, false };
560         // Test failures have been observed on GitHub builds with a 100 limit.
561         for (int i = 0; i < LOOP_COUNT; i++) {
562             final String randString = rsu.nextAscii(10);
563             for (int j = 0; j < testChars.length; j++) {
564                 if (randString.indexOf(testChars[j]) > 0) {
565                     found[j] = true;
566                 }
567             }
568         }
569         for (int i = 0; i < testChars.length; i++) {
570             assertTrue(found[i], "ascii character not generated in 1000 attempts: " + (int) testChars[i] + " -- repeated failures indicate a problem");
571         }
572     }
573 
574     @ParameterizedTest
575     @MethodSource("randomProvider")
576     public void testRandomAsciiRange(final RandomStringUtils rsu) {
577         final int expectedMinLengthInclusive = 1;
578         final int expectedMaxLengthExclusive = 11;
579         final String pattern = "^\\p{ASCII}{" + expectedMinLengthInclusive + ',' + expectedMaxLengthExclusive + "}$";
580 
581         int maxCreatedLength = expectedMinLengthInclusive;
582         int minCreatedLength = expectedMaxLengthExclusive - 1;
583         for (int i = 0; i < LOOP_COUNT; i++) {
584             final String s = rsu.nextAscii(expectedMinLengthInclusive, expectedMaxLengthExclusive);
585             assertThat("within range", s.length(), allOf(greaterThanOrEqualTo(expectedMinLengthInclusive), lessThanOrEqualTo(expectedMaxLengthExclusive - 1)));
586             assertTrue(s.matches(pattern), s);
587 
588             if (s.length() < minCreatedLength) {
589                 minCreatedLength = s.length();
590             }
591 
592             if (s.length() > maxCreatedLength) {
593                 maxCreatedLength = s.length();
594             }
595         }
596         assertThat("min generated, may fail randomly rarely", minCreatedLength, is(expectedMinLengthInclusive));
597         assertThat("max generated, may fail randomly rarely", maxCreatedLength, is(expectedMaxLengthExclusive - 1));
598     }
599 
600     @ParameterizedTest
601     @MethodSource("randomProvider")
602     public void testRandomGraphRange(final RandomStringUtils rsu) {
603         final int expectedMinLengthInclusive = 1;
604         final int expectedMaxLengthExclusive = 11;
605         final String pattern = "^\\p{Graph}{" + expectedMinLengthInclusive + ',' + expectedMaxLengthExclusive + "}$";
606 
607         int maxCreatedLength = expectedMinLengthInclusive;
608         int minCreatedLength = expectedMaxLengthExclusive - 1;
609         for (int i = 0; i < LOOP_COUNT; i++) {
610             final String s = rsu.nextGraph(expectedMinLengthInclusive, expectedMaxLengthExclusive);
611             assertThat("within range", s.length(), allOf(greaterThanOrEqualTo(expectedMinLengthInclusive), lessThanOrEqualTo(expectedMaxLengthExclusive - 1)));
612             assertTrue(s.matches(pattern), s);
613 
614             if (s.length() < minCreatedLength) {
615                 minCreatedLength = s.length();
616             }
617 
618             if (s.length() > maxCreatedLength) {
619                 maxCreatedLength = s.length();
620             }
621         }
622         assertThat("min generated, may fail randomly rarely", minCreatedLength, is(expectedMinLengthInclusive));
623         assertThat("max generated, may fail randomly rarely", maxCreatedLength, is(expectedMaxLengthExclusive - 1));
624     }
625 
626     /**
627      * Make sure '0' and '9' are generated by randomNumeric This test will fail randomly with probability = 2 * (9/10)**1000 ~ 3.5E-46
628      *
629      * @param rsu the instance to test
630      */
631     @ParameterizedTest
632     @MethodSource("randomProvider")
633     public void testRandomNumeric(final RandomStringUtils rsu) {
634         final char[] testChars = { '0', '9' };
635         final boolean[] found = { false, false };
636         for (int i = 0; i < LOOP_COUNT; i++) {
637             final String randString = rsu.nextNumeric(10);
638             for (int j = 0; j < testChars.length; j++) {
639                 if (randString.indexOf(testChars[j]) > 0) {
640                     found[j] = true;
641                 }
642             }
643         }
644         for (int i = 0; i < testChars.length; i++) {
645             assertTrue(found[i], "digit not generated in 1000 attempts: " + testChars[i] + " -- repeated failures indicate a problem ");
646         }
647     }
648 
649     @ParameterizedTest
650     @MethodSource("randomProvider")
651     public void testRandomNumericRange(final RandomStringUtils rsu) {
652         final int expectedMinLengthInclusive = 1;
653         final int expectedMaxLengthExclusive = 11;
654         final String pattern = "^\\p{Digit}{" + expectedMinLengthInclusive + ',' + expectedMaxLengthExclusive + "}$";
655 
656         int maxCreatedLength = expectedMinLengthInclusive;
657         int minCreatedLength = expectedMaxLengthExclusive - 1;
658         for (int i = 0; i < LOOP_COUNT; i++) {
659             final String s = rsu.nextNumeric(expectedMinLengthInclusive, expectedMaxLengthExclusive);
660             assertThat("within range", s.length(), allOf(greaterThanOrEqualTo(expectedMinLengthInclusive), lessThanOrEqualTo(expectedMaxLengthExclusive - 1)));
661             assertTrue(s.matches(pattern), s);
662 
663             if (s.length() < minCreatedLength) {
664                 minCreatedLength = s.length();
665             }
666 
667             if (s.length() > maxCreatedLength) {
668                 maxCreatedLength = s.length();
669             }
670         }
671         assertThat("min generated, may fail randomly rarely", minCreatedLength, is(expectedMinLengthInclusive));
672         assertThat("max generated, may fail randomly rarely", maxCreatedLength, is(expectedMaxLengthExclusive - 1));
673     }
674 
675     @Test
676     public void testRandomParameter() {
677         final long seedMillis = System.currentTimeMillis();
678         final String r1 = RandomStringUtils.random(50, 0, 0, true, true, null, new Random(seedMillis));
679         final String r2 = RandomStringUtils.random(50, 0, 0, true, true, null, new Random(seedMillis));
680         assertEquals(r1, r2, "r1.equals(r2)");
681     }
682 
683     @ParameterizedTest
684     @MethodSource("randomProvider")
685     public void testRandomPrintRange(final RandomStringUtils rsu) {
686         final int expectedMinLengthInclusive = 1;
687         final int expectedMaxLengthExclusive = 11;
688         final String pattern = "^\\p{Print}{" + expectedMinLengthInclusive + ',' + expectedMaxLengthExclusive + "}$";
689 
690         int maxCreatedLength = expectedMinLengthInclusive;
691         int minCreatedLength = expectedMaxLengthExclusive - 1;
692         for (int i = 0; i < LOOP_COUNT; i++) {
693             final String s = rsu.nextPrint(expectedMinLengthInclusive, expectedMaxLengthExclusive);
694             assertThat("within range", s.length(), allOf(greaterThanOrEqualTo(expectedMinLengthInclusive), lessThanOrEqualTo(expectedMaxLengthExclusive - 1)));
695             assertTrue(s.matches(pattern), s);
696 
697             if (s.length() < minCreatedLength) {
698                 minCreatedLength = s.length();
699             }
700 
701             if (s.length() > maxCreatedLength) {
702                 maxCreatedLength = s.length();
703             }
704         }
705         assertThat("min generated, may fail randomly rarely", minCreatedLength, is(expectedMinLengthInclusive));
706         assertThat("max generated, may fail randomly rarely", maxCreatedLength, is(expectedMaxLengthExclusive - 1));
707     }
708 
709     /**
710      * Test {@code RandomStringUtils.random} works appropriately when chars specified.
711      *
712      * @param rsu the instance to test.
713      */
714     @ParameterizedTest
715     @MethodSource("randomProvider")
716     public void testRandomWithChars(final RandomStringUtils rsu) {
717         final char[] digitChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
718 
719         String r1, r2, r3;
720 
721         r1 = rsu.next(50, 0, 0, true, true, digitChars);
722         assertEquals(50, r1.length(), "randomNumeric(50)");
723         for (int i = 0; i < r1.length(); i++) {
724             assertTrue(
725                     Character.isDigit(r1.charAt(i)) && !Character.isLetter(r1.charAt(i)),
726                     "r1 contains numeric");
727         }
728         r2 = rsu.nextNumeric(50);
729         assertNotEquals(r1, r2);
730 
731         r3 = rsu.next(50, 0, 0, true, true, digitChars);
732         assertNotEquals(r1, r3);
733         assertNotEquals(r2, r3);
734     }
735 }