
org.specs.matcher.ScalacheckMatchers.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of specs_2.8.1_java_1.5
Show all versions of specs_2.8.1_java_1.5
specs is a Behaviour-Driven-Design
framework
The newest version!
/**
* Copyright (c) 2007-2010 Eric Torreborre
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
* the Software. Neither the name of specs nor the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written permission.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package org.specs.matcher
import org.scalacheck.{Gen, Prop, Arg, Test, Arbitrary, Shrink}
import org.scalacheck.util.StdRand
import org.scalacheck.Prop._
import org.scalacheck.Test.{Status, Params, Proved, Passed, Failed, Exhausted, GenException, PropException, Result}
import org.scalacheck.Pretty._
import org.scalacheck.Pretty
import org.scalacheck.ConsoleReporter._
import scala.collection.Map
import org.specs.io.ConsoleOutput
import org.specs.matcher._
import org.specs.util.ExtendedFunctions._
import org.specs.matcher.MatcherUtils.q
import org.specs.execute._
import org.specs.specification._
/**
* The ScalaCheckMatchers
trait provides matchers which allow to
* assess properties multiple times with generated data.
* @see the ScalaCheck project
*/
trait ScalaCheckMatchers extends ConsoleOutput with ScalaCheckFunctions with ScalaCheckParameters with SuccessValues with ExpectationsListener { outer =>
/**
* This implicit value is useful to transform the SuccessValue returned by matchers to properties.
* More specifically, this allows to write expectations as properties:
* (1 + 1) must_== 2 will return a SuccessValue if it is not throwing a FailureException.
* That success value can then be considered as a property in an example:
*
* { (1 + 1) must_== 2 } must pass
*
* @see Expectable
*/
implicit val successValueToProp: SuccessValue => Prop = (s: SuccessValue) => Prop.passed
/**
* default parameters. Uses ScalaCheck default values and doesn't print anything to the console
*/
implicit def defaultParameters = new Parameters(setParams(Nil))
/** default parameters to display pretty messages */
val defaultPrettyParams = Pretty.defaultParams
/**
* Matches ok if the function T => Boolean
returns true
for any generated value
* Usage: function must pass(generated_values)
* @param params are the given by the implicit default parameters of ScalaCheck
*/
def pass[T](g: Gen[T])(implicit params: Parameters) = new Matcher[T => Boolean]() {
def apply(f: => (T => Boolean)) = checkFunction(g)(f)(params)
}
implicit def booleanFunctionToPropFunction[T](f: T => Boolean): T => Prop = (t: T) => {
if (f(t)) proved else falsified
}
/**
* This implicit definition and associated ForAll class allows to write
* { function }.forAll must pass
*/
implicit def toProp[T](f: T => Boolean): ForAll[T] = new ForAll(f)
class ForAll[T](f: T => Boolean) {
def forAll(implicit a: Arbitrary[T], s: Shrink[T]) = Prop.forAll(f)
}
/**
* Matches ok if the function T => Prop
returns atrue
Property for any generated value
* Usage: generated_values must pass(function)
*/
def pass[T](f: T => Prop)(implicit params: Parameters) = new Matcher[Gen[T]](){
def apply(g: => Gen[T]) = checkProperty(forAllProp(g)(f))(params)
}
/**
* Matches ok if the function T => Boolean
returns true
for any generated value
* Usage: generated_values must pass(function)
*/
def pass[T](f: T => SuccessValue)(implicit params: Parameters) = new GenMatcher[T](f)(params)
/**
* Matches ok if the partial function { case t => exp }
returns true
for any generated value
* exp is an expression returning a SuccessValue, so that any specs expectation can be used here, like a must_== b
* Usage: generated_values must validate(partial function)
*/
def validate[T](f: Function[T, SuccessValue])(implicit params: Parameters) = new GenMatcher[T](t => f.applySafely(t).getOrElse(false))(params)
/** transforms a boolean to a SuccessValue so that Partial functions returning booleans can be accepted by the validate matcher */
implicit def booleanToSuccessValue(b: => Boolean) = new SuccessValue { if (!b) throw new FailureException("false") }
/** adds a validates method to a generator so that gen validates partialFunction can be written */
implicit def aGen[T](g: Gen[T]) = new AGen(g)
class AGen[T](g: Gen[T]) {
def validates(f: Function[T, SuccessValue])(implicit params: Parameters) = outer.validate(f)(params).apply(g)
}
/**
* workaround class used to avoid an ambiguous definition of the pass method with
* f: T => Boolean and f: T => SuccessValue
*/
class GenMatcher[T](f: T => SuccessValue)(implicit params: Parameters) extends Matcher[Gen[T]] {
def apply(g: => Gen[T]) = checkFunction(g){(t: T) => f(t); true}(params)
}
/**
* Matches ok if the property
is proved for any generated value
* Usage: generated_values must pass(property)
*/
def pass[T](prop: Prop)(implicit params: Parameters) = new Matcher[Gen[T]](){
def apply(g: => Gen[T]) = checkProperty(forAllProp(g)(a => prop))(params)
}
/**
* Matches ok if the property
is proved for any generated value
* Usage: property must pass
*/
def pass(implicit params: Parameters) = new Matcher[Prop](){
def apply(p: => Prop) = checkProperty(p)(params)
}
private [matcher] def checkFunction[T](g: Gen[T])(f: T => Boolean)(p: Parameters) = {
// create a scalacheck property which states that the function must return true
// for each generated value
val prop = forAllProp(g)(a => if (f(a)) proved else falsified)
checkProperty(prop)(p)
}
/**
* checks if the property is true for each generated value, and with the specified
* generation parameters p
. p
is transformed into a scalacheck parameters
* and indicates if the generation should be verbose or not
*/
private [matcher] def checkProperty(prop: Prop)(p: Parameters) = {
checkScalaCheckProperty(prop)(Params(p(minTestsOk), p(maxDiscarded), p(minSize), p(maxSize), StdRand, p(workers), p(wrkSize)), p.verbose)
}
/**
* checks if the property is true for each generated value, and with the specified
* scalacheck parameters. If verbose is true, then print the results on the console
*/
private [matcher] def checkScalaCheckProperty(prop: Prop)(params: Params, verbose: Boolean) = {
// will print the result of each test if verbose = true
def printResult(succeeded: Int, discarded: Int): Unit = {
if (!verbose) return
if (discarded == 0)
printf("\rPassed %d tests", succeeded)
else
printf("\rPassed %d tests; %d discarded", succeeded, discarded)
flush
}
// check the property
def propToCheck = if (!shouldCountExpectations) prop else (prop && Prop.forAll((t: Boolean) => true.isExpectation))
val results = checkProp(params, propToCheck, printResult)
// display the final result if verbose = true
if (verbose) {
val s = prettyTestRes(results)(defaultPrettyParams)
printf("\r%s %s%s\n", if (results.passed) "+" else "!", s, List.fill(70 - s.length)(" ").mkString(""))
}
results match {
case Result(Proved(as), succeeded, discarded, _) => (true, noCounterExample(succeeded), "A counter-example was found " + afterNTries(succeeded))
case Result(Passed, succeeded, discarded, _) => (true, noCounterExample(succeeded), "A counter-example was found " + afterNTries(succeeded))
case r@Result(GenException(e), n, _, _) => (false, noCounterExample(n), prettyTestRes(r)(defaultPrettyParams))
case r@Result(Exhausted, n, _, _) => (false, noCounterExample(n), prettyTestRes(r)(defaultPrettyParams))
case Result(Failed(args, labels), n, _, _) =>
(false, noCounterExample(n), "A counter-example is "+counterExample(args)+" (" + afterNTries(n) + afterNShrinks(args) + ")" + failedLabels(labels))
case Result(PropException(args, FailureException(ex), labels), n, _, _) =>
(false, noCounterExample(n), "A counter-example is "+counterExample(args)+": " + ex + " ("+afterNTries(n)+")"+ failedLabels(labels))
case r@Result(PropException(m, ex, labels), n, _, _) =>
(false, noCounterExample(n), prettyTestRes(r)(defaultPrettyParams)+ failedLabels(labels))
}
}
// depending on the result, return the appropriate success status and messages
// the failure message indicates a counter-example to the property
private [matcher] def noCounterExample(n: Int) = "The property passed without any counter-example " + afterNTries(n)
private [matcher] def afterNTries(n: Int) = "after " + (if (n == 1) n + " try" else n + " tries")
private [matcher] def afterNShrinks(args: List[Arg[_]]) = {
if (args.forall(_.shrinks == 0))
""
else
args.map { arg =>
if (arg.origArg != arg.arg)
q(arg.origArg) +" -> " + q(arg.arg)
else
" = "
}.mkString(" - shrinked (", ",", ")")
}
private [matcher] def counterExample(args: List[Arg[_]]) = {
if (args.size == 1)
args.map(a => if (a.arg == null) "null" else a.arg.toString).mkString("'", "", "'")
else if (args.exists(_.arg.toString.isEmpty))
args.map(_.arg).mkString("['", "', '", "']")
else
args.map(_.arg).mkString("[", ", ", "]")
}
private [matcher] def failedLabels(labels: Set[String]) = {
if (labels.isEmpty)
""
else
labels.mkString("\nlabels of failing property: ", ", ", "\n")
}
}
/**
* This trait is used to facilitate testing by mocking ScalaCheck functionalities
*/
trait ScalaCheckFunctions {
def checkProp(params: Params, prop: Prop, printResult: (Int, Int) => Unit) = Test.check(params, prop, printResult)
def forAllProp[A,P](g: Gen[A])(f: A => Prop): Prop = Prop.forAll(g)(f)
}
/**
* This trait provides generation parameters to use with the ScalaCheckMatchers
*/
trait ScalaCheckParameters {
/**
* Values which can be used as Symbol aliases to specify ScalaCheck parameters
* The naming is a bit different, in order to keep short names for frequent use cases
* - minTestsOk == minSuccessfulTests
*
- maxDiscarded == maxDiscardedTests
*
- minSize and maxSize keep their name
*/
val (minSize, maxSize, maxDiscarded, minTestsOk, workers, wrkSize) = ('minSize, 'maxSize, 'maxDiscarded, 'minTestsOk, 'workers, 'wrkSize)
/** This variable is used to track if we need to add an expectation each time a property is evaluated */
private var countExpectations = true
/** declare that an expectation should be added each time a property is evaluated (default) */
def expectProperties() = { countExpectations = true; this }
/** declare that no expectation should be added each time a property is evaluated */
def dontExpectProperties() = { countExpectations = false; this }
private [matcher] def shouldCountExpectations = countExpectations
/**
* Default values for ScalaCheck parameters
*/
def defaultValues = Map(minTestsOk->100, maxDiscarded ->500, minSize->0, maxSize->100, workers->1, wrkSize->20)
/**
* This object is used to set parameters but nothing will be printed to the console
* Usage:
* generated_values must pass { v =>
* property(v) mustBe ok
* }(set(minTestsOk->15, maxDiscarded->20))
*/
object set extends Parameters(setParams(Nil)) {
def apply(p: (Symbol, Int)*) = new Parameters(setParams(p))
}
/**
* Those parameters will print the result on the console and use the default settings, or specified parameters
* Usage:
* generated_values must pass { v =
* property(v) mustBe ok
* }(display)
*
* or
*
* generated_values must pass { v =>
* property(v) mustBe ok
* }(display(minTestsOk->15, maxDiscarded->20))
*/
object display extends Parameters(setParams(Nil)) {
def apply(p: (Symbol, Int)*) = new Parameters(setParams(p)) { override def verbose = true }
override def verbose = true
}
/**
* This function transform the varargs parameters into a Map with default values
* if some expected values are not provided by the user
*/
def setParams(p: Seq[(Symbol, Int)]): Map[Symbol, Int] = {
var params: Map[Symbol, Int] = new scala.collection.immutable.HashMap[Symbol, Int].withDefault(defaultValues)
p foreach { pair: (Symbol, Int) =>
// this is a useful check in case of print(null) or set(null)
if (pair == null || pair._1 == null)
throw new RuntimeException("null values are not accepted in scalacheck parameters: " + q(pair))
else {
val (s, i) = pair
params = params + Pair(s, i)
}
}
params
}
}
/**
* This class is the base class for the display and set case classes.* It contains a Map of generation parameters and indicates if the generation * must be verbose. */ case class Parameters(params: Map[Symbol, Int]) { def apply(s: Symbol) = params(s) def verbose = false }
© 2015 - 2025 Weber Informatics LLC | Privacy Policy