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

org.scalatest.Inside.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

import org.scalatest.exceptions.TestFailedException
import org.scalatest.exceptions.StackDepthException
import scala.annotation.tailrec
import org.scalactic.{Resources => _, _}

/**
 * Trait containing the inside construct, which allows you to make statements about nested object graphs using pattern matching.
 *
 * 

* For example, given the following case classes: * *

 * case class Address(street: String, city: String, state: String, zip: String)
 * case class Name(first: String, middle: String, last: String)
 * case class Record(name: Name, address: Address, age: Int)
 * 
* * You could write: * *
 * inside (rec) { case Record(name, address, age) =>
 *   inside (name) { case Name(first, middle, last) =>
 *     first should be ("Sally")
 *     middle should be ("Ann")
 *     last should be ("Jones")
 *   }
 *   inside (address) { case Address(street, city, state, zip) =>
 *     street should startWith ("25")
 *     city should endWith ("Angeles")
 *     state should equal ("CA")
 *     zip should be ("12345")
 *   }
 *   age should be < 99
 * }
 * 
* *

* If an assertion fails, the error message will include the toString of each value passed * to inside clauses enclosing the failed assertion. For example, if rec in * the previous expression was defined like this: *

* *
 * val rec = Record(
 *   Name("Sally", "Anna", "Jones"),
 *   Address("25 Main St", "Los Angeles", "CA", "12345"),
 *   38
 * )
 * 
* *

* The error message will read: *

* *
 * "Ann[a]" was not equal to "Ann[]", inside Name(Sally,Anna,Jones),
 * inside Record(Name(Sally,Anna,Jones),Address(25 Main St,Los Angeles,CA,12345),38)
 * 
* */ trait Inside { /** * Inspects inside the passed value using the passed partial function. * *

* The inside method checks to see whether the partial function passed as the second curried * parameter is defined at the value passed as the first parameter, and if so, passes that value to the * partial function. *

* *

* If the partial function is not defined at the passed value, inside will throw a * TestFailedException with a detail message describing the problem. Otherwise, if the * partial function returns normally, inside will return normally. If the partial function * completes abruptly with an exception that mixes in ModifiableMessage (such as * TestFailedException), inside will append the value's toString of * to the exception's detail message, and rethrow it. If the exception thrown by the partial function does not mix * in ModifiableMessage, inside completes abruptly with that same exception. *

* * @param value the value inside of which to inspect * @param pf the partial function to use to inspect inside the passed value * @throws TestFailedException if the passed partial function is not defined at the passed value */ inline def inside[T, U](value: T)(pf: PartialFunction[T, U]): U = ${ Inside.insideMacro('{value})('{pf}) } } /** * Companion object that facilitates the importing of the inside construct as * an alternative to mixing it in. One use case is to import the inside construct so you can use * it in the Scala interpreter: * *
 * $ scala -cp scalatest-1.8.jar
 * Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_29).
 * Type in expressions to have them evaluated.
 * Type :help for more information.
 * 
 * scala> import org.scalatest._
 * import org.scalatest._
 * 
 * scala> import matchers.Matchers._
 * import matchers.Matchers._
 * 
 * scala> import Inside._
 * import Inside._
 * 
 * scala> inside (List(1, 2, 3)) { case List(x, y, z) =>
 *     |   y should equal (2)
 *     | }
 *
 * scala> inside (List(1, 2, 3)) { case List(x, y, z) =>
 *      |   x should equal (2)
 *      | }
 * org.scalatest.TestFailedException: 1 did not equal 2, inside List(1, 2, 3)
 *   at org.scalatest.matchers.Matchers$class.newTestFailedException(Matchers.scala:150)
 *   at org.scalatest.matchers.Matchers$.newTestFailedException(Matchers.scala:2331)
 *   at org.scalatest.matchers.Matchers$ShouldMethodHelper$.shouldMatcher(Matchers.scala:873)
 *   ...
 * 
*/ object Inside extends Inside { private val level = new ThreadLocal[Int] def insideWithPos[T, U](value: T, pf: PartialFunction[T, U], pos: source.Position): U = { def appendInsideMessage(currentMessage: Option[String]) = { val st = Thread.currentThread.getStackTrace /*val levelCount = st.count { elem => elem.getClassName == "org.scalatest.Inside$class" && elem.getMethodName == "inside" }*/ val levelCount = Inside.level.get val indentation = " " * (levelCount) currentMessage match { case Some(msg) => Some(Resources.insidePartialFunctionAppendSomeMsg(msg.trim, indentation, value.toString())) case None => Some(Resources.insidePartialFunctionAppendNone(indentation, value.toString())) } } Inside.level.set(Option(Inside.level.get).getOrElse(0) + 1) if (pf.isDefinedAt(value)) { try { val result = pf(value) Inside.level.set(Inside.level.get - 1) result } catch { case e: org.scalatest.exceptions.ModifiableMessage[_] => Inside.level.set(Inside.level.get - 1) throw e.modifyMessage(appendInsideMessage) } } else { Inside.level.set(Inside.level.get - 1) throw new TestFailedException((_: StackDepthException) => Some(Resources.insidePartialFunctionNotDefined(value.toString())), None, pos) } } import scala.quoted._ private[scalatest] def insideMacro[T, U](value: Expr[T])(pf: Expr[PartialFunction[T, U]])(using quotes: Quotes, typeT: Type[T], typeU: Type[U]): Expr[U] = { source.Position.withPosition[U]('{(pos: source.Position) => insideWithPos(${value}, ${pf}, pos) }) } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy