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

org.scalatest.enablers.Containing.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.scalatest.enablers

import org.scalactic.{Equality, NormalizingEquality, Every}
import scala.collection.{GenTraversableOnce, GenTraversable}

import org.scalactic.ColCompatHelper.aggregate

/**
 * Supertrait for typeclasses that enable certain contain matcher syntax for containers.
 *
 * 

* A Containing[C] provides access to the "containing nature" of type C in such * a way that relevant contain matcher syntax can be used with type C. A C * can be any type of "container," a type that in some way can contains one or more other objects. ScalaTest provides * implicit implementations for several types. You can enable the contain matcher syntax on your own * type U by defining an Containing[U] for the type and making it available implicitly. * *

* ScalaTest provides implicit Containing instances for scala.collection.GenTraversable, * java.util.Collection, java.util.Map, String, Array, * and scala.Option in the Containing companion object. *

* * *

Containing versus Aggregating

* *

* The difference between Containing and Aggregating is that * Containing enables contain matcher syntax that makes sense for "box" types that can * contain at most one value (for example, scala.Option), * whereas Aggregating enables contain matcher syntax for full-blown collections and other * aggregations of potentially more than one object. For example, it makes sense to make assertions like these, which * are enabled by Containing, for scala.Option: *

* *
 * val option: Option[Int] = Some(7)
 * option should contain (7)
 * option should contain oneOf (6, 7, 8)
 * option should contain noneOf (3, 4, 5)
 * 
* *

* However, given a scala.Option can only ever contain at most one object, it doesn't make * sense to make assertions like the following, which are enabled via Aggregation: *

* *
 * // Could never succeed, so does not compile
 * option should contain allOf (6, 7, 8)
 * 
* *

* The above assertion could never succceed, because an option cannot contain more than * one value. By default the above statement does not compile, because contain allOf * is enabled by Aggregating, and ScalaTest provides no implicit Aggregating instance * for type scala.Option. *

*/ trait Containing[-C] { /** * Implements contain <value> syntax for containers of type C. * * @param container a container about which an assertion is being made * @param element an element that should be contained in the passed container * @return true if the passed container contains the passed element */ def contains(container: C, element: Any): Boolean /** * Implements contain oneOf syntax for containers of type C. * * @param container a container about which an assertion is being made * @param elements elements exactly one (i.e., one and only one) of which should be contained in the passed container * @return true if the passed container contains exactly one of the passed elements */ def containsOneOf(container: C, elements: scala.collection.Seq[Any]): Boolean /** * Implements contain noneOf syntax for containers of type C. * * @param container a container about which an assertion is being made * @param elements elements none of which should be contained in the passed container * @return true if the passed container contains none of the passed elements */ def containsNoneOf(container: C, elements: scala.collection.Seq[Any]): Boolean } /* @tailrec def containsOneOf[T](left: T, rightItr: Iterator[Any])(implicit holder: Containing[T]): Boolean = { if (rightItr.hasNext) { val nextRight = rightItr.next if (holder.contains(left, nextRight)) // Found one of right in left, can succeed early true else containsOneOf(left, rightItr) } else // No more elements in right, left does not contain one of right. false } */ trait ContainingImpls { private def tryEquality[T](left: Any, right: Any, equality: Equality[T]): Boolean = try equality.areEqual(left.asInstanceOf[T], right) catch { case cce: ClassCastException => false } private[scalatest] def checkOneOf[T](left: GenTraversableOnce[T], right: GenTraversable[Any], equality: Equality[T]): Set[Any] = { // aggregate version is more verbose, but it allows parallel execution. aggregate(right, Set.empty[Any])( { case (fs, r) => if (left.toIterable.exists(t => equality.areEqual(t, r))) { // r is in the left if (fs.size != 0) // This .size should be safe, it won't go > 1 return fs + r // fail early by returning early, hmm.. not so 'functional'?? else fs + r } else fs // r is not in the left }, { case (fs1, fs2) => val fs = fs1 + fs2 if (fs.size > 1) return fs // fail early by returning early else fs } ) } private[scalatest] def checkNoneOf[T](left: GenTraversableOnce[T], right: GenTraversable[Any], equality: Equality[T]): Option[Any] = { aggregate(right, None)( { case (f, r) => if (left.toIterable.exists(t => equality.areEqual(t, r))) return Some(r) // r is in the left, fail early by returning. else None // r is not in the left }, { case (f1, f2) => None } ) } } trait JavaContainingImplicits extends ContainingImpls { /** * Implicit to support Containing nature of java.util.Collection. * * @param equality Equality type class that is used to check equality of element in the java.util.Collection * @tparam E the type of the element in the java.util.Collection * @tparam JCOL any subtype of java.util.Collection * @return Containing[JCOL[E]] that supports java.util.Collection in relevant contain syntax */ implicit def containingNatureOfJavaCollection[E, JCOL[e] <: java.util.Collection[e]](implicit equality: Equality[E]): Containing[JCOL[E]] = new Containing[JCOL[E]] { def contains(javaColl: JCOL[E], ele: Any): Boolean = { val it: java.util.Iterator[E] = javaColl.iterator var found = false while (!found && it.hasNext) { found = equality.areEqual(it.next , ele) } found } import scala.collection.JavaConverters._ def containsOneOf(javaColl: JCOL[E], elements: scala.collection.Seq[Any]): Boolean = { val foundSet = checkOneOf[E](javaColl.asScala, elements, equality) foundSet.size == 1 } def containsNoneOf(javaColl: JCOL[E], elements: scala.collection.Seq[Any]): Boolean = { val found = checkNoneOf[E](javaColl.asScala, elements, equality) !found.isDefined } } /** * Implicit conversion that converts an Equality of type E * into Containing of type JCOL[E], where JCOL is a subtype of java.util.Collection. * This is required to support the explicit Equality syntax, for example: * *
    * val javaList = new java.util.ArrayList[String]()
    * javaList.add("hi")
    * (javaList should contain oneOf ("HI")) (after being lowerCased)
    * 
* * (after being lowerCased) will returns an Equality[String] * and this implicit conversion will convert it into Containing[java.util.ArrayList[String]]. * * @param equality Equality of type E * @tparam E type of elements in the java.util.Collection * @tparam JCOL subtype of java.util.Collection * @return Containing of type JCOL[E] */ implicit def convertEqualityToJavaCollectionContaining[E, JCOL[e] <: java.util.Collection[e]](equality: Equality[E]): Containing[JCOL[E]] = containingNatureOfJavaCollection(equality) /** * Implicit to support Containing nature of java.util.Map. * * @param equality Equality type class that is used to check equality of entry in the java.util.Map * @tparam K the type of the key in the java.util.Map * @tparam V the type of the value in the java.util.Map * @tparam JMAP any subtype of java.util.Map * @return Containing[JMAP[K, V]] that supports java.util.Map in relevant contain syntax */ implicit def containingNatureOfJavaMap[K, V, JMAP[k, v] <: java.util.Map[k, v]](implicit equality: Equality[java.util.Map.Entry[K, V]]): Containing[JMAP[K, V]] = new Containing[JMAP[K, V]] { import scala.collection.JavaConverters._ def contains(map: JMAP[K, V], ele: Any): Boolean = { map.entrySet.asScala.exists((e: java.util.Map.Entry[K, V]) => equality.areEqual(e, ele)) } def containsOneOf(map: JMAP[K, V], elements: scala.collection.Seq[Any]): Boolean = { val foundSet = checkOneOf[java.util.Map.Entry[K, V]](map.entrySet.asScala, elements, equality) foundSet.size == 1 } def containsNoneOf(map: JMAP[K, V], elements: scala.collection.Seq[Any]): Boolean = { val found = checkNoneOf[java.util.Map.Entry[K, V]](map.entrySet.asScala, elements, equality) !found.isDefined } } /** * Implicit conversion that converts an Equality of type java.util.Map.Entry[K, V] * into Containing of type JMAP[K, V], where JMAP is a subtype of java.util.Map. * This is required to support the explicit Equality syntax, for example: * *
    * val javaMap = new java.util.HashMap[Int, String]()
    * javaMap.put(1, "one")
    * // lowerCased needs to be implemented as Normalization[java.util.Map.Entry[K, V]]
    * (javaMap should contain (Entry(1, "ONE"))) (after being lowerCased)
    * 
* * (after being lowerCased) will returns an java.util.Map.Entry[Int, String] * and this implicit conversion will convert it into Containing[java.util.HashMap[Int, String]]. * * @param equality Equality of type java.util.Map.Entry[K, V] * @tparam K the type of the key in the java.util.Map * @tparam V the type of the value in the java.util.Map * @tparam JMAP any subtype of java.util.Map * @return Containing of type JMAP[K, V] */ implicit def convertEqualityToJavaMapContaining[K, V, JMAP[k, v] <: java.util.Map[k, v]](equality: Equality[java.util.Map.Entry[K, V]]): Containing[JMAP[K, V]] = containingNatureOfJavaMap(equality) } trait ContainingStandardImplicits extends JavaContainingImplicits { import scala.language.higherKinds import scala.language.implicitConversions /** * Implicit to support Containing nature of GenTraversable. * * @param equality Equality type class that is used to check equality of element in the GenTraversable * @tparam E the type of the element in the GenTraversable * @tparam TRAV any subtype of GenTraversable * @return Containing[TRAV[E]] that supports GenTraversable in relevant contain syntax */ implicit def containingNatureOfGenTraversable[E, TRAV[e] <: scala.collection.GenTraversable[e]](implicit equality: Equality[E]): Containing[TRAV[E]] = new Containing[TRAV[E]] { def contains(trav: TRAV[E], ele: Any): Boolean = { equality match { case normEq: NormalizingEquality[_] => val normRight = normEq.normalizedOrSame(ele) trav.exists((e: E) => normEq.afterNormalizationEquality.areEqual(normEq.normalized(e), normRight)) case _ => trav.exists((e: E) => equality.areEqual(e, ele)) } } def containsOneOf(trav: TRAV[E], elements: scala.collection.Seq[Any]): Boolean = { val foundSet = checkOneOf[E](trav, elements, equality) foundSet.size == 1 } def containsNoneOf(trav: TRAV[E], elements: scala.collection.Seq[Any]): Boolean = { val found = checkNoneOf[E](trav, elements, equality) !found.isDefined } } /** * Implicit conversion that converts an Equality of type E * into Containing of type TRAV[E], where TRAV is a subtype of GenTraversable. * This is required to support the explicit Equality syntax, for example: * *
    * (List("hi") should contain oneOf ("HI")) (after being lowerCased)
    * 
* * (after being lowerCased) will returns an Equality[String] * and this implicit conversion will convert it into Containing[List[String]]. * * @param equality Equality of type E * @tparam E type of elements in the GenTraversable * @tparam TRAV subtype of GenTraversable * @return Containing of type TRAV[E] */ implicit def convertEqualityToGenTraversableContaining[E, TRAV[e] <: scala.collection.GenTraversable[e]](equality: Equality[E]): Containing[TRAV[E]] = containingNatureOfGenTraversable(equality) // OPT so that it will work with Some also, but it doesn't work with None /** * Implicit to support Containing nature of scala.Option. * * @param equality Equality type class that is used to check equality of element in the Option * @tparam E the type of the element in the scala.Option * @tparam OPT any subtype of scala.Option * @return Containing[OPT[E]] that supports scala.Option in relevant contain syntax */ implicit def containingNatureOfOption[E, OPT[e] <: Option[e]](implicit equality: Equality[E]): Containing[OPT[E]] = new Containing[OPT[E]] { def contains(opt: OPT[E], ele: Any): Boolean = { opt.exists((e: E) => equality.areEqual(e, ele)) } def containsOneOf(opt: OPT[E], elements: scala.collection.Seq[Any]): Boolean = { val foundSet = checkOneOf[E](opt, elements, equality) foundSet.size == 1 } def containsNoneOf(opt: OPT[E], elements: scala.collection.Seq[Any]): Boolean = { val found = checkNoneOf[E](opt, elements, equality) !found.isDefined } } /** * Implicit conversion that converts an Equality of type E * into Containing of type OPT[E], where OPT is a subtype of scala.Option. * This is required to support the explicit Equality syntax, for example: * *
    * (Some("hi") should contain oneOf ("HI")) (after being lowerCased)
    * 
* * (after being lowerCased) will returns an Equality[String] * and this implicit conversion will convert it into Containing[Some[String]]. * * @param equality Equality of type E * @tparam E type of elements in the scala.Option * @tparam OPT subtype of scala.Option * @return Containing of type OPT[E] */ implicit def convertEqualityToOptionContaining[E, OPT[e] <: Option[e]](equality: Equality[E]): Containing[OPT[E]] = containingNatureOfOption(equality) /** * Implicit to support Containing nature of Array. * * @param equality Equality type class that is used to check equality of element in the Array * @tparam E the type of the element in the Array * @return Containing[Array[E]] that supports Array in relevant contain syntax */ implicit def containingNatureOfArray[E](implicit equality: Equality[E]): Containing[Array[E]] = new Containing[Array[E]] { def contains(arr: Array[E], ele: Any): Boolean = arr.exists((e: E) => equality.areEqual(e, ele)) def containsOneOf(arr: Array[E], elements: scala.collection.Seq[Any]): Boolean = { val foundSet = checkOneOf[E](arr, elements, equality) foundSet.size == 1 } def containsNoneOf(arr: Array[E], elements: scala.collection.Seq[Any]): Boolean = { val found = checkNoneOf[E](arr, elements, equality) !found.isDefined } } /** * Implicit conversion that converts an Equality of type E * into Containing of type Array[E]. * This is required to support the explicit Equality syntax, for example: * *
    * (Array("hi") should contain oneOf ("HI")) (after being lowerCased)
    * 
* * (after being lowerCased) will returns an Equality[String] * and this implicit conversion will convert it into Containing[Array[String]]. * * @param equality Equality of type E * @tparam E type of elements in the Array * @return Containing of type Array[E] */ implicit def convertEqualityToArrayContaining[E](equality: Equality[E]): Containing[Array[E]] = containingNatureOfArray(equality) /** * Implicit to support Containing nature of String. * * @param equality Equality type class that is used to check equality of Char in the String * @return Containing[String] that supports String in relevant contain syntax */ implicit def containingNatureOfString(implicit equality: Equality[Char]): Containing[String] = new Containing[String] { def contains(str: String, ele: Any): Boolean = str.exists((e: Char) => equality.areEqual(e, ele)) def containsOneOf(str: String, elements: scala.collection.Seq[Any]): Boolean = { val foundSet = checkOneOf[Char](str, elements, equality) foundSet.size == 1 } def containsNoneOf(str: String, elements: scala.collection.Seq[Any]): Boolean = { val found = checkNoneOf[Char](str, elements, equality) !found.isDefined } } /** * Implicit conversion that converts an Equality of type Char * into Containing of type String. * This is required to support the explicit Equality syntax, for example: * *
    * // lowerCased needs to be implemented as Normalization[Char]
    * ("hi hello" should contain oneOf ('E')) (after being lowerCased)
    * 
* * (after being lowerCased) will returns an Equality[Char] * and this implicit conversion will convert it into Containing[String]. * * @param equality Equality of type Char * @return Containing of type String */ implicit def convertEqualityToStringContaining(equality: Equality[Char]): Containing[String] = containingNatureOfString(equality) /** * Implicit to support Containing nature of Every. * * @param equality Equality type class that is used to check equality of element in the Every * @tparam E the type of the element in the Every * @return Containing[Every[E]] that supports Every in relevant contain syntax */ implicit def containingNatureOfEvery[E](implicit equality: Equality[E]): Containing[Every[E]] = new Containing[Every[E]] { def contains(every: Every[E], ele: Any): Boolean = equality match { case normEq: NormalizingEquality[_] => val normRight = normEq.normalizedOrSame(ele) every.exists((e: E) => normEq.afterNormalizationEquality.areEqual(normEq.normalized(e), normRight)) case _ => every.exists((e: E) => equality.areEqual(e, ele)) } def containsOneOf(every: Every[E], elements: scala.collection.Seq[Any]): Boolean = { val foundSet = checkOneOf[E](every, elements, equality) foundSet.size == 1 } def containsNoneOf(every: Every[E], elements: scala.collection.Seq[Any]): Boolean = { val found = checkNoneOf[E](every, elements, equality) !found.isDefined } } /** * Implicit conversion that converts an Equality of type E * into Containing of type Every[E]. * This is required to support the explicit Equality syntax, for example: * *
    * (Every("hi", "he", "ho") should contain oneOf ("HI")) (after being lowerCased)
    * 
* * (after being lowerCased) will returns an Equality[String] * and this implicit conversion will convert it into Containing[Every[String]]. * * @param equality Equality of type E * @tparam E type of elements in the Every * @return Containing of type Every[E] */ implicit def convertEqualityToEveryContaining[E](equality: Equality[E]): Containing[Every[E]] = containingNatureOfEvery(equality) } trait ContainingHighPriorityImplicits extends ContainingStandardImplicits { /** * Implicit to support Containing nature of scala.collection.GenMap. * * @param equality Equality type class that is used to check equality of entry in the scala.collection.GenMap * @tparam K the type of the key in the scala.collection.GenMap * @tparam V the type of the value in the scala.collection.GenMap * @tparam MAP any subtype of scala.collection.GenMap * @return Containing[MAP[K, V]] that supports scala.collection.GenMap in relevant contain syntax */ implicit def containingNatureOfMap[K, V, MAP[k, v] <: scala.collection.GenMap[k, v]](implicit equality: Equality[(K, V)]): Containing[MAP[K, V]] = new Containing[MAP[K, V]] { def contains(map: MAP[K, V], ele: Any): Boolean = { map.exists((e: (K, V)) => equality.areEqual(e, ele)) } def containsOneOf(map: MAP[K, V], elements: scala.collection.Seq[Any]): Boolean = { val foundSet = checkOneOf[(K, V)](map, elements, equality) foundSet.size == 1 } def containsNoneOf(map: MAP[K, V], elements: scala.collection.Seq[Any]): Boolean = { val found = checkNoneOf[(K, V)](map, elements, equality) !found.isDefined } } /** * Implicit conversion that converts an Equality of type Tuple2[K, V] * into Containing of type MAP[K, V], where MAP is a subtype of scala.collection.GenMap. * This is required to support the explicit Equality syntax, for example: * *
    * val map = Map(1 -> "one")
    * // lowerCased needs to be implemented as Normalization[Tuple2[K, V]]
    * (map should contain ((1, "ONE"))) (after being lowerCased)
    * 
* * (after being lowerCased) will returns an Tuple2[Int, String] * and this implicit conversion will convert it into Containing[scala.collection.GenMap[Int, String]]. * * @param equality Equality of type Tuple2[K, V] * @tparam K the type of the key in the scala.collection.GenMap * @tparam V the type of the value in the scala.collection.GenMap * @tparam MAP any subtype of scala.collection.GenMap * @return Containing of type MAP[K, V] */ implicit def convertEqualityToMapContaining[K, V, MAP[k, v] <: scala.collection.GenMap[k, v]](equality: Equality[(K, V)]): Containing[MAP[K, V]] = containingNatureOfMap(equality) } /** * Companion object for Containing that provides implicit implementations for the following types: * *
    *
  • scala.collection.GenTraversable
  • *
  • String
  • *
  • Array
  • *
  • scala.Option
  • *
  • java.util.Collection
  • *
  • java.util.Map
  • *
*/ object Containing extends ContainingHighPriorityImplicits




© 2015 - 2024 Weber Informatics LLC | Privacy Policy