org.scalatest.Outcome.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._ import Requirements._ import org.scalatest.exceptions.StackDepthException /** * Superclass for the possible outcomes of running a test. * *Canceled outcomes, * extracting the contained exception. * **
* *Outcomeis the result type of thewithFixturemethods of traits *Suiteandfixture.Suite, as well as their *NoArgTestandOneArgTestfunction types. * The four possible outcomes are: **
* *- *
Succeeded- indicates a test succeeded- *
Failed- indicates a test failed and contains an exception describing the failure- *
Canceled- indicates a test was canceled and contains an exception describing the cancelation- *
Pending- indicates a test was pending* Note that "ignored" does not appear as a type of
*/ sealed abstract class Outcome extends Product with Serializable { /** * Indicates whether thisOutcome, because tests are * marked as ignored on the outside and skipped over as the suite executes. So an ignored test never runs, and therefore * never has an outcome. By contrast, a test is determined to be pending by running the test * and observing the actual outcome. If the test body completes abruptly with aTestPendingException, * then the outcome was that the test was pending. *Outcomerepresents a test that succeeded. * ** This class's implementation of this method always returns
* * @return true if thisfalse. *Outcomeis an instance ofSucceeded. */ val isSucceeded: Boolean = false /** * Indicates whether thisOutcomerepresents a test that failed. * ** This class's implementation of this method always returns
* * @return true if thisfalse. *Outcomeis an instance ofFailed. */ val isFailed: Boolean = false /** * Indicates whether thisOutcomerepresents a test that was canceled. * ** This class's implementation of this method always returns
* * @return true if thisfalse. *Outcomeis an instance ofCanceled. */ val isCanceled: Boolean = false /** * Indicates whether thisOutcomerepresents a test that was pending. * ** This class's implementation of this method always returns
* * @return true if thisfalse. *Outcomeis an instance ofPending. */ val isPending: Boolean = false /** * Indicates whether thisOutcomerepresents a test that either failed or was canceled, in which case thisOutcomewill contain an exception. * * @return true if thisOutcomeis an instance of eitherFailedorCanceled. */ val isExceptional: Boolean = false /** * Converts thisOutcometo anOption[Throwable]. * ** This class's implementation of this method always returns
* * @return aNone. *Somewrapping the contained exception if thisOutcomeis an instance of eitherFailedorCanceled. */ def toOption: Option[Throwable] = None /** * Converts thisOutcometo aSucceeded. * ** When this
* *Outcomeinstance is not Succeeded, it behaves as followed: **
* * @return Succeeded if this- Failed(ex) - throws ex
*- Canceled(tce) - throws tce
*- Pending - throws TestPendingException
*Outcomeinstance is a Succeeded. */ def toSucceeded: Succeeded.type // Used internally to resuse the old code that was catching these exceptions when running tests. Eventually I would // like to rewrite that old code to use the result type, but it will still needs to catch and handle these exceptions // in the same way in case they come back from a user's withFixture implementation. private[scalatest] def toUnit: Unit = { this match { case Succeeded => case Exceptional(e) => throw e case Pending => throw new exceptions.TestPendingException } } } /** * Companion object for traitOutcomethat contains an implicit method that enables * collections ofOutcomes to be flattened into a collections of contained exceptions. */ object Outcome { import scala.language.implicitConversions /** * Enables collections ofOutcomes to be flattened into a collections of contained exceptions. * * ** Here's an example: *
* ** scala> import org.scalatest._ * import org.scalatest._ * * scala> import prop.TableDrivenPropertyChecks._ * import prop.TableDrivenPropertyChecks._ * * scala> val squares = // (includes errors) * | Table( * | ("x", "square"), * | ( 0 , 0 ), * | ( 1 , 1 ), * | ( 2 , 4 ), * | ( 3 , 8 ), * | ( 4 , 16 ), * | ( 5 , 26 ), * | ( 6 , 36 ) * | ) * squares: org.scalatest.prop.TableFor2[Int,Int] = * TableFor2((x,square), (0,0), (1,1), (2,4), (3,8), (4,16), (5,26), (6,36)) ** ** Given the above table, which includes some errors, you can obtain an
* *IndexedSeqof theOutcomes * of executing an assertion on each row of the table withoutcomeOf, like this: ** scala> import OutcomeOf._ * import OutcomeOf._ * * scala> import Matchers._ * import Matchers._ * * scala> val outcomes = for ((x, square) <- squares) yield outcomeOf { square shouldEqual x * x } * outcomes: IndexedSeq[org.scalatest.Outcome] = * Vector(Succeeded, Succeeded, Succeeded, * Failed(org.scalatest.exceptions.TestFailedException: 8 did not equal 9), Succeeded, * Failed(org.scalatest.exceptions.TestFailedException: 26 did not equal 25), Succeeded) ** ** Now you have a collection of all the outcomes, including successful ones. If you just want the
* *FailedandCanceledoutcomes, which * contain exceptions, you can filter out anything that isn't "exceptional," like this: ** scala> outcomes.filter(_.isExceptional) * res1: IndexedSeq[org.scalatest.Outcome] = * Vector(Failed(org.scalatest.exceptions.TestFailedException: 8 did not equal 9), * Failed(org.scalatest.exceptions.TestFailedException: 26 did not equal 25)) ** ** But if you just wanted the contained exceptions, you can (thanks to this implicit method) invoke
* *flattenon your collection: ** scala> outcomes.flatten * res2: IndexedSeq[Throwable] = * Vector(org.scalatest.exceptions.TestFailedException: 8 did not equal 9, * org.scalatest.exceptions.TestFailedException: 26 did not equal 25) **/ implicit def convertOutcomeToIterator(outcome: Outcome): Iterator[Throwable] = outcome match { case Exceptional(ex) => // Return an iterator with one Throwable in it new Iterator[Throwable] { private var spent: Boolean = false def hasNext: Boolean = !spent def next: Throwable = if (!spent) { spent = true ex } else throw new NoSuchElementException } case _ => // Return an empty iterator new Iterator[Throwable] { def hasNext: Boolean = false def next: Throwable = throw new NoSuchElementException } } } /** * Superclass for the two outcomes of running a test that contain an exception:FailedandCanceled. * ** This class provides a
* *toOptionmethod that returns aSomewrapping the contained exception, and * anisExceptionalfield with the valuetrue. It's companion object provides an extractor that * enables patterns that match a test that either failed or canceled, as in: ** outcome match { * case Exceptional(ex) => // handle failed or canceled case * case _ => // handle succeeded, pending, or omitted case * } ** * @param ex theThrowablecontained in thisExceptional. */ sealed abstract class Exceptional(ex: Throwable) extends Outcome { /** * Indicates that thisOutcomerepresents a test that either failed or was canceled. * * @return true */ override val isExceptional: Boolean = true /** * Converts thisExceptionalto aSomethat wraps the contained exception. * * @return ASomewrapping the exception contained in thisExceptional. */ override def toOption: Option[Throwable] = Some(ex) } /** * Companion object to classExceptionalthat provides a factory method and an extractor that enables * patterns that match bothFailedandCanceledoutcomes and * extracts the contained exception and a factory method. */ object Exceptional { /** * Creates anExceptionalinstance given the passedThrowable. * ** If the passed
* *Throwableis an instance ofTestCanceledException, this * method will returnCanceledcontaining thatTestCanceledException. Otherwise, * it returns aFailedcontaining theThrowable. ** For example, trait
* *SeveredStackTracesuses this * factory method to sever the stack trace of the exception contained in either aFailedandCanceled* like this: ** abstract override def withFixture(test: NoArgTest): Outcome = { * super.withFixture(test) match { * case Exceptional(e: StackDepth) => Exceptional(e.severedAtStackDepth) * case o => o * } * } ** * @return aFailedorCanceledcontaining the passed exception. */ def apply(e: Throwable): Exceptional = e match { case tce: exceptions.TestCanceledException => Canceled(tce) case _ => Failed(e) } /** * Extractor enabling patterns that match bothFailedand* For example, trait
* *SeveredStackTracesuses this * extractor to sever the stack trace of the exception contained in either aFailedandCanceled* like this: ** abstract override def withFixture(test: NoArgTest): Outcome = { * super.withFixture(test) match { * case Exceptional(e: StackDepth) => Exceptional(e.severedAtStackDepth) * case o => o * } * } ** * @param res theOutcometo extract the throwable from. * @return aSomewrapping the contained throwable ifresis an instance of * eitherFailedorCanceled, elseNone. */ def unapply(res: Outcome): Option[Throwable] = res match { case Failed(ex) => Some(ex) case Canceled(ex) => Some(ex) case _ => None } } /** * Outcome for a test that succeeded. * ** Note: the difference between this
*/ case object Succeeded extends Outcome with compatible.Assertion { /** * Indicates that thisSucceededobject and the similarly namedSucceededStatus* object is that this object indicates one test (or assertion) succeeded, whereas theSucceededStatusobject indicates the absence of any failed tests or * aborted suites during a run. Both are used as the result type ofSuitelifecycle methods, butSucceeded* is a possible result ofwithFixture, whereasSucceededStatusis a possible result ofrun,runNestedSuites, *runTests, orrunTest. In short,Succeededis always just about one test (or assertion), whereasSucceededStatuscould be * about something larger: multiple tests or an entire suite. *Outcomerepresents a test that succeeded. * ** This class's implementation of this method always returns
* * @return true */ override val isSucceeded: Boolean = true /** * Converts thistrue. *Outcometo aSucceeded. * * @return This Succeeded instance. */ def toSucceeded: Succeeded.type = this } /** * Outcome for a test that failed, containing an exception describing the cause of the failure. * ** Note: the difference between this
* * @param ex theFailedclass and the similarly namedFailedStatus* object is that an instance of this class indicates one test failed, whereas theFailedStatusobject indicates either one or more tests failed * and/or one or more suites aborted during a run. Both are used as the result type ofSuitelifecycle methods, butFailed* is a possible result ofwithFixture, whereasFailedStatusis a possible result ofrun,runNestedSuites, *runTests, orrunTest. In short,Failedis always just about one test, whereasFailedStatuscould be * about something larger: multiple tests or an entire suite. *Throwablecontained in thisFailed. */ case class Failed(exception: Throwable) extends Exceptional(exception) { require(!exception.isInstanceOf[exceptions.TestCanceledException], "a TestCanceledException was passed to Failed's constructor") require(!exception.isInstanceOf[exceptions.TestPendingException], "a TestPendingException was passed to Failed's constructor") /** * Indicates that thisOutcomerepresents a test that failed. * ** This class's implementation of this method always returns
* * @return true */ override val isFailed: Boolean = true /** * Converts thistrue. *Outcometo aSucceeded. * ** The implmentation of this class will re-throw the passed in exception. *
*/ def toSucceeded: Succeeded.type = throw exception } object Failed { /** * Creates aFailedinstance, with aTestFailedExceptionset as itsexceptionfield. * * @return An instance ofFailedwith aTestFailedExceptionset as itsexceptionfield. */ def apply()(implicit pos: source.Position): Failed = new Failed(new exceptions.TestFailedException((_: StackDepthException) => None, None, pos)) /** * Creates aFailedinstance with the passed in message. * * @param message the message for theTestFailedExceptionset as itsexceptionfield * @return An instance ofFailedwith aTestFailedExceptioncreated from passed inmessageset as itsexceptionfield. */ def apply(message: String)(implicit pos: source.Position): Failed = new Failed(new exceptions.TestFailedException((_: StackDepthException) => Some(message), None, pos)) /** * Creates aFailedinstance with the passed in message and cause. * * @param message the message for theTestFailedExceptionset as itsexceptionfield * @param cause the cause for theTestFailedExceptionset as itsexceptionfield * @return An instance ofFailedwith aTestFailedExceptioncreated from passed inmessageandcauseset as itsexceptionfield. */ def apply(message: String, cause: Throwable)(implicit pos: source.Position): Failed = { // I always wrap this in a TFE because I need to do that to get the message in there. require(!cause.isInstanceOf[exceptions.TestCanceledException], "a TestCanceledException was passed to a factory method in object Failed") require(!cause.isInstanceOf[exceptions.TestPendingException], "a TestPendingException was passed to a factory method in object Failed") new Failed(new exceptions.TestFailedException((_: StackDepthException) => Some(message), Some(cause), pos)) } /** * Creates aFailedwith the passed in cause. * * @param cause the passed in cause * @return AFailedwithexceptionfield set to a newly createdTestFailedExceptionusing the passed incause. */ def here(cause: Throwable)(implicit pos: source.Position): Failed = { require(!cause.isInstanceOf[exceptions.TestCanceledException], "a TestCanceledException was passed to the \"here\" factory method in object Failed") require(!cause.isInstanceOf[exceptions.TestPendingException], "a TestPendingException was passed to the \"here\" factory method in object Failed") new Failed( if (cause.getMessage != null) new exceptions.TestFailedException((_: StackDepthException) => Some(cause.getMessage), Some(cause), pos) else new exceptions.TestFailedException((_: StackDepthException) => None, Some(cause), pos) ) } } /** * Outcome for a test that was canceled, containing an exception describing the cause of the cancelation. * * @param ex theTestCanceledExceptioncontained in thisExceptional. */ case class Canceled(exception: exceptions.TestCanceledException) extends Exceptional(exception) { /** * Indicates that thisOutcomerepresents a test that was canceled. * ** This class's implementation of this method always returns
* * @return true */ override val isCanceled: Boolean = true /** * Converts thistrue. *Outcometo aSucceeded. * ** The implmentation of this class will re-throw the passed in exception. *
*/ def toSucceeded: Succeeded.type = throw exception } /** * Companion object to classCanceledthat provides, in addition to the extractor and factory method * provided by the compiler given its companion is a case class, a second factory method * that produces aCanceledoutcome given a string message. */ object Canceled { /** * Creates aCanceledinstance, with aTestCanceledExceptionset as itsexceptionfield. * * @return An instance ofCanceledwith aTestCanceledExceptionset as itsexceptionfield. */ def apply()(implicit pos: source.Position): Canceled = new Canceled(new exceptions.TestCanceledException((_: StackDepthException) => None, None, Left(pos), None)) /** * Creates aCanceledinstance with the passed in message and cause. * * @param message the message for theTestCanceledExceptionset as itsexceptionfield * @param cause the cause for theTestCanceledExceptionset as itsexceptionfield * @return An instance ofCanceledwith aTestCanceledExceptioncreated from passed inmessageandcauseset as itsexceptionfield. */ def apply(message: String, cause: Throwable)(implicit pos: source.Position): Canceled = // TODO write tests for NPEs new Canceled(new exceptions.TestCanceledException((_: StackDepthException) => Some(message), Some(cause), Left(pos), None)) /** * Creates aCanceledinstance with the passed inThrowable. If the passed inThrowableis aTestCanceledException, * it will be set asexceptionfield, in other case a newTestCanceledExceptionwill be created usingexas itscause* * @param ex the passed inThrowable* @return An instance ofCanceledwithexset as itsexceptionfield ifexis aTestCanceledException, or a newly createdTestCanceledExceptionwithexset as itscauseifexis not aTestCanceledException. */ def apply(ex: Throwable)(implicit pos: source.Position): Canceled = { // TODO write tests for NPEs ex match { case tce: exceptions.TestCanceledException => new Canceled(tce) case _ => val msg = ex.getMessage if (msg == null) new Canceled(new exceptions.TestCanceledException((_: StackDepthException) => None, Some(ex), Left(pos), None)) else new Canceled(new exceptions.TestCanceledException((_: StackDepthException) => Some(msg), Some(ex), Left(pos), None)) } } /** * Creates aCanceledoutcome given a string message. * ** For example, trait
* *CancelAfterFailureuses this factory method to create * aCanceledstatus if acancelRemainingflag is set, which will * be the case if a test failed previously while running the suite: ** abstract override def withFixture(test: NoArgTest): Outcome = { * if (cancelRemaining) * Canceled("Canceled by CancelOnFailure because a test failed previously") * else * super.withFixture(test) match { * case failed: Failed => * cancelRemaining = true * failed * case outcome => outcome * } * } **/ def apply(message: String)(implicit pos: source.Position): Canceled = { requireNonNull(message) val e = new exceptions.TestCanceledException((_: StackDepthException) => Some(message), None, Left(pos), None) //e.fillInStackTrace() Canceled(e) } /** * Creates aCanceledwith the passed in cause. * * @param cause the passed in cause * @return ACanceledwithexceptionfield set to a newly createdTestCanceledExceptionusing the passed incause. */ def here(cause: Throwable)(implicit pos: source.Position): Canceled = { new Canceled( if (cause.getMessage != null) new exceptions.TestCanceledException((_: StackDepthException) => Some(cause.getMessage), Some(cause), Left(pos), None) else new exceptions.TestCanceledException((_: StackDepthException) => None, Some(cause), Left(pos), None) ) } } /** * Outcome for a test that was pending, which contains an optional string giving more information on what exactly is needed * for the test to become non-pending. * * @param message an optional message describing the reason the test is pending */ case object Pending extends Outcome { /** * Indicates that thisOutcomerepresents a test that was pending. * ** This class's implementation of this method always returns
* * @return true */ override val isPending: Boolean = true /** * Converts thistrue. *Outcometo aSucceeded. * ** The implmentation of this class will throw
*/ def toSucceeded: Succeeded.type = throw new exceptions.TestPendingException }TestPendingExceptionwith the passed in message. *