org.scalatest.matchers.MatchResult.scala Maven / Gradle / Ivy
/* * Copyright 2001-2013 Artima, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.scalatest.matchers import java.util.concurrent.atomic.AtomicReference import org.scalactic.Prettifier import org.scalatest.Resources /** * The result of a match operation, such as one performed by a
or. * For example: * * *Matcher
or *BeMatcher
, which * contains one field that indicates whether the match succeeded, four fields that provide * raw failure messages to report under different circumstances, four fields providing * arguments used to construct the final failure messages using raw failure messages * and aPrettifier
. Using the default constructor, * failure messages will be constructed lazily (when required). * ** A
* *MatchResult
'smatches
field indicates whether a match succeeded. If it succeeded, *matches
will betrue
. * There are four methods,failureMessage
,negatedfailureMessage
,midSentenceFailureMessage
* andnegatedMidSentenceFailureMessage
that can be called to get final failure message strings, one of which will be * presented to the user in case of a match failure. If a match succeeds, none of these strings will be used, because no failure * message will be reported (i.e., because there was no failure to report). If a match fails (matches
isfalse
), * thefailureMessage
(ormidSentenceFailure
—more on that below) will be reported to help the user understand what went wrong. *Understanding
* *negatedFailureMessage
* The
* *negatedFailureMessage
exists so that it can become thefailureMessage
if the matcher is inverted, * which happens, for instance, if it is passed tonot
. Here's an example: ** val equalSeven = equal (7) * val notEqualSeven = not (equalSeven) ** ** The
* *Matcher[Int]
that results from passing 7 toequal
, which is assigned to theequalSeven
* variable, will compareInt
s passed to its *apply
method with 7. If 7 is passed, theequalSeven
match will succeed. If anything other than 7 is passed, it * will fail. By contrast, thenotEqualSeven
matcher, which results from passingequalSeven
tonot
, does * just the opposite. If 7 is passed, thenotEqualSeven
match will fail. If anything other than 7 is passed, it will succeed. ** For example, if 8 is passed,
* *equalSeven
'sMatchResult
will contain: ** expression: equalSeven(8) * matches: false * failureMessage: 8 did not equal 7 * negatedFailureMessage: 8 equaled 7 ** ** Although the
* *negatedFailureMessage
is nonsensical, it will not be reported to the user. Only thefailureMessage
, * which does actually explain what caused the failure, will be reported by the user. If you pass 8 tonotEqualSeven
'sapply
* method, by contrast, thefailureMessage
andnegatedFailureMessage
will be: ** expression: notEqualSeven(8) * matches: true * failureMessage: 8 equaled 7 * negatedFailureMessage: 8 did not equal 7 ** ** Note that the messages are swapped from the
* *equalSeven
messages. This swapping was effectively performed by thenot
matcher, * which in addition to swapping thefailureMessage
andnegatedFailureMessage
, also inverted the *matches
value. Thus when you pass the same value to bothequalSeven
andnotEqualSeven
thematches
* field of oneMatchResult
will betrue
and the otherfalse
. Because the *matches
field of theMatchResult
returned bynotEqualSeven(8)
istrue
, * the nonsensicalfailureMessage
, "8 equaled 7
", will not be reported to the user. ** If 7 is passed, by contrast, the
* *failureMessage
andnegatedFailureMessage
ofequalSeven
* will be: ** expression: equalSeven(7) * matches: true * failureMessage: 7 did not equal 7 * negatedFailureMessage: 7 equaled 7 ** ** In this case
* *equalSeven
'sfailureMessage
is nonsensical, but because the match succeeded, the nonsensical message will * not be reported to the user. * If you pass 7 tonotEqualSeven
'sapply
* method, you'll get: ** expression: notEqualSeven(7) * matches: false * failureMessage: 7 equaled 7 * negatedFailureMessage: 7 did not equal 7 ** ** Again the messages are swapped from the
* *equalSeven
messages, but this time, thefailureMessage
makes sense * and explains what went wrong: thenotEqualSeven
match failed because the number passed did in fact equal 7. Since * the match failed, this failure message, "7 equaled 7
", will be reported to the user. *Understanding the "
* *midSentence
" messages* When a ScalaTest matcher expression that involves
and
oror
fails, the failure message that * results is composed from the failure messages of the left and right matcher operatnds toand
or* 8 should (equal (7) or equal (9)) ** ** This above expression would fail with the following failure message reported to the user: *
* ** 8 did not equal 7, and 8 did not equal 9 ** ** This works fine, but what if the failure messages being combined begin with a capital letter, such as: *
* ** The name property did not equal "Ricky" ** ** A combination of two such failure messages might result in an abomination of English punctuation, such as: *
* ** The name property did not equal "Ricky", and The name property did not equal "Bobby" ** ** Because ScalaTest is an internationalized application, taking all of its strings from a property file * enabling it to be localized, it isn't a good idea to force the first character to lower case. Besides, * it might actually represent a String value which should stay upper case. The
* *midSentenceFailureMessage
* exists for this situation. If the failure message is used at the beginning of the sentence,failureMessage
* will be used. But if it appears mid-sentence, or at the end of the sentence,midSentenceFailureMessage
* will be used. Given these failure message strings: ** failureMessage: The name property did not equal "Bobby" * midSentenceFailureMessage: the name property did not equal "Bobby" ** ** The resulting failure of the
* *or
expression involving to matchers would make any English teacher proud: ** The name property did not equal "Ricky", and the name property did not equal "Bobby" ** * @param matches indicates whether or not the matcher matched * @param rawFailureMessage raw failure message to report if a match fails * @param rawNegatedFailureMessage raw message with a meaning opposite to that of the failure message * @param rawMidSentenceFailureMessage raw failure message suitable for appearing mid-sentence * @param rawMidSentenceNegatedFailureMessage raw negated failure message suitable for appearing mid-sentence * @param failureMessageArgs arguments for constructing failure message to report if a match fails * @param negatedFailureMessageArgs arguments for constructing message with a meaning opposite to that of the failure message * @param midSentenceFailureMessageArgs arguments for constructing failure message suitable for appearing mid-sentence * @param midSentenceNegatedFailureMessageArgs arguments for constructing negated failure message suitable for appearing mid-sentence * * @author Bill Venners * @author Chee Seng */ sealed case class MatchResult( matches: Boolean, rawFailureMessage: String, rawNegatedFailureMessage: String, rawMidSentenceFailureMessage: String, rawMidSentenceNegatedFailureMessage: String, failureMessageArgs: IndexedSeq[Any], negatedFailureMessageArgs: IndexedSeq[Any], midSentenceFailureMessageArgs: IndexedSeq[Any], midSentenceNegatedFailureMessageArgs: IndexedSeq[Any] ) { /** * Constructs a newMatchResult
with passedmatches
,rawFailureMessage
, and *rawNegativeFailureMessage
fields. TherawMidSentenceFailureMessage
will return the same * string asrawFailureMessage
, and therawMidSentenceNegatedFailureMessage
will return the * same string asrawNegatedFailureMessage
.failureMessageArgs
,negatedFailureMessageArgs
, *midSentenceFailureMessageArgs
,midSentenceNegatedFailureMessageArgs
will beVector.empty
* andPrettifier.default
will be used. * * @param matches indicates whether or not the matcher matched * @param rawFailureMessage raw failure message to report if a match fails * @param rawNegatedFailureMessage raw message with a meaning opposite to that of the failure message */ def this(matches: Boolean, rawFailureMessage: String, rawNegatedFailureMessage: String) = this( matches, rawFailureMessage, rawNegatedFailureMessage, rawFailureMessage, rawNegatedFailureMessage, Vector.empty, Vector.empty, Vector.empty, Vector.empty ) /** * Construct failure message to report if a match fails, usingrawFailureMessage
,failureMessageArgs
andprettifier
* * @return failure message to report if a match fails */ def failureMessage(implicit prettifier: Prettifier): String = if (failureMessageArgs.isEmpty) rawFailureMessage else makeString(rawFailureMessage, failureMessageArgs, prettifier) /** * Construct message with a meaning opposite to that of the failure message, usingrawNegatedFailureMessage
,negatedFailureMessageArgs
andprettifier
* * @return message with a meaning opposite to that of the failure message */ def negatedFailureMessage(implicit prettifier: Prettifier): String = if (negatedFailureMessageArgs.isEmpty) rawNegatedFailureMessage else makeString(rawNegatedFailureMessage, negatedFailureMessageArgs, prettifier) /** * Construct failure message suitable for appearing mid-sentence, usingrawMidSentenceFailureMessage
,midSentenceFailureMessageArgs
andprettifier
* * @return failure message suitable for appearing mid-sentence */ def midSentenceFailureMessage(implicit prettifier: Prettifier): String = if (midSentenceFailureMessageArgs.isEmpty) rawMidSentenceFailureMessage else makeString(rawMidSentenceFailureMessage, midSentenceFailureMessageArgs, prettifier) /** * Construct negated failure message suitable for appearing mid-sentence, usingrawMidSentenceNegatedFailureMessage
,midSentenceNegatedFailureMessageArgs
andprettifier
* * @return negated failure message suitable for appearing mid-sentence */ def midSentenceNegatedFailureMessage(implicit prettifier: Prettifier): String = if (midSentenceNegatedFailureMessageArgs.isEmpty) rawMidSentenceNegatedFailureMessage else makeString(rawMidSentenceNegatedFailureMessage, midSentenceNegatedFailureMessageArgs, prettifier) /** * Get a negated version of this MatchResult, matches field will be negated and all messages field will be substituted with its counter-part. * * @return a negated version of this MatchResult */ def negated: MatchResult = MatchResult(!matches, rawNegatedFailureMessage, rawFailureMessage, rawMidSentenceNegatedFailureMessage, rawMidSentenceFailureMessage, negatedFailureMessageArgs, failureMessageArgs, midSentenceNegatedFailureMessageArgs, midSentenceFailureMessageArgs) private def makeString(rawString: String, args: IndexedSeq[Any], prettifier: Prettifier): String = Resources.formatString(rawString, args.map(arg => if (arg.isInstanceOf[LazyMessage]) arg.asInstanceOf[LazyMessage].message(prettifier) else prettifier(arg)).toArray) } /** * Companion object for theMatchResult
case class. * * @author Bill Venners */ object MatchResult { /** * Factory method that constructs a newMatchResult
with passedmatches
,failureMessage
, *negativeFailureMessage
,midSentenceFailureMessage
, *midSentenceNegatedFailureMessage
,failureMessageArgs
, andnegatedFailureMessageArgs
fields. *failureMessageArgs
, andnegatedFailureMessageArgs
will be used in place ofmidSentenceFailureMessageArgs
* andmidSentenceNegatedFailureMessageArgs
. * * @param matches indicates whether or not the matcher matched * @param rawFailureMessage raw failure message to report if a match fails * @param rawNegatedFailureMessage raw message with a meaning opposite to that of the failure message * @param rawMidSentenceFailureMessage raw failure message to report if a match fails * @param rawMidSentenceNegatedFailureMessage raw message with a meaning opposite to that of the failure message * @param failureMessageArgs arguments for constructing failure message to report if a match fails * @param negatedFailureMessageArgs arguments for constructing message with a meaning opposite to that of the failure message * @return aMatchResult
instance */ def apply(matches: Boolean, rawFailureMessage: String, rawNegatedFailureMessage: String, rawMidSentenceFailureMessage: String, rawMidSentenceNegatedFailureMessage: String, failureMessageArgs: IndexedSeq[Any], negatedFailureMessageArgs: IndexedSeq[Any]): MatchResult = new MatchResult(matches, rawFailureMessage, rawNegatedFailureMessage, rawMidSentenceFailureMessage, rawMidSentenceNegatedFailureMessage, failureMessageArgs, negatedFailureMessageArgs, failureMessageArgs, negatedFailureMessageArgs) /** * Factory method that constructs a newMatchResult
with passedmatches
,rawFailureMessage
, *rawNegativeFailureMessage
,rawMidSentenceFailureMessage
, and *rawMidSentenceNegatedFailureMessage
fields. All argument fields will haveVector.empty
values. * This is suitable to create MatchResult with eager error messages, and its mid-sentence messages need to be different. * * @param matches indicates whether or not the matcher matched * @param rawFailureMessage raw failure message to report if a match fails * @param rawNegatedFailureMessage raw message with a meaning opposite to that of the failure message * @param rawMidSentenceFailureMessage raw failure message to report if a match fails * @param rawMidSentenceNegatedFailureMessage raw message with a meaning opposite to that of the failure message * @return aMatchResult
instance */ def apply(matches: Boolean, rawFailureMessage: String, rawNegatedFailureMessage: String, rawMidSentenceFailureMessage: String, rawMidSentenceNegatedFailureMessage: String): MatchResult = new MatchResult(matches, rawFailureMessage, rawNegatedFailureMessage, rawMidSentenceFailureMessage, rawMidSentenceNegatedFailureMessage, Vector.empty, Vector.empty, Vector.empty, Vector.empty) /** * Factory method that constructs a newMatchResult
with passedmatches
,rawFailureMessage
, and *rawNegativeFailureMessage
fields. TherawMidSentenceFailureMessage
will return the same * string asrawFailureMessage
, and therawMidSentenceNegatedFailureMessage
will return the * same string asrawNegatedFailureMessage
. All argument fields will haveVector.empty
values. * This is suitable to create MatchResult with eager error messages that have same mid-sentence messages. * * @param matches indicates whether or not the matcher matched * @param rawFailureMessage raw failure message to report if a match fails * @param rawNegatedFailureMessage raw message with a meaning opposite to that of the failure message * @return aMatchResult
instance */ def apply(matches: Boolean, rawFailureMessage: String, rawNegatedFailureMessage: String): MatchResult = new MatchResult(matches, rawFailureMessage, rawNegatedFailureMessage, rawFailureMessage, rawNegatedFailureMessage, Vector.empty, Vector.empty, Vector.empty, Vector.empty) /** * Factory method that constructs a newMatchResult
with passedmatches
,rawFailureMessage
, *rawNegativeFailureMessage
andargs
fields. TherawMidSentenceFailureMessage
will return the same * string asrawFailureMessage
, and therawMidSentenceNegatedFailureMessage
will return the * same string asrawNegatedFailureMessage
. All argument fields will useargs
as arguments. * This is suitable to create MatchResult with lazy error messages that have same mid-sentence messages and arguments. * * @param matches indicates whether or not the matcher matched * @param rawFailureMessage raw failure message to report if a match fails * @param rawNegatedFailureMessage raw message with a meaning opposite to that of the failure message * @param args arguments for error messages construction * @return aMatchResult
instance */ def apply(matches: Boolean, rawFailureMessage: String, rawNegatedFailureMessage: String, args: IndexedSeq[Any]) = new MatchResult( matches, rawFailureMessage, rawNegatedFailureMessage, rawFailureMessage, rawNegatedFailureMessage, args, args, args, args ) /** * Factory method that constructs a newMatchResult
with passedmatches
,rawFailureMessage
, *rawNegativeFailureMessage
,failureMessageArgs
andnegatedFailureMessageArgs
fields. * TherawMidSentenceFailureMessage
will return the same string asrawFailureMessage
, and the *rawMidSentenceNegatedFailureMessage
will return the same string asrawNegatedFailureMessage
. * ThemidSentenceFailureMessageArgs
will return the same asfailureMessageArgs
, and the *midSentenceNegatedFailureMessageArgs
will return the same asnegatedFailureMessageArgs
. * This is suitable to create MatchResult with lazy error messages that have same mid-sentence and use different arguments for * negated messages. * * @param matches indicates whether or not the matcher matched * @param rawFailureMessage raw failure message to report if a match fails * @param rawNegatedFailureMessage raw message with a meaning opposite to that of the failure message * @param failureMessageArgs arguments for constructing failure message to report if a match fails * @param negatedFailureMessageArgs arguments for constructing message with a meaning opposite to that of the failure message * @return aMatchResult
instance */ def apply(matches: Boolean, rawFailureMessage: String, rawNegatedFailureMessage: String, failureMessageArgs: IndexedSeq[Any], negatedFailureMessageArgs: IndexedSeq[Any]) = new MatchResult( matches, rawFailureMessage, rawNegatedFailureMessage, rawFailureMessage, rawNegatedFailureMessage, failureMessageArgs, negatedFailureMessageArgs, failureMessageArgs, negatedFailureMessageArgs ) } private[scalatest] class EqualMatchResult( matches: Boolean, rawFailureMessage: String, rawNegatedFailureMessage: String, failureMessageArgs: IndexedSeq[Any], negatedFailureMessageArgs: IndexedSeq[Any] ) extends MatchResult(matches, rawFailureMessage, rawNegatedFailureMessage, rawFailureMessage, rawNegatedFailureMessage, failureMessageArgs, negatedFailureMessageArgs, failureMessageArgs, negatedFailureMessageArgs) { private[scalatest] val atomicAnalysis: AtomicReference[Option[String]] = new AtomicReference[Option[String]](None) private[scalatest] def analysis = { atomicAnalysis.get } override def failureMessage(implicit prettifier: Prettifier): String = { val prettyPair = prettifier(failureMessageArgs(0), failureMessageArgs(1)) atomicAnalysis.getAndSet(prettyPair.analysis) Resources.formatString(rawFailureMessage, Array(prettyPair.left, prettyPair.right)) } override def midSentenceFailureMessage(implicit prettifier: Prettifier): String = { val prettyPair = prettifier(midSentenceFailureMessageArgs(0), midSentenceFailureMessageArgs(1)) atomicAnalysis.getAndSet(prettyPair.analysis) Resources.formatString(rawMidSentenceFailureMessage, Array(prettyPair.left, prettyPair.right)) } override def negated: MatchResult = new NotEqualMatchResult(!matches, rawNegatedFailureMessage, rawFailureMessage, negatedFailureMessageArgs, failureMessageArgs) } private[scalatest] class NotEqualMatchResult( matches: Boolean, rawFailureMessage: String, rawNegatedFailureMessage: String, failureMessageArgs: IndexedSeq[Any], negatedFailureMessageArgs: IndexedSeq[Any] ) extends MatchResult(matches, rawFailureMessage, rawNegatedFailureMessage, rawFailureMessage, rawNegatedFailureMessage, failureMessageArgs, negatedFailureMessageArgs, failureMessageArgs, negatedFailureMessageArgs) { private[scalatest] val atomicAnalysis: AtomicReference[Option[String]] = new AtomicReference[Option[String]](None) private[scalatest] def analysis = { atomicAnalysis.get } override def negatedFailureMessage(implicit prettifier: Prettifier): String = { val prettyPair = prettifier(negatedFailureMessageArgs(0), negatedFailureMessageArgs(1)) atomicAnalysis.getAndSet(prettyPair.analysis) Resources.formatString(rawNegatedFailureMessage, Array(prettyPair.left, prettyPair.right)) } override def midSentenceNegatedFailureMessage(implicit prettifier: Prettifier): String = { val prettyPair = prettifier(midSentenceNegatedFailureMessageArgs(0), midSentenceNegatedFailureMessageArgs(1)) atomicAnalysis.getAndSet(prettyPair.analysis) Resources.formatString(rawMidSentenceNegatedFailureMessage, Array(prettyPair.left, prettyPair.right)) } override def negated: MatchResult = new EqualMatchResult(!matches, rawNegatedFailureMessage, rawFailureMessage, negatedFailureMessageArgs, failureMessageArgs) }