org.scalatest.ParallelTestExecution.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.scalatest.time.{Seconds, Span} import org.scalatest.tools.{DistributedTestRunnerSuite, TestSortingReporter, TestSpecificReporter} /** * Trait that causes that the tests of any suite it is mixed into to be run in parallel if * a
Runner, or 2 seconds, if noDistributor
is passed torunTests
. * ** ScalaTest's normal approach for running suites of tests in parallel is to run different suites in parallel, * but the tests of any one suite sequentially. This approach should provide sufficient distribution of the work load * in most cases, but some suites may encapsulate multiple long-running tests. Such suites may dominate the execution * time of the run. If so, mixing in this trait into just those suites will allow their long-running tests to run in parallel with each * other, thereby helping to reduce the total time required to run an entire run. *
* ** To make it easier for users to write tests that run in parallel, this trait runs each test in its own instance of the class. * Running each test in its own instance enables tests to use the same instance
* *vars
and mutable objects referenced from * instance variables without needing to synchronize. Although ScalaTest provides functional approaches to * factoring out common test code that can help avoid such issues, running each test in its own instance is an insurance policy that makes * running tests in parallel easier and less error prone. ** For the details on how
* *ParallelTestExecution
works, see the documentation for methodsrun
,runTests
, andrunTest
, * which this trait overrides. ** Note: This trait's implementation of
* * @author Bill Venners */ trait ParallelTestExecution extends OneInstancePerTest { this: Suite => /** * Modifies the behavior ofrunTest
isfinal
, to ensure that behavior * related to individual tests are executed by the same thread that executes the actual test. This means, * for example, that you won't be allowed to write...with ParallelTestExecution with BeforeAndAfter
. * Instead, you'd need to putParallelTestExecution
last, as * in:with BeforeAndAfter with ParallelTestExecution
. For more details, see the documentation * for therunTest
method. *super.runTests
to facilitate parallel test execution. * ** This trait's implementation of this method always invokes
* * @param testName an optional name of one test to run. Ifsuper.runTests
to delegate * toOneInstancePerTest
's implementation, but it may pass in a modifiedargs
object. * Ifargs.runTestInNewInstance
isfalse
andargs.distributor
is defined, * this trait's implementation of this method will wrap the passedargs.reporter
in a newReporter
* that can sort events fired by parallel tests back into sequential order, with a timeout. It will pass this new reporter to *super.runTests
(inargs.reporter
) as well as a definedDistributedTestSorter
* (in args.distributedTestSorter) that can be used to communicate with the sorting reporter. Otherwise, ifargs.runTestInNewInstance
is *true
orargs.distributor
is empty, this trait's implementation of this method simply callssuper.runTests
, * passing along the sametestName
andargs
. *None
, all relevant tests should be run. * I.e.,None
acts like a wildcard that means run all relevant tests in thisSuite
. * @param args theArgs
for this run * @return aStatus
object that indicates when all tests started by this method have completed, and whether or not a failure occurred. */ protected abstract override def runTests(testName: Option[String], args: Args): Status = { val newArgs = if (args.runTestInNewInstance) args // This is the test-specific instance else { args.distributor match { // This is the initial instance case Some(distributor) => val testSortingReporter = new TestSortingReporter(suiteId, args.reporter, sortingTimeout, expectedTestCount(args.filter), args.distributedSuiteSorter, System.err) args.copy(reporter = testSortingReporter, distributedTestSorter = Some(testSortingReporter)) case None => args } } // Always call super.runTests, which is OneInstancePerTest's runTests. But if RTINI is NOT // set, that means we are in the initial instance. In that case, we wrap the reporter in // a new TestSortingReporter, and wrap the distributor in a new DistributorWrapper that // knows is passed the TestSortingReporter. We then call super.runTests, which is OIPT's runTests. super.runTests(testName, newArgs) } /** * Modifies the behavior ofsuper.runTest
to facilitate parallel test execution. * ** This trait's implementation of this method only changes the supertrait implementation if *
* *args.distributor
is defined. Ifargs.distributor
is empty, it * simply invokessuper.runTests
, passing along the sametestName
* andargs
object. ** If
* *args.distributor
is defined, then it uses theargs.runTestInNewInstance
* flag to decide what to do. IfrunTestInNewInstance
* istrue
, this is the general instance responsible for running all tests, so * it first notifiesargs.distributedTestSorter
(if defined) that it is * distributing this test by invokingdistributingTest
on it, passing in the *testName
. Then it wraps a new instance of this class, obtained by invoking *newInstance
in a suite whose run method will ensure that only the test whose * name was passed to this method astestName
is executed. Finally, this trait's * implementation of this method submits this wrapper suite to the distributor. ** If
* *runTestInNewInstance
isfalse
, this is the test-specific (distributed) * instance, so this trait's implementation of this method simply invokessuper.runTest
, * passing along the sametestName
andargs
object, delegating responsibility * for actually running the test to the super implementation. Aftersuper.runTest
returns * (or completes abruptly by throwing an exception), it notifiesargs.distributedTestSorter
* (if defined) that it has completed running the test by invokingcompletedTest
on it, * passing in thetestName
. ** Note: this trait's implementation of this method is
* * @param testName the name of one test to execute. * @param args thefinal
to ensure that * any other desiredrunTest
behavior is executed by the same thread that executes * the test. For example, if you were to mix inBeforeAndAfter
after *ParallelTestExecution
, thebefore
andafter
code would * be executed by the general instance on the main test thread, rather than by the test-specific * instance on the distributed thread. Marking this methodfinal
ensures that * traits likeBeforeAndAfter
can only be "super" toParallelTestExecution
* and, therefore, that itsbefore
andafter
code will be run * by the same distributed thread that runs the test itself. *Args
for this run * @return aStatus
object that indicates when the test started by this method has completed, and whether or not it failed . */ final protected abstract override def runTest(testName: String, args: Args): Status = { args.distributor match { case Some(distribute) => if (args.runTestInNewInstance) { // Tell the TSR that the test is being distributed for (sorter <- args.distributedTestSorter) sorter.distributingTest(testName) // It will be oneInstance, testName, args.copy(reporter = ...) distribute(new DistributedTestRunnerSuite(newInstance, testName, args), args.copy(tracker = args.tracker.nextTracker())) } else { // In test-specific (distributed) instance, so just run the test. (RTINI was // removed by OIPT's implementation of runTests.) try { super.runTest(testName, args) } finally { // Tell the TSR that the distributed test has completed for (sorter <- args.distributedTestSorter) sorter.completedTest(testName) } } case None => super.runTest(testName, args) } } /** * Construct a new instance of thisSuite
. * ** This trait's implementation of
* *runTests
invokes this method to create * a new instance of thisSuite
for each test. This trait's implementation * of this method uses reflection to callthis.getClass.newInstance
. This * approach will succeed only if thisSuite
's class has a public, no-arg * constructor. In most cases this is likely to be true, because to be instantiated * by ScalaTest'sRunner
aSuite
needs a public, no-arg * constructor. However, this will not be true of anySuite
defined as * an inner class of another class or trait, because every constructor of an inner * class type takes a reference to the enclosing instance. In such cases, and in * cases where aSuite
class is explicitly defined without a public, * no-arg constructor, you will need to override this method to construct a new * instance of theSuite
in some other way. ** Here's an example of how you could override
* *newInstance
to construct * a new instance of an inner class: ** import org.scalatest.Suite * * class Outer { * class InnerSuite extends Suite with ParallelTestExecution { * def testOne() {} * def testTwo() {} * override def newInstance = new InnerSuite * } * } **/ // SKIP-SCALATESTJS,NATIVE-START override def newInstance: Suite with ParallelTestExecution = { val instance = getClass.newInstance.asInstanceOf[Suite with ParallelTestExecution] instance } // SKIP-SCALATESTJS,NATIVE-END //SCALATESTJS,NATIVE-ONLY override def newInstance: Suite with ParallelTestExecution /** * A maximum amount of time to wait for out-of-order events generated by running the tests * of thisSuite
in parallel while sorting the events back into a more * user-friendly, sequential order. * ** The default implementation of this method returns the value specified via
-T
to *-T
was supplied. * * * @return a maximum amount of time to wait for events while resorting them into sequential order */ protected def sortingTimeout: Span = Span(Suite.defaultTestSortingReporterTimeoutInSeconds, Seconds) /** * Modifies the behavior ofsuper.run
to facilitate parallel test execution. * ** This trait's implementation of this method only changes the supertrait implementation if both *
* *testName
andargs.distributedTestSorter
are defined. If either *testName
orargs.distributedTestSorter
is empty, it * simply invokessuper.run
, passing along the sametestName
* andargs
object. ** If both
* * @param testName an optional name of one test to execute. IftestName
andargs.distributedTestSorter
are defined, however, * this trait's implementation of this method will create a "test-specific reporter" whoseapply
* method will invoke theapply
method of theDistributedTestSorter
, which takes * a test name as well as the event. It will then invokesuper.run
passing along * the sametestName
and anArgs
object that is the same except with the * original reporter replaced by the test-specific reporter. *None
, all relevant tests should be executed. * I.e.,None
acts like a wildcard that means execute all relevant tests in thisSuite
. * @param args theArgs
for this run * @return aStatus
object that indicates when all tests and nested suites started by this method have completed, and whether or not a failure occurred. */ abstract override def run(testName: Option[String], args: Args): Status = { (testName, args.distributedTestSorter) match { case (Some(name), Some(sorter)) => super.run(testName, args.copy(reporter = createTestSpecificReporter(sorter, name))) case _ => super.run(testName, args) } } protected[scalatest] def createTestSpecificReporter(testSorter: DistributedTestSorter, testName: String): Reporter = new TestSpecificReporter(testSorter, testName) }