Skip to content

Add continuously growing collection Generators #849

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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