Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: rickynils/scalacheck
...
head fork: robinp/scalacheck
Checking mergeability… Don't worry, you can still create the pull request.
  • 7 commits
  • 5 files changed
  • 0 commit comments
  • 1 contributor
View
74 src/main/scala/org/scalacheck/Gen.scala
@@ -24,18 +24,18 @@ object Choose {
implicit val chooseLong: Choose[Long] = new Choose[Long] {
def choose(low: Long, high: Long) =
if(low > high || (high-low < 0)) fail
- else parameterized(prms => value(prms.choose(low,high)))
+ else parameterized(prms => value(prms.choose(low,high)), (x: Long) => (low <= x && x <= high))
}
implicit val chooseDouble: Choose[Double] = new Choose[Double] {
def choose(low: Double, high: Double) =
if (low > high || (high-low > Double.MaxValue)) fail
- else parameterized(prms => value(prms.choose(low,high)))
+ else parameterized(prms => value(prms.choose(low,high)), (x: Double) => (x <= low && x <= high))
}
implicit val chooseInt: Choose[Int] = new Choose[Int] {
def choose(low: Int, high: Int) =
- chooseLong.choose(low, high).map(_.toInt)
+ chooseLong.choose(low, high).bimap(_.toInt)(x => Some(x.toLong))
}
implicit val chooseByte: Choose[Byte] = new Choose[Byte] {
@@ -65,14 +65,30 @@ case class FiniteGenRes[+T](
sealed trait FiniteGen[+T] extends Gen[FiniteGenRes[T]]
-
/** Class that represents a generator. */
sealed trait Gen[+T] {
- import Gen.choose
+ import Gen.{ShrinkGuard, choose}
var label = "" // TODO: Ugly mutable field
-
+
+ def guard[U >: T]: ShrinkGuard[U] = Gen.noGuard
+
+ def c[U >: T](u: U) = {
+ val g = guard
+ (g sizeDependentGuard u) && (g contentGuard u)
+ }
+
+ def withGuard(f: ShrinkGuard[T]) = new Gen[T] {
+ label = Gen.this.label
+ // Note: this conversion (and the application of the resulting guard) should never fail
+ // if the guard is suitable for the generator
+ override def guard[U >: T]: ShrinkGuard[U] = f.asInstanceOf[ShrinkGuard[U]]
+ def apply(p: Gen.Params) = Gen.this.apply(p)
+ }
+
+ def withGuard(f: T => Boolean): Gen[T] = withGuard(Gen.mkContentGuard(f))
+
/** Put a label on the generator to make test reports clearer */
def label(l: String): Gen[T] = {
label = l
@@ -94,6 +110,9 @@ sealed trait Gen[+T] {
def apply(prms: Gen.Params): Option[T]
def map[U](f: T => U): Gen[U] = Gen(prms => this(prms).map(f)).label(label)
+
+ def bimap[U, V >: T](f: T => U)(g: U => Option[V]): Gen[U] = Gen(prms => this(prms).map(f)) withGuard {
+ u => g(u) map c getOrElse false} label (label)
def map2[U, V](g: Gen[U])(f: (T, U) => V) =
combine(g)((t, u) => t.flatMap(t => u.flatMap(u => Some(f(t, u)))))
@@ -118,7 +137,7 @@ sealed trait Gen[+T] {
def filter(p: T => Boolean): Gen[T] = Gen(prms => for {
t <- this(prms)
u <- if (p(t)) Some(t) else None
- } yield u).label(label)
+ } yield u) withGuard {t => p(t) && c(t)} label (label)
def withFilter(p: T => Boolean) = new GenWithFilter[T](this, p)
@@ -175,6 +194,7 @@ sealed trait Gen[+T] {
}
)
+ // TODO check if should be copied when using withGuard
private var freq = 1
def |[U >: T](g: Gen[U]): Gen[U] = {
val h = Gen.frequency((freq, this), (1, g))
@@ -219,10 +239,18 @@ object Gen {
else rng.nextDouble * (h-l) + l
}
}
+
+ sealed case class ShrinkGuard[-T](sizeDependentGuard: T => Boolean, contentGuard: T => Boolean) {
+ def ignoreSizeDependent = this.copy(sizeDependentGuard = (_:T) => true)
+ }
+ def mkContentGuard[T](g: T => Boolean) = ShrinkGuard((_:T) => true, g)
+ val noGuard = ShrinkGuard((_:Any) => true, (_:Any) => true)
+
+ private def alwaysTrue[T](t: T) = true
/* Generator factory method */
def apply[T](g: Gen.Params => Option[T]) = new Gen[T] {
- def apply(p: Gen.Params) = g(p)
+ def apply(p: Gen.Params) = g(p)
}
/* Convenience method for using the <code>frequency</code> method like this:
@@ -234,7 +262,8 @@ object Gen {
/** Sequences generators. If any of the given generators fails, the
* resulting generator will also fail. */
- def sequence[C[_],T](gs: Iterable[Gen[T]])(implicit b: Buildable[T,C]): Gen[C[T]] = Gen(prms => {
+ def sequence[C[_],T](gs: Iterable[Gen[T]], f: (C[T] => Boolean) = alwaysTrue _)(
+ implicit b: Buildable[T,C]): Gen[C[T]] = Gen(prms => {
val builder = b.builder
var none = false
val xs = gs.iterator
@@ -243,13 +272,14 @@ object Gen {
case Some(x) => builder += x
}
if(none) None else Some(builder.result())
- })
+ }) withGuard f
/** Wraps a generator lazily. The given parameter is only evalutated once,
* and not until the wrapper generator is evaluated. */
def lzy[T](g: => Gen[T]) = new Gen[T] {
lazy val h = g
def apply(prms: Params) = h(prms)
+ override def c[U >: T](u: U): Boolean = h.c(u)
}
/** Wraps a generator for later evaluation. The given parameter is
@@ -270,10 +300,26 @@ object Gen {
}
/** Creates a generator that can access its generation parameters */
- def parameterized[T](f: Params => Gen[T]): Gen[T] = Gen(prms => f(prms)(prms))
+ def parameterized[T](f: Params => Gen[T], g: (T => Boolean) = alwaysTrue _): Gen[T] = Gen(prms => f(prms)(prms)) withGuard g
/** Creates a generator that can access its generation size */
- def sized[T](f: Int => Gen[T]) = parameterized(prms => f(prms.size))
+ def sized[T](f: Int => Gen[T]) = new Gen[T] {
+ /** Different instances assigned to h should be the same, not regarding p.size based differences */
+ @volatile var h: Gen[T] = null
+
+ /** Guard of any instance in h. The size-dependent guard condition is ignored. */
+ override def guard[U >: T]: ShrinkGuard[U] =
+ if (h == null) sys.error("Shrink guard applied before any values were generated")
+ else h.guard.ignoreSizeDependent
+
+ def fetchGen(s: Int) = {
+ val g = f(s)
+ h = g // h is unsafe for general access now, since can be mutated by other threads
+ g
+ }
+
+ def apply(p: Gen.Params) = fetchGen(p.size)(p)
+ }
/** Creates a resized version of a generator */
def resize[T](s: Int, g: Gen[T]) = Gen(prms => g(prms.resize(s)))
@@ -318,6 +364,10 @@ object Gen {
def hasNext = i < n
def next = { i += 1; g }
}
+ }, (ts: C[T]) => {
+ b.size(ts) map { k =>
+ n == k && b.forall(ts, {g c _})
+ } getOrElse false
})
/** Generates a container of any type for which there exists an implicit
View
2  src/main/scala/org/scalacheck/Prop.scala
@@ -567,7 +567,7 @@ object Prop {
p: P => Prop,
s1: Shrink[T1],
pp1: T1 => Pretty
- ): Prop = forAllShrink(g1, shrink[T1])(f)
+ ): Prop = forAllShrink(g1, (t: T1) => shrink(t)(s1 withGuard g1.c))(f)
/** Universal quantifier for two explicit generators. Shrinks failed arguments
* with the default shrink function for the type */
View
9 src/main/scala/org/scalacheck/Shrink.scala
@@ -13,7 +13,10 @@ import util.Buildable
import scala.collection.{ JavaConversions => jcl }
sealed abstract class Shrink[T] {
- def shrink(x: T): Stream[T]
+ def shrink(x: T): Stream[T]
+ def withGuard(g: T => Boolean) = new Shrink[T] {
+ override def shrink(x: T) = Shrink.this.shrink(x) filter g
+ }
}
object Shrink {
@@ -30,9 +33,9 @@ object Shrink {
/** Shrink instance factory */
def apply[T](s: T => Stream[T]): Shrink[T] = new Shrink[T] {
- override def shrink(x: T) = s(x)
+ override def shrink(x: T) = s(x)
}
-
+
/** Shrink a value */
def shrink[T](x: T)(implicit s: Shrink[T]): Stream[T] = s.shrink(x)
View
5 src/main/scala/org/scalacheck/util/Buildable.scala
@@ -18,12 +18,17 @@ trait Buildable[T,C[_]] {
b ++= it
b.result()
}
+ // TODO make abstract once supported everywhere
+ def size(c: C[T]): Option[Int] = None
+ def forall(c: C[T], p: T => Boolean): Boolean = true
}
object Buildable {
implicit def buildableList[T] = new Buildable[T,List] {
def builder = new mutable.ListBuffer[T]
+ override def size(c: List[T]) = Some(c.size)
+ override def forall(c: List[T], p: T => Boolean) = c forall p
}
implicit def buildableStream[T] = new Buildable[T,Stream] {
View
79 src/test/scala/org/scalacheck/ShrinkGuardSpecification.scala
@@ -0,0 +1,79 @@
+/*-------------------------------------------------------------------------*\
+** ScalaCheck **
+** Copyright (c) 2007-2011 Rickard Nilsson. All rights reserved. **
+** http://www.scalacheck.org **
+** **
+** Added by Robin Palotai, 2012 **
+** **
+** This software is released under the terms of the Revised BSD License. **
+** There is NO WARRANTY. See the file LICENSE for the full text. **
+\*------------------------------------------------------------------------ */
+
+package org.scalacheck
+
+import Gen._
+import Prop.{forAll, someFailing, noneFailing, sizedProp, classify, propBoolean}
+import Arbitrary._
+import Shrink._
+import Test.{Exhausted, Failed}
+
+object ShrinkGuardSpecification extends Properties("ShrinkGuard") {
+
+ val g1 = choose(1, 100)
+
+ def propShouldFailWithShrinkedArg[T](prms: Test.Params, p: Prop, argCond: T => Boolean) =
+ Test.check(prms.copy(minSuccessfulTests = 1000), p).status match {
+ case Failed(Arg(_,a,_,_)::Nil,_) if argCond(a.asInstanceOf[T]) => true
+ case Exhausted => sys.error("test exhausted")
+ case x => println("***"+x); false
+ }
+
+ property("ShrinkGuard.choose") = forAll { (prms: Test.Params, a: Int, b: Int) =>
+ (a <= b) ==> {
+ val g = choose(a, b)
+ val k = ((a:Long) + b)/2
+ val p = forAll(g)((n: Int) => n > k)
+ propShouldFailWithShrinkedArg(prms, p, {(x: Int) => (a <= x && x <= k)})
+ }
+ }
+
+ val nonExtremeRange = choose(-10000, 10000)
+
+ property("ShrinkGuard.suchThat") = {
+ implicit val arbInt = Arbitrary(nonExtremeRange)
+ forAll { (prms: Test.Params, a: Int, b: Int) => (a + 2 <= b) ==> {
+ val (ia, ib) = (a + 1, b - 1)
+ val g = choose(a, b) suchThat { x => (ia <= x && x <= ib)}
+ val k = (((ia:Long) + ib)/2).toInt
+ val p = forAll(g){(n: Int) => n > k}
+ propShouldFailWithShrinkedArg(prms, p, {(x: Int) => (ia <= x && x <= k)})
+ }}
+ }
+
+ // The mapped function would need to be a bijection in order to
+ // be able to check the generated value.
+ //
+ // We can't decide if the fun is a bijection without help, so this doesn't work as expected.
+ // Currently when being mapped, the resulting Gen will have no fence.
+ val g4 = g1 suchThat { x => (1 <= x && x <= 100)} map { _ + 1 }
+ property("[FAIL map] good shrink if ARG_0 = 2") = forAll(g4)((n: Int) => n > 3)
+
+ // Bijections work
+ val g5: Gen[Int] = (g1 suchThat { x => (1 <= x && x <= 100)}).bimap(_ + 1)(x => Some(x - 1))
+ property("[bimap] good shrink if ARG_0 = 2") = forAll(g5)((n: Int) => n > 3)
+
+ // Container sample
+ val want = 3
+ val cg1 = listOfN(want, g1)
+ property("[listOfN] good shrink if no IOOBE and all three elem of ARG_0 is 1") = forAll(cg1) { xs: List[Int] =>
+ xs(want - 1) > 3
+ }
+
+ // Multi-generator forAll
+ val mg1 = choose(1, 10)
+ val mg2 = choose(2, 10)
+ property("[multi-gen forAll] good shrink if ARG_0 = (1, 2)") = forAll(mg1, mg2) { (x, y) => x > 3 && y > 3 }
+
+ // Sized sample
+
+}

No commit comments for this range

Something went wrong with that request. Please try again.