diff --git a/core/shared/src/main/scala/org/scalacheck/Gen.scala b/core/shared/src/main/scala/org/scalacheck/Gen.scala index a89e7ec2..82e4a9e8 100644 --- a/core/shared/src/main/scala/org/scalacheck/Gen.scala +++ b/core/shared/src/main/scala/org/scalacheck/Gen.scala @@ -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 @@ -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)`. */ @@ -1056,6 +1132,16 @@ object Gen extends GenArities with GenVersionSpecific { * containerOf[Map,(T,U)](g). */ 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 * nonEmptyContainerOf[Map,(T,U)](g). */