org.scalatest.LoneElement.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
import org.scalactic.{FailureMessages => _, _}
import enablers.Collecting
import org.scalatest.exceptions._
/**
* Trait that provides an implicit conversion that adds to collection types a loneElement
method, which
* will return the value of the lone element if the collection does
* indeed contain one and only one element, or throw TestFailedException
if not.
*
*
* This construct allows you to express in one statement that a collection should contain one and only one element
* and that the element value should meet some expectation. Here's an example:
*
*
*
* set.loneElement should be > 9
*
*
*
* Or, using an assertion instead of a matcher expression:
*
*
*
* assert(set.loneElement > 9)
*
*
*
* The loneElement
syntax can be used with any collection type C
for which an
* implicit Collecting[C]
is available. ScalaTest provides
* implicit Collecting
instances for scala.collection.GenTraversable
, Array
,
* and java.util.Collection
. You can enable the loneElement
* syntax on other collection types by defining an implicit Collecting
instances for those types.
*
*
*
* If you want to use loneElement
with a java.util.Map
, first transform it to a
* set of entries with entrySet
, and if helpful, use ScalaTest's Entry
class:
*
*
*
* scala> import org.scalatest._
* import org.scalatest._
*
* scala> import LoneElement._
* import LoneElement._
*
* scala> import Matchers._
* import Matchers._
*
* scala> val jmap = new java.util.HashMap[String, Int]
* jmap: java.util.HashMap[String,Int] = {}
*
* scala> jmap.put("one", 1)
* res0: Int = 0
*
* scala> jmap.entrySet.loneElement should be (Entry("one", 1))
*
*
* @author Bill Venners
*/
trait LoneElement {
import scala.language.higherKinds
/**
* Wrapper class that adds a loneElement
method to any collection type C
for which
* an implicit Collecting[C]
is available.
*
*
* Through the implicit conversion provided by trait LoneElement
, this class allows you to make statements like:
*
*
*
* trav.loneElement should be > 9
*
*
* @tparam E the element type of the collection on which to add the loneElement
method
* @tparam CTC the "collection type constructor" for the collection on which to add the loneElement
method
* @param collection a collection to wrap in a LoneElementCollectionWrapper
, which provides the loneElement
method.
* @param collecting a typeclass that enables the loneElement
syntax
*/
final class LoneElementCollectionWrapper[E, CTC[_]](collection: CTC[E], collecting: Collecting[E, CTC[E]], prettifier: Prettifier, pos: source.Position) {
/**
* Returns the value contained in the wrapped collection, if it contains one and only one element, else throws TestFailedException
with
* a detail message describing the problem.
*
*
* This method enables syntax such as the following:
*
*
*
* trav.loneElement should be > 9
* ^
*
*/
def loneElement: E = {
collecting.loneElementOf(collection) match {
case Some(ele) => ele
case None =>
throw new TestFailedException(
(_: StackDepthException) => Some(FailureMessages.notLoneElement(prettifier,
collection,
collecting.sizeOf(collection))),
None,
pos
)
}
}
}
import scala.language.implicitConversions
/**
* Implicit conversion that adds a loneElement
method to any collection type C
for which an
* implicit Collecting[C]
is available.
*
* @tparam E the element type of the collection on which to add the loneElement
method
* @tparam CTC the "collection type constructor" for the collection on which to add the loneElement
method
* @param collection the collection on which to add the loneElement
method
* @param collecting a typeclass that enables the loneElement
syntax
*/
implicit def convertToCollectionLoneElementWrapper[E, CTC[_]](collection: CTC[E])(implicit collecting: Collecting[E, CTC[E]], prettifier: Prettifier, pos: source.Position): LoneElementCollectionWrapper[E, CTC] = new LoneElementCollectionWrapper[E, CTC](collection, collecting, prettifier, pos)
/**
* Wrapper class that adds a loneElement
method to Map for which
* an implicit Collecting[(K, V), scala.collection.GenTraversable[(K, V)]]
is available.
*
*
* Through the implicit conversion provided by trait LoneElement
, this class allows you to make statements like:
*
*
*
* map.loneElement._1 should be > 9
*
*
* @tparam K the element type of the Map key on which to add the loneElement
method
* @tparam V the element type of the Map value on which to add the loneElement
method
* @tparam MAP the "Map type constructor" for the collection on which to add the loneElement
method
* @param collecting a typeclass that enables the loneElement
syntax
*/
final class LoneElementMapWrapper[K, V, MAP[k, v] <: scala.collection.GenMap[k, v]](map: MAP[K, V], collecting: Collecting[(K, V), scala.collection.GenTraversable[(K, V)]], prettifier: Prettifier, pos: source.Position) {
def loneElement: (K, V) = {
collecting.loneElementOf(map) match {
case Some(ele) => ele
case None =>
throw new TestFailedException(
(_: StackDepthException) => Some(FailureMessages.notLoneElement(prettifier,
map,
collecting.sizeOf(map))),
None,
pos
)
}
}
}
// Needed for Map to work, any better solution?
implicit def convertMapToCollectionLoneElementWrapper[K, V, MAP[k, v] <: scala.collection.GenMap[k, v]](map: MAP[K, V])(implicit collecting: Collecting[(K, V), scala.collection.GenTraversable[(K, V)]], prettifier: Prettifier, pos: source.Position): LoneElementMapWrapper[K, V, MAP] = {
new LoneElementMapWrapper[K, V, MAP](map, collecting, prettifier, pos)
}
/**
* Wrapper class that adds a loneElement
method to Java Map for which
* an implicit Collecting[org.scalatest.Entry, java.util.Map]
is available.
*
*
* Through the implicit conversion provided by trait LoneElement
, this class allows you to make statements like:
*
*
*
* jmap.loneElement.getKey should be > 9
*
*
* @tparam K the element type of the Java Map key on which to add the loneElement
method
* @tparam V the element type of the Java Map value on which to add the loneElement
method
* @tparam JMAP the "Java Map type constructor" for the collection on which to add the loneElement
method
* @param collecting a typeclass that enables the loneElement
syntax
*/
final class LoneElementJavaMapWrapper[K, V, JMAP[_, _] <: java.util.Map[_, _]](jmap: JMAP[K, V], collecting: Collecting[org.scalatest.Entry[K, V], JMAP[K, V]], prettifier: Prettifier, pos: source.Position) {
def loneElement: org.scalatest.Entry[K, V] = {
collecting.loneElementOf(jmap) match {
case Some(ele) => ele
case None =>
throw new TestFailedException(
(_: StackDepthException) => Some(FailureMessages.notLoneElement(prettifier,
jmap,
collecting.sizeOf(jmap))),
None,
pos
)
}
}
}
// Needed for Java Map to work, any better solution?
implicit def convertJavaMapToCollectionLoneElementWrapper[K, V, JMAP[_, _] <: java.util.Map[_, _]](jmap: JMAP[K, V])(implicit collecting: Collecting[org.scalatest.Entry[K, V], JMAP[K, V]], prettifier: Prettifier, pos: source.Position): LoneElementJavaMapWrapper[K, V, JMAP] = {
new LoneElementJavaMapWrapper[K, V, JMAP](jmap, collecting, prettifier, pos)
}
/**
* Wrapper class that adds a loneElement
method to String
for which an
* implicit Collecting[C]
is available.
*
*
* Through the implicit conversion provided by trait LoneElement
, this class allows you to make statements like:
*
*
*
* "9".loneElement should be ('9')
*
*
* @param s the String
to wrap
* @param collecting a typeclass that enables the loneElement
syntax
*/
final class LoneElementStringWrapper(s: String, prettifier: Prettifier, pos: source.Position) {
def loneElement: Char = {
if (s.length == 1)
s.charAt(0)
else
throw new TestFailedException(
(_: StackDepthException) => Some(FailureMessages.notLoneElement(prettifier,
s,
s.length)),
None,
pos
)
}
}
/**
* Implicit conversion that adds a loneElement
method to String for which an
* implicit Collecting[C]
is available.
*
* @param s the String
to wrap
* @param collecting a typeclass that enables the loneElement
syntax
*/
implicit def convertToStringLoneElementWrapper(s: String)(implicit prettifier: Prettifier, pos: source.Position): LoneElementStringWrapper =
new LoneElementStringWrapper(s, prettifier, pos)
}
/**
* Companion object that facilitates the importing of LoneElement
members as
* an alternative to mixing it in. One use case is to import LoneElement
's members so you can use
* loneElement
in the Scala interpreter.
*/
object LoneElement extends LoneElement