org.scalacheck.Prop.scala Maven / Gradle / Ivy
The newest version!
/*
* ScalaCheck
* Copyright (c) 2007-2021 Rickard Nilsson. All rights reserved.
* http://www.scalacheck.org
*
* 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 scala.annotation.tailrec
import rng.Seed
import util.{Pretty, ConsoleReporter}
/** Helper class to satisfy ScalaJS compilation. Do not use this directly, use `Prop.apply` instead.
*/
sealed class PropFromFun(f: Gen.Parameters => Prop.Result) extends Prop {
def apply(prms: Gen.Parameters) = f(prms)
}
@Platform.EnableReflectiveInstantiation
sealed abstract class Prop extends Serializable { self =>
import Prop.{Result, True, False, Undecided, provedToTrue, mergeRes}
def apply(prms: Gen.Parameters): Result
def viewSeed(name: String): Prop =
Prop { prms0 =>
val (prms, seed) = prms0.initialSeed match {
case Some(sd) =>
(prms0, sd)
case None =>
val sd = Seed.random()
(prms0.withInitialSeed(sd), sd)
}
val res = self(prms)
if (res.failure) println(s"failing seed for $name is ${seed.toBase64}")
res
}
@deprecated(message = "Please use useSeed which only takes a seed. name is, and has been, unused.", since = "1.18.0")
def useSeed(name: String, seed: Seed): Prop =
useSeed(seed)
def useSeed(seed: Seed): Prop =
Prop(prms0 => self(prms0.withInitialSeed(seed)))
def contramap(f: Gen.Parameters => Gen.Parameters): Prop =
new PropFromFun(params => apply(f(params)))
def map(f: Result => Result): Prop = Prop(prms => f(this(prms)))
def flatMap(f: Result => Prop): Prop =
Prop { prms0 =>
val res = this(prms0)
val prms1 = Prop.slideSeed(prms0)
f(res)(prms1)
}
def combine(p: => Prop)(f: (Result, Result) => Result) =
for (r1 <- this; r2 <- p) yield f(r1, r2)
/** Convenience method that checks this property with the given parameters and reports the result on the console.
* Should only be used when running tests interactively within the Scala REPL.
*/
def check(prms: Test.Parameters): Unit = Test.check_(
if (prms.testCallback.isInstanceOf[ConsoleReporter]) prms
else prms.withTestCallback(prms.testCallback.chain(ConsoleReporter(1))),
this
)
/** Convenience method that checks this property and reports the result on the console. Should only be used when
* running tests interactively within the Scala REPL.
*
* The default test parameters ([[Test.Parameters.default]]) are used for the check.
*/
def check(): Unit = check(Test.Parameters.default)
/** Convenience method that checks this property and reports the result on the console. Should only be used when
* running tests interactively within the Scala REPL.
*
* The provided argument should be a function that takes the default test parameters ([[Test.Parameters.default]]) as
* input and outputs a modified [[Test.Parameters]] instance that Example use:
*
* {{{
* p.check(_.withMinSuccessfulTests(500))
*
* p.check { _.
* withMinSuccessfulTests(80000).
* withWorkers(4)
* }
* }}}
*/
def check(paramFun: Test.Parameters => Test.Parameters): Unit = check(
paramFun(Test.Parameters.default)
)
/** Convenience method that makes it possible to use this property as an application that checks itself on execution.
* Calls `System.exit` with a non-zero exit code if the property check fails.
*/
def main(args: Array[String]): Unit = {
val ret = Test.CmdLineParser.parseParams(args) match {
case (applyCmdParams, Nil) =>
val params = applyCmdParams(Test.Parameters.default)
if (Test.check(params, this).passed) 0
else 1
case (_, os) =>
Console.out.println("Incorrect options:\n " + os.mkString(", "))
Test.CmdLineParser.printHelp()
-1
}
if (ret != 0) System.exit(ret)
}
/** Returns a new property that holds if and only if both this and the given property hold. If one of the properties
* doesn't generate a result, the new property will generate false.
*/
def &&(p: => Prop) = combine(p)(_ && _)
/** Returns a new property that holds if either this or the given property (or both) hold.
*/
def ||(p: => Prop) = combine(p)(_ || _)
/** Returns a new property that holds if and only if both this and the given property hold. If one of the properties
* doesn't generate a result, the new property will generate the same result as the other property.
*/
def ++(p: => Prop): Prop = combine(p)(_ ++ _)
/** Combines two properties through implication */
def ==>(p: => Prop): Prop = flatMap { r1 =>
if (r1.proved) p map { r2 => mergeRes(r1, r2, r2.status) }
else if (!r1.success) Prop(r1.copy(status = Undecided))
else p map { r2 => provedToTrue(mergeRes(r1, r2, r2.status)) }
}
/** Returns a new property that holds if and only if both this and the given property generates a result with the
* exact same status. Note that this means that if one of the properties is proved, and the other one passed, then
* the resulting property will fail.
*/
def ==(p: => Prop) = this.flatMap { r1 =>
p.map { r2 =>
mergeRes(r1, r2, if (r1.status == r2.status) True else False)
}
}
override def toString = "Prop"
/** Put a label on the property to make test reports clearer */
def label(l: String) = map(_.label(l))
/** Put a label on the property to make test reports clearer */
def :|(l: String) = label(l)
/** Put a label on the property to make test reports clearer */
def |:(l: String) = label(l)
/** Put a label on the property to make test reports clearer */
def :|(l: Symbol) = label(l.name)
/** Put a label on the property to make test reports clearer */
def |:(l: Symbol) = label(l.name)
}
object Prop {
import Arbitrary.{arbitrary}
// Types
/** A property argument */
case class Arg[+T](
label: String,
arg: T,
shrinks: Int,
origArg: T,
prettyArg: Pretty,
prettyOrigArg: Pretty
)
private[scalacheck] def mergeRes(x: Result, y: Result, st: Status) =
Result(
status = st,
args = x.args ++ y.args,
collected = x.collected ++ y.collected,
labels = x.labels ++ y.labels
)
/** The result of evaluating a property */
case class Result(
status: Status,
args: List[Arg[Any]] = Nil,
collected: Set[Any] = Set.empty,
labels: Set[String] = Set.empty
) {
def success = status match {
case True => true
case Proof => true
case _ => false
}
def failure = status match {
case False => true
case Exception(_) => true
case _ => false
}
def proved = status == Proof
def addArg(a: Arg[Any]) = copy(args = a :: args)
def collect(x: Any) = copy(collected = collected + x)
def label(l: String) = copy(labels = labels + l)
def &&(r: Result) = (this.status, r.status) match {
case (Exception(_), _) => this
case (_, Exception(_)) => r
case (False, _) => this
case (_, False) => r
case (Undecided, _) => this
case (_, Undecided) => r
case (_, Proof) => mergeRes(this, r, this.status)
case (Proof, _) => mergeRes(this, r, r.status)
case (True, True) => mergeRes(this, r, True)
}
def ||(r: Result) = (this.status, r.status) match {
case (Exception(_), _) => this
case (_, Exception(_)) => r
case (False, False) => mergeRes(this, r, False)
case (False, _) => r
case (_, False) => this
case (Proof, _) => this
case (_, Proof) => r
case (True, _) => this
case (_, True) => r
case (Undecided, Undecided) => mergeRes(this, r, Undecided)
}
def ++(r: Result) = (this.status, r.status) match {
case (Exception(_), _) => this
case (_, Exception(_)) => r
case (_, Undecided) => this
case (Undecided, _) => r
case (_, Proof) => this
case (Proof, _) => r
case (_, True) => this
case (True, _) => r
case (False, _) => this
}
def ==>(r: Result) = (this.status, r.status) match {
case (Exception(_), _) => this
case (_, Exception(_)) => r
case (False, _) => mergeRes(this, r, Undecided)
case (Undecided, _) => this
case (Proof, _) => mergeRes(this, r, r.status)
case (True, _) => mergeRes(this, r, r.status)
}
}
sealed trait Status
/** The property was proved */
case object Proof extends Status
/** The property was true */
case object True extends Status
/** The property was false */
case object False extends Status
/** The property could not be falsified or proved */
case object Undecided extends Status
/** Evaluating the property raised an exception */
sealed case class Exception(e: Throwable) extends Status {
override def equals(o: Any) = o match {
case Exception(_) => true
case _ => false
}
}
/** Create a new property from the given function. */
def apply(f: Gen.Parameters => Result): Prop = new PropFromFun(prms =>
try f(prms)
catch {
case e: Throwable => Result(status = Exception(e))
})
/** Create a property that returns the given result */
def apply(r: Result): Prop = Prop.apply(_ => r)
/** Create a property from a boolean value */
def apply(b: Boolean): Prop = if (b) proved else falsified
// Implicits
/** A collection of property operators on `Any` values. Import [[Prop.AnyOperators]] to make the operators available.
*/
class ExtendedAny[T](x: => T)(implicit ev: T => Pretty) {
/** See [[Prop.imply]] */
def imply(f: PartialFunction[T, Prop]) = Prop.imply(x, f)
/** See [[Prop.iff]] */
def iff(f: PartialFunction[T, Prop]) = Prop.iff(x, f)
/** See [[Prop.?=]] */
def ?=(y: T) = Prop.?=(x, y)
/** See [[Prop.=?]] */
def =?(y: T) = Prop.=?(x, y)
}
/** A collection of property operators on `Boolean` values. Import [[Prop.propBoolean]] to make the operators
* available. The availability of this class as an implicit via `BooleanOperators` was removed in 1.15.0.
*/
class ExtendedBoolean(b: => Boolean) {
/** See the documentation for [[org.scalacheck.Prop]] */
def ==>(p: => Prop) = Prop(b) ==> p
/** See the documentation for [[org.scalacheck.Prop]] */
def :|(l: String) = Prop(b) :| l
/** See the documentation for [[org.scalacheck.Prop]] */
def |:(l: String) = l |: Prop(b)
/** See the documentation for [[org.scalacheck.Prop]] */
def :|(l: Symbol) = Prop(b) :| l
/** See the documentation for [[org.scalacheck.Prop]] */
def |:(l: Symbol) = l |: Prop(b)
}
/** Implicit method that makes a number of property operators on values of type `Any` available in the current scope.
* See [[Prop.ExtendedAny]] for documentation on the operators.
*/
implicit def AnyOperators[T](x: => T)(implicit ev: T => Pretty): ExtendedAny[T] = new ExtendedAny[T](x)
/** Implicit method that makes a number of property operators on boolean values available in the current scope. See
* [[Prop.ExtendedBoolean]] for documentation on the operators.
*/
@deprecated("Please import Prop.propBoolean instead", since = "1.14.1")
def BooleanOperators(b: => Boolean): ExtendedBoolean = new ExtendedBoolean(b)
/** Implicit conversion of Boolean values to Prop values. */
implicit def propBoolean(b: Boolean): Prop = Prop(b)
// Private support functions
private def provedToTrue(r: Result) = r.status match {
case Proof => r.copy(status = True)
case _ => r
}
// Property combinators
/** A property that never is proved or falsified */
lazy val undecided = Prop(Result(status = Undecided))
/** A property that always is false */
lazy val falsified = Prop(Result(status = False))
/** A property that always is proved */
lazy val proved = Prop(Result(status = Proof))
/** A property that always is passed */
lazy val passed = Prop(Result(status = True))
/** A property that denotes an exception */
def exception(e: Throwable): Prop = Prop(Result(status = Exception(e)))
/** A property that denotes an exception */
lazy val exception: Prop = exception(null)
/** Create a property that compares two values. If the values aren't equal, the property will fail and report that
* first value doesn't match the expected (second) value.
*/
def ?=[T](x: T, y: T)(implicit pp: T => Pretty): Prop =
if (x == y) proved
else falsified :| {
val exp = Pretty.pretty[T](y, Pretty.Params(0))
val act = Pretty.pretty[T](x, Pretty.Params(0))
"Expected " + exp + " but got " + act
}
/** Create a property that compares two values. If the values aren't equal, the property will fail and report that
* second value doesn't match the expected (first) value.
*/
def =?[T](x: T, y: T)(implicit pp: T => Pretty): Prop = ?=(y, x)
/** A property that depends on the generator size */
def sizedProp(f: Int => Prop): Prop = Prop { prms =>
// provedToTrue since if the property is proved for
// one size, it shouldn't be regarded as proved for
// all sizes.
provedToTrue(f(prms.size)(prms))
}
/** Implication with several conditions */
def imply[T](x: T, f: PartialFunction[T, Prop]): Prop = secure {
if (f.isDefinedAt(x)) f(x) else undecided
}
/** Property holds only if the given partial function is defined at `x`, and returns a property that holds
*/
def iff[T](x: T, f: PartialFunction[T, Prop]): Prop = secure {
if (f.isDefinedAt(x)) f(x) else falsified
}
/** Combines properties into one, which is true if and only if all the properties are true
*/
def all(ps: Prop*) =
if (ps.isEmpty) proved
else Prop(prms => ps.map(p => p(prms)).reduceLeft(_ && _))
/** Combines properties into one, which is true if at least one of the properties is true
*/
def atLeastOne(ps: Prop*) =
if (ps.isEmpty) falsified
else Prop(prms => ps.map(p => p(prms)).reduceLeft(_ || _))
/** A property that holds if at least one of the given generators fails generating a value
*/
def someFailing[T](gs: Seq[Gen[T]]): Prop =
atLeastOne(gs.map(_ == Gen.fail)*)
/** A property that holds iff none of the given generators fails generating a value
*/
def noneFailing[T](gs: Seq[Gen[T]]): Prop =
all(gs.map(_ !== Gen.fail)*)
/** Returns true if the given statement throws an exception of the specified type
*/
def throws[T <: Throwable](c: Class[T])(x: => Any): Boolean =
try { x; false }
catch { case e if c.isInstance(e) => true }
/** Collect data for presentation in test report */
@deprecated("Use Prop.forAll(t => Prop.collect(t)(...)) instead of Prop.forAll(Prop.collect(t => ...))", "1.15.0")
def collect[T, P](f: T => P)(implicit ev: P => Prop): T => Prop = t =>
Prop { prms =>
val prop = ev(f(t))
prop(prms).collect(t)
}
/** Collect data for presentation in test report */
def collect[T](t: T)(prop: Prop) = Prop { prms =>
prop(prms).collect(t)
}
/** Collect data for presentation in test report */
def classify(c: => Boolean, ifTrue: Any)(prop: Prop): Prop =
if (c) collect(ifTrue)(prop) else collect(())(prop)
/** Collect data for presentation in test report */
def classify(c: => Boolean, ifTrue: Any, ifFalse: Any)(prop: Prop): Prop =
if (c) collect(ifTrue)(prop) else collect(ifFalse)(prop)
/** Wraps and protects a property, turning exceptions thrown by the property into test failures.
*/
def secure[P](p: => P)(implicit ev: P => Prop): Prop =
try ev(p)
catch { case e: Throwable => exception(e) }
/** Wraps a property to delay its evaluation. The given parameter is evaluated each time the wrapper property is
* evaluated.
*/
def delay(p: => Prop): Prop =
Prop(params => p(params))
/** Wraps a property lazily. The given parameter is only evaluated once, and not until the wrapper property is
* evaluated.
*/
def lzy(p: => Prop): Prop = {
lazy val q = p
Prop(params => q(params))
}
/** Wraps and protects a property, delaying its evaluation and turning exceptions into test failures.
*/
def protect(p: => Prop): Prop =
delay(secure(p))
/** Existential quantifier for an explicit generator. */
def exists[A, P](f: A => P)(implicit
pv: P => Prop,
pp: A => Pretty,
aa: Arbitrary[A]
): Prop = exists(aa.arbitrary)(f)
/** This handles situations where we have a starting seed in our parameters.
*
* If we do, then we remove it from parameters and return it. If not, we create a new random seed. The new parameters
* from this method should be used with all the generation that this prop needs itself.
*
* Note that if this Prop needs to evaluate other Props (e.g. in forAll), you should make sure *not* to use the
* parameters returned from this method. We need for all Props evaluated by this one to behave deterministically if
* this Prop was given a seed. In that case you should use `slideSeed` to update the parameters.
*/
def startSeed(prms: Gen.Parameters): (Gen.Parameters, Seed) =
prms.initialSeed match {
case Some(seed) => (prms.withNoInitialSeed, seed)
case None => (prms, Seed.random())
}
/** */
def slideSeed(prms: Gen.Parameters): Gen.Parameters =
prms.initialSeed match {
case Some(seed) => prms.withInitialSeed(seed.slide)
case None => prms
}
/** Existential quantifier for an explicit generator. */
def exists[A, P](g: Gen[A])(f: A => P)(implicit
pv: P => Prop,
pp: A => Pretty
): Prop = Prop { prms0 =>
val (prms, seed) = startSeed(prms0)
val gr = g.doApply(prms, seed)
gr.retrieve match {
case None => undecided(prms)
case Some(x) =>
val p = secure(f(x))
val labels = gr.labels.mkString(",")
val r = p(slideSeed(prms0)).addArg(Arg(labels, x, 0, x, pp(x), pp(x)))
r.status match {
case True => r.copy(status = Proof)
case False => r.copy(status = Undecided)
case _ => r
}
}
}
/** Universal quantifier for an explicit generator. Does not shrink failed test cases.
*/
def forAllNoShrink[T1, P](
g1: Gen[T1]
)(
f: T1 => P
)(implicit
pv: P => Prop,
pp1: T1 => Pretty
): Prop = Prop { prms0 =>
val (prms, seed) = startSeed(prms0)
val gr = g1.doApply(prms, seed)
gr.retrieve match {
case None => undecided(prms)
case Some(x) =>
val p = secure(f(x))
val labels = gr.labels.mkString(",")
provedToTrue(p(slideSeed(prms0))).addArg(Arg(labels, x, 0, x, pp1(x), pp1(x)))
}
}
/** Universal quantifier for two explicit generators. Does not shrink failed test cases.
*/
def forAllNoShrink[T1, T2, P](
g1: Gen[T1],
g2: Gen[T2]
)(
f: (T1, T2) => P
)(implicit
p: P => Prop,
pp1: T1 => Pretty,
pp2: T2 => Pretty
): Prop = forAllNoShrink(g1)(t => forAllNoShrink(g2)(f(t, _: T2)))
/** Universal quantifier for three explicit generators. Does not shrink failed test cases.
*/
def forAllNoShrink[T1, T2, T3, P](
g1: Gen[T1],
g2: Gen[T2],
g3: Gen[T3]
)(
f: (T1, T2, T3) => P
)(implicit
p: P => Prop,
pp1: T1 => Pretty,
pp2: T2 => Pretty,
pp3: T3 => Pretty
): Prop = forAllNoShrink(g1)(t => forAllNoShrink(g2, g3)(f(t, _: T2, _: T3)))
/** Universal quantifier for four explicit generators. Does not shrink failed test cases.
*/
def forAllNoShrink[T1, T2, T3, T4, P](
g1: Gen[T1],
g2: Gen[T2],
g3: Gen[T3],
g4: Gen[T4]
)(
f: (T1, T2, T3, T4) => P
)(implicit
p: P => Prop,
pp1: T1 => Pretty,
pp2: T2 => Pretty,
pp3: T3 => Pretty,
pp4: T4 => Pretty
): Prop = forAllNoShrink(g1)(t => forAllNoShrink(g2, g3, g4)(f(t, _: T2, _: T3, _: T4)))
/** Universal quantifier for five explicit generators. Does not shrink failed test cases.
*/
def forAllNoShrink[T1, T2, T3, T4, T5, P](
g1: Gen[T1],
g2: Gen[T2],
g3: Gen[T3],
g4: Gen[T4],
g5: Gen[T5]
)(
f: (T1, T2, T3, T4, T5) => P
)(implicit
p: P => Prop,
pp1: T1 => Pretty,
pp2: T2 => Pretty,
pp3: T3 => Pretty,
pp4: T4 => Pretty,
pp5: T5 => Pretty
): Prop = forAllNoShrink(g1)(t => forAllNoShrink(g2, g3, g4, g5)(f(t, _: T2, _: T3, _: T4, _: T5)))
/** Universal quantifier for six explicit generators. Does not shrink failed test cases.
*/
def forAllNoShrink[T1, T2, T3, T4, T5, T6, P](
g1: Gen[T1],
g2: Gen[T2],
g3: Gen[T3],
g4: Gen[T4],
g5: Gen[T5],
g6: Gen[T6]
)(
f: (T1, T2, T3, T4, T5, T6) => P
)(implicit
p: P => Prop,
pp1: T1 => Pretty,
pp2: T2 => Pretty,
pp3: T3 => Pretty,
pp4: T4 => Pretty,
pp5: T5 => Pretty,
pp6: T6 => Pretty
): Prop = forAllNoShrink(g1)(t => forAllNoShrink(g2, g3, g4, g5, g6)(f(t, _: T2, _: T3, _: T4, _: T5, _: T6)))
/** Universal quantifier for seven explicit generators. Does not shrink failed test cases.
*/
def forAllNoShrink[T1, T2, T3, T4, T5, T6, T7, P](
g1: Gen[T1],
g2: Gen[T2],
g3: Gen[T3],
g4: Gen[T4],
g5: Gen[T5],
g6: Gen[T6],
g7: Gen[T7]
)(
f: (T1, T2, T3, T4, T5, T6, T7) => P
)(implicit
p: P => Prop,
pp1: T1 => Pretty,
pp2: T2 => Pretty,
pp3: T3 => Pretty,
pp4: T4 => Pretty,
pp5: T5 => Pretty,
pp6: T6 => Pretty,
pp7: T7 => Pretty
): Prop =
forAllNoShrink(g1)(t => forAllNoShrink(g2, g3, g4, g5, g6, g7)(f(t, _: T2, _: T3, _: T4, _: T5, _: T6, _: T7)))
/** Universal quantifier for eight explicit generators. Does not shrink failed test cases.
*/
def forAllNoShrink[T1, T2, T3, T4, T5, T6, T7, T8, P](
g1: Gen[T1],
g2: Gen[T2],
g3: Gen[T3],
g4: Gen[T4],
g5: Gen[T5],
g6: Gen[T6],
g7: Gen[T7],
g8: Gen[T8]
)(
f: (T1, T2, T3, T4, T5, T6, T7, T8) => P
)(implicit
p: P => Prop,
pp1: T1 => Pretty,
pp2: T2 => Pretty,
pp3: T3 => Pretty,
pp4: T4 => Pretty,
pp5: T5 => Pretty,
pp6: T6 => Pretty,
pp7: T7 => Pretty,
pp8: T8 => Pretty
): Prop = forAllNoShrink(g1)(t =>
forAllNoShrink(g2, g3, g4, g5, g6, g7, g8)(f(t, _: T2, _: T3, _: T4, _: T5, _: T6, _: T7, _: T8)))
/** Converts a function into a universally quantified property */
def forAllNoShrink[A1, P](
f: A1 => P
)(implicit
pv: P => Prop,
a1: Arbitrary[A1],
pp1: A1 => Pretty
): Prop = forAllNoShrink(arbitrary[A1])(f)
/** Converts a function into a universally quantified property */
def forAllNoShrink[A1, A2, P](
f: (A1, A2) => P
)(implicit
pv: P => Prop,
a1: Arbitrary[A1],
pp1: A1 => Pretty,
a2: Arbitrary[A2],
pp2: A2 => Pretty
): Prop = forAllNoShrink(arbitrary[A1], arbitrary[A2])(f)
/** Converts a function into a universally quantified property */
def forAllNoShrink[A1, A2, A3, P](
f: (A1, A2, A3) => P
)(implicit
pv: P => Prop,
a1: Arbitrary[A1],
pp1: A1 => Pretty,
a2: Arbitrary[A2],
pp2: A2 => Pretty,
a3: Arbitrary[A3],
pp3: A3 => Pretty
): Prop = forAllNoShrink(arbitrary[A1], arbitrary[A2], arbitrary[A3])(f)
/** Converts a function into a universally quantified property */
def forAllNoShrink[A1, A2, A3, A4, P](
f: (A1, A2, A3, A4) => P
)(implicit
pv: P => Prop,
a1: Arbitrary[A1],
pp1: A1 => Pretty,
a2: Arbitrary[A2],
pp2: A2 => Pretty,
a3: Arbitrary[A3],
pp3: A3 => Pretty,
a4: Arbitrary[A4],
pp4: A4 => Pretty
): Prop = forAllNoShrink(arbitrary[A1], arbitrary[A2], arbitrary[A3], arbitrary[A4])(f)
/** Converts a function into a universally quantified property */
def forAllNoShrink[A1, A2, A3, A4, A5, P](
f: (A1, A2, A3, A4, A5) => P
)(implicit
pv: P => Prop,
a1: Arbitrary[A1],
pp1: A1 => Pretty,
a2: Arbitrary[A2],
pp2: A2 => Pretty,
a3: Arbitrary[A3],
pp3: A3 => Pretty,
a4: Arbitrary[A4],
pp4: A4 => Pretty,
a5: Arbitrary[A5],
pp5: A5 => Pretty
): Prop = forAllNoShrink(arbitrary[A1], arbitrary[A2], arbitrary[A3], arbitrary[A4], arbitrary[A5])(f)
/** Converts a function into a universally quantified property */
def forAllNoShrink[A1, A2, A3, A4, A5, A6, P](
f: (A1, A2, A3, A4, A5, A6) => P
)(implicit
pv: P => Prop,
a1: Arbitrary[A1],
pp1: A1 => Pretty,
a2: Arbitrary[A2],
pp2: A2 => Pretty,
a3: Arbitrary[A3],
pp3: A3 => Pretty,
a4: Arbitrary[A4],
pp4: A4 => Pretty,
a5: Arbitrary[A5],
pp5: A5 => Pretty,
a6: Arbitrary[A6],
pp6: A6 => Pretty
): Prop = forAllNoShrink(arbitrary[A1], arbitrary[A2], arbitrary[A3], arbitrary[A4], arbitrary[A5], arbitrary[A6])(f)
/** Converts a function into a universally quantified property */
def forAllNoShrink[A1, A2, A3, A4, A5, A6, A7, P](
f: (A1, A2, A3, A4, A5, A6, A7) => P
)(implicit
pv: P => Prop,
a1: Arbitrary[A1],
pp1: A1 => Pretty,
a2: Arbitrary[A2],
pp2: A2 => Pretty,
a3: Arbitrary[A3],
pp3: A3 => Pretty,
a4: Arbitrary[A4],
pp4: A4 => Pretty,
a5: Arbitrary[A5],
pp5: A5 => Pretty,
a6: Arbitrary[A6],
pp6: A6 => Pretty,
a7: Arbitrary[A7],
pp7: A7 => Pretty
): Prop = {
forAllNoShrink(
arbitrary[A1],
arbitrary[A2],
arbitrary[A3],
arbitrary[A4],
arbitrary[A5],
arbitrary[A6],
arbitrary[A7])(f)
}
/** Converts a function into a universally quantified property */
def forAllNoShrink[A1, A2, A3, A4, A5, A6, A7, A8, P](
f: (A1, A2, A3, A4, A5, A6, A7, A8) => P
)(implicit
pv: P => Prop,
a1: Arbitrary[A1],
pp1: A1 => Pretty,
a2: Arbitrary[A2],
pp2: A2 => Pretty,
a3: Arbitrary[A3],
pp3: A3 => Pretty,
a4: Arbitrary[A4],
pp4: A4 => Pretty,
a5: Arbitrary[A5],
pp5: A5 => Pretty,
a6: Arbitrary[A6],
pp6: A6 => Pretty,
a7: Arbitrary[A7],
pp7: A7 => Pretty,
a8: Arbitrary[A8],
pp8: A8 => Pretty
): Prop = {
forAllNoShrink(
arbitrary[A1],
arbitrary[A2],
arbitrary[A3],
arbitrary[A4],
arbitrary[A5],
arbitrary[A6],
arbitrary[A7],
arbitrary[A8])(f)
}
/** Universal quantifier for an explicit generator. Shrinks failed arguments with the given shrink function
*/
def forAllShrink[T, P](g: Gen[T], shrink: T => Stream[T])(f: T => P)(implicit pv: P => Prop, pp: T => Pretty): Prop =
Prop { prms0 =>
val (prms, seed) = startSeed(prms0)
val gr = g.doApply(prms, seed)
val labels = gr.labels.mkString(",")
def result(x: T) = {
val p = secure(pv(f(x)))
provedToTrue(p(slideSeed(prms0)))
}
/*
* Returns the first failed result in Left or success in Right.
*/
def getFirstFailure(
xs: Stream[T],
exceptionFilter: Option[Class[? <: Throwable]]
): Either[(T, Result), (T, Result)] = {
assert(!xs.isEmpty, "Stream cannot be empty")
val results = xs.map(x => (x, result(x)))
results.dropWhile {
case (_, Result(Exception(e), _, _, _)) => !exceptionFilter.contains(e.getClass)
case (_, r) => !r.failure
}.headOption match {
case None => Right(results.head)
case Some(xr) => Left(xr)
}
}
def shrinker(x: T, r: Result, shrinks: Int, orig: T): Result = {
val xs = shrink(x)
val res = r.addArg(Arg(labels, x, shrinks, orig, pp(x), pp(orig)))
val originalException = Some(r.status).collect { case Prop.Exception(e) => e.getClass }
if (xs.isEmpty) res
else getFirstFailure(xs, originalException) match {
case Right(_) => res
case Left((x2, r2)) => shrinker(x2, replOrig(r, r2), shrinks + 1, orig)
}
}
def replOrig(r0: Result, r1: Result) = (r0.args, r1.args) match {
case (a0 :: _, a1 :: as) =>
r1.copy(
args = a1.copy(
origArg = a0.origArg,
prettyOrigArg = a0.prettyOrigArg
) :: as
)
case _ => r1
}
gr.retrieve match {
case None => undecided(prms)
case Some(x) =>
val r = result(x)
if (r.failure && prms.useLegacyShrinking) shrinker(x, r, 0, x)
else r.addArg(Arg(labels, x, 0, x, pp(x), pp(x)))
}
}
/** Universal quantifier for an explicit generator. Shrinks failed arguments with the default shrink function for the
* type
*/
def forAll[T1, P](
g1: Gen[T1]
)(
f: T1 => P
)(implicit
p: P => Prop,
s1: Shrink[T1],
pp1: T1 => Pretty
): Prop = forAllShrink[T1, P](g1, s1.shrink)(f)
/** Universal quantifier for two explicit generators. Shrinks failed arguments with the default shrink function for
* the type
*/
def forAll[T1, T2, P](
g1: Gen[T1],
g2: Gen[T2]
)(
f: (T1, T2) => P
)(implicit
p: P => Prop,
s1: Shrink[T1],
pp1: T1 => Pretty,
s2: Shrink[T2],
pp2: T2 => Pretty
): Prop = forAll(g1)(t => forAll(g2)(f(t, _: T2)))
/** Universal quantifier for three explicit generators. Shrinks failed arguments with the default shrink function for
* the type
*/
def forAll[T1, T2, T3, P](
g1: Gen[T1],
g2: Gen[T2],
g3: Gen[T3]
)(
f: (T1, T2, T3) => P
)(implicit
p: P => Prop,
s1: Shrink[T1],
pp1: T1 => Pretty,
s2: Shrink[T2],
pp2: T2 => Pretty,
s3: Shrink[T3],
pp3: T3 => Pretty
): Prop = forAll(g1)(t => forAll(g2, g3)(f(t, _: T2, _: T3)))
/** Universal quantifier for four explicit generators. Shrinks failed arguments with the default shrink function for
* the type
*/
def forAll[T1, T2, T3, T4, P](
g1: Gen[T1],
g2: Gen[T2],
g3: Gen[T3],
g4: Gen[T4]
)(
f: (T1, T2, T3, T4) => P
)(implicit
p: P => Prop,
s1: Shrink[T1],
pp1: T1 => Pretty,
s2: Shrink[T2],
pp2: T2 => Pretty,
s3: Shrink[T3],
pp3: T3 => Pretty,
s4: Shrink[T4],
pp4: T4 => Pretty
): Prop = forAll(g1)(t => forAll(g2, g3, g4)(f(t, _: T2, _: T3, _: T4)))
/** Universal quantifier for five explicit generators. Shrinks failed arguments with the default shrink function for
* the type
*/
def forAll[T1, T2, T3, T4, T5, P](
g1: Gen[T1],
g2: Gen[T2],
g3: Gen[T3],
g4: Gen[T4],
g5: Gen[T5]
)(
f: (T1, T2, T3, T4, T5) => P
)(implicit
p: P => Prop,
s1: Shrink[T1],
pp1: T1 => Pretty,
s2: Shrink[T2],
pp2: T2 => Pretty,
s3: Shrink[T3],
pp3: T3 => Pretty,
s4: Shrink[T4],
pp4: T4 => Pretty,
s5: Shrink[T5],
pp5: T5 => Pretty
): Prop = forAll(g1)(t => forAll(g2, g3, g4, g5)(f(t, _: T2, _: T3, _: T4, _: T5)))
/** Universal quantifier for six explicit generators. Shrinks failed arguments with the default shrink function for
* the type
*/
def forAll[T1, T2, T3, T4, T5, T6, P](
g1: Gen[T1],
g2: Gen[T2],
g3: Gen[T3],
g4: Gen[T4],
g5: Gen[T5],
g6: Gen[T6]
)(
f: (T1, T2, T3, T4, T5, T6) => P
)(implicit
p: P => Prop,
s1: Shrink[T1],
pp1: T1 => Pretty,
s2: Shrink[T2],
pp2: T2 => Pretty,
s3: Shrink[T3],
pp3: T3 => Pretty,
s4: Shrink[T4],
pp4: T4 => Pretty,
s5: Shrink[T5],
pp5: T5 => Pretty,
s6: Shrink[T6],
pp6: T6 => Pretty
): Prop = forAll(g1)(t => forAll(g2, g3, g4, g5, g6)(f(t, _: T2, _: T3, _: T4, _: T5, _: T6)))
/** Universal quantifier for seven explicit generators. Shrinks failed arguments with the default shrink function for
* the type
*/
def forAll[T1, T2, T3, T4, T5, T6, T7, P](
g1: Gen[T1],
g2: Gen[T2],
g3: Gen[T3],
g4: Gen[T4],
g5: Gen[T5],
g6: Gen[T6],
g7: Gen[T7]
)(
f: (T1, T2, T3, T4, T5, T6, T7) => P
)(implicit
p: P => Prop,
s1: Shrink[T1],
pp1: T1 => Pretty,
s2: Shrink[T2],
pp2: T2 => Pretty,
s3: Shrink[T3],
pp3: T3 => Pretty,
s4: Shrink[T4],
pp4: T4 => Pretty,
s5: Shrink[T5],
pp5: T5 => Pretty,
s6: Shrink[T6],
pp6: T6 => Pretty,
s7: Shrink[T7],
pp7: T7 => Pretty
): Prop = forAll(g1)(t => forAll(g2, g3, g4, g5, g6, g7)(f(t, _: T2, _: T3, _: T4, _: T5, _: T6, _: T7)))
/** Universal quantifier for eight explicit generators. Shrinks failed arguments with the default shrink function for
* the type
*/
def forAll[T1, T2, T3, T4, T5, T6, T7, T8, P](
g1: Gen[T1],
g2: Gen[T2],
g3: Gen[T3],
g4: Gen[T4],
g5: Gen[T5],
g6: Gen[T6],
g7: Gen[T7],
g8: Gen[T8]
)(
f: (T1, T2, T3, T4, T5, T6, T7, T8) => P
)(implicit
p: P => Prop,
s1: Shrink[T1],
pp1: T1 => Pretty,
s2: Shrink[T2],
pp2: T2 => Pretty,
s3: Shrink[T3],
pp3: T3 => Pretty,
s4: Shrink[T4],
pp4: T4 => Pretty,
s5: Shrink[T5],
pp5: T5 => Pretty,
s6: Shrink[T6],
pp6: T6 => Pretty,
s7: Shrink[T7],
pp7: T7 => Pretty,
s8: Shrink[T8],
pp8: T8 => Pretty
): Prop = forAll(g1)(t => forAll(g2, g3, g4, g5, g6, g7, g8)(f(t, _: T2, _: T3, _: T4, _: T5, _: T6, _: T7, _: T8)))
/** Converts a function into a universally quantified property */
def forAll[A1, P](
f: A1 => P
)(implicit
p: P => Prop,
a1: Arbitrary[A1],
s1: Shrink[A1],
pp1: A1 => Pretty
): Prop = forAllShrink(arbitrary[A1], s1.shrink)(f andThen p)
/** Converts a function into a universally quantified property */
def forAll[A1, A2, P](
f: (A1, A2) => P
)(implicit
p: P => Prop,
a1: Arbitrary[A1],
s1: Shrink[A1],
pp1: A1 => Pretty,
a2: Arbitrary[A2],
s2: Shrink[A2],
pp2: A2 => Pretty
): Prop = forAll((a: A1) => forAll(f(a, _: A2)))
/** Converts a function into a universally quantified property */
def forAll[A1, A2, A3, P](
f: (A1, A2, A3) => P
)(implicit
p: P => Prop,
a1: Arbitrary[A1],
s1: Shrink[A1],
pp1: A1 => Pretty,
a2: Arbitrary[A2],
s2: Shrink[A2],
pp2: A2 => Pretty,
a3: Arbitrary[A3],
s3: Shrink[A3],
pp3: A3 => Pretty
): Prop = forAll((a: A1) => forAll(f(a, _: A2, _: A3)))
/** Converts a function into a universally quantified property */
def forAll[A1, A2, A3, A4, P](
f: (A1, A2, A3, A4) => P
)(implicit
p: P => Prop,
a1: Arbitrary[A1],
s1: Shrink[A1],
pp1: A1 => Pretty,
a2: Arbitrary[A2],
s2: Shrink[A2],
pp2: A2 => Pretty,
a3: Arbitrary[A3],
s3: Shrink[A3],
pp3: A3 => Pretty,
a4: Arbitrary[A4],
s4: Shrink[A4],
pp4: A4 => Pretty
): Prop = forAll((a: A1) => forAll(f(a, _: A2, _: A3, _: A4)))
/** Converts a function into a universally quantified property */
def forAll[A1, A2, A3, A4, A5, P](
f: (A1, A2, A3, A4, A5) => P
)(implicit
p: P => Prop,
a1: Arbitrary[A1],
s1: Shrink[A1],
pp1: A1 => Pretty,
a2: Arbitrary[A2],
s2: Shrink[A2],
pp2: A2 => Pretty,
a3: Arbitrary[A3],
s3: Shrink[A3],
pp3: A3 => Pretty,
a4: Arbitrary[A4],
s4: Shrink[A4],
pp4: A4 => Pretty,
a5: Arbitrary[A5],
s5: Shrink[A5],
pp5: A5 => Pretty
): Prop = forAll((a: A1) => forAll(f(a, _: A2, _: A3, _: A4, _: A5)))
/** Converts a function into a universally quantified property */
def forAll[A1, A2, A3, A4, A5, A6, P](
f: (A1, A2, A3, A4, A5, A6) => P
)(implicit
p: P => Prop,
a1: Arbitrary[A1],
s1: Shrink[A1],
pp1: A1 => Pretty,
a2: Arbitrary[A2],
s2: Shrink[A2],
pp2: A2 => Pretty,
a3: Arbitrary[A3],
s3: Shrink[A3],
pp3: A3 => Pretty,
a4: Arbitrary[A4],
s4: Shrink[A4],
pp4: A4 => Pretty,
a5: Arbitrary[A5],
s5: Shrink[A5],
pp5: A5 => Pretty,
a6: Arbitrary[A6],
s6: Shrink[A6],
pp6: A6 => Pretty
): Prop = forAll((a: A1) => forAll(f(a, _: A2, _: A3, _: A4, _: A5, _: A6)))
/** Converts a function into a universally quantified property */
def forAll[A1, A2, A3, A4, A5, A6, A7, P](
f: (A1, A2, A3, A4, A5, A6, A7) => P
)(implicit
p: P => Prop,
a1: Arbitrary[A1],
s1: Shrink[A1],
pp1: A1 => Pretty,
a2: Arbitrary[A2],
s2: Shrink[A2],
pp2: A2 => Pretty,
a3: Arbitrary[A3],
s3: Shrink[A3],
pp3: A3 => Pretty,
a4: Arbitrary[A4],
s4: Shrink[A4],
pp4: A4 => Pretty,
a5: Arbitrary[A5],
s5: Shrink[A5],
pp5: A5 => Pretty,
a6: Arbitrary[A6],
s6: Shrink[A6],
pp6: A6 => Pretty,
a7: Arbitrary[A7],
s7: Shrink[A7],
pp7: A7 => Pretty
): Prop = forAll((a: A1) => forAll(f(a, _: A2, _: A3, _: A4, _: A5, _: A6, _: A7)))
/** Converts a function into a universally quantified property */
def forAll[A1, A2, A3, A4, A5, A6, A7, A8, P](
f: (A1, A2, A3, A4, A5, A6, A7, A8) => P
)(implicit
p: P => Prop,
a1: Arbitrary[A1],
s1: Shrink[A1],
pp1: A1 => Pretty,
a2: Arbitrary[A2],
s2: Shrink[A2],
pp2: A2 => Pretty,
a3: Arbitrary[A3],
s3: Shrink[A3],
pp3: A3 => Pretty,
a4: Arbitrary[A4],
s4: Shrink[A4],
pp4: A4 => Pretty,
a5: Arbitrary[A5],
s5: Shrink[A5],
pp5: A5 => Pretty,
a6: Arbitrary[A6],
s6: Shrink[A6],
pp6: A6 => Pretty,
a7: Arbitrary[A7],
s7: Shrink[A7],
pp7: A7 => Pretty,
a8: Arbitrary[A8],
s8: Shrink[A8],
pp8: A8 => Pretty
): Prop = forAll((a: A1) => forAll(f(a, _: A2, _: A3, _: A4, _: A5, _: A6, _: A7, _: A8)))
/** Ensures that the property expression passed in completes within the given space of time.
*/
def within(maximumMs: Long)(wrappedProp: => Prop): Prop = {
@tailrec def attempt(prms: Gen.Parameters, endTime: Long): Result = {
val result = wrappedProp.apply(prms)
if (System.currentTimeMillis > endTime) {
(if (result.failure) result else Result(status = False)).label("Timeout")
} else {
if (result.success) result
else attempt(prms, endTime)
}
}
Prop.apply(prms => attempt(prms, System.currentTimeMillis + maximumMs))
}
}