org.scalautils.LegacyCheckingEqualizer.scala Maven / Gradle / Ivy
Show all versions of scalatest_2.9.0 Show documentation
/*
* Copyright 2001-2008 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.scalautils
/**
* Class used via an implicit conversion to enable any two objects to be compared with
* ===
in assertions in tests. For example:
*
*
* assert(a === b)
*
*
*
* The benefit of using assert(a === b)
rather than assert(a == b)
is
* that a TestFailedException
produced by the former will include the values of a
and b
* in its detail message.
* The implicit method that performs the conversion from Any
to Equalizer
is
* convertToEqualizer
in trait Assertions
.
*
*
*
* In case you're not familiar with how implicit conversions work in Scala, here's a quick explanation.
* The convertToEqualizer
method in Assertions
is defined as an "implicit" method that takes an
* Any
, which means you can pass in any object, and it will convert it to an Equalizer
.
* The Equalizer
has ===
defined. Most objects don't have ===
defined as a method
* on them. Take two Strings, for example:
*
*
*
* assert("hello" === "world")
*
*
*
* Given this code, the Scala compiler looks for an ===
method on class String
, because that's the class of
* "hello"
. String
doesn't define ===
, so the compiler looks for an implicit conversion from
* String
to something that does have an ===
method, and it finds the convertToEqualizer
method. It
* then rewrites the code to this:
*
*
*
* assert(convertToEqualizer("hello").===("world"))
*
*
*
* So inside a Suite
(which mixes in Assertions
, ===
will work on anything. The only
* situation in which the implicit conversion wouldn't
* happen is on types that have an ===
method already defined.
*
*
*
* The primary constructor takes one object, left
, whose type is being converted to Equalizer
. The left
* value may be a null
reference, because this is allowed by Scala's ==
operator.
*
*
* @param left An object to convert to Equalizer
, which represents the left
value
* of an assertion.
*
* @author Bill Venners
*/
// final class Equalizer(left: Any) {
/**
* The ===
operation compares this Equalizer
's left
value (passed
* to the constructor, usually via an implicit conversion) with the passed right
value
* for equality as determined by the expression left == right
.
* If true
, ===
returns None
. Else, ===
returns
* a Some
whose String
value indicates the left
and right
values.
*
*
* In its typical usage, the Option[String]
returned by ===
will be passed to one of two
* of trait Assertion
' overloaded assert
methods. If None
,
* which indicates the assertion succeeded, assert
will return normally. But if Some
is passed,
* which indicates the assertion failed, assert
will throw a TestFailedException
whose detail
* message will include the String
contained inside the Some
, which in turn includes the
* left
and right
values. This TestFailedException
is typically embedded in a
* Report
and passed to a Reporter
, which can present the left
and right
* values to the user.
*
*/
/*
def ===(right: Any) = {
def diffStrings(s: String, t: String): Tuple2[String, String] = {
def findCommonPrefixLength(s: String, t: String): Int = {
val max = s.length.min(t.length) // the maximum potential size of the prefix
var i = 0
var found = false
while (i < max & !found) {
found = (s.charAt(i) != t.charAt(i))
if (!found)
i = i + 1
}
i
}
def findCommonSuffixLength(s: String, t: String): Int = {
val max = s.length.min(t.length) // the maximum potential size of the suffix
var i = 0
var found = false
while (i < max & !found) {
found = (s.charAt(s.length - 1 - i) != t.charAt(t.length - 1 - i))
if (!found)
i = i + 1
}
i
}
val commonPrefixLength = findCommonPrefixLength(s, t)
val commonSuffixLength = findCommonSuffixLength(s.substring(commonPrefixLength), t.substring(commonPrefixLength))
val prefix = s.substring(0, commonPrefixLength)
val suffix = if (s.length - commonSuffixLength < 0) "" else s.substring(s.length - commonSuffixLength)
val sMiddleEnd = s.length - commonSuffixLength
val tMiddleEnd = t.length - commonSuffixLength
val sMiddle = s.substring(commonPrefixLength, sMiddleEnd)
val tMiddle = t.substring(commonPrefixLength, tMiddleEnd)
val MaxContext = 20
val shortPrefix = if (commonPrefixLength > MaxContext) "..." + prefix.substring(prefix.length - MaxContext) else prefix
val shortSuffix = if (commonSuffixLength > MaxContext) suffix.substring(0, MaxContext) + "..." else suffix
(shortPrefix + "[" + sMiddle + "]" + shortSuffix, shortPrefix + "[" + tMiddle + "]" + shortSuffix)
}
// If the objects are two strings, replace them with whatever is returned by diffStrings.
// Otherwise, use the same objects.
def getObjectsForFailureMessage(a: Any, b: Any) =
a match {
case aStr: String => {
b match {
case bStr: String => {
diffStrings(aStr, bStr)
}
case _ => (a, b)
}
}
case _ => (a, b)
}
def areEqualComparingArraysStructurally(left: Any, right: Any) = {
left match {
case leftArray: Array[_] =>
right match {
case rightArray: Array[_] => leftArray.deep.equals(rightArray.deep)
case _ => left == right
}
case _ => left == right
}
}
if (areEqualComparingArraysStructurally(left, right))
None
else {
val (leftee, rightee) = getObjectsForFailureMessage(left, right)
Some(FailureMessages("didNotEqual", leftee, rightee))
//Some(leftee + " did not equal " + rightee)
}
}
*/
/*
def !==(right: Any) =
if (left != right)
None
else {
val (leftee, rightee) = Suite.getObjectsForFailureMessage(left, right)
Some(FailureMessages("equaled", leftee, rightee))
}
*/
// }
class LegacyCheckingEqualizer[L](left: L) {
private def diffStrings(s: String, t: String): Tuple2[String, String] = {
def findCommonPrefixLength(s: String, t: String): Int = {
val max = s.length.min(t.length) // the maximum potential size of the prefix
var i = 0
var found = false
while (i < max & !found) {
found = (s.charAt(i) != t.charAt(i))
if (!found)
i = i + 1
}
i
}
def findCommonSuffixLength(s: String, t: String): Int = {
val max = s.length.min(t.length) // the maximum potential size of the suffix
var i = 0
var found = false
while (i < max & !found) {
found = (s.charAt(s.length - 1 - i) != t.charAt(t.length - 1 - i))
if (!found)
i = i + 1
}
i
}
val commonPrefixLength = findCommonPrefixLength(s, t)
val commonSuffixLength = findCommonSuffixLength(s.substring(commonPrefixLength), t.substring(commonPrefixLength))
val prefix = s.substring(0, commonPrefixLength)
val suffix = if (s.length - commonSuffixLength < 0) "" else s.substring(s.length - commonSuffixLength)
val sMiddleEnd = s.length - commonSuffixLength
val tMiddleEnd = t.length - commonSuffixLength
val sMiddle = s.substring(commonPrefixLength, sMiddleEnd)
val tMiddle = t.substring(commonPrefixLength, tMiddleEnd)
val MaxContext = 20
val shortPrefix = if (commonPrefixLength > MaxContext) "..." + prefix.substring(prefix.length - MaxContext) else prefix
val shortSuffix = if (commonSuffixLength > MaxContext) suffix.substring(0, MaxContext) + "..." else suffix
(shortPrefix + "[" + sMiddle + "]" + shortSuffix, shortPrefix + "[" + tMiddle + "]" + shortSuffix)
}
// If the objects are two strings, replace them with whatever is returned by diffStrings.
// Otherwise, use the same objects.
def getObjectsForFailureMessage(a: Any, b: Any) =
a match {
case aStr: String => {
b match {
case bStr: String => {
diffStrings(aStr, bStr)
}
case _ => (a, b)
}
}
case _ => (a, b)
}
def ===[R](right: R)(implicit constraint: EqualityConstraint[L, R]): Option[String] =
if (constraint.areEqual(left, right))
None
else {
val (leftee, rightee) = getObjectsForFailureMessage(left, right)
Some(FailureMessages("didNotEqual", leftee, rightee))
}
def !==[R](right: R)(implicit constraint: EqualityConstraint[L, R]): Option[String] =
if (!constraint.areEqual(left, right))
None
else {
val (leftee, rightee) = getObjectsForFailureMessage(left, right)
Some(FailureMessages("equaled", leftee, rightee))
}
def ===(interval: Interval[L]): Option[String] =
if (interval == null) {
if (left == null)
None
else {
val (leftee, rightee) = getObjectsForFailureMessage(left, interval)
Some(FailureMessages("equaled", leftee, rightee))
}
}
else {
if (interval.isWithin(left))
None
else
Some(FailureMessages("wasNotPlusOrMinus", left, interval.right, interval.tolerance))
}
def !==(interval: Interval[L]): Option[String] =
if (interval == null) {
if (left != null)
None
else {
val (leftee, rightee) = getObjectsForFailureMessage(left, interval)
Some(FailureMessages("equaled", leftee, rightee))
}
}
else {
if (if (interval != null) !interval.isWithin(left) else left != interval)
None
else
Some(FailureMessages("wasPlusOrMinus", left, interval.right, interval.tolerance))
}
}