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

org.scalatest.Status.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.tools.SuiteRunner
import java.util.concurrent.CountDownLatch
import scala.collection.GenSet
import java.util.concurrent.ConcurrentLinkedQueue
import collection.JavaConverters._
import java.io.Serializable

/**
 * The result status of running a test or a suite.
 *
 * 

* This trait is the return type of the "run" lifecycle methods of trait Suite: run, runNestedSuites, * runTests, and runTest. It can be used to determine whether a test or suite has completed, and if completed, * whether it succeeded or failed. The main use case for this trait in ScalaTest is to enable BeforeAndAfterAll's afterAll * method to wait until all relevant tests and nested suites have completed before performing the "after all" code, even if those tests are * nested suites are run in parallel. *

* * @author cheeseng */ trait Status { /** * Blocking call that waits until completion, then returns returns true if no tests failed and no suites aborted, else returns false. * *

* This only reports false if there was a failed test or aborted suite in the context of the "run" lifecycle method it was returned from. For example, * if you call succeeds on the return Status of runTest, it returns (after that test has completed) true if the * test whose name was passed to runTest succeeded, false if that test failed (or the suite aborts). If you call * succeeds on the return value of runTests, by contrast, it returns (after the suite's tests have completed) true only * if all tests in the suite succeeded. If any test in the suite fails (or the whole suite aborts), the succeeds call will return false. * The Status returned from runNestedSuites will return true only if all tests in all nested suites (and their nested suites, etc.) fired * off by that runNestedSuites call succeed and no suites abort. * Simlarly, the Status returned from run will return true only if all tests in all nested suites (and their nested suites, etc.) fired * off by that run call succeed and no suites abort. *

* * @return true if no tests failed and no suites aborted, false otherwise */ def succeeds(): Boolean /** * Non-blocking call that indicates whether the all the tests or nested suites fired off by the run method that returned the Status have completed. * Because this is non-blocking, you can use this to poll the completion status. * * @return true if the test or suite run is already completed, false otherwise. */ def isCompleted: Boolean /** * Blocking call that returns only after the underlying test or suite is completed. */ def waitUntilCompleted() /** * Registers the passed function to be executed when this status completes. * *

* You may register multiple functions, which on completion will be executed in an undefined * order. *

*/ def whenCompleted(f: Boolean => Unit) } /** * Singleton status that represents an already completed run with no tests failed and no suites aborted. * *

* Note: the difference between this SucceededStatus object and the similarly named Succeeded * object is that the Succeeded object indicates one test succeeded, whereas this SucceededStatus object indicates the absence * of any failed tests or aborted suites during a run. Both are used as the result type of Suite lifecycle methods, but Succeeded * is a possible result of withFixture, whereas SucceededStatus is a possible result of run, runNestedSuites, * runTests, or runTest. In short, Succeeded is always just about one test, whereas SucceededStatus could be * about something larger: multiple tests or an entire suite. *

*/ object SucceededStatus extends Status with Serializable { /** * Always returns true. * * @return true */ def succeeds() = true /** * Always returns true. * * @return true */ def isCompleted = true /** * Always returns immediately. */ def waitUntilCompleted() {} /** * Executes the passed function immediately on the calling thread. */ def whenCompleted(f: Boolean => Unit) { f(true) } } /** * Singleton status that represents an already completed run with at least one failed test or aborted suite. * *

* Note: the difference between this FailedStatus object and the similarly named Failed * class is that a Failed instance indicates one test failed, whereas this FailedStatus object indicates either one or more tests failed * and/or one or more suites aborted during a run. Both are used as the result type of Suite lifecycle methods, but Failed * is a possible result of withFixture, whereas FailedStatus is a possible result of run, runNestedSuites, * runTests, or runTest. In short, Failed is always just about one test, whereas FailedStatus could be * about something larger: multiple tests or an entire suite. *

*/ object FailedStatus extends Status with Serializable { /** * Always returns false. * * @return true */ def succeeds() = false /** * Always returns true. * * @return true */ def isCompleted = true /** * Always returns immediately. */ def waitUntilCompleted() {} /** * Executes the passed function immediately on the calling thread. */ def whenCompleted(f: Boolean => Unit) { f(false) } } // Used internally in ScalaTest. We don't use the StatefulStatus, because // then user code could pattern match on it and then access the setCompleted // and setFailed methods. We wouldn't want that. private[scalatest] final class ScalaTestStatefulStatus extends Status with Serializable { @transient private final val latch = new CountDownLatch(1) @volatile private var succeeded = true private final val queue = new ConcurrentLinkedQueue[Boolean => Unit] def succeeds() = { waitUntilCompleted() succeeded } def isCompleted = latch.getCount == 0L def waitUntilCompleted() { latch.await() } def setFailed() { if (isCompleted) throw new IllegalStateException("status is already completed") succeeded = false } def setCompleted() { synchronized { latch.countDown() } for (f <- queue.iterator.asScala) f(succeeded) } def whenCompleted(f: Boolean => Unit) { var executeLocally = false synchronized { if (!isCompleted) queue.add(f) else executeLocally = true } if (executeLocally) f(succeeded) } } /** * Status implementation that can change its state over time. * *

* A StatefulStatus begins its life in a successful state, and will remain successful unless setFailed is called. * Once setFailed is called, the status will remain at failed. The setFailed method can be called multiple times (even * though invoking it once is sufficient to permanently set the status to failed), but only up until setCompleted has been called. * After setCompleted has been called, any invocation of setFailed will be greeted with an IllegalStateException. *

* *

* Instances of this class are thread safe. *

*/ final class StatefulStatus extends Status with Serializable { @transient private final val latch = new CountDownLatch(1) @volatile private var succeeded = true private final val queue = new ConcurrentLinkedQueue[Boolean => Unit] /** * Blocking call that waits until completion, as indicated by an invocation of setCompleted on this instance, then returns returns false * if setFailed was called on this instance, else returns true. * * @return true if no tests failed and no suites aborted, false otherwise */ def succeeds() = { waitUntilCompleted() succeeded } /** * Non-blocking call that returns true if setCompleted has been invoked on this instance, false otherwise. * * @return true if the test or suite run is already completed, false otherwise. */ def isCompleted = latch.getCount == 0L /** * Blocking call that returns only after setCompleted has been invoked on this StatefulStatus instance. */ def waitUntilCompleted() { latch.await() } /** * Sets the status to failed without changing the completion status. * *

* This method may be invoked repeatedly, even though invoking it once is sufficient to set the state of the Status to failed, but only * up until setCompleted has been called. Once setCompleted has been called, invoking this method will result in a * thrown IllegalStateException. *

* * @throws IllegalStateException if this method is invoked on this instance after setCompleted has been invoked on this instance. */ def setFailed() { if (isCompleted) throw new IllegalStateException("status is already completed") succeeded = false } /** * Sets the status to completed. * *

* This method may be invoked repeatedly, even though invoking it once is sufficient to set the state of the Status to completed. *

*/ def setCompleted() { synchronized { latch.countDown() } for (f <- queue.iterator.asScala) f(succeeded) } /** * Registers the passed function to be executed when this status completes. * *

* You may register multiple functions, which on completion will be executed in an undefined * order. *

*/ def whenCompleted(f: Boolean => Unit) { var executeLocally = false synchronized { if (!isCompleted) queue.add(f) else executeLocally = true } if (executeLocally) f(succeeded) } } /** * Composite Status that aggregates its completion and failed states of set of other Statuses passed to its constructor. * * @param status the Statuses out of which this status is composed. */ final class CompositeStatus(statuses: Set[Status]) extends Status with Serializable { // TODO: Ensure this is visible to another thread, because I'm letting the reference // escape with my for loop below prior to finishing this object's construction. @transient private final val latch = new CountDownLatch(statuses.size) @volatile private var succeeded = true private final val queue = new ConcurrentLinkedQueue[Boolean => Unit] for (status <- statuses) { status.whenCompleted { st => synchronized { latch.countDown() } if (!st) succeeded = false if (latch.getCount == 0) { for (f <- queue.iterator.asScala) f(succeeded) } } } /** * Blocking call that waits until all composite Statuses have completed, then returns * true only if all of the composite Statuses succeeded. If any Status passed in the statuses set fails, this method * will return false. * * @return true if all composite Statuses succeed, false otherwise. */ def succeeds() = statuses.forall(_.succeeds()) /** * Non-blocking call to check if the test or suite run is completed, returns true if all composite Statuses have completed, * false otherwise. You can use this to poll the run status. * * @return true if all composite Statuses have completed, false otherwise. */ def isCompleted = statuses.forall(_.isCompleted) /** * Blocking call that returns only after all composite Statuss have completed. */ def waitUntilCompleted() { statuses.foreach(_.waitUntilCompleted()) } /** * Registers the passed function to be executed when this status completes. * *

* You may register multiple functions, which on completion will be executed in an undefined * order. *

*/ def whenCompleted(f: Boolean => Unit) { var executeLocally = false synchronized { if (!isCompleted) queue.add(f) else executeLocally = true } if (executeLocally) f(succeeded) } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy