org.scalatest.Status.scala Maven / Gradle / Ivy
Show all versions of scalatest_2.9.3 Show documentation
package org.scalatest
import org.scalatest.tools.SuiteRunner
import java.util.concurrent.CountDownLatch
/**
* 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 {
// TODO: Do we have tests that ensure that runTest and runTests succeeds method returns false if that test completes abruptly with say a VirtualMachineError
// and the like? I.e., something that usually causes a SuiteAborted rather than a TestFailed.
/**
* 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()
}
/**
* Singleton status that represents an already completed run with no tests failed and no suites aborted.
*/
object SucceededStatus extends Status {
/**
* Always returns true.
*
* @return true
*/
def succeeds() = true
/**
* Always returns true.
*
* @return true
*/
def isCompleted = true
/**
* Always returns immediately.
*/
def waitUntilCompleted() {}
}
/**
* Singleton status that represents an already completed run with at least one failed test or aborted suite.
*/
object FailedStatus extends Status {
/**
* Always returns false.
*
* @return true
*/
def succeeds() = false
/**
* Always returns true.
*
* @return true
*/
def isCompleted = true
/**
* Always returns immediately.
*/
def waitUntilCompleted() {}
}
// Used internally in ScalaTest
private[scalatest] final class ScalaTestStatefulStatus extends Status {
private val latch = new CountDownLatch(1)
@volatile private var succeeded = true
def succeeds() = {
waitUntilCompleted()
succeeded
}
def isCompleted = latch.getCount() == 0L
def waitUntilCompleted() {
latch.await()
}
def setFailed() {
succeeded = false
}
def setCompleted() {
latch.countDown()
}
}
/**
* 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 {
private val latch = new CountDownLatch(1)
@volatile private var succeeded = true
/**
* 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()
}
// TODO: Need to write a test and ensure that this method throws IllegalStateException if it is called after setCompleted is called.
/**
* 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() {
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() {
// Note: count down latches ignore invocations of countDown after the count has already reached 0, so this can safely be invoked multiple times
// But TODO anyway, write a test that ensures that behavior
latch.countDown()
}
}
// TODO: Why is this a Seq? Should it be a Set instead? Moreover, should it be a GenSet so it can be run in parallel?
// Reason is that if you toss a bunch of succeededstatus and a bunch of failedstatuses in there, it doesn't matter. One failed
// status is enough.
/**
* 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 {
/**
* 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 compositite Statuses have completed,
* false otherwise. You can use this to poll the run status.
*
* @return true if all compositite Statuses have completed, false otherwise.
*/
def isCompleted = statuses.forall(_.isCompleted)
/**
* Blocking call that returns only after all compositite Statuss have completed.
*/
def waitUntilCompleted() {
statuses.foreach(_.waitUntilCompleted())
}
}