@@ -125,6 +125,17 @@ object Test {
125125 def withLegacyShrinking (b : Boolean ): Parameters =
126126 cpy(useLegacyShrinking0 = b)
127127
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+
128139 override def toString : String = {
129140 val sb = new StringBuilder
130141 sb.append(" Parameters(" )
@@ -137,7 +148,8 @@ object Test {
137148 sb.append(s " customClassLoader= $customClassLoader, " )
138149 sb.append(s " propFilter= $propFilter, " )
139150 sb.append(s " initialSeed= $initialSeed, " )
140- sb.append(s " useLegacyShrinking= $useLegacyShrinking) " )
151+ sb.append(s " useLegacyShrinking= $useLegacyShrinking, " )
152+ sb.append(s " maxRNGSpins= $maxRNGSpins) " )
141153 sb.toString
142154 }
143155
@@ -152,7 +164,8 @@ object Test {
152164 customClassLoader0 : Option [ClassLoader ] = outer.customClassLoader,
153165 propFilter0 : Option [String ] = outer.propFilter,
154166 initialSeed0 : Option [rng.Seed ] = outer.initialSeed,
155- useLegacyShrinking0 : Boolean = outer.useLegacyShrinking
167+ useLegacyShrinking0 : Boolean = outer.useLegacyShrinking,
168+ maxRNGSpins0 : Int = outer.maxRNGSpins
156169 ): Parameters =
157170 new Parameters {
158171 val minSuccessfulTests : Int = minSuccessfulTests0
@@ -165,6 +178,7 @@ object Test {
165178 val propFilter : Option [String ] = propFilter0
166179 val initialSeed : Option [rng.Seed ] = initialSeed0
167180 override val useLegacyShrinking : Boolean = useLegacyShrinking0
181+ override val maxRNGSpins : Int = maxRNGSpins0
168182 }
169183
170184 // no longer used, but preserved for binary compatibility
@@ -342,10 +356,17 @@ object Test {
342356 val help = " Disable legacy shrinking using Shrink instances"
343357 }
344358
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+
345365 val opts = Set [Opt [_]](
346366 OptMinSuccess , OptMaxDiscardRatio , OptMinSize ,
347367 OptMaxSize , OptWorkers , OptVerbosity ,
348- OptPropFilter , OptInitialSeed , OptDisableLegacyShrinking
368+ OptPropFilter , OptInitialSeed , OptDisableLegacyShrinking ,
369+ OptMaxRNGSpins
349370 )
350371
351372 def parseParams (args : Array [String ]): (Parameters => Parameters , List [String ]) = {
@@ -369,6 +390,7 @@ object Test {
369390 }
370391
371392 val useLegacyShrinking0 : Boolean = ! optMap(OptDisableLegacyShrinking )
393+ val maxRNGSpins : Int = optMap(OptMaxRNGSpins )
372394 val params = { (p : Parameters ) =>
373395 p.withMinSuccessfulTests(minSuccess0)
374396 .withMinSize(minSize0)
@@ -379,6 +401,7 @@ object Test {
379401 .withPropFilter(propFilter0)
380402 .withInitialSeed(initialSeed0)
381403 .withLegacyShrinking(useLegacyShrinking0)
404+ .withMaxRNGSpins(maxRNGSpins)
382405 }
383406 (params, us)
384407 }
@@ -405,6 +428,7 @@ object Test {
405428
406429 val iterations = Math .ceil(params.minSuccessfulTests / params.workers.toDouble)
407430 val sizeStep = (params.maxSize - params.minSize) / (iterations * params.workers)
431+ val maxSpinsBetween = params.maxRNGSpins.max(1 )
408432 var stop = false
409433
410434 def workerFun (workerIdx : Int ): Result = {
@@ -420,6 +444,22 @@ object Test {
420444 if (workerIdx == 0 ) seed0 else seed0.reseed(workerIdx.toLong)
421445 }
422446
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+
423463 while (! stop && res == null && n < iterations) {
424464
425465 val count = workerIdx + (params.workers * (n + d))
@@ -429,7 +469,7 @@ object Test {
429469 .withInitialSeed(Some (seed))
430470 .withSize(size.round.toInt)
431471
432- seed = seed.slide
472+ spinner()
433473
434474 val propRes = p(genPrms)
435475 if (propRes.collected.nonEmpty) {
0 commit comments