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