1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3  *
4  * This code is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 2 only, as
6  * published by the Free Software Foundation.
7  *
8  * This code is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * version 2 for more details (a copy is included in the LICENSE file that
12  * accompanied this code).
13  *
14  * You should have received a copy of the GNU General Public License version
15  * 2 along with this work; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
19  * or visit www.oracle.com if you need additional information or have any
20  * questions.
21  */
22 
23 /*
24  * This file is available under and governed by the GNU General Public
25  * License version 2 only, as published by the Free Software Foundation.
26  * However, the following notice accompanied the original version of this
27  * file:
28  *
29  * Written by Doug Lea with assistance from members of JCP JSR-166
30  * Expert Group and released to the public domain, as explained at
31  * http://creativecommons.org/publicdomain/zero/1.0/
32  */
33 
34 import java.util.Arrays;
35 import java.util.List;
36 import java.util.SplittableRandom;
37 import java.util.concurrent.atomic.AtomicInteger;
38 import java.util.concurrent.atomic.LongAdder;
39 import java.lang.reflect.Method;
40 import java.util.function.Predicate;
41 import java.util.stream.Collectors;
42 
43 import junit.framework.Test;
44 import junit.framework.TestSuite;
45 
46 public class SplittableRandomTest extends JSR166TestCase {
47 
main(String[] args)48     public static void main(String[] args) {
49         main(suite(), args);
50     }
suite()51     public static Test suite() {
52         return new TestSuite(SplittableRandomTest.class);
53     }
54 
55     /*
56      * Testing coverage notes:
57      *
58      * 1. Many of the test methods are adapted from ThreadLocalRandomTest.
59      *
60      * 2. These tests do not check for random number generator quality.
61      * But we check for minimal API compliance by requiring that
62      * repeated calls to nextX methods, up to NCALLS tries, produce at
63      * least two distinct results. (In some possible universe, a
64      * "correct" implementation might fail, but the odds are vastly
65      * less than that of encountering a hardware failure while running
66      * the test.) For bounded nextX methods, we sample various
67      * intervals across multiples of primes. In other tests, we repeat
68      * under REPS different values.
69      */
70 
71     // max numbers of calls to detect getting stuck on one value
72     static final int NCALLS = 10000;
73 
74     // max sampled int bound
75     static final int MAX_INT_BOUND = (1 << 26);
76 
77     // max sampled long bound
78     static final long MAX_LONG_BOUND = (1L << 40);
79 
80     // Number of replications for other checks
81     static final int REPS =
82         Integer.getInteger("SplittableRandomTest.reps", 4);
83 
84     /**
85      * Repeated calls to nextInt produce at least two distinct results
86      */
testNextInt()87     public void testNextInt() {
88         SplittableRandom sr = new SplittableRandom();
89         int f = sr.nextInt();
90         int i = 0;
91         while (i < NCALLS && sr.nextInt() == f)
92             ++i;
93         assertTrue(i < NCALLS);
94     }
95 
96     /**
97      * Repeated calls to nextLong produce at least two distinct results
98      */
99     public void testNextLong() {
100         SplittableRandom sr = new SplittableRandom();
101         long f = sr.nextLong();
102         int i = 0;
103         while (i < NCALLS && sr.nextLong() == f)
104             ++i;
105         assertTrue(i < NCALLS);
106     }
107 
108     /**
109      * Repeated calls to nextDouble produce at least two distinct results
110      */
111     public void testNextDouble() {
112         SplittableRandom sr = new SplittableRandom();
113         double f = sr.nextDouble();
114         int i = 0;
115         while (i < NCALLS && sr.nextDouble() == f)
116             ++i;
117         assertTrue(i < NCALLS);
118     }
119 
120     /**
121      * Two SplittableRandoms created with the same seed produce the
122      * same values for nextLong.
123      */
124     public void testSeedConstructor() {
125         for (long seed = 2; seed < MAX_LONG_BOUND; seed += 15485863) {
126             SplittableRandom sr1 = new SplittableRandom(seed);
127             SplittableRandom sr2 = new SplittableRandom(seed);
128             for (int i = 0; i < REPS; ++i)
129                 assertEquals(sr1.nextLong(), sr2.nextLong());
130         }
131     }
132 
133     /**
134      * A SplittableRandom produced by split() of a default-constructed
135      * SplittableRandom generates a different sequence
136      */
137     public void testSplit1() {
138         SplittableRandom sr = new SplittableRandom();
139         for (int reps = 0; reps < REPS; ++reps) {
140             SplittableRandom sc = sr.split();
141             int i = 0;
142             while (i < NCALLS && sr.nextLong() == sc.nextLong())
143                 ++i;
144             assertTrue(i < NCALLS);
145         }
146     }
147 
148     /**
149      * A SplittableRandom produced by split() of a seeded-constructed
150      * SplittableRandom generates a different sequence
151      */
152     public void testSplit2() {
153         SplittableRandom sr = new SplittableRandom(12345);
154         for (int reps = 0; reps < REPS; ++reps) {
155             SplittableRandom sc = sr.split();
156             int i = 0;
157             while (i < NCALLS && sr.nextLong() == sc.nextLong())
158                 ++i;
159             assertTrue(i < NCALLS);
160         }
161     }
162 
163     /**
164      * nextInt(non-positive) throws IllegalArgumentException
165      */
166     public void testNextIntBoundNonPositive() {
167         SplittableRandom sr = new SplittableRandom();
168         Runnable[] throwingActions = {
169             () -> sr.nextInt(-17),
170             () -> sr.nextInt(0),
171             () -> sr.nextInt(Integer.MIN_VALUE),
172         };
173         assertThrows(IllegalArgumentException.class, throwingActions);
174     }
175 
176     /**
177      * nextInt(least >= bound) throws IllegalArgumentException
178      */
179     public void testNextIntBadBounds() {
180         SplittableRandom sr = new SplittableRandom();
181         Runnable[] throwingActions = {
182             () -> sr.nextInt(17, 2),
183             () -> sr.nextInt(-42, -42),
184             () -> sr.nextInt(Integer.MAX_VALUE, Integer.MIN_VALUE),
185         };
186         assertThrows(IllegalArgumentException.class, throwingActions);
187     }
188 
189     /**
190      * nextInt(bound) returns 0 <= value < bound;
191      * repeated calls produce at least two distinct results
192      */
193     public void testNextIntBounded() {
194         SplittableRandom sr = new SplittableRandom();
195         for (int i = 0; i < 2; i++) assertEquals(0, sr.nextInt(1));
196         // sample bound space across prime number increments
197         for (int bound = 2; bound < MAX_INT_BOUND; bound += 524959) {
198             int f = sr.nextInt(bound);
199             assertTrue(0 <= f && f < bound);
200             int i = 0;
201             int j;
202             while (i < NCALLS &&
203                    (j = sr.nextInt(bound)) == f) {
204                 assertTrue(0 <= j && j < bound);
205                 ++i;
206             }
207             assertTrue(i < NCALLS);
208         }
209     }
210 
211     /**
212      * nextInt(least, bound) returns least <= value < bound;
213      * repeated calls produce at least two distinct results
214      */
215     public void testNextIntBounded2() {
216         SplittableRandom sr = new SplittableRandom();
217         for (int least = -15485863; least < MAX_INT_BOUND; least += 524959) {
218             for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 49979687) {
219                 int f = sr.nextInt(least, bound);
220                 assertTrue(least <= f && f < bound);
221                 int i = 0;
222                 int j;
223                 while (i < NCALLS &&
224                        (j = sr.nextInt(least, bound)) == f) {
225                     assertTrue(least <= j && j < bound);
226                     ++i;
227                 }
228                 assertTrue(i < NCALLS);
229             }
230         }
231     }
232 
233     /**
234      * nextLong(non-positive) throws IllegalArgumentException
235      */
236     public void testNextLongBoundNonPositive() {
237         SplittableRandom sr = new SplittableRandom();
238         Runnable[] throwingActions = {
239             () -> sr.nextLong(-17L),
240             () -> sr.nextLong(0L),
241             () -> sr.nextLong(Long.MIN_VALUE),
242         };
243         assertThrows(IllegalArgumentException.class, throwingActions);
244     }
245 
246     /**
247      * nextLong(least >= bound) throws IllegalArgumentException
248      */
249     public void testNextLongBadBounds() {
250         SplittableRandom sr = new SplittableRandom();
251         Runnable[] throwingActions = {
252             () -> sr.nextLong(17L, 2L),
253             () -> sr.nextLong(-42L, -42L),
254             () -> sr.nextLong(Long.MAX_VALUE, Long.MIN_VALUE),
255         };
256         assertThrows(IllegalArgumentException.class, throwingActions);
257     }
258 
259     /**
260      * nextLong(bound) returns 0 <= value < bound;
261      * repeated calls produce at least two distinct results
262      */
263     public void testNextLongBounded() {
264         SplittableRandom sr = new SplittableRandom();
265         for (int i = 0; i < 2; i++) assertEquals(0L, sr.nextLong(1L));
266         for (long bound = 2; bound < MAX_LONG_BOUND; bound += 15485863) {
267             long f = sr.nextLong(bound);
268             assertTrue(0 <= f && f < bound);
269             int i = 0;
270             long j;
271             while (i < NCALLS &&
272                    (j = sr.nextLong(bound)) == f) {
273                 assertTrue(0 <= j && j < bound);
274                 ++i;
275             }
276             assertTrue(i < NCALLS);
277         }
278     }
279 
280     /**
281      * nextLong(least, bound) returns least <= value < bound;
282      * repeated calls produce at least two distinct results
283      */
284     public void testNextLongBounded2() {
285         SplittableRandom sr = new SplittableRandom();
286         for (long least = -86028121; least < MAX_LONG_BOUND; least += 982451653L) {
287             for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
288                 long f = sr.nextLong(least, bound);
289                 assertTrue(least <= f && f < bound);
290                 int i = 0;
291                 long j;
292                 while (i < NCALLS &&
293                        (j = sr.nextLong(least, bound)) == f) {
294                     assertTrue(least <= j && j < bound);
295                     ++i;
296                 }
297                 assertTrue(i < NCALLS);
298             }
299         }
300     }
301 
302     /**
303      * nextDouble(non-positive) throws IllegalArgumentException
304      */
305     public void testNextDoubleBoundNonPositive() {
306         SplittableRandom sr = new SplittableRandom();
307         Runnable[] throwingActions = {
308             () -> sr.nextDouble(-17.0d),
309             () -> sr.nextDouble(0.0d),
310             () -> sr.nextDouble(-Double.MIN_VALUE),
311             () -> sr.nextDouble(Double.NEGATIVE_INFINITY),
312             () -> sr.nextDouble(Double.NaN),
313         };
314         assertThrows(IllegalArgumentException.class, throwingActions);
315     }
316 
317     /**
318      * nextDouble(! (least < bound)) throws IllegalArgumentException
319      */
320     public void testNextDoubleBadBounds() {
321         SplittableRandom sr = new SplittableRandom();
322         Runnable[] throwingActions = {
323             () -> sr.nextDouble(17.0d, 2.0d),
324             () -> sr.nextDouble(-42.0d, -42.0d),
325             () -> sr.nextDouble(Double.MAX_VALUE, Double.MIN_VALUE),
326             () -> sr.nextDouble(Double.NaN, 0.0d),
327             () -> sr.nextDouble(0.0d, Double.NaN),
328         };
329         assertThrows(IllegalArgumentException.class, throwingActions);
330     }
331 
332     // TODO: Test infinite bounds!
333     //() -> sr.nextDouble(Double.NEGATIVE_INFINITY, 0.0d),
334     //() -> sr.nextDouble(0.0d, Double.POSITIVE_INFINITY),
335 
336     /**
337      * nextDouble(least, bound) returns least <= value < bound;
338      * repeated calls produce at least two distinct results
339      */
340     public void testNextDoubleBounded2() {
341         SplittableRandom sr = new SplittableRandom();
342         for (double least = 0.0001; least < 1.0e20; least *= 8) {
343             for (double bound = least * 1.001; bound < 1.0e20; bound *= 16) {
344                 double f = sr.nextDouble(least, bound);
345                 assertTrue(least <= f && f < bound);
346                 int i = 0;
347                 double j;
348                 while (i < NCALLS &&
349                        (j = sr.nextDouble(least, bound)) == f) {
350                     assertTrue(least <= j && j < bound);
351                     ++i;
352                 }
353                 assertTrue(i < NCALLS);
354             }
355         }
356     }
357 
358     /**
359      * Invoking sized ints, long, doubles, with negative sizes throws
360      * IllegalArgumentException
361      */
362     public void testBadStreamSize() {
363         SplittableRandom r = new SplittableRandom();
364         Runnable[] throwingActions = {
365             () -> { java.util.stream.IntStream x = r.ints(-1L); },
366             () -> { java.util.stream.IntStream x = r.ints(-1L, 2, 3); },
367             () -> { java.util.stream.LongStream x = r.longs(-1L); },
368             () -> { java.util.stream.LongStream x = r.longs(-1L, -1L, 1L); },
369             () -> { java.util.stream.DoubleStream x = r.doubles(-1L); },
370             () -> { java.util.stream.DoubleStream x = r.doubles(-1L, .5, .6); },
371         };
372         assertThrows(IllegalArgumentException.class, throwingActions);
373     }
374 
375     /**
376      * Invoking bounded ints, long, doubles, with illegal bounds throws
377      * IllegalArgumentException
378      */
379     public void testBadStreamBounds() {
380         SplittableRandom r = new SplittableRandom();
381         Runnable[] throwingActions = {
382             () -> { java.util.stream.IntStream x = r.ints(2, 1); },
383             () -> { java.util.stream.IntStream x = r.ints(10, 42, 42); },
384             () -> { java.util.stream.LongStream x = r.longs(-1L, -1L); },
385             () -> { java.util.stream.LongStream x = r.longs(10, 1L, -2L); },
386             () -> { java.util.stream.DoubleStream x = r.doubles(0.0, 0.0); },
387             () -> { java.util.stream.DoubleStream x = r.doubles(10, .5, .4); },
388         };
389         assertThrows(IllegalArgumentException.class, throwingActions);
390     }
391 
392     /**
393      * A parallel sized stream of ints generates the given number of values
394      */
395     public void testIntsCount() {
396         LongAdder counter = new LongAdder();
397         SplittableRandom r = new SplittableRandom();
398         long size = 0;
399         for (int reps = 0; reps < REPS; ++reps) {
400             counter.reset();
401             r.ints(size).parallel().forEach(x -> counter.increment());
402             assertEquals(size, counter.sum());
403             size += 524959;
404         }
405     }
406 
407     /**
408      * A parallel sized stream of longs generates the given number of values
409      */
410     public void testLongsCount() {
411         LongAdder counter = new LongAdder();
412         SplittableRandom r = new SplittableRandom();
413         long size = 0;
414         for (int reps = 0; reps < REPS; ++reps) {
415             counter.reset();
416             r.longs(size).parallel().forEach(x -> counter.increment());
417             assertEquals(size, counter.sum());
418             size += 524959;
419         }
420     }
421 
422     /**
423      * A parallel sized stream of doubles generates the given number of values
424      */
425     public void testDoublesCount() {
426         LongAdder counter = new LongAdder();
427         SplittableRandom r = new SplittableRandom();
428         long size = 0;
429         for (int reps = 0; reps < REPS; ++reps) {
430             counter.reset();
431             r.doubles(size).parallel().forEach(x -> counter.increment());
432             assertEquals(size, counter.sum());
433             size += 524959;
434         }
435     }
436 
437     /**
438      * Each of a parallel sized stream of bounded ints is within bounds
439      */
440     public void testBoundedInts() {
441         AtomicInteger fails = new AtomicInteger(0);
442         SplittableRandom r = new SplittableRandom();
443         long size = 12345L;
444         for (int least = -15485867; least < MAX_INT_BOUND; least += 524959) {
445             for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 67867967) {
446                 final int lo = least, hi = bound;
447                 r.ints(size, lo, hi).parallel().forEach(
448                     x -> {
449                         if (x < lo || x >= hi)
450                             fails.getAndIncrement(); });
451             }
452         }
453         assertEquals(0, fails.get());
454     }
455 
456     /**
457      * Each of a parallel sized stream of bounded longs is within bounds
458      */
459     public void testBoundedLongs() {
460         AtomicInteger fails = new AtomicInteger(0);
461         SplittableRandom r = new SplittableRandom();
462         long size = 123L;
463         for (long least = -86028121; least < MAX_LONG_BOUND; least += 1982451653L) {
464             for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
465                 final long lo = least, hi = bound;
466                 r.longs(size, lo, hi).parallel().forEach(
467                     x -> {
468                         if (x < lo || x >= hi)
469                             fails.getAndIncrement(); });
470             }
471         }
472         assertEquals(0, fails.get());
473     }
474 
475     /**
476      * Each of a parallel sized stream of bounded doubles is within bounds
477      */
478     public void testBoundedDoubles() {
479         AtomicInteger fails = new AtomicInteger(0);
480         SplittableRandom r = new SplittableRandom();
481         long size = 456;
482         for (double least = 0.00011; least < 1.0e20; least *= 9) {
483             for (double bound = least * 1.0011; bound < 1.0e20; bound *= 17) {
484                 final double lo = least, hi = bound;
485                 r.doubles(size, lo, hi).parallel().forEach(
486                     x -> {
487                         if (x < lo || x >= hi)
488                             fails.getAndIncrement(); });
489             }
490         }
491         assertEquals(0, fails.get());
492     }
493 
494     /**
495      * A parallel unsized stream of ints generates at least 100 values
496      */
497     public void testUnsizedIntsCount() {
498         LongAdder counter = new LongAdder();
499         SplittableRandom r = new SplittableRandom();
500         long size = 100;
501         r.ints().limit(size).parallel().forEach(x -> counter.increment());
502         assertEquals(size, counter.sum());
503     }
504 
505     /**
506      * A parallel unsized stream of longs generates at least 100 values
507      */
508     public void testUnsizedLongsCount() {
509         LongAdder counter = new LongAdder();
510         SplittableRandom r = new SplittableRandom();
511         long size = 100;
512         r.longs().limit(size).parallel().forEach(x -> counter.increment());
513         assertEquals(size, counter.sum());
514     }
515 
516     /**
517      * A parallel unsized stream of doubles generates at least 100 values
518      */
519     public void testUnsizedDoublesCount() {
520         LongAdder counter = new LongAdder();
521         SplittableRandom r = new SplittableRandom();
522         long size = 100;
523         r.doubles().limit(size).parallel().forEach(x -> counter.increment());
524         assertEquals(size, counter.sum());
525     }
526 
527     /**
528      * A sequential unsized stream of ints generates at least 100 values
529      */
530     public void testUnsizedIntsCountSeq() {
531         LongAdder counter = new LongAdder();
532         SplittableRandom r = new SplittableRandom();
533         long size = 100;
534         r.ints().limit(size).forEach(x -> counter.increment());
535         assertEquals(size, counter.sum());
536     }
537 
538     /**
539      * A sequential unsized stream of longs generates at least 100 values
540      */
541     public void testUnsizedLongsCountSeq() {
542         LongAdder counter = new LongAdder();
543         SplittableRandom r = new SplittableRandom();
544         long size = 100;
545         r.longs().limit(size).forEach(x -> counter.increment());
546         assertEquals(size, counter.sum());
547     }
548 
549     /**
550      * A sequential unsized stream of doubles generates at least 100 values
551      */
552     public void testUnsizedDoublesCountSeq() {
553         LongAdder counter = new LongAdder();
554         SplittableRandom r = new SplittableRandom();
555         long size = 100;
556         r.doubles().limit(size).forEach(x -> counter.increment());
557         assertEquals(size, counter.sum());
558     }
559 
560     /**
561      * SplittableRandom should implement most of Random's public methods
562      */
563     public void testShouldImplementMostRandomMethods() throws Throwable {
564         Predicate<Method> wasForgotten = method -> {
565             String name = method.getName();
566             // some methods deliberately not implemented
567             if (name.equals("setSeed")) return false;
568             if (name.equals("nextFloat")) return false;
569             if (name.equals("nextGaussian")) return false;
570             try {
571                 SplittableRandom.class.getMethod(
572                     method.getName(), method.getParameterTypes());
573             } catch (ReflectiveOperationException ex) {
574                 return true;
575             }
576             return false;
577         };
578         List<Method> forgotten =
579             Arrays.stream(java.util.Random.class.getMethods())
580             .filter(wasForgotten)
581             .collect(Collectors.toList());
582         if (!forgotten.isEmpty())
583             throw new AssertionError("Please implement: " + forgotten);
584     }
585 
586     /**
587      * Repeated calls to nextBytes produce at least values of different signs for every byte
588      */
589     public void testNextBytes() {
590         SplittableRandom sr = new SplittableRandom();
591         int n = sr.nextInt(1, 20);
592         byte[] bytes = new byte[n];
593         outer:
594         for (int i = 0; i < n; i++) {
595             for (int tries = NCALLS; tries-->0; ) {
596                 byte before = bytes[i];
597                 sr.nextBytes(bytes);
598                 byte after = bytes[i];
599                 if (after * before < 0)
600                     continue outer;
601             }
602             fail("not enough variation in random bytes");
603         }
604     }
605 
606     /**
607      * Filling an empty array with random bytes succeeds without effect.
608      */
609     public void testNextBytes_emptyArray() {
610         new SplittableRandom().nextBytes(new byte[0]);
611     }
612 
613     public void testNextBytes_nullArray() {
614         try {
615             new SplittableRandom().nextBytes(null);
616             shouldThrow();
617         } catch (NullPointerException success) {}
618     }
619 
620 }
621