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

org.scalatest.AppendedClues.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.Requirements._
import exceptions.ModifiableMessage

/**
 * Trait providing an implicit conversion that allows clues to be placed after a block of code.
 *
 * 

* You can use the withClue construct provided by Assertions, which is * extended by every style trait in ScalaTest, to add extra information to reports of failed or canceled tests. * The withClue from Assertions places the "clue string" at the front, both * in the code and in the resulting message: * *

 * withClue("This is a prepended clue;") {
 *   1 + 1 should equal (3)
 * }
 * 
* *

* The above expression will yield the failure message: *

* *

* This is a prepended clue; 2 did not equal 3 *

* *

* If you mix in this trait, or import its members via its companion object, you can alternatively place * the clue string at the end, like this: *

* *
 * { 1 + 1 should equal (3) } withClue "now the clue comes after"
 * 
* *

* The above expression will yield the failure message: *

* *

* 2 did not equal 3 now the clue comes after *

* *

* If no space is already present, either at the beginning of the clue string or at the end * of the current message, a space will be placed between the two, unless the clue string * starts with one of the punctuation characters: comma (,), period (.), * or semicolon (;). For example, the failure message in the above example * includes an extra space inserted between 3 and now. *

* *

* By contrast this code, which has a clue string starting with comma: *

* *
 * { 1 + 1 should equal (3) } withClue ", now the clue comes after"
 * 
* *

* Will yield a failure message with no extra inserted space: *

* *

* 2 did not equal 3, now the clue comes after *

* *

* The withClue method will only append the clue string to the detail * message of exception types that mix in the ModifiableMessage trait. * See the documentation for ModifiableMessage for more * information. *

* *

* Note: the reason this functionality is not provided by Assertions directly, like the * prepended withClue construct, is because appended clues require an implicit conversion. * ScalaTest only gives you one implicit conversion by default in any test class to minimize the * potential for conflicts with other implicit conversions you may be using. All other implicit conversions, * including the one provided by this trait, you must explicitly invite into your code through inheritance * or an import. *

* * @author Bill Venners */ trait AppendedClues { /** * Class that provides a withClue method that appends clue strings to any * ModifiableMessage exception * thrown by the passed by-name parameter. * * @author Bill Venners */ class Clueful[T](fun: => T) { /** * Executes the block of code passed as the constructor parameter to this Clueful, and, if it * completes abruptly with a ModifiableMessage exception, * appends the "clue" string passed to this method to the end of the detail message * of that thrown exception, then rethrows it. If clue does not begin in a white space * character or one of the punctuation characters: comma (,), * period (.), or semicolon (;), one space will be added * between it and the existing detail message (unless the detail message is * not defined). * *

* This method allows you to add more information about what went wrong that will be * reported when a test fails or cancels. For example, this code: *

* *
     * { 1 + 1 should equal (3) } withClue ", not even for very large values of 1"
     * 
* *

* Would yield a TestFailed exception whose message would be: *

* *
     * 2 did not equal 3, not even for very large values of 1
     * 
* * @throws NullArgumentException if the passed clue is null */ def withClue(clue: Any): T = { requireNonNull(clue) def append(currentMessage: Option[String]) = currentMessage match { case Some(msg) => Some(AppendedClues.appendClue(msg, clue.toString)) case None => Some(clue.toString) } try { val outcome = fun outcome match { case Failed(e: org.scalatest.exceptions.ModifiableMessage[_]) if clue.toString != "" => Failed(e.modifyMessage(append)).asInstanceOf[T] case Canceled(e: org.scalatest.exceptions.ModifiableMessage[_]) if clue.toString != "" => Canceled(e.modifyMessage(append)).asInstanceOf[T] case _ => outcome } } catch { case e: ModifiableMessage[_] => if (clue.toString != "") throw e.modifyMessage(append) else throw e.asInstanceOf[Throwable] // Safe cast because self type is Throwable, but Dotty is stricter and does not allow this without the cast. } } } import scala.language.implicitConversions /** * Implicit conversion that allows clues to be place after a block of code. */ implicit def convertToClueful[T](fun: => T): Clueful[T] = new Clueful(fun) } /** * Companion object that facilitates the importing of AppendedClues members as * an alternative to mixing it in. One use case is to import AppendedClues * members so you can use them in the Scala interpreter. */ object AppendedClues extends AppendedClues { private[scalatest] def appendClue(original: String, clue: String): String = clue.toString.headOption match { case Some(firstChar) if firstChar.isWhitespace || firstChar == '.' || firstChar == ',' || firstChar == ';' => original + clue case _ => original + " " + clue } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy