All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.specs.specification.Expectable.scala Maven / Gradle / Ivy

/**
 * Copyright (c) 2007-2009 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.specification
import org.specs.matcher._
import org.specs.matcher.Matchers._
import org.specs.util.ExtendedThrowable._
import org.specs.Sugar._
import org.specs.execute._

/**
 * An expectable is an object supporting the execution of expectations through matchers.
 *   thisExpectable must passMatcher
 * 
* It can be optionally related to an example when created for an anonymous example. * Otherwise it just fires a FailureException when failing:
 * object spec extends Specification {
 *   // is automatically related to an anonymous example
 *   // it will be executed only once the example is executed
 *   // @see org.specs.specification.ExpectableFactory
 *   // @see org.specs.specification.ExampleExpectationsListener
 *   1 must_== 1
 *
 *   // in that case, no example is set but during the execution of the "in" part
 *   // the failure exception will be caught by the example and stored
 *   "this example fails" in { 1 must_== 0 }
 * }
 * object test extends SpecsMatchers {
 *   // this expectable is not related to any example and executes right away throwing an exception if failing
 *   1 must_== 1
 * }
 * 
*/ class Expectable[T](value: => T) { /** related example. */ private var example: Option[Examples] = None /** function used to display success values as a result of a match. By default nothing is displayed. */ private var successValueToString: SuccessValue => String = s => "" /** the listener will be called for every match to register a new Expectation. */ private var expectationsListener: Option[ExampleExpectationsListener] = None /** the function creating failure exceptions. */ private var failureFactory: FailureFactory = new SpecsFailureFactory {} /** * stores a precise description of the thing being expected. * This description is meant to be passed to the matcher for better failure reporting. */ protected var description: Option[String] = None /** state variable storing how the next matcher should be applied, negated or not */ private var nextMatcherMustBeNegated = false /** set the state variable declaring that the next match should be negated, in the case of an xor combination to force a fail for example */ private[specification] def nextSignificantMatchMustBeNegated() = { nextMatcherMustBeNegated = true; this } /** previous previous messages in the case of or-ed matchers*/ private var matchMessages: List[String] = Nil /** add a previous message in the case of or-ed matchers*/ protected def addMatchMessage(m: String): this.type = { matchMessages = matchMessages ::: List(m); this } /** * Apply a matcher for this expectable value. * * Execute the matcher directly or add it to its related example for execution. * It either throws a FailureException or return a SuccessValue object. * * The expectation listener gets notified of a new expectation with a fresh copy of this expectable. * The matcher gets */ private[specification] def applyMatcher(m: => Matcher[T]): Result[T] = { val failureTemplate = FailureException("") val matcher = m matcher match { case okMatcher: org.specs.matcher.OkWordMatcher[_] => return new Result(this, successValueToString) case notMatcher: org.specs.matcher.NotMatcher[_] => { nextMatcherMustBeNegated = true return new Result(this, successValueToString) } case _ => expectationsListener.map(_.addExpectation(Some(this))) } def executeMatch = { matcher.setDescription(description) val (result, okMessage, koMessage) = { if (nextMatcherMustBeNegated) { nextMatcherMustBeNegated = false matcher.not.apply(value) } else matcher.apply(value) } result match { case false => { addMatchMessage(koMessage) failureFactory.createFailure(makeFailureMessage, new Result(this, successValueToString)).throwWithStackTraceOf(failureTemplate.removeTracesWhileNameMatches("(Expectable.scala|Matchers.scala)")) } case _ => { addMatchMessage(okMessage) new Result(this, successValueToString) } } } example match { case None => executeMatch case Some(e) => { var res = new Result(this, successValueToString) e.specifyExample(res = executeMatch) res } } } /** create the failure message from all previous match messages */ private def makeFailureMessage: String = { if (matchMessages.size == 0) "" else if (matchMessages.size == 1) matchMessages(0) else matchMessages.mkString(" and ") } /** * Set a specific example to hold the results of this matcher */ def setExample[T](ex: Examples) = example = Some(ex) /** setter for the expectation listener. */ def setExpectationsListener(listener: ExampleExpectationsListener): this.type = { expectationsListener = Some(listener) this } /** setter for the failure factory. */ def setFailureFactory(factory: FailureFactory): this.type = { failureFactory = factory this } /** * Set a new function to render success values */ def setSuccessValueToString(f: SuccessValue =>String) = successValueToString = f } /** * The Expect class adds matcher methods to objects which are being specified
* Usage: new Expect(value, example) must beMatching(otherValue)

* * An assert is created with its parent Example in order to register failures * and errors if a matcher is not ok * */ class Expectation[T](value: => T) extends Expectable[T](value) { override def toString() = value.toString def createClone = new Expectation(value) /** set a better description on the value. */ def aka(desc: String): this.type = { description = Some(desc); this } /** * applies a matcher to the current value and throw a failure is the result is not true */ def must(m: => Matcher[T]) = applyMatcher(m) /** * applies the negation of a matcher */ def mustNot(m: => Matcher[T]) = must(m.not) /** alias for must verify(f) */ def mustVerify(f: T => Boolean) = must(verify(f)) /** alias for mustVerify(f) */ def verifies(f: T => Boolean) = mustVerify(f) /** alias for must be(other) */ def mustBe(otherValue: Any) = must(be(otherValue)) /** alias for must be(other) */ def mustEq(otherValue: Any) = must(be(otherValue)) /** alias for must notEq(other) */ def mustNotBe(otherValue: Any) = must(notEq(otherValue)) /** alias for must notEq(other) */ def mustNotEq(otherValue: Any) = mustNotBe(otherValue) /** alias for must is_!=(other) */ def must_!=(otherValue: Any)(implicit details: Detailed) = must(is_!=(otherValue)(details)) /** alias for must is_==(other) */ def must_==(otherValue: Any)(implicit details: Detailed) = { must(is_==(otherValue)(details)) } /** alias for must is_==(other) */ def mustEqual(otherValue: Any)(implicit details: Detailed) = must(is_==(otherValue)(details)) } /** Specialized expectable class with string matchers aliases */ class StringExpectable[A <: String](value: => A) extends Expectable[A](value) { def createClone = new StringExpectable(value) /** alias for must(beMatching(a)) */ def mustMatch(a: String) = applyMatcher(beMatching(a)) /** alias for must(not(beMatching(a))) */ def mustNotMatch(a: String) = applyMatcher(not(beMatching(a))) /** alias for must(beEqualToIgnoringCase(a)) */ def must_==/(a: String) = applyMatcher(beEqualToIgnoringCase(a)) /** alias for must(notBeEqualToIgnoringCase(a)) */ def must_!=/(a: String) = applyMatcher(notBeEqualToIgnoringCase(a)) } /** Specialized expectable class with iterable matchers aliases */ class IterableExpectable[I <: AnyRef](value: =>Iterable[I]) extends Expectable[Iterable[I]](value) { def createClone = new IterableExpectable(value) /** alias for must(exist(function(_)) */ def mustHave(function: I => Boolean) = applyMatcher(have(function((_:I)))) /** alias for must(notExist(function(_)) */ def mustNotHave(function: I => Boolean) = applyMatcher(notHave(function((_:I)))) /** * alias for must(exist(function(_)) * @deprecated use mustHave instead */ def mustExist(function: I => Boolean) = mustHave(function) /** * alias for must(notExist(function(_)) * @deprecated use mustNotHave instead */ def mustNotExist(function: I => Boolean) = mustNotHave(function) /** alias for must(contain(a)) */ def mustContain(elem: I) = applyMatcher(contain(elem)) /** alias for must(notContain(a)) */ def mustNotContain(elem: I) = applyMatcher(notContain(elem)) } /** Specialized expectable class with iterable[String] matchers aliases */ class IterableStringExpectable(value: =>Iterable[String]) extends Expectable[Iterable[String]](value) { def createClone = new IterableStringExpectable(value) /** alias for must(containMatch(pattern)) */ def mustContainMatch(pattern: String) = applyMatcher(containMatch(pattern)) /** alias for must(notContainMatch(pattern)) */ def mustNotContainMatch(pattern: String) = applyMatcher(notContainMatch(pattern)) /** alias for must(containMatch(pattern)) */ def mustHaveMatch(pattern: String) = applyMatcher(containMatch(pattern)) /** alias for must(notContainMatch(pattern)) */ def mustNotHaveMatch(pattern: String) = applyMatcher(notContainMatch(pattern)) /** * alias for must(containMatch(pattern)) * @deprecated: use mustContainMatch or mustHaveMatch instead */ def mustExistMatch(pattern: String) = applyMatcher(containMatch(pattern)) /** * alias for must(notContainMatch(pattern)) * @deprecated: use mustNotContainMatch or mustNotHaveMatch instead */ def mustNotExistMatch(pattern: String) = applyMatcher(notContainMatch(pattern)) } /** * By default the result value of an expectable expression doesn't output anything when * toString is called. */ /** * This trait transforms SuccessValue objects to a Boolean value if it is necessary, for example in * ScalaCheck properties. */ trait SuccessValues { /** transforms a SuccessValue to a boolean */ implicit def successValueToBoolean(s: SuccessValue) = true /** by default a SuccessValue is "silent" */ def successValueToString(s: SuccessValue) = "" /** * this implicit def allows a result to be or-ed with a matcher. */ } trait OrResults { implicit def toOrResult[T](r: =>Result[T]) = new OrResult(r) /** * this class allows a result to be or-ed with a matcher so that * if the result fails, the next matcher will still be tried */ class OrResult[T](r: =>Result[T]) { def or(m: => Matcher[T]): Result[T] = { var result: Result[T] = null try { result = r result.setAlreadyOk() } catch { case f: HasResult[_] => return f.asInstanceOf[HasResult[T]].result.matchWith(m) case t => throw t } result } def xor(m: => Matcher[T]): Result[T] = { var result: Result[T] = null try { result = r // if the first result is ok, then the next matcher must fail try { result.nextSignificantMatchMustFail().matchWith(m) } catch { case f: HasResult[_] => return f.asInstanceOf[HasResult[T]].result.matchWith(m) case t => throw t } } catch { case f: HasResult[_] => return f.asInstanceOf[HasResult[T]].result.matchWith(m) case t => throw t } result } } } /** * Special failure exception carrying a Result object, carrying an Expectable. * This Exception is necessary to handle the "OR" case "value must be equalTo(bad) or be equalTo(good)" * where the first match is not ok. */ class FailureExceptionWithResult[T](m: String, val result: Result[T]) extends FailureException(m) with HasResult[T] /** value returned by an expectable whose string representation can vary. */ trait SuccessValue





© 2015 - 2025 Weber Informatics LLC | Privacy Policy