@@ -125,6 +125,17 @@ object Test {
125
125
def withLegacyShrinking (b : Boolean ): Parameters =
126
126
cpy(useLegacyShrinking0 = b)
127
127
128
+ /** Maximum number of spins of the RNG to perform between checks.
129
+ * Greater values will reduce reuse of values (with dimimishing returns)
130
+ * for a given number of arguments to Prop.forAll tests. Greater values
131
+ * will also generally lead to slower tests, so be careful.
132
+ */
133
+ val maxRNGSpins : Int = 1
134
+
135
+ /** Set maximum RNG spins between checks */
136
+ def withMaxRNGSpins (n : Int ): Parameters =
137
+ cpy(maxRNGSpins0 = n)
138
+
128
139
override def toString : String = {
129
140
val sb = new StringBuilder
130
141
sb.append(" Parameters(" )
@@ -137,7 +148,8 @@ object Test {
137
148
sb.append(s " customClassLoader= $customClassLoader, " )
138
149
sb.append(s " propFilter= $propFilter, " )
139
150
sb.append(s " initialSeed= $initialSeed, " )
140
- sb.append(s " useLegacyShrinking= $useLegacyShrinking) " )
151
+ sb.append(s " useLegacyShrinking= $useLegacyShrinking, " )
152
+ sb.append(s " maxRNGSpins= $maxRNGSpins) " )
141
153
sb.toString
142
154
}
143
155
@@ -152,7 +164,8 @@ object Test {
152
164
customClassLoader0 : Option [ClassLoader ] = outer.customClassLoader,
153
165
propFilter0 : Option [String ] = outer.propFilter,
154
166
initialSeed0 : Option [rng.Seed ] = outer.initialSeed,
155
- useLegacyShrinking0 : Boolean = outer.useLegacyShrinking
167
+ useLegacyShrinking0 : Boolean = outer.useLegacyShrinking,
168
+ maxRNGSpins0 : Int = outer.maxRNGSpins
156
169
): Parameters =
157
170
new Parameters {
158
171
val minSuccessfulTests : Int = minSuccessfulTests0
@@ -165,6 +178,7 @@ object Test {
165
178
val propFilter : Option [String ] = propFilter0
166
179
val initialSeed : Option [rng.Seed ] = initialSeed0
167
180
override val useLegacyShrinking : Boolean = useLegacyShrinking0
181
+ override val maxRNGSpins : Int = maxRNGSpins0
168
182
}
169
183
170
184
// no longer used, but preserved for binary compatibility
@@ -342,10 +356,17 @@ object Test {
342
356
val help = " Disable legacy shrinking using Shrink instances"
343
357
}
344
358
359
+ object OptMaxRNGSpins extends IntOpt {
360
+ val default = 1
361
+ val names = Set (" maxRNGSpins" )
362
+ val help = " Maximum number of RNG spins to perform between checks"
363
+ }
364
+
345
365
val opts = Set [Opt [_]](
346
366
OptMinSuccess , OptMaxDiscardRatio , OptMinSize ,
347
367
OptMaxSize , OptWorkers , OptVerbosity ,
348
- OptPropFilter , OptInitialSeed , OptDisableLegacyShrinking
368
+ OptPropFilter , OptInitialSeed , OptDisableLegacyShrinking ,
369
+ OptMaxRNGSpins
349
370
)
350
371
351
372
def parseParams (args : Array [String ]): (Parameters => Parameters , List [String ]) = {
@@ -369,6 +390,7 @@ object Test {
369
390
}
370
391
371
392
val useLegacyShrinking0 : Boolean = ! optMap(OptDisableLegacyShrinking )
393
+ val maxRNGSpins : Int = optMap(OptMaxRNGSpins )
372
394
val params = { (p : Parameters ) =>
373
395
p.withMinSuccessfulTests(minSuccess0)
374
396
.withMinSize(minSize0)
@@ -379,6 +401,7 @@ object Test {
379
401
.withPropFilter(propFilter0)
380
402
.withInitialSeed(initialSeed0)
381
403
.withLegacyShrinking(useLegacyShrinking0)
404
+ .withMaxRNGSpins(maxRNGSpins)
382
405
}
383
406
(params, us)
384
407
}
@@ -405,6 +428,7 @@ object Test {
405
428
406
429
val iterations = Math .ceil(params.minSuccessfulTests / params.workers.toDouble)
407
430
val sizeStep = (params.maxSize - params.minSize) / (iterations * params.workers)
431
+ val maxSpinsBetween = params.maxRNGSpins.max(1 )
408
432
var stop = false
409
433
410
434
def workerFun (workerIdx : Int ): Result = {
@@ -420,6 +444,22 @@ object Test {
420
444
if (workerIdx == 0 ) seed0 else seed0.reseed(workerIdx.toLong)
421
445
}
422
446
447
+ val spinner : () => Unit =
448
+ if (maxSpinsBetween > 1 ) {
449
+ () => {
450
+ var slides = 1 + ((n + d) % maxSpinsBetween)
451
+
452
+ while (slides > 0 ) {
453
+ seed = seed.slide
454
+ slides -= 1
455
+ }
456
+ }
457
+ } else {
458
+ () => {
459
+ seed = seed.slide
460
+ }
461
+ }
462
+
423
463
while (! stop && res == null && n < iterations) {
424
464
425
465
val count = workerIdx + (params.workers * (n + d))
@@ -429,7 +469,7 @@ object Test {
429
469
.withInitialSeed(Some (seed))
430
470
.withSize(size.round.toInt)
431
471
432
- seed = seed.slide
472
+ spinner()
433
473
434
474
val propRes = p(genPrms)
435
475
if (propRes.collected.nonEmpty) {
0 commit comments