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