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 
  48     public static void main(String[] args) {
  49         main(suite(), args);
  50     }
  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      */
  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         assertThrows(
 169             IllegalArgumentException.class,
 170             () -> sr.nextInt(-17),
 171             () -> sr.nextInt(0),
 172             () -> sr.nextInt(Integer.MIN_VALUE));
 173     }
 174 
 175     /**
 176      * nextInt(least >= bound) throws IllegalArgumentException
 177      */
 178     public void testNextIntBadBounds() {
 179         SplittableRandom sr = new SplittableRandom();
 180         assertThrows(
 181             IllegalArgumentException.class,
 182             () -> sr.nextInt(17, 2),
 183             () -> sr.nextInt(-42, -42),
 184             () -> sr.nextInt(Integer.MAX_VALUE, Integer.MIN_VALUE));
 185     }
 186 
 187     /**
 188      * nextInt(bound) returns 0 <= value < bound;
 189      * repeated calls produce at least two distinct results
 190      */
 191     public void testNextIntBounded() {
 192         SplittableRandom sr = new SplittableRandom();
 193         for (int i = 0; i < 2; i++) assertEquals(0, sr.nextInt(1));
 194         // sample bound space across prime number increments
 195         for (int bound = 2; bound < MAX_INT_BOUND; bound += 524959) {
 196             int f = sr.nextInt(bound);
 197             assertTrue(0 <= f && f < bound);
 198             int i = 0;
 199             int j;
 200             while (i < NCALLS &&
 201                    (j = sr.nextInt(bound)) == f) {
 202                 assertTrue(0 <= j && j < bound);
 203                 ++i;
 204             }
 205             assertTrue(i < NCALLS);
 206         }
 207     }
 208 
 209     /**
 210      * nextInt(least, bound) returns least <= value < bound;
 211      * repeated calls produce at least two distinct results
 212      */
 213     public void testNextIntBounded2() {
 214         SplittableRandom sr = new SplittableRandom();
 215         for (int least = -15485863; least < MAX_INT_BOUND; least += 524959) {
 216             for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 49979687) {
 217                 int f = sr.nextInt(least, bound);
 218                 assertTrue(least <= f && f < bound);
 219                 int i = 0;
 220                 int j;
 221                 while (i < NCALLS &&
 222                        (j = sr.nextInt(least, bound)) == f) {
 223                     assertTrue(least <= j && j < bound);
 224                     ++i;
 225                 }
 226                 assertTrue(i < NCALLS);
 227             }
 228         }
 229     }
 230 
 231     /**
 232      * nextLong(non-positive) throws IllegalArgumentException
 233      */
 234     public void testNextLongBoundNonPositive() {
 235         SplittableRandom sr = new SplittableRandom();
 236         assertThrows(
 237             IllegalArgumentException.class,
 238             () -> sr.nextLong(-17L),
 239             () -> sr.nextLong(0L),
 240             () -> sr.nextLong(Long.MIN_VALUE));
 241     }
 242 
 243     /**
 244      * nextLong(least >= bound) throws IllegalArgumentException
 245      */
 246     public void testNextLongBadBounds() {
 247         SplittableRandom sr = new SplittableRandom();
 248         assertThrows(
 249             IllegalArgumentException.class,
 250             () -> sr.nextLong(17L, 2L),
 251             () -> sr.nextLong(-42L, -42L),
 252             () -> sr.nextLong(Long.MAX_VALUE, Long.MIN_VALUE));
 253     }
 254 
 255     /**
 256      * nextLong(bound) returns 0 <= value < bound;
 257      * repeated calls produce at least two distinct results
 258      */
 259     public void testNextLongBounded() {
 260         SplittableRandom sr = new SplittableRandom();
 261         for (int i = 0; i < 2; i++) assertEquals(0L, sr.nextLong(1L));
 262         for (long bound = 2; bound < MAX_LONG_BOUND; bound += 15485863) {
 263             long f = sr.nextLong(bound);
 264             assertTrue(0 <= f && f < bound);
 265             int i = 0;
 266             long j;
 267             while (i < NCALLS &&
 268                    (j = sr.nextLong(bound)) == f) {
 269                 assertTrue(0 <= j && j < bound);
 270                 ++i;
 271             }
 272             assertTrue(i < NCALLS);
 273         }
 274     }
 275 
 276     /**
 277      * nextLong(least, bound) returns least <= value < bound;
 278      * repeated calls produce at least two distinct results
 279      */
 280     public void testNextLongBounded2() {
 281         SplittableRandom sr = new SplittableRandom();
 282         for (long least = -86028121; least < MAX_LONG_BOUND; least += 982451653L) {
 283             for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
 284                 long f = sr.nextLong(least, bound);
 285                 assertTrue(least <= f && f < bound);
 286                 int i = 0;
 287                 long j;
 288                 while (i < NCALLS &&
 289                        (j = sr.nextLong(least, bound)) == f) {
 290                     assertTrue(least <= j && j < bound);
 291                     ++i;
 292                 }
 293                 assertTrue(i < NCALLS);
 294             }
 295         }
 296     }
 297 
 298     /**
 299      * nextDouble(non-positive) throws IllegalArgumentException
 300      */
 301     public void testNextDoubleBoundNonPositive() {
 302         SplittableRandom sr = new SplittableRandom();
 303         assertThrows(
 304             IllegalArgumentException.class,
 305             () -> sr.nextDouble(-17.0d),
 306             () -> sr.nextDouble(0.0d),
 307             () -> sr.nextDouble(-Double.MIN_VALUE),
 308             () -> sr.nextDouble(Double.NEGATIVE_INFINITY),
 309             () -> sr.nextDouble(Double.NaN));
 310     }
 311 
 312     /**
 313      * nextDouble(! (least < bound)) throws IllegalArgumentException
 314      */
 315     public void testNextDoubleBadBounds() {
 316         SplittableRandom sr = new SplittableRandom();
 317         assertThrows(
 318             IllegalArgumentException.class,
 319             () -> sr.nextDouble(17.0d, 2.0d),
 320             () -> sr.nextDouble(-42.0d, -42.0d),
 321             () -> sr.nextDouble(Double.MAX_VALUE, Double.MIN_VALUE),
 322             () -> sr.nextDouble(Double.NaN, 0.0d),
 323             () -> sr.nextDouble(0.0d, Double.NaN));
 324     }
 325 
 326     // TODO: Test infinite bounds!
 327     //() -> sr.nextDouble(Double.NEGATIVE_INFINITY, 0.0d),
 328     //() -> sr.nextDouble(0.0d, Double.POSITIVE_INFINITY),
 329 
 330     /**
 331      * nextDouble(least, bound) returns least <= value < bound;
 332      * repeated calls produce at least two distinct results
 333      */
 334     public void testNextDoubleBounded2() {
 335         SplittableRandom sr = new SplittableRandom();
 336         for (double least = 0.0001; least < 1.0e20; least *= 8) {
 337             for (double bound = least * 1.001; bound < 1.0e20; bound *= 16) {
 338                 double f = sr.nextDouble(least, bound);
 339                 assertTrue(least <= f && f < bound);
 340                 int i = 0;
 341                 double j;
 342                 while (i < NCALLS &&
 343                        (j = sr.nextDouble(least, bound)) == f) {
 344                     assertTrue(least <= j && j < bound);
 345                     ++i;
 346                 }
 347                 assertTrue(i < NCALLS);
 348             }
 349         }
 350     }
 351 
 352     /**
 353      * Invoking sized ints, long, doubles, with negative sizes throws
 354      * IllegalArgumentException
 355      */
 356     public void testBadStreamSize() {
 357         SplittableRandom r = new SplittableRandom();
 358         assertThrows(
 359             IllegalArgumentException.class,
 360             () -> { java.util.stream.IntStream x = r.ints(-1L); },
 361             () -> { java.util.stream.IntStream x = r.ints(-1L, 2, 3); },
 362             () -> { java.util.stream.LongStream x = r.longs(-1L); },
 363             () -> { java.util.stream.LongStream x = r.longs(-1L, -1L, 1L); },
 364             () -> { java.util.stream.DoubleStream x = r.doubles(-1L); },
 365             () -> { java.util.stream.DoubleStream x = r.doubles(-1L, .5, .6); });
 366     }
 367 
 368     /**
 369      * Invoking bounded ints, long, doubles, with illegal bounds throws
 370      * IllegalArgumentException
 371      */
 372     public void testBadStreamBounds() {
 373         SplittableRandom r = new SplittableRandom();
 374         assertThrows(
 375             IllegalArgumentException.class,
 376             () -> { java.util.stream.IntStream x = r.ints(2, 1); },
 377             () -> { java.util.stream.IntStream x = r.ints(10, 42, 42); },
 378             () -> { java.util.stream.LongStream x = r.longs(-1L, -1L); },
 379             () -> { java.util.stream.LongStream x = r.longs(10, 1L, -2L); },
 380             () -> { java.util.stream.DoubleStream x = r.doubles(0.0, 0.0); },
 381             () -> { java.util.stream.DoubleStream x = r.doubles(10, .5, .4); });
 382     }
 383 
 384     /**
 385      * A parallel sized stream of ints generates the given number of values
 386      */
 387     public void testIntsCount() {
 388         LongAdder counter = new LongAdder();
 389         SplittableRandom r = new SplittableRandom();
 390         long size = 0;
 391         for (int reps = 0; reps < REPS; ++reps) {
 392             counter.reset();
 393             r.ints(size).parallel().forEach(x -> counter.increment());
 394             assertEquals(size, counter.sum());
 395             size += 524959;
 396         }
 397     }
 398 
 399     /**
 400      * A parallel sized stream of longs generates the given number of values
 401      */
 402     public void testLongsCount() {
 403         LongAdder counter = new LongAdder();
 404         SplittableRandom r = new SplittableRandom();
 405         long size = 0;
 406         for (int reps = 0; reps < REPS; ++reps) {
 407             counter.reset();
 408             r.longs(size).parallel().forEach(x -> counter.increment());
 409             assertEquals(size, counter.sum());
 410             size += 524959;
 411         }
 412     }
 413 
 414     /**
 415      * A parallel sized stream of doubles generates the given number of values
 416      */
 417     public void testDoublesCount() {
 418         LongAdder counter = new LongAdder();
 419         SplittableRandom r = new SplittableRandom();
 420         long size = 0;
 421         for (int reps = 0; reps < REPS; ++reps) {
 422             counter.reset();
 423             r.doubles(size).parallel().forEach(x -> counter.increment());
 424             assertEquals(size, counter.sum());
 425             size += 524959;
 426         }
 427     }
 428 
 429     /**
 430      * Each of a parallel sized stream of bounded ints is within bounds
 431      */
 432     public void testBoundedInts() {
 433         AtomicInteger fails = new AtomicInteger(0);
 434         SplittableRandom r = new SplittableRandom();
 435         long size = 12345L;
 436         for (int least = -15485867; least < MAX_INT_BOUND; least += 524959) {
 437             for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 67867967) {
 438                 final int lo = least, hi = bound;
 439                 r.ints(size, lo, hi).parallel().forEach(
 440                     x -> {
 441                         if (x < lo || x >= hi)
 442                             fails.getAndIncrement(); });
 443             }
 444         }
 445         assertEquals(0, fails.get());
 446     }
 447 
 448     /**
 449      * Each of a parallel sized stream of bounded longs is within bounds
 450      */
 451     public void testBoundedLongs() {
 452         AtomicInteger fails = new AtomicInteger(0);
 453         SplittableRandom r = new SplittableRandom();
 454         long size = 123L;
 455         for (long least = -86028121; least < MAX_LONG_BOUND; least += 1982451653L) {
 456             for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
 457                 final long lo = least, hi = bound;
 458                 r.longs(size, lo, hi).parallel().forEach(
 459                     x -> {
 460                         if (x < lo || x >= hi)
 461                             fails.getAndIncrement(); });
 462             }
 463         }
 464         assertEquals(0, fails.get());
 465     }
 466 
 467     /**
 468      * Each of a parallel sized stream of bounded doubles is within bounds
 469      */
 470     public void testBoundedDoubles() {
 471         AtomicInteger fails = new AtomicInteger(0);
 472         SplittableRandom r = new SplittableRandom();
 473         long size = 456;
 474         for (double least = 0.00011; least < 1.0e20; least *= 9) {
 475             for (double bound = least * 1.0011; bound < 1.0e20; bound *= 17) {
 476                 final double lo = least, hi = bound;
 477                 r.doubles(size, lo, hi).parallel().forEach(
 478                     x -> {
 479                         if (x < lo || x >= hi)
 480                             fails.getAndIncrement(); });
 481             }
 482         }
 483         assertEquals(0, fails.get());
 484     }
 485 
 486     /**
 487      * A parallel unsized stream of ints generates at least 100 values
 488      */
 489     public void testUnsizedIntsCount() {
 490         LongAdder counter = new LongAdder();
 491         SplittableRandom r = new SplittableRandom();
 492         long size = 100;
 493         r.ints().limit(size).parallel().forEach(x -> counter.increment());
 494         assertEquals(size, counter.sum());
 495     }
 496 
 497     /**
 498      * A parallel unsized stream of longs generates at least 100 values
 499      */
 500     public void testUnsizedLongsCount() {
 501         LongAdder counter = new LongAdder();
 502         SplittableRandom r = new SplittableRandom();
 503         long size = 100;
 504         r.longs().limit(size).parallel().forEach(x -> counter.increment());
 505         assertEquals(size, counter.sum());
 506     }
 507 
 508     /**
 509      * A parallel unsized stream of doubles generates at least 100 values
 510      */
 511     public void testUnsizedDoublesCount() {
 512         LongAdder counter = new LongAdder();
 513         SplittableRandom r = new SplittableRandom();
 514         long size = 100;
 515         r.doubles().limit(size).parallel().forEach(x -> counter.increment());
 516         assertEquals(size, counter.sum());
 517     }
 518 
 519     /**
 520      * A sequential unsized stream of ints generates at least 100 values
 521      */
 522     public void testUnsizedIntsCountSeq() {
 523         LongAdder counter = new LongAdder();
 524         SplittableRandom r = new SplittableRandom();
 525         long size = 100;
 526         r.ints().limit(size).forEach(x -> counter.increment());
 527         assertEquals(size, counter.sum());
 528     }
 529 
 530     /**
 531      * A sequential unsized stream of longs generates at least 100 values
 532      */
 533     public void testUnsizedLongsCountSeq() {
 534         LongAdder counter = new LongAdder();
 535         SplittableRandom r = new SplittableRandom();
 536         long size = 100;
 537         r.longs().limit(size).forEach(x -> counter.increment());
 538         assertEquals(size, counter.sum());
 539     }
 540 
 541     /**
 542      * A sequential unsized stream of doubles generates at least 100 values
 543      */
 544     public void testUnsizedDoublesCountSeq() {
 545         LongAdder counter = new LongAdder();
 546         SplittableRandom r = new SplittableRandom();
 547         long size = 100;
 548         r.doubles().limit(size).forEach(x -> counter.increment());
 549         assertEquals(size, counter.sum());
 550     }
 551 
 552     /**
 553      * SplittableRandom should implement most of Random's public methods
 554      */
 555     public void testShouldImplementMostRandomMethods() throws Throwable {
 556         Predicate<Method> wasForgotten = method -> {
 557             String name = method.getName();
 558             // some methods deliberately not implemented
 559             if (name.equals("setSeed")) return false;
 560             if (name.equals("nextFloat")) return false;
 561             if (name.equals("nextGaussian")) return false;
 562             try {
 563                 SplittableRandom.class.getMethod(
 564                     method.getName(), method.getParameterTypes());
 565             } catch (ReflectiveOperationException ex) {
 566                 return true;
 567             }
 568             return false;
 569         };
 570         List<Method> forgotten =
 571             Arrays.stream(java.util.Random.class.getMethods())
 572             .filter(wasForgotten)
 573             .collect(Collectors.toList());
 574         if (!forgotten.isEmpty())
 575             throw new AssertionError("Please implement: " + forgotten);
 576     }
 577 
 578     /**
 579      * Repeated calls to nextBytes produce at least values of different signs for every byte
 580      */
 581     public void testNextBytes() {
 582         SplittableRandom sr = new SplittableRandom();
 583         int n = sr.nextInt(1, 20);
 584         byte[] bytes = new byte[n];
 585         outer:
 586         for (int i = 0; i < n; i++) {
 587             for (int tries = NCALLS; tries-->0; ) {
 588                 byte before = bytes[i];
 589                 sr.nextBytes(bytes);
 590                 byte after = bytes[i];
 591                 if (after * before < 0)
 592                     continue outer;
 593             }
 594             fail("not enough variation in random bytes");
 595         }
 596     }
 597 
 598     /**
 599      * Filling an empty array with random bytes succeeds without effect.
 600      */
 601     public void testNextBytes_emptyArray() {
 602         new SplittableRandom().nextBytes(new byte[0]);
 603     }
 604 
 605     public void testNextBytes_nullArray() {
 606         try {
 607             new SplittableRandom().nextBytes(null);
 608             shouldThrow();
 609         } catch (NullPointerException success) {}
 610     }
 611 
 612 }