diff options
Diffstat (limited to 'src/partest-extras/scala/org/scalacheck/Prop.scala')
-rw-r--r-- | src/partest-extras/scala/org/scalacheck/Prop.scala | 953 |
1 files changed, 953 insertions, 0 deletions
diff --git a/src/partest-extras/scala/org/scalacheck/Prop.scala b/src/partest-extras/scala/org/scalacheck/Prop.scala new file mode 100644 index 0000000000..6b607002fd --- /dev/null +++ b/src/partest-extras/scala/org/scalacheck/Prop.scala @@ -0,0 +1,953 @@ +/*-------------------------------------------------------------------------*\ +** ScalaCheck ** +** Copyright (c) 2007-2014 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 util.{Pretty, FreqMap, Buildable, ConsoleReporter} +import scala.annotation.tailrec + +trait Prop { + + import Prop.{Result, Proof, True, False, Exception, Undecided, + provedToTrue, secure, mergeRes} + import Gen.Parameters + + def apply(prms: Parameters): Result + + def map(f: Result => Result): Prop = Prop(prms => f(this(prms))) + + def flatMap(f: Result => Prop): Prop = Prop(prms => f(this(prms))(prms)) + + // TODO In 1.12.0, make p call-by-name, and remove the calls to secure() + // in the methods that use combine() + 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. */ + 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. 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. 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 checks this property with specified minimal + * number of successful test and the given testing parameters, and + * reports the result on the console. If you need to get the results + * from the test use the `check` methods in [[org.scalacheck.Test]] + * instead. */ + @deprecated("Use check(prms.withMinSuccessfulTests(n)) instead", "1.11.2") + def check(minSuccessfulTests: Int, prms: Test.Parameters): Unit = check( + prms.withMinSuccessfulTests(minSuccessfulTests) + ) + + /** Convenience method that checks this property with specified minimal + * number of successful test and reports the result on the console. + * If you need to get the results from the test use + * the `check` methods in [[org.scalacheck.Test]] instead. */ + @deprecated("Use check(_.withMinSuccessfulTests(n)) instead", "1.11.2") + def check(minSuccessfulTests: Int): Unit = check( + _.withMinSuccessfulTests(minSuccessfulTests) + ) + + /** The logic for main, separated out to make it easier to + * avoid System.exit calls. Returns exit code. + */ + def mainRunner(args: Array[String]): Int = { + Test.parseParams(args) match { + case Some(params) => + if (Test.check(params, this).passed) 0 + else 1 + case None => + println("Incorrect options") + -1 + } + } + + /** Whether main should call System.exit with an exit code. + * Defaults to true; override to change. */ + def mainCallsExit = true + + /** Convenience method that makes it possible to use this property + * as an application that checks itself on execution */ + def main(args: Array[String]): Unit = { + val code = mainRunner(args) + if (mainCallsExit && code != 0) + System exit code + } + + /** 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(secure(p))(_ && _) + + /** Returns a new property that holds if either this + * or the given property (or both) hold. */ + def ||(p: => Prop) = combine(secure(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(secure(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.toString.drop(1)) + + /** Put a label on the property to make test reports clearer */ + def |:(l: Symbol) = label(l.toString.drop(1)) + +} + +object Prop { + + import Gen.{value, fail, frequency, oneOf, Parameters} + import Arbitrary.{arbitrary} + import Shrink.{shrink} + + // Types + + /** A property argument */ + case class Arg[+T]( + label: String, + arg: T, + shrinks: Int, + origArg: T, + prettyArg: Pretty, + prettyOrigArg: Pretty + ) + + object Result { + @deprecated("Will be removed in 1.12.0", "1.11.2") + def apply(st: Status): Result = Result(status = st) + @deprecated("Will be removed in 1.12.0", "1.11.2") + def merge(x: Result, y: Result, status: Status) = mergeRes(x,y,status) + } + + 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 + case (_, False) => r + } + + 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: Parameters => Result): Prop = new Prop { + def apply(prms: Parameters) = 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(prms => 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 <% Pretty](x: => T) { + /** 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.BooleanOperators]] to make the operators available. */ + 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 <% Pretty](x: => 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. */ + implicit def BooleanOperators(b: => Boolean) = 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 to 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 to 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]]) = atLeastOne(gs.map(_ == fail):_*) + + /** A property that holds iff none of the given generators + * fails generating a value */ + def noneFailing[T](gs: Seq[Gen[T]]) = all(gs.map(_ !== 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 */ + def collect[T, P <% Prop](f: T => P): T => Prop = t => Prop { prms => + val prop = 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 */ + def secure[P <% Prop](p: => P): Prop = + try (p: Prop) catch { case e: Throwable => exception(e) } + + /** 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) + + /** 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 { prms => + val gr = g.doApply(prms) + gr.retrieve match { + case None => undecided(prms) + case Some(x) => + val p = secure(f(x)) + val labels = gr.labels.mkString(",") + val r = p(prms).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 { prms => + val gr = g1.doApply(prms) + gr.retrieve match { + case None => undecided(prms) + case Some(x) => + val p = secure(f(x)) + val labels = gr.labels.mkString(",") + provedToTrue(p(prms)).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 { prms => + + val gr = g.doApply(prms) + val labels = gr.labels.mkString(",") + + def result(x: T) = { + val p = secure(pv(f(x))) + provedToTrue(p(prms)) + } + + /** Returns the first failed result in Left or success in Right */ + def getFirstFailure(xs: Stream[T]): Either[(T,Result),(T,Result)] = { + assert(!xs.isEmpty, "Stream cannot be empty") + val results = xs.map(x => (x, result(x))) + results.dropWhile(!_._2.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).filter(gr.sieve) + val res = r.addArg(Arg(labels,x,shrinks,orig,pp(x),pp(orig))) + if(xs.isEmpty) res else getFirstFailure(xs) match { + case Right((x2,r2)) => 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) r.addArg(Arg(labels,x,0,x,pp(x),pp(x))) + else shrinker(x,r,0,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, shrink[T1])(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],shrink[A1])(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 = new Prop { + @tailrec private def attempt(prms: 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) + } + } + def apply(prms: Parameters) = attempt(prms, System.currentTimeMillis + maximumMs) + } +} |