Skip to content

Commit

Permalink
Add continuously growing collection Generators
Browse files Browse the repository at this point in the history
  • Loading branch information
mdedetrich committed Oct 2, 2022
1 parent 0d68f35 commit 1b9c0da
Showing 1 changed file with 86 additions and 0 deletions.
86 changes: 86 additions & 0 deletions core/shared/src/main/scala/org/scalacheck/Gen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,70 @@ object Gen extends GenArities with GenVersionSpecific {
}
}

/** Generates a container of any Traversable type for which there exists an
* implicit [[org.scalacheck.util.Buildable]] instance. The elements in the
* container will be generated by the given generator. The generated container
* will continuously grow in size until the `fillCondition` returns true.
* If the given generator fails generating a value, the
* complete container generator will also fail. */
def buildableOfCond[C,T](fillCondition: C => Boolean, g: Gen[T], failureHint: Int= Integer.MAX_VALUE)(implicit
evb: Buildable[T,C], evt: C => Traversable[T]
): Gen[C] = {
new Gen[C] {
def doApply(p: P, seed0: Seed): R[C] = {
var seed: Seed = p.initialSeed.getOrElse(seed0)
val bldr = evb.builder
val allowedFailures = Gen.collectionRetries(failureHint)
var failures = 0
while (!fillCondition(bldr.result)) {
val res = g.doApply(p, seed)
res.retrieve match {
case Some(t) =>
bldr += t
case None =>
failures += 1
if (failures >= allowedFailures) return r(None, res.seed)
}
seed = res.seed
}
r(Some(bldr.result), seed)
}
}
}

/** Generates a container of any Traversable type for which there exists an
* implicit [[org.scalacheck.util.Buildable]] instance. The elements in the
* container will be generated by the given generator. The generated container
* will continuously frow in size until the `fillCondition` returns true.
* Unlike `buildableOfCond`, this version of the method lets you specify another
* collection Gen which helps in speeding up the process of growing the collection.
* If the given generator fails generating a value, the
* complete container generator will also fail. */
def buildableOfCollCond[C <: Traversable[T],T](cond: C => Boolean, g: Gen[C], failureHint: Int= Integer.MAX_VALUE)(implicit
evb: Buildable[T,C], evt: C => Traversable[T]
): Gen[C] = {
new Gen[C] {
def doApply(p: P, seed0: Seed): R[C] = {
var seed: Seed = p.initialSeed.getOrElse(seed0)
val bldr = evb.builder
val allowedFailures = Gen.collectionRetries(failureHint)
var failures = 0
while (!cond(bldr.result)) {
val res = g.doApply(p, seed)
res.retrieve match {
case Some(t) =>
bldr ++= t
case None =>
failures += 1
if (failures >= allowedFailures) return r(None, res.seed)
}
seed = res.seed
}
r(Some(bldr.result), seed)
}
}
}

/** Generates a container of any Traversable type for which there exists an
* implicit [[org.scalacheck.util.Buildable]] instance. The elements in the
* container will be generated by the given generator. The size of the
Expand Down Expand Up @@ -1042,6 +1106,18 @@ object Gen extends GenArities with GenVersionSpecific {
* `containerOf[List,T](g)`. */
def listOf[T](g: => Gen[T]) = buildableOf[List[T], T](g)

/** Generates a list that continuously grows in size until
* `finishCondition` returns true. */
def listOfCond[T](finishCondition: List[T] => Boolean, g: => Gen[T], failureHint: Int = Integer.MAX_VALUE) =
buildableOfCond[List[T], T](finishCondition, g, failureHint)

/** Generates a list that continuously grows in size until
* `finishCondition` returns true. Unlike `listOfCond` it
* accepts a list generator argument which lets you generate
* larger lists quicker */
def listOfFillCond[T](finishCondition: List[T] => Boolean, g: => Gen[List[T]], failureHint: Int = Integer.MAX_VALUE) =
buildableOfCollCond[List[T], T](finishCondition, g, failureHint)

/** Generates a non-empty list of random length. The maximum length depends
* on the size parameter. This method is equal to calling
* `nonEmptyContainerOf[List,T](g)`. */
Expand All @@ -1056,6 +1132,16 @@ object Gen extends GenArities with GenVersionSpecific {
* <code>containerOf[Map,(T,U)](g)</code>. */
def mapOf[T, U](g: => Gen[(T, U)]) = buildableOf[Map[T, U], (T, U)](g)

/** Generates a map that continuously grows in size until
* `finishCondition` returns true. */
def mapOfCond[T, U](cond: Map[T,U] => Boolean, g: => Gen[(T, U)], failureHint: Int = Integer.MAX_VALUE) = buildableOfCond[Map[T, U],(T, U)](cond, g, failureHint)

/** Generates a map that continuously grows in size until
* `finishCondition` returns true. Unlike `mapOfCond` it
* accepts a map generator argument which lets you generate
* larger maps quicker */
def mapOfFillCond[T, U](cond: Map[T,U] => Boolean, g: => Gen[Map[T, U]], failureHint: Int = Integer.MAX_VALUE) = buildableOfCollCond[Map[T, U],(T, U)](cond, g, failureHint)

/** Generates a non-empty map of random length. The maximum length depends
* on the size parameter. This method is equal to calling
* <code>nonEmptyContainerOf[Map,(T,U)](g)</code>. */
Expand Down

0 comments on commit 1b9c0da

Please sign in to comment.