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

org.scalatest.FutureOutcome.scala Maven / Gradle / Ivy

There is a newer version: 3.0.0-RC3
Show newest version
/*
 * Copyright 2001-2016 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 scala.concurrent.Future
import scala.concurrent.ExecutionContext
import org.scalactic.{Or, Good, Bad}
import scala.util.{Try, Success, Failure}
import exceptions.TestCanceledException
import exceptions.TestPendingException
import Suite.anExceptionThatShouldCauseAnAbort
import scala.concurrent.ExecutionException

class FutureOutcome(private[scalatest] val underlying: Future[Outcome]) {
  // TODO: add tests for pretty toString

  /**
   * Registers a callback function to be executed after this future completes, returning
   * a new future that completes only after the callback has finished execution.
   *
   * @return a new FutureOutcome that will complete only after this FutureOutcome
   *    and, subsequently, the passed callback function have completed execution.
   */
  def onCompletedThen(callback: Outcome Or Throwable => Unit)(implicit executionContext: ExecutionContext): FutureOutcome = {
    FutureOutcome {
      underlying recoverWith {
        case ex =>
          try {
            callback(Bad(ex))
            Future.failed(ex)
          }
          catch {
            case _: TestPendingException => Future.successful(Pending)
            case ex: TestCanceledException => Future.successful(Canceled(ex))
            case ex: Throwable if !anExceptionThatShouldCauseAnAbort(ex) => Future.successful(Failed(ex))
            case ex: Throwable => Future.failed(new ExecutionException(ex))
          }
      } flatMap { outcome =>
        try {
          callback(Good(outcome))
          Future.successful(outcome)
        }
        catch {
          case _: TestPendingException => Future.successful(Pending)
          case ex: TestCanceledException => Future.successful(Canceled(ex))
          case ex: Throwable if !anExceptionThatShouldCauseAnAbort(ex) => Future.successful(Failed(ex))
          case ex: Throwable => Future.failed(new ExecutionException(ex))
        }
      }
    }
  }

  /**
   * Registers a callback function to be executed if this future completes with
   * Succeeded, returning a new future that completes only after the
   * callback has finished execution.
   *
   * @return a new FutureOutcome that will complete only after this FutureOutcome
   *    has completed and, if this FutureOutcome completes with Succeeded, the
   *    passed callback function has completed execution.
   */
  def onSucceededThen(callback: => Unit)(implicit executionContext: ExecutionContext): FutureOutcome = {
    FutureOutcome {
      underlying flatMap { outcome =>
        if (outcome.isSucceeded) {
          try {
            callback
            Future.successful(outcome)
          }
          catch {
            case _: TestPendingException => Future.successful(Pending)
            case ex: TestCanceledException => Future.successful(Canceled(ex))
            case ex: Throwable if !anExceptionThatShouldCauseAnAbort(ex) => Future.successful(Failed(ex))
            case ex: Throwable => Future.failed(new ExecutionException(ex))
          }
        } else Future.successful(outcome)
      }
    }
  }

  /**
   * Registers a callback function to be executed if this future completes with
   * Failed, returning a new future that completes only after the
   * callback has finished execution.
   *
   * @return a new FutureOutcome that will complete only after this FutureOutcome
   *    has completed and, if this FutureOutcome completes with Failed, the
   *    passed callback function has completed execution.
   */
  def onFailedThen(callback: Throwable => Unit)(implicit executionContext: ExecutionContext): FutureOutcome = {
    FutureOutcome {
      underlying flatMap { outcome =>
        outcome match {
          case Failed(originalEx) =>
            try {
              callback(originalEx)
              Future.successful(outcome)
            }
            catch {
              case _: TestPendingException => Future.successful(Pending)
              case ex: TestCanceledException => Future.successful(Canceled(ex))
              case ex: Throwable if !anExceptionThatShouldCauseAnAbort(ex) => Future.successful(Failed(ex))
              case ex: Throwable => Future.failed(new ExecutionException(ex))
            }
          case _ =>
            Future.successful(outcome)
        }
      }
    }
  }

  /**
   * Registers a callback function to be executed if this future completes with
   * Canceled, returning a new future that completes only after the
   * callback has finished execution.
   *
   * @return a new FutureOutcome that will complete only after this FutureOutcome
   *    has completed and, if this FutureOutcome completes with Canceled, the
   *    passed callback function has completed execution.
   */
  def onCanceledThen(callback: TestCanceledException => Unit)(implicit executionContext: ExecutionContext): FutureOutcome = {
    FutureOutcome {
      underlying flatMap { outcome =>
        outcome match {
          case Canceled(originalEx) =>
            try {
              callback(originalEx)
              Future.successful(outcome)
            }
            catch {
              case _: TestPendingException => Future.successful(Pending)
              case ex: TestCanceledException => Future.successful(Canceled(ex))
              case ex: Throwable if !anExceptionThatShouldCauseAnAbort(ex) => Future.successful(Failed(ex))
              case ex: Throwable => Future.failed(new ExecutionException(ex))
            }
          case _ =>
            Future.successful(outcome)
        }
      }
    }
  }

  /**
   * Registers a callback function to be executed if this future completes with
   * Pending, returning a new future that completes only after the
   * callback has finished execution.
   *
   * @return a new FutureOutcome that will complete only after this FutureOutcome
   *    has completed and, if this FutureOutcome completes with Pending, the
   *    passed callback function has completed execution.
   */
  def onPendingThen(callback: => Unit)(implicit executionContext: ExecutionContext): FutureOutcome = {
    FutureOutcome {
      underlying flatMap { outcome =>
        if (outcome.isPending) {
          try {
            callback
            Future.successful(outcome)
          }
          catch {
            case _: TestPendingException => Future.successful(Pending)
            case ex: TestCanceledException => Future.successful(Canceled(ex))
            case ex: Throwable if !anExceptionThatShouldCauseAnAbort(ex) => Future.successful(Failed(ex))
            case ex: Throwable => Future.failed(new ExecutionException(ex))
          }
        } else Future.successful(outcome)
      }
    }
  }

  def change(f: Outcome => Outcome)(implicit executionContext: ExecutionContext): FutureOutcome = {
    FutureOutcome {
      underlying flatMap { outcome =>
        try Future.successful(f(outcome))
        catch {
          case _: TestPendingException => Future.successful(Pending)
          case ex: TestCanceledException => Future.successful(Canceled(ex))
          case ex: Throwable if !anExceptionThatShouldCauseAnAbort(ex) => Future.successful(Failed(ex))
          case ex: Throwable => Future.failed(new ExecutionException(ex))
        }
      }
    }
  }

  /**
   * Registers a callback function to be executed if this future completes because
   * a suite-aborting exception was thrown, returning a new future that completes only after the
   * callback has finished execution.
   *
   * @return a new FutureOutcome that will complete only after this FutureOutcome
   *    has completed and, if this FutureOutcome completes abnormally with
   *    a suite-aborting exception, the passed callback function has completed execution.
   */
  def onAbortedThen(callback: Throwable => Unit)(implicit executionContext: ExecutionContext): FutureOutcome = {
    FutureOutcome {
      underlying recoverWith {
        case originalEx =>
          try {
            callback(originalEx)
            Future.failed(originalEx)
          }
          catch {
            case _: TestPendingException => Future.successful(Pending)
            case ex: TestCanceledException => Future.successful(Canceled(ex))
            case ex: Throwable if !anExceptionThatShouldCauseAnAbort(ex) => Future.successful(Failed(ex))
            case ex: Throwable => Future.failed(new ExecutionException(ex))
          }
      }
    }
  }

  /**
   * Registers a callback function to be executed if this future completes with any
   * Outcome (i.e., no suite-aborting exception is thrown), returning
   * a new future that completes only after the callback has finished execution.
   *
   * @return a new FutureOutcome that will complete only after this FutureOutcome
   *    has completed and, if this FutureOutcome completes with a valid
   *    Outcome, the passed callback function has completed execution.
   */
  def onOutcomeThen(callback: Outcome => Unit)(implicit executionContext: ExecutionContext): FutureOutcome = {
    FutureOutcome {
      underlying flatMap { outcome =>
        try {
          callback(outcome)
          Future.successful(outcome)
        }
        catch {
          case _: TestPendingException => Future.successful(Pending)
          case ex: TestCanceledException => Future.successful(Canceled(ex))
          case ex: Throwable if !anExceptionThatShouldCauseAnAbort(ex) => Future.successful(Failed(ex))
          case ex: Throwable => Future.failed(new ExecutionException(ex))
        }
      }
    }
  }

  /**
   * Indicates whether this FutureOutcome has completed.
   *
   * @return true if this FutureOutcome has completed; false otherwise.
   */
  def isCompleted: Boolean = underlying.isCompleted

  /**
   * Returns a value that indicates whether this FutureOutcome has completed,
   * and if so, indicates its result.
   *
   * 

* If this FutureOutcome has not yet completed, this method will return * None. Otherwise, this method will return a Some that contains * either a Good[Outcome], if this FutureOutcome completed with * a valid Outcome result, or if it completed with a thrown suite-aborting * exception, a Bad[Throwable]. *

* * @return a Some containing an Or value that indicates the result of this * FutureOutcome if it has completed; None otherwise. */ def value: Option[Outcome Or Throwable] = underlying.value match { case None => None case Some(Success(outcome)) => Some(Good(outcome)) case Some(Failure(ex)) => Some(Bad(ex)) } def toFuture: Future[Outcome] = underlying } object FutureOutcome { // Make this private so only ScalaTest can make one, so we can "promise" that // you'll never need to look for things like a TestCanceledException being passed // to onAbortedThen. private[scalatest] def apply(underlying: Future[Outcome]): FutureOutcome = new FutureOutcome(underlying) def canceled(): FutureOutcome = FutureOutcome { Future.successful(Canceled()) } def canceled(msg: String): FutureOutcome = FutureOutcome { Future.successful(Canceled(msg)) } def canceled(t: Throwable): FutureOutcome = FutureOutcome { Future.successful(Canceled(t)) } def canceled(message: String, cause: Throwable) = FutureOutcome { Future.successful(Canceled(message, cause)) } def succeeded: FutureOutcome = FutureOutcome { Future.successful( Succeeded ) } def failed(): FutureOutcome = FutureOutcome { Future.successful(Failed()) } def failed(msg: String): FutureOutcome = FutureOutcome { Future.successful(Failed(msg)) } def failed(message: String, cause: Throwable) = FutureOutcome { Future.successful(Failed(message, cause)) } def failed(t: Throwable): FutureOutcome = FutureOutcome { Future.successful(Failed(t)) } def pending: FutureOutcome = FutureOutcome { Future.successful( Pending ) } } /* FutureOutcome.fromOutcome(Canceled("...")) */




© 2015 - 2025 Weber Informatics LLC | Privacy Policy