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 }