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

org.scalatest.enablers.Sequencing.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 scala.collection.JavaConverters._
import org.scalactic.{Equality, Every}
import scala.collection.GenTraversable
import org.scalatest.verbs.ArrayWrapper
import scala.annotation.tailrec

/**
 * Typeclass that enables for sequencing certain contain syntax in the ScalaTest matchers DSL.
 *
 * 

* An Sequencing[A] provides access to the "sequenching nature" of type A in such * a way that relevant contain matcher syntax can be used with type A. An A * can be any type of sequencing—an object that in some way brings together other objects in order. * ScalaTest provides implicit implementations for several types out of the box in the * Sequencing companion object: *

* *
    *
  • scala.collection.GenSeq
  • *
  • scala.collection.SortedSet
  • *
  • scala.collection.SortedMap
  • *
  • Array
  • *
  • java.util.List
  • *
  • java.util.SortedSet
  • *
  • java.util.SortedMap
  • *
  • String
  • *
* *

* The contain syntax enabled by this trait is: *

* *

    *
  • result should contain inOrder (1, 2, 3)
  • *
  • result should contain inOrderOnly (1, 2, 3)
  • *
  • result should contain theSameElementsInOrderAs List(1, 2, 3)
  • *
* *

* You can enable the contain matcher syntax enabled by Sequencing on your own * type U by defining an Sequencing[U] for the type and making it available implicitly. *

*/ trait Sequencing[-S] { /** * Implements contain inOrder syntax for sequences of type S. * * @param sequence an sequence about which an assertion is being made * @param eles elements all of which should be contained, in order of appearance in eles, in the passed sequence * @return true if the passed sequence contains all of the passed elements in (iteration) order */ def containsInOrder(sequence: S, eles: scala.collection.Seq[Any]): Boolean /** * Implements contain inOrderOnly syntax for sequences of type S. * * @param sequence an sequence about which an assertion is being made * @param eles the only elements that should be contained, in order of appearence in eles, in the passed sequence * @return true if the passed sequence contains only the passed elements in (iteration) order */ def containsInOrderOnly(sequence: S, eles: scala.collection.Seq[Any]): Boolean /** * Implements contain theSameElementsInOrderAs syntax for sequences of type S. * * @param leftSequence an sequence about which an assertion is being made * @param rightSequence an sequence that should contain the same elements, in (iterated) order as the passed leftSequence * @return true if the passed leftSequence contains the same elements, in (iterated) order, as the passed rightSequence */ def containsTheSameElementsInOrderAs(leftSequence: S, rightSequence: GenTraversable[Any]): Boolean } /** * Companion object for Sequencing that provides implicit implementations for the following types: * *
    *
  • scala.collection.GenSeq
  • *
  • scala.collection.SortedSet
  • *
  • scala.collection.SortedMap
  • *
  • Array
  • *
  • java.util.List
  • *
  • java.util.SortedSet
  • *
  • java.util.SortedMap
  • *
  • String
  • *
*/ object Sequencing { private def checkTheSameElementsInOrderAs[T](left: GenTraversable[T], right: GenTraversable[Any], equality: Equality[T]): Boolean = { @tailrec def checkEqual(left: Iterator[T], right: Iterator[Any]): Boolean = { if (left.hasNext && right.hasNext) { val nextLeft = left.next val nextRight = right.next if (!equality.areEqual(nextLeft, nextRight)) false else checkEqual(left, right) } else left.isEmpty && right.isEmpty } checkEqual(left.toIterator, right.toIterator) } private def checkInOrderOnly[T](left: GenTraversable[T], right: GenTraversable[Any], equality: Equality[T]): Boolean = { @tailrec def checkEqual(left: T, right: Any, leftItr: Iterator[T], rightItr: Iterator[Any]): Boolean = { if (equality.areEqual(left, right)) { // The first time in, left must equal right // Now need to iterate through the left while it is equal to the right @tailrec def checkNextLeftAgainstCurrentRight(): Option[T] = { // Returns first left that doesn't match the current right, or None, if all remaining lefts matched current right if (leftItr.hasNext) { val nextLeft = leftItr.next if (equality.areEqual(nextLeft, right)) checkNextLeftAgainstCurrentRight() else Some(nextLeft) } else None // No more lefts } val nextLeftOption = checkNextLeftAgainstCurrentRight() nextLeftOption match { case Some(nextLeft) => if (rightItr.hasNext) { checkEqual(nextLeft, rightItr.next, leftItr, rightItr) } else false case None => !rightItr.hasNext // No more lefts remaining, so we're good so long as no more rights remaining either. } } else false } val leftItr: Iterator[T] = left.toIterator val rightItr: Iterator[Any] = right.toIterator if (leftItr.hasNext && rightItr.hasNext) checkEqual(leftItr.next, rightItr.next, leftItr, rightItr) else left.isEmpty && right.isEmpty } private def checkInOrder[T](left: GenTraversable[T], right: GenTraversable[Any], equality: Equality[T]): Boolean = { @tailrec def lastIndexOf(itr: Iterator[T], element: Any, idx: Option[Int], i: Int): Option[Int] = { if (itr.hasNext) { val next = itr.next if (equality.areEqual(next, element)) lastIndexOf(itr, element, Some(i), i + 1) else lastIndexOf(itr, element, idx, i + 1) } else idx } @tailrec def checkEqual(leftItr: Iterator[T], currentRight: Any, matchedCurrentRight: Boolean, rightItr: Iterator[Any]): Boolean = if (leftItr.hasNext) { val left = leftItr.next if (equality.areEqual(left, currentRight)) { if (rightItr.hasNext) { val right = rightItr.next checkEqual(leftItr, right, false, rightItr) } else true } else checkEqual(leftItr, currentRight, matchedCurrentRight, rightItr) } else matchedCurrentRight && !rightItr.hasNext @tailrec def checkEqualRec(left: GenTraversable[T], rightItr: Iterator[Any]): Boolean = { if (rightItr.hasNext) { val nextRight = rightItr.next lastIndexOf(left.toIterator, nextRight, None, 0) match { case Some(idx) => if (checkEqual(left.drop(idx).toIterator, nextRight, false, rightItr)) true else checkEqualRec(left.take(idx), right.toIterator) case None => false // Element not found, let's fail early } } else // No more element in right, left contains all of right. true } checkEqualRec(left, right.toIterator) } import scala.language.higherKinds /** * Implicit to support Sequencing nature of scala.collection.GenSeq. * * @param equality Equality type class that is used to check equality of element in the scala.collection.GenSeq * @tparam E the type of the element in the scala.collection.GenSeq * @tparam SEQ any subtype of scala.collection.GenSeq * @return Sequencing[SEQ[E]] that supports scala.collection.GenSeq in relevant contain syntax */ implicit def sequencingNatureOfGenSeq[E, SEQ[e] <: scala.collection.GenSeq[e]](implicit equality: Equality[E]): Sequencing[SEQ[E]] = new Sequencing[SEQ[E]] { def containsInOrder(seq: SEQ[E], elements: scala.collection.Seq[Any]): Boolean = { checkInOrder(seq, elements, equality) } def containsInOrderOnly(seq: SEQ[E], elements: scala.collection.Seq[Any]): Boolean = { checkInOrderOnly[E](seq, elements, equality) } // TODO: Make elements a Sequencing def containsTheSameElementsInOrderAs(seq: SEQ[E], elements: GenTraversable[Any]): Boolean = { checkTheSameElementsInOrderAs[E](seq, elements, equality) } } import scala.language.implicitConversions /** * Implicit conversion that converts an Equality of type E * into Sequencing of type SEQ[E], where SEQ is a subtype of scala.collection.GenSeq. * This is required to support the explicit Equality syntax, for example: * *
   * (List("hi", "he") should contain inOrderOnly ("HI", "HE")) (after being lowerCased)
   * 
* * (after being lowerCased) will returns an Equality[String] * and this implicit conversion will convert it into Sequencing[List[String]]. * * @param equality Equality of type E * @tparam E type of elements in the scala.collection.GenSeq * @tparam SEQ subtype of scala.collection.GenSeq * @return Sequencing of type SEQ[E] */ implicit def convertEqualityToGenSeqSequencing[E, SEQ[e] <: scala.collection.GenSeq[e]](equality: Equality[E]): Sequencing[SEQ[E]] = sequencingNatureOfGenSeq(equality) /** * Implicit to support Sequencing nature of scala.collection.SortedSet. * * @param equality Equality type class that is used to check equality of element in the scala.collection.SortedSet * @tparam E the type of the element in the scala.collection.SortedSet * @tparam SET any subtype of scala.collection.SortedSet * @return Sequencing[SET[E]] that supports scala.collection.SortedSet in relevant contain syntax */ implicit def sequencingNatureOfSortedSet[E, SET[e] <: scala.collection.SortedSet[e]](implicit equality: Equality[E]): Sequencing[SET[E]] = new Sequencing[SET[E]] { def containsInOrder(set: SET[E], elements: scala.collection.Seq[Any]): Boolean = { checkInOrder(set, elements, equality) } def containsInOrderOnly(set: SET[E], elements: scala.collection.Seq[Any]): Boolean = { checkInOrderOnly[E](set, elements, equality) } def containsTheSameElementsInOrderAs(set: SET[E], elements: GenTraversable[Any]): Boolean = { checkTheSameElementsInOrderAs[E](set, elements, equality) } } /** * Implicit conversion that converts an Equality of type E * into Sequencing of type SET[E], where SET is a subtype of scala.collection.SortedSet. * This is required to support the explicit Equality syntax, for example: * *
   * (SortedSet("hi", "he") should contain inOrderOnly ("HI", "HE")) (after being lowerCased)
   * 
* * (after being lowerCased) will returns an Equality[String] * and this implicit conversion will convert it into Sequencing[SortedSet[String]]. * * @param equality Equality of type E * @tparam E type of elements in the scala.collection.SortedSet * @tparam SET subtype of scala.collection.SortedSet * @return Sequencing of type SET[E] */ implicit def convertEqualityToSortedSetSequencing[E, SET[e] <: scala.collection.SortedSet[e]](equality: Equality[E]): Sequencing[SET[E]] = sequencingNatureOfSortedSet(equality) /** * Implicit to support Sequencing nature of scala.collection.SortedMap. * * @param equality Equality type class that is used to check equality of element in the scala.collection.SortedMap * @tparam K the type of the key in the scala.collection.SortedMap * @tparam V the type of the value in the scala.collection.SortedMap * @tparam MAP any subtype of scala.collection.SortedMap * @return Sequencing[MAP[K, V]] that supports scala.collection.SortedMap in relevant contain syntax */ implicit def sequencingNatureOfSortedMap[K, V, MAP[k, v] <: scala.collection.SortedMap[k, v]](implicit equality: Equality[(K, V)]): Sequencing[MAP[K, V]] = new Sequencing[MAP[K, V]] { def containsInOrder(map: MAP[K, V], elements: scala.collection.Seq[Any]): Boolean = { checkInOrder(map, elements, equality) } def containsInOrderOnly(map: MAP[K, V], elements: scala.collection.Seq[Any]): Boolean = { checkInOrderOnly(map, elements, equality) } def containsTheSameElementsInOrderAs(map: MAP[K, V], elements: GenTraversable[Any]): Boolean = { checkTheSameElementsInOrderAs(map, elements, equality) } } /** * Implicit conversion that converts an Equality of type (K, V) * into Sequencing of type MAP[K, V], where MAP is a subtype of scala.collection.SortedMap. * This is required to support the explicit Equality syntax, for example: * *
   * // lowerCased needs to be implemented as Normalization[(K, V)]
   * (SortedMap("hi" -> "hi", "he" -> "he") should contain inOrderOnly ("HI" -> "HI", "HE" -> "HE")) (after being lowerCased)
   * 
* * (after being lowerCased) will returns an Equality[String] * and this implicit conversion will convert it into Sequencing[SortedMap[String, String]]. * * @param equality Equality of type (K, V) * @tparam K the type of the key in the scala.collection.SortedMap * @tparam V the type of the value in the scala.collection.SortedMap * @tparam MAP subtype of scala.collection.SortedMap * @return Sequencing of type MAP[K, V] */ implicit def convertEqualityToSortedMapSequencing[K, V, MAP[k, v] <: scala.collection.SortedMap[k, v]](equality: Equality[(K, V)]): Sequencing[MAP[K, V]] = sequencingNatureOfSortedMap(equality) /** * Implicit to support Sequencing 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 Sequencing[Array[E]] that supports Array in relevant contain syntax */ implicit def sequencingNatureOfArray[E](implicit equality: Equality[E]): Sequencing[Array[E]] = new Sequencing[Array[E]] { def containsInOrder(array: Array[E], elements: scala.collection.Seq[Any]): Boolean = { checkInOrder(new ArrayWrapper(array), elements, equality) } def containsInOrderOnly(array: Array[E], elements: scala.collection.Seq[Any]): Boolean = { checkInOrderOnly(new ArrayWrapper(array), elements, equality) } def containsTheSameElementsInOrderAs(array: Array[E], elements: GenTraversable[Any]): Boolean = { checkTheSameElementsInOrderAs[E](new ArrayWrapper(array), elements, equality) } } /** * Implicit conversion that converts an Equality of type E * into Sequencing of type Array[E]. * This is required to support the explicit Equality syntax, for example: * *
   * (Array("hi", "he") should contain inOrderOnly ("HI", "HE")) (after being lowerCased)
   * 
* * (after being lowerCased) will returns an Equality[String] * and this implicit conversion will convert it into Sequencing[Array[String]]. * * @param equality Equality of type E * @tparam E type of elements in the Array * @return Sequencing of type Array[E] */ implicit def convertEqualityToArraySequencing[E](equality: Equality[E]): Sequencing[Array[E]] = sequencingNatureOfArray(equality) /** * Implicit to support Sequencing nature of java.util.List. * * @param equality Equality type class that is used to check equality of element in the java.util.List * @tparam E the type of the element in the java.util.List * @tparam JLIST any subtype of java.util.List * @return Sequencing[JLIST[E]] that supports java.util.List in relevant contain syntax */ implicit def sequencingNatureOfJavaList[E, JLIST[e] <: java.util.List[e]](implicit equality: Equality[E]): Sequencing[JLIST[E]] = new Sequencing[JLIST[E]] { def containsInOrder(col: JLIST[E], elements: scala.collection.Seq[Any]): Boolean = { checkInOrder(col.asScala, elements, equality) } def containsInOrderOnly(col: JLIST[E], elements: scala.collection.Seq[Any]): Boolean = { checkInOrderOnly(col.asScala, elements, equality) } def containsTheSameElementsInOrderAs(col: JLIST[E], elements: GenTraversable[Any]): Boolean = { checkTheSameElementsInOrderAs(col.asScala, elements, equality) } } /** * Implicit conversion that converts an Equality of type E * into Sequencing of type JLIST[E], where JLIST is a subtype of java.util.List. * This is required to support the explicit Equality syntax, for example: * *
   * val javaList = new java.util.ArrayList[String]()
   * javaList.add("hi", "he")
   * (javaList should contain ("HI", "HE")) (after being lowerCased)
   * 
* * (after being lowerCased) will returns an Equality[String] * and this implicit conversion will convert it into Sequencing[java.util.ArrayList[String]]. * * @param equality Equality of type E * @tparam E type of elements in the java.util.List * @tparam JLIST subtype of java.util.List * @return Sequencing of type JLIST[E] */ implicit def convertEqualityToJavaListSequencing[E, JLIST[e] <: java.util.List[e]](equality: Equality[E]): Sequencing[JLIST[E]] = sequencingNatureOfJavaList(equality) /** * Implicit to support Sequencing nature of java.util.SortedSet. * * @param equality Equality type class that is used to check equality of element in the java.util.SortedSet * @tparam E the type of the element in the java.util.SortedSet * @tparam JSET any subtype of java.util.SortedSet * @return Sequencing[JSET[E]] that supports java.util.SortedSet in relevant contain syntax */ implicit def sequencingNatureOfJavaSortedSet[E, JSET[e] <: java.util.SortedSet[e]](implicit equality: Equality[E]): Sequencing[JSET[E]] = new Sequencing[JSET[E]] { def containsInOrder(set: JSET[E], elements: scala.collection.Seq[Any]): Boolean = { checkInOrder(set.iterator.asScala.toVector, elements, equality) } def containsInOrderOnly(set: JSET[E], elements: scala.collection.Seq[Any]): Boolean = { checkInOrderOnly[E](set.iterator.asScala.toVector, elements, equality) } def containsTheSameElementsInOrderAs(set: JSET[E], elements: GenTraversable[Any]): Boolean = { checkTheSameElementsInOrderAs[E](set.iterator.asScala.toVector, elements, equality) } } /** * Implicit conversion that converts an Equality of type E * into Sequencing of type JSET[E], where JSET is a subtype of java.util.SortedSet. * This is required to support the explicit Equality syntax, for example: * *
   * val javaSet = new java.util.TreeSet[String]()
   * javaSet.add("hi", "he")
   * (javaSet should contain inOrderOnly ("HI", "HE")) (after being lowerCased)
   * 
* * (after being lowerCased) will returns an Equality[String] * and this implicit conversion will convert it into Sequencing[java.util.TreeSet[String]]. * * @param equality Equality of type E * @tparam E type of elements in the java.util.List * @tparam JSET subtype of java.util.List * @return Sequencing of type JLIST[E] */ implicit def convertEqualityToJavaSortedSetSequencing[E, JSET[e] <: java.util.SortedSet[e]](equality: Equality[E]): Sequencing[JSET[E]] = sequencingNatureOfJavaSortedSet(equality) /** * Implicit to support Sequencing nature of java.util.SortedMap. * * @param equality Equality type class that is used to check equality of entry in the java.util.SortedMap * @tparam K the type of the key in the java.util.SortedMap * @tparam V the type of the value in the java.util.SortedMap * @tparam JMAP any subtype of java.util.SortedMap * @return Sequencing[JMAP[K, V]] that supports java.util.SortedMap in relevant contain syntax */ implicit def sequencingNatureOfJavaSortedMap[K, V, JMAP[k, v] <: java.util.SortedMap[k, v]](implicit equality: Equality[java.util.Map.Entry[K, V]]): Sequencing[JMAP[K, V]] = new Sequencing[JMAP[K, V]] { def containsInOrder(map: JMAP[K, V], elements: scala.collection.Seq[Any]): Boolean = { checkInOrder(map.entrySet.iterator.asScala.toVector, elements, equality) } def containsInOrderOnly(map: JMAP[K, V], elements: scala.collection.Seq[Any]): Boolean = { checkInOrderOnly(map.entrySet.iterator.asScala.toVector, elements, equality) } def containsTheSameElementsInOrderAs(map: JMAP[K, V], elements: GenTraversable[Any]): Boolean = { checkTheSameElementsInOrderAs(map.entrySet.iterator.asScala.toVector, elements, equality) } } /** * Implicit conversion that converts an Equality of type java.util.Map.Entry[K, V] * into Sequencing of type JMAP[K, V], where JMAP is a subtype of java.util.SortedMap. * This is required to support the explicit Equality syntax, for example: * *
   * val javaMap = new java.util.TreeMap[Int, String]()
   * javaMap.put(1, "one")
   * // lowerCased needs to be implemented as Normalization[java.util.Map.Entry[K, V]]
   * (javaMap should contain inOrderOnly (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 Aggregating[java.util.TreeMap[Int, String]]. * * @param equality Equality of type java.util.Map.Entry[K, V] * @tparam K the type of the key in the java.util.SortedMap * @tparam V the type of the value in the java.util.SortedMap * @tparam JMAP subtype of java.util.SortedMap * @return Sequencing of type JMAP[K, V] */ implicit def convertEqualityToJavaSortedMapSequencing[K, V, JMAP[k, v] <: java.util.SortedMap[k, v]](equality: Equality[java.util.Map.Entry[K, V]]): Sequencing[JMAP[K, V]] = sequencingNatureOfJavaSortedMap(equality) /** * Implicit to support Sequencing nature of String. * * @param equality Equality type class that is used to check equality of Char in the String * @return Sequencing[String] that supports String in relevant contain syntax */ implicit def sequencingNatureOfString(implicit equality: Equality[Char]): Sequencing[String] = new Sequencing[String] { def containsInOrder(s: String, elements: scala.collection.Seq[Any]): Boolean = { checkInOrder(s, elements, equality) } def containsInOrderOnly(s: String, elements: scala.collection.Seq[Any]): Boolean = { checkInOrderOnly(s, elements, equality) } def containsTheSameElementsInOrderAs(s: String, elements: GenTraversable[Any]): Boolean = { checkTheSameElementsInOrderAs(s, elements, equality) } } /** * Implicit conversion that converts an Equality of type Char * into Sequencing 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 inOrderOnly ('E')) (after being lowerCased)
   * 
* * (after being lowerCased) will returns an Equality[Char] * and this implicit conversion will convert it into Sequencing[String]. * * @param equality Equality of type Char * @return Sequencing of type String */ implicit def convertEqualityToStringSequencing(equality: Equality[Char]): Sequencing[String] = sequencingNatureOfString(equality) /** * Implicit to support Sequencing 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 Sequencing[Every[E]] that supports Every in relevant contain syntax */ implicit def sequencingNatureOfEvery[E](implicit equality: Equality[E]): Sequencing[Every[E]] = new Sequencing[Every[E]] { def containsInOrder(every: Every[E], elements: scala.collection.Seq[Any]): Boolean = checkInOrder(every, elements, equality) def containsInOrderOnly(every: Every[E], elements: scala.collection.Seq[Any]): Boolean = checkInOrderOnly(every, elements, equality) def containsTheSameElementsInOrderAs(every: Every[E], elements: GenTraversable[Any]): Boolean = checkTheSameElementsInOrderAs[E](every, elements, equality) } /** * Implicit conversion that converts an Equality of type E * into Sequencing of type Every[E]. * This is required to support the explicit Equality syntax, for example: * *
   * (Every("hi", "he") should contain inOrderOnly ("HI", "HE")) (after being lowerCased)
   * 
* * (after being lowerCased) will returns an Equality[String] * and this implicit conversion will convert it into Sequencing[Every[String]]. * * @param equality Equality of type E * @tparam E type of elements in the Every * @return Sequencing of type Every[E] */ implicit def convertEqualityToEverySequencing[E](equality: Equality[E]): Sequencing[Every[E]] = sequencingNatureOfEvery(equality) }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy