Skip to content

Commit

Permalink
Fix retrying code in doPureApply.
Browse files Browse the repository at this point in the history
This commit fixes a bug pointed out by @alexarchambault. When
constructing an `A => B` value from `Cogen[A]` and `Gen[B]`, we need
to be able to reliably generate a `B` value given a `Seed`. The
`doPureApply` method was given the ability to retry -- but
unfortunately, it used the same result value (with the same seed)
instead of trying a new one, defeating the retry code.

This commit fixes that problem. It adds tests to ensure that filtered
generators that can also produce real values can be used with
Gen.function1. (If a generator can never produce values it will still
be a problem.)

Fixes typelevel#300.
  • Loading branch information
erik-stripe committed Nov 8, 2016
1 parent dcdd1ab commit d9c61aa
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 1 deletion.
14 changes: 14 additions & 0 deletions jvm/src/test/scala/org/scalacheck/GenSpecification.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ object GenSpecification extends Properties("Gen") {
arbitrary[Long] flatMap Seed.apply
)

property("pureApply #300") = {
def testCase[A](gen: Gen[A]): Prop =
sizedProp { sz =>
val g = Gen.function1(gen)(Cogen[Int])
if (sz > 0) forAll(g) { f => f(999); true } else Prop(true)
}
val p0 = testCase(arbitrary[Int].suchThat(_ != 0))
val p1 = testCase(arbitrary[String].suchThat(_ != ""))
val p2 = testCase(arbitrary[Boolean].suchThat(_ != false))
val p3 = testCase(arbitrary[List[Double]].suchThat(_ != Nil))
val p4 = testCase(oneOf(1, 2, 3, 4, 5).suchThat(_ == 1))
p0 && p1 && p2 && p3 && p4
}

property("sequence") =
forAll(listOf(frequency((10,const(arbitrary[Int])),(1,const(fail)))))(l =>
(someFailing(l) && (sequence[List[Int],Int](l) == fail)) ||
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/org/scalacheck/Gen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ sealed abstract class Gen[+T] extends Serializable { self =>
def doPureApply(p: Gen.Parameters, seed: Seed, retries: Int = 100): Gen.R[T] = {
@tailrec def loop(r: Gen.R[T], i: Int): Gen.R[T] =
if (r.retrieve.isDefined) r
else if (i > 0) loop(r, i - 1)
else if (i > 0) loop(doApply(p, r.seed), i - 1)
else throw new Gen.RetrievalError()
loop(doApply(p, seed), retries)
}
Expand Down

0 comments on commit d9c61aa

Please sign in to comment.