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

org.scalactic.TripleEqualsSupport.scala Maven / Gradle / Ivy

The newest version!
/*
 * 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.scalactic

import TripleEqualsSupport._

/**
 * Trait that defines abstract methods used to enforce compile-time type constraints for equality comparisons, and defines === and !== operators
 * used by matchers.
 *
 * 

* The abstract methods of this trait are selectively implemented as implicit by subclasses to enable a spectrum of type constraints for the * === and !== operators. As an illustration, if in the expression, a === b, the type of a * is A and b is B, the following three levels of compile-time checking can be obtained from * TripleEqualsSupport subtraits: *

* *

* Unchecked - A and B can be any two types. This constraint level is available from * subtrait TripleEquals. *

* *

* Statically-checked - A must be a subtype of B, or vice versa, or must cooperate such that the * equality laws stated in the equals contract are preserved. * This (intermediate) constraint level is available by using subtrait TripleEquals and installing the SuperSafe Community Edition * Scala compiler plugin. *

* *

* Type-checked - A must be a subtype of B, or vice versa. * (Both A and B can be the same type, because a type is considered a subtype * of itself.) * This (strongest) constraint level is available from subtrait TypeCheckedTripleEquals. *

* *

* This trait defines all methods that need to be defined implicitly by the subtraits so that if multiple subtraits are used together, the inner-most * subtrait in scope can not only enable the implicits it needs by overriding or hiding those methods (currently-in-scope as regular, non-implicit * methods) and making * them implicit, it can also disable any implicits enabled by its sibling subtraits in enclosing scopes. For example, if your test class mixes * in TypeCheckedTripleEquals, inside your test class the following methods will be implicit: *

* *
    *
  • convertToCheckingEqualizer
  • *
  • typeCheckedConstraint
  • *
  • lowPriorityTypeCheckedConstraint
  • *
  • convertEquivalenceToAToBConstraint
  • *
  • convertEquivalenceToBToAConstraint
  • *
* *

* If in the body of a test you want to turn off the type checking, you can import the members * of TripleEquals in the body of that test. This will not only hide * non-implicit methods convertToEqualizer unconstrainedEquality of TypeCheckedTripleEquals, * replacing those with implicit ones defined in TripleEquals, it will also hide the three methods made implicit in TypeCheckedTripleEquals * (and listed above), replacing them by non-implicit ones. *

* *

* In short, you should be able to select a primary constraint level via either a mixin or import, then change that in nested scopes * however you want, again either through a mixin or import, without getting any implicit conversion ambiguity. The innermost constraint level in scope * will always be in force. *

* * @author Bill Venners */ trait TripleEqualsSupport { /** * Class used via an implicit conversion to enable any two objects to be compared with * === and !== with a Boolean result and no enforced type constraint between * two object types. For example: * *

   * assert(a === b)
   * assert(c !== d)
   * 
* *

* You can also check numeric values against another with a tolerance. Here are some examples: *

* *
   * assert(a === (2.0 +- 0.1))
   * assert(c !== (2.0 +- 0.1))
   * 
* * @param leftSide An object to convert to Equalizer, which represents the value * on the left side of a === or !== invocation. * * @author Bill Venners */ class Equalizer[L](val leftSide: L) { // Note: This is called leftSide not left to avoid a conflict with scalaz's implicit that adds left /** * Compare two objects for equality, returning a Boolean, using the Equality type class passed as equality. * * @param rightSide the object to compare for equality with leftSide, passed to the constructor * @param equality an implicit Equality type class that defines a way of calculating equality for objects of type L * @return true if the leftSide and rightSide objects are equal according to the passed Equality type class. */ def ===(rightSide: Any)(implicit equality: Equality[L]): Boolean = equality.areEqual(leftSide, rightSide) /** * Compare two objects for inequality, returning a Boolean, using the Equality type class passed as equality. * * @param rightSide the object to compare for inequality with leftSide, passed to the constructor * @param equality an implicit Equality type class that defines a way of calculating equality for objects of type L * @return true if the leftSide and rightSide objects are not equal according to the passed Equality type class. */ def !==(rightSide: Any)(implicit equality: Equality[L]): Boolean = !equality.areEqual(leftSide, rightSide) /** * Determine whether a numeric object is within the passed Spread, returning a Boolean. * * @param spread the Spread against which to compare the value passed to the constructor as leftSide * @return true if the value passed to the constructor as leftSide is within the Spread passed to this method. */ def ===(spread: Spread[L]): Boolean = if (spread != null) spread.isWithin(leftSide) else leftSide == spread /** * Determine whether a numeric object is outside the passed Spread, returning a Boolean. * * @param spread the Spread against which to compare the value passed to the constructor as leftSide * @return true if the value passed to the constructor as leftSide is not within the Spread passed to this method. */ def !==(spread: Spread[L]): Boolean = if (spread != null) !spread.isWithin(leftSide) else leftSide != spread /** * Determine whether an object reference is null. * * @param literalNull a null value against which to compare the value passed to the constructor as leftSide for equality * @return true if the value passed to the constructor as leftSide is null. */ def ===(literalNull: Null): Boolean = leftSide == null /** * Determines whether an object reference is non-null. * * @param literalNull a null value against which to compare the value passed to the constructor as leftSide for inequality * @return true if the value passed to the constructor as leftSide is non-null. */ def !==(literalNull: Null): Boolean = leftSide != null } /** * Class used via an implicit conversion to enable two objects to be compared with * === and !== with a Boolean result and an enforced type constraint between * two object types. For example: * *
   * assert(a === b)
   * assert(c !== d)
   * 
* *

* You can also check numeric values against another with a tolerance. Here are some examples: *

* *
   * assert(a === (2.0 +- 0.1))
   * assert(c !== (2.0 +- 0.1))
   * 
* * @param leftSide An object to convert to Equalizer, which represents the value * on the left side of a === or !== invocation. * * @author Bill Venners */ class CheckingEqualizer[L](val leftSide: L) { // Note: This is called leftSide not left to avoid a conflict with scalaz's implicit that adds left /** * Compare two objects for equality, returning a Boolean, using the Constraint instance passed as constraint. * * @param rightSide the object to compare for equality with leftSide, passed to the constructor * @param constraint an implicit Constraint instance that enforces a relationship between types L and R and * defines a way of calculating equality for objects of type L * @return true if the leftSide and rightSide objects are equal according to the passed Constraint instance. */ def ===[R](rightSide: R)(implicit constraint: L CanEqual R): Boolean = constraint.areEqual(leftSide, rightSide) /** * Compare two objects for inequality, returning a Boolean, using the Constraint instance passed as constraint. * * @param rightSide the object to compare for inequality with leftSide, passed to the constructor * @param constraint an implicit Constraint instance that enforces a relationship between types L and R and * defines a way of calculating equality for objects of type L * @return true if the leftSide and rightSide objects are not equal according to the passed Constraint instance. */ def !==[R](rightSide: R)(implicit constraint: L CanEqual R): Boolean = !constraint.areEqual(leftSide, rightSide) /** * Determine whether a numeric object is within the passed Spread, returning a Boolean. * * @param spread the Spread against which to compare the value passed to the constructor as leftSide * @return true if the value passed to the constructor as leftSide is not within the Spread passed to this method. */ def ===(spread: Spread[L]): Boolean = if (spread != null) spread.isWithin(leftSide) else leftSide == spread /** * Determine whether a numeric object is outside the passed Spread, returning a Boolean. * * @param spread the Spread against which to compare the value passed to the constructor as leftSide * @return true if the value passed to the constructor as leftSide is not within the Spread passed to this method. */ def !==(spread: Spread[L]): Boolean = if (spread != null) !spread.isWithin(leftSide) else leftSide != spread } /** * Returns an Equality[A] for any type A that determines equality * by first calling .deep on any Array (on either the left or right side), * then comparing the resulting objects with ==. * * @return a default Equality for type A */ def defaultEquality[A]: Equality[A] = Equality.default /** * Converts to an Equalizer that provides === and !== operators that * result in Boolean and enforce no type constraint. * *

* This method is overridden and made implicit by subtrait TripleEquals and overriden as non-implicit by the other * subtraits in this package. *

* * @param left the object whose type to convert to Equalizer. * @throws NullPointerException if left is null. */ def convertToEqualizer[T](left: T): Equalizer[T] /** * Converts to an CheckingEqualizer that provides === and !== operators * that result in Boolean and enforce a type constraint. * *

* This method is overridden and made implicit by subtrait TypeCheckedTripleEquals, and * overriden as non-implicit by the other subtraits in this package. *

* * @param left the object whose type to convert to CheckingEqualizer. * @throws NullPointerException if left is null. */ def convertToCheckingEqualizer[T](left: T): CheckingEqualizer[T] /** * Provides an A CanEqual B instance for any two types A and B, with no type constraint enforced, given an * implicit Equality[A]. * *

* The returned Constraint's areEqual method uses the implicitly passed Equality[A]'s * areEqual method to determine equality. *

* *

* This method is overridden and made implicit by subtraits TripleEquals and * overriden as non-implicit by the other subtraits in this package. *

* * @param equalityOfA an Equality[A] type class to which the Constraint.areEqual method will delegate to determine equality. * @return an A CanEqual B instance whose areEqual method delegates to the areEqual method of * the passed Equality[A]. */ def unconstrainedEquality[A, B](implicit equalityOfA: Equality[A]): A CanEqual B /** * Provides an A CanEqual B for any two types A and B, enforcing the type constraint * that A must be a subtype of B, given an implicit Equivalence[B]. * *

* The returned Constraint's areEqual method uses the implicitly passed Equivalence[A]'s * areEquivalent method to determine equality. *

* *

* This method is overridden and made implicit by subtraits * LowPriorityTypeCheckedConstraint (extended by * TypeCheckedTripleEquals), and * overriden as non-implicit by the other subtraits in this package. *

* * @param equivalenceOfB an Equivalence[B] type class to which the Constraint.areEqual method * will delegate to determine equality. * @param ev evidence that A is a subype of
B * @return an A CanEqual B instance whose areEqual method delegates to the * areEquivalent method of the passed Equivalence[B]. */ def lowPriorityTypeCheckedConstraint[A, B](implicit equivalenceOfB: Equivalence[B], ev: A <:< B): A CanEqual B /** * Provides a A CanEqual B for any two types A and B, enforcing the type constraint * that A must be a subtype of B, given an explicit Equivalence[B]. * *

* This method is used to enable the Explicitly DSL for * TypeCheckedTripleEquals by requiring an explicit Equivalance[B], but * taking an implicit function that provides evidence that A is a subtype of B. *

* *

* The returned Constraint's areEqual method uses the implicitly passed Equivalence[B]'s * areEquivalent method to determine equality. *

* *

* This method is overridden and made implicit by subtraits * LowPriorityTypeCheckedConstraint (extended by * TypeCheckedTripleEquals), and * overriden as non-implicit by the other subtraits in this package. *

* * @param equivalenceOfB an Equivalence[B] type class to which the Constraint.areEqual method * will delegate to determine equality. * @param ev evidence that A is a subype of B * @return an A CanEqual B instance whose areEqual method delegates to the * areEquivalent method of the passed Equivalence[B]. */ def convertEquivalenceToAToBConstraint[A, B](equivalenceOfB: Equivalence[B])(implicit ev: A <:< B): A CanEqual B /** * Provides an A CanEqual B instance for any two types A and B, enforcing the type constraint * that B must be a subtype of A, given an implicit Equivalence[A]. * *

* The returned Constraint's areEqual method uses the implicitly passed Equivalence[A]'s * areEquivalent method to determine equality. *

* *

* This method is overridden and made implicit by subtraits * TypeCheckedTripleEquals) and * overriden as non-implicit by the other subtraits in this package. *

* * @param equalityOfA an Equivalence[A] type class to which the Constraint.areEqual method will delegate to determine equality. * @param ev evidence that B is a subype of A * @return an A CanEqual B instance whose areEqual method delegates to the areEquivalent method of * the passed Equivalence[A]. */ def typeCheckedConstraint[A, B](implicit equivalenceOfA: Equivalence[A], ev: B <:< A): A CanEqual B /** * Provides an A CanEqual B instance for any two types A and B, enforcing the type constraint * that B must be a subtype of A, given an explicit Equivalence[A]. * *

* This method is used to enable the Explicitly DSL for * TypeCheckedTripleEquals by requiring an explicit Equivalance[B], but * taking an implicit function that provides evidence that A is a subtype of B. For example, under TypeCheckedTripleEquals, * this method (as an implicit method), would be used to compile this statement: *

* *
   * def closeEnoughTo1(num: Double): Boolean =
   *   (num === 1.0)(decided by forgivingEquality)
   * 
* *

* The returned Constraint's areEqual method uses the implicitly passed Equivalence[A]'s * areEquivalent method to determine equality. *

* *

* This method is overridden and made implicit by subtraits * TypeCheckedTripleEquals) and * overriden as non-implicit by the other subtraits in this package. *

* * @param equalityOfA an Equivalence[A] type class to which the Constraint.areEqual method will delegate to determine equality. * @param ev evidence that B is a subype of A * @return an A CanEqual B instance whose areEqual method delegates to the areEquivalent method of * the passed Equivalence[A]. */ def convertEquivalenceToBToAConstraint[A, B](equivalenceOfA: Equivalence[A])(implicit ev: B <:< A): A CanEqual B /** * The lowPriorityConversionCheckedConstraint method has been deprecated and will be removed in a future version of Scalactic. It * is no longer needed now that the deprecation period of ConversionCheckedTripleEquals has expired. It will not be replaced. * *

* Provides an A CanEqual B instance for any two types A and B, enforcing the type constraint that A is * implicitly convertible to B, given an implicit Equivalence[B]. *

* *

* The returned Constraint's areEqual method uses the implicitly passed Equivalence[B]'s * areEquivalent method to determine equality. *

* *

* This method is overridden and made implicit by subtraits * LowPriorityConversionCheckedConstraint (extended by * ConversionCheckedTripleEquals), and * overriden as non-implicit by the other subtraits in this package. *

* * @param equalityOfB an Equivalence[B] type class to which the Constraint.areEqual method will delegate to determine equality. * @param cnv an implicit conversion from A to B * @return an A CanEqual B instance whose areEqual method delegates to the areEquivalent method of * the passed Equivalence[B]. */ @deprecated("The lowPriorityConversionCheckedConstraint method has been deprecated and will be removed in a future version of Scalactic. It is no longer needed now that the deprecation period of ConversionCheckedTripleEquals has expired. It will not be replaced.", "3.1.0") def lowPriorityConversionCheckedConstraint[A, B](implicit equivalenceOfB: Equivalence[B], cnv: A => B): A CanEqual B /** * The convertEquivalenceToAToBConversionConstraint method has been deprecated and will be removed in a future version of Scalactic. * It is no longer needed now that the deprecation period of ConversionCheckedTripleEquals has expired. It will not be replaced. * *

* Provides an A CanEqual B instance for any two types A and B, enforcing the type constraint that A is * implicitly convertible to B, given an explicit Equivalence[B]. *

* *

* This method is used to enable the Explicitly DSL for * ConversionCheckedTripleEquals by requiring an explicit Equivalance[B], but * taking an implicit function that converts from A to B. *

* *

* The returned Constraint's areEqual method uses the implicitly passed Equivalence[B]'s * areEquivalent method to determine equality. *

* *

* This method is overridden and made implicit by subtraits * LowPriorityConversionCheckedConstraint (extended by * ConversionCheckedTripleEquals), and * overriden as non-implicit by the other subtraits in this package. *

* * @param equalityOfB an Equivalence[B] type class to which the Constraint.areEqual method will delegate to determine equality. * @param cnv an implicit conversion from A to B * @return an A CanEqual B instance whose areEqual method delegates to the areEquivalent method of * the passed Equivalence[B]. */ @deprecated("The convertEquivalenceToAToBConversionConstraint method has been deprecated and will be removed in a future version of Scalactic. It is no longer needed now that the deprecation period of ConversionCheckedTripleEquals has expired. It will not be replaced.", "3.1.0") def convertEquivalenceToAToBConversionConstraint[A, B](equivalenceOfB: Equivalence[B])(implicit ev: A => B): A CanEqual B /** * The conversionCheckedConstraint method has been deprecated and will be removed in a future version of Scalactic. It * is no longer needed now that the deprecation period of ConversionCheckedTripleEquals has expired. It will not be replaced. * *

* Provides an A CanEqual B instance for any two types A and B, enforcing the type constraint that B is * implicitly convertible to A, given an implicit Equivalence[A]. *

* *

* The returned Constraint's areEqual method uses the implicitly passed Equivalence[A]'s * areEquivalent method to determine equality. *

* *

* This method is overridden and made implicit by subtraits * ConversionCheckedTripleEquals) and * overriden as non-implicit by the other subtraits in this package. *

* * @param equivalenceOfA an Equivalence[A] type class to which the Constraint.areEqual method will delegate to determine equality. * @param cnv an implicit conversion from B to A * @return an A CanEqual B instance whose areEqual method delegates to the areEquivalent method of * the passed Equivalence[A]. */ @deprecated("The conversionCheckedConstraint method has been deprecated and will be removed in a future version of Scalactic. It is no longer needed now that the deprecation period of ConversionCheckedTripleEquals has expired. It will not be replaced.", "3.1.0") def conversionCheckedConstraint[A, B](implicit equivalenceOfA: Equivalence[A], cnv: B => A): A CanEqual B /** * The convertEquivalenceToBToAConversionConstraint method has been deprecated and will be removed in a future version of Scalactic. * It is no longer needed now that the deprecation period of ConversionCheckedTripleEquals has expired. It will not be replaced. * *

* Provides an A CanEqual B instance for any two types A and B, enforcing the type constraint that B is * implicitly convertible to A, given an explicit Equivalence[A]. *

* *

* This method is used to enable the Explicitly DSL for * ConversionCheckedTripleEquals by requiring an explicit Equivalance[A], but * taking an implicit function that converts from B to A. For example, under ConversionCheckedTripleEquals, * this method (as an implicit method), would be used to compile this statement: *

* *
   * def closeEnoughTo1(num: Double): Boolean =
   *   (num === 1.0)(decided by forgivingEquality)
   * 
* *

* The returned Constraint's areEqual method uses the implicitly passed Equivalence[A]'s * areEquivalent method to determine equality. *

* *

* This method is overridden and made implicit by subtraits * ConversionCheckedTripleEquals) and * overriden as non-implicit by the other subtraits in this package. *

* * @param equivalenceOfA an Equivalence[A] type class to which the Constraint.areEqual method will delegate to determine equality. * @param cnv an implicit conversion from B to A * @return an A CanEqual B instance whose areEqual method delegates to the areEquivalent method of * the passed Equivalence[A]. */ @deprecated("The convertEquivalenceToBToAConversionConstraint method has been deprecated and will be removed in a future version of Scalactic. It is no longer needed now that the deprecation period of ConversionCheckedTripleEquals has expired. It will not be replaced.", "3.1.0") def convertEquivalenceToBToAConversionConstraint[A, B](equivalenceOfA: Equivalence[A])(implicit ev: B => A): A CanEqual B /** * Returns a TripleEqualsInvocation[T], given an object of type T, to facilitate * the “<left> should === <right>” syntax * of Matchers. * * @param right the right-hand side value for an equality assertion * @return a TripleEqualsInvocation wrapping the passed right value, with expectingEqual * set to true. */ def ===[T](right: T): TripleEqualsInvocation[T] = new TripleEqualsInvocation[T](right, true) /** * Returns a TripleEqualsInvocation[T], given an object of type T, to facilitate * the “<left> should !== <right>” syntax * of Matchers. * * @param right the right-hand side value for an equality assertion * @return a TripleEqualsInvocation wrapping the passed right value, with expectingEqual * set to false. */ def !==[T](right: T): TripleEqualsInvocation[T] = new TripleEqualsInvocation[T](right, false) /** * Returns a TripleEqualsInvocation[Null], given a null reference, to facilitate * the “<left> should === null” syntax * of Matchers. * * @param right a null reference * @return a TripleEqualsInvocation wrapping the passed null value, with expectingEqual * set to true. */ def ===(right: Null): TripleEqualsInvocation[Null] = new TripleEqualsInvocation[Null](right, true) /** * Returns a TripleEqualsInvocation[Null], given a null reference, to facilitate * the “<left> should !== null” syntax * of Matchers. * * @param right a null reference * @return a TripleEqualsInvocation wrapping the passed null value, with expectingEqual * set to false. */ def !==(right: Null): TripleEqualsInvocation[Null] = new TripleEqualsInvocation[Null](right, false) /** * Returns a TripleEqualsInvocationOnSpread[T], given an Spread[T], to facilitate * the “<left> should === (<pivot> +- <tolerance>)” * syntax of Matchers. * * @param right the Spread[T] against which to compare the left-hand value * @return a TripleEqualsInvocationOnSpread wrapping the passed Spread[T] value, with * expectingEqual set to true. */ def ===[T](right: Spread[T]): TripleEqualsInvocationOnSpread[T] = new TripleEqualsInvocationOnSpread[T](right, true) /** * Returns a TripleEqualsInvocationOnSpread[T], given an Spread[T], to facilitate * the “<left> should !== (<pivot> +- <tolerance>)” * syntax of Matchers. * * @param right the Spread[T] against which to compare the left-hand value * @return a TripleEqualsInvocationOnSpread wrapping the passed Spread[T] value, with * expectingEqual set to false. */ def !==[T](right: Spread[T]): TripleEqualsInvocationOnSpread[T] = new TripleEqualsInvocationOnSpread[T](right, false) } object TripleEqualsSupport { /** * An implementation of Constraint for two types A and B that requires an Equality[A] to * which its areEqual method can delegate an equality comparison. * * @param equalityOfA an Equality type class for A */ final class EqualityConstraint[A, B](equalityOfA: Equality[A]) extends (A CanEqual B) { /** * Indicates whether the objects passed as a and b are equal by returning the * result of invoking areEqual(a, b) on the passed equalityOfA object. * * @param a a left-hand-side object being compared with another (right-hand-side one) for equality (e.g., a == b) * @param b a right-hand-side object being compared with another (left-hand-side one) for equality (e.g., a == b) */ def areEqual(a: A, b: B): Boolean = equalityOfA.areEqual(a, b) } /** * An implementation of Constraint for two types A and B that requires an Equality[B] * and a conversion function from A to B. * * @param equivalenceOfB an Equivalence type class for B */ final class AToBEquivalenceConstraint[A, B](equivalenceOfB: Equivalence[B], cnv: A => B) extends (A CanEqual B) { /** * Indicates whether the objects passed as a and b are equal by return the * result of invoking areEqual(cnv(a), b) on the passed equalityOfB object. * *

* In other words, the a object of type A is first converted to a B via the passed conversion * function, cnv, then compared for equality with the b object. *

* * @param a a left-hand-side object being compared with another (right-hand-side one) for equality (e.g., a == b) * @param b a right-hand-side object being compared with another (left-hand-side one) for equality (e.g., a == b) */ override def areEqual(a: A, b: B): Boolean = equivalenceOfB.areEquivalent(cnv(a), b) } /** * An implementation of Constraint for two types A and B that requires an Equality[A] * and a conversion function from B to A. * * @param equivalenceOfA an Equivalence type class for A */ final class BToAEquivalenceConstraint[A, B](equivalenceOfA: Equivalence[A], cnv: B => A) extends (A CanEqual B) { /** * Indicates whether the objects passed as a and b are equal by returning the * result of invoking areEqual(a, cnv(b)) on the passed equalityOfA object. * *

* In other words, the b object of type B is first converted to an A via the passed conversion * function, cnv, then compared for equality with the a object. *

* * @param a a left-hand-side object being compared with another (right-hand-side one) for equality (e.g., a == b) * @param b a right-hand-side object being compared with another (left-hand-side one) for equality (e.g., a == b) */ override def areEqual(a: A, b: B): Boolean = equivalenceOfA.areEquivalent(a, cnv(b)) } /** * Facilitates the “should === (x += y)” and “should !== (x += y)” syntax of ScalaTest's matchers DSL. * *

* Instances of this class are created and returned by the === and !== methods of * trait TripleEqualsSupport. *

* * @param spread the Spread[T] against which to compare the left-hand value * @param expectingEqual true if the result of a === invocation; false if the result of a !== invocation. */ final case class TripleEqualsInvocationOnSpread[T](spread: Spread[T], expectingEqual: Boolean) /** * Facilitates the “should ===” and “should !==” syntax of ScalaTest's matchers DSL. * *

* Instances of this class are created and returned by the === and !== methods of * trait TripleEqualsSupport. *

* * @param right the right-hand side value for an equality assertion * @param expectingEqual true if the result of a === invocation; false if the result of a !== invocation. */ final case class TripleEqualsInvocation[T](right: T, expectingEqual: Boolean) { override def toString: String = (if (expectingEqual) "===" else "!==") + " " + Prettifier.default(right) } /** * Class representing an spread (i.e., range) between two numbers. * *

* The spread is expressed in terms of a Numeric pivot and tolerance. * The spread extends from pivot - tolerance to pivot + tolerance, inclusive. *

* * @param pivot the pivot number at the center of the spread * @param tolerance the tolerance that determines the high and low point of the spread * * @author Bill Venners */ final case class Spread[T : Numeric](pivot: T, tolerance: T) { private val numeric = implicitly[Numeric[T]] require(numeric.signum(tolerance) >= 0, "tolerance must be zero or greater, but was " + tolerance) private val max = numeric.plus(pivot, tolerance) private val min = numeric.minus(pivot, tolerance) /** * Determines whether the passed Numeric value n is within the spread represented * by this Spread instance. */ def isWithin(n: T): Boolean = { numeric.gteq(n, min) && numeric.lteq(n, max) } /** * Returns true if the passed number, n, is within the spread represented by this Spread instance * *

* The purpose of this method, which will likely be used only rarely, is to achieve symmetry around the === operator. The * TripleEquals trait (and its type-checking sibling TypeCheckedTripleEquals) enable you to write: *

* *
     * a === (1.0 +- 0.1)
     * 
* *

* This method ensures the following mirrored form means the same thing: *

* *
     * (1.0 +- 0.1) === a
     * 
* * @param n a number that may or may not lie within this spread */ def ===(n: T): Boolean = isWithin(n) /** * Returns false if the passed number, n, is within the spread represented by this Spread instance * *

* The purpose of this method, which will likely be used only rarely, is to achieve symmetry around the !== operator. The * TripleEquals trait (and its type-checking sibling TypeCheckedTripleEquals) enable you to write: *

* *
     * a !== (1.0 +- 0.1)
     * 
* *

* This method ensures the following mirrored form means the same thing: *

* *
     * (1.0 +- 0.1) !== a
     * 
* * @param n a number that may or may not lie within this spread */ def !==(n: T): Boolean = !isWithin(n) /** * Overrides toString to return "[pivot] +- [tolerance]" */ override def toString: String = Prettifier.default(pivot) + " +- " + Prettifier.default(tolerance) } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy