org.scalatest.ParallelTestExecution.scala Maven / Gradle / Ivy
Show all versions of scalatest_2.9.3 Show documentation
/* * Copyright 2001-2009 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 events.Event import org.scalatest.time.Span import tools.{DistributedTestRunnerSuite, TestSortingReporter, Runner} /** * Trait that causes that the tests of any suite it is mixed into to be run in parallel if * aRunner, or 2 seconds, if noDistributoris 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
* *varsand 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
* *ParallelTestExecutionworks, 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 ofrunTestisfinal, 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 putParallelTestExecutionlast, as * in:with BeforeAndAfter with ParallelTestExecution. For more details, see the documentation * for therunTestmethod. *super.runTeststo facilitate parallel test execution. * ** This trait's implementation of this method always invokes
* * @param testName an optional name of one test to run. Ifsuper.runTeststo delegate * toOneInstancePerTest's implementation, but it may pass in a modifiedargsobject. * Ifargs.runTestInNewInstanceisfalseandargs.distributoris defined, * this trait's implementation of this method will wrap the passedargs.reporterin 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.runTestInNewInstanceis *trueorargs.distributoris empty, this trait's implementation of this method simply callssuper.runTests, * passing along the sametestNameandargs. *None, all relevant tests should be run. * I.e.,Noneacts like a wildcard that means run all relevant tests in thisSuite. * @param args theArgsfor this run * @return aStatusobject 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, testNames.size, 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.runTestto facilitate parallel test execution. * ** This trait's implementation of this method only changes the supertrait implementation if *
* *args.distributoris defined. Ifargs.distributoris empty, it * simply invokessuper.runTests, passing along the sametestName* andargsobject. ** If
* *args.distributoris 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 invokingdistributingTeston it, passing in the *testName. Then it wraps a new instance of this class, obtained by invoking *newInstancein a suite whose run method will ensure that only the test whose * name was passed to this method astestNameis executed. Finally, this trait's * implementation of this method submits this wrapper suite to the distributor. ** If
* *runTestInNewInstanceisfalse, this is the test-specific (distributed) * instance, so this trait's implementation of this method simply invokessuper.runTest, * passing along the sametestNameandargsobject, delegating responsibility * for actually running the test to the super implementation. Aftersuper.runTestreturns * (or completes abruptly by throwing an exception), it notifiesargs.distributedTestSorter* (if defined) that it has completed running the test by invokingcompletedTeston it, * passing in thetestName. ** Note: this trait's implementation of this method is
* * @param testName the name of one test to execute. * @param args thefinalto ensure that * any other desiredrunTestbehavior is executed by the same thread that executes * the test. For example, if you were to mix inBeforeAndAfterafter *ParallelTestExecution, thebeforeandaftercode 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 methodfinalensures that * traits likeBeforeAndAftercan only be "super" toParallelTestExecution* and, therefore, that itsbeforeandaftercode will be run * by the same distributed thread that runs the test itself. *Argsfor this run * @return aStatusobject 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
* *runTestsinvokes this method to create * a new instance of thisSuitefor 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'sRunneraSuiteneeds a public, no-arg * constructor. However, this will not be true of anySuitedefined 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 aSuiteclass is explicitly defined without a public, * no-arg constructor, you will need to override this method to construct a new * instance of theSuitein some other way. ** Here's an example of how you could override
* *newInstanceto 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 * } * } **/ override def newInstance: Suite with ParallelTestExecution = { val instance = getClass.newInstance.asInstanceOf[Suite with ParallelTestExecution] instance } /** * A maximum amount of time to wait for out-of-order events generated by running the tests * of thisSuitein parallel while sorting the events back into a more * user-friendly, sequential order. * ** The default implementation of this method returns the value specified via
-Tto *-Twas supplied. * * * @return a maximum amount of time to wait for events while resorting them into sequential order */ protected def sortingTimeout: Span = Runner.testSortingReporterTimeout /** * Modifies the behavior ofsuper.runto facilitate parallel test execution. * ** This trait's implementation of this method only changes the supertrait implementation if both *
* *testNameandargs.distributedTestSorterare defined. If either *testNameorargs.distributedTestSorteris empty, it * simply invokessuper.run, passing along the sametestName* andargsobject. ** If both
* * @param testName an optional name of one test to execute. IftestNameandargs.distributedTestSorterare defined, however, * this trait's implementation of this method will create a "test-specific reporter" whoseapply* method will invoke theapplymethod of theDistributedTestSorter, which takes * a test name as well as the event. It will then invokesuper.runpassing along * the sametestNameand anArgsobject that is the same except with the * original reporter replaced by the test-specific reporter. *None, all relevant tests should be executed. * I.e.,Noneacts like a wildcard that means execute all relevant tests in thisSuite. * @param args theArgsfor this run * @return aStatusobject 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) } } private[scalatest] def createTestSpecificReporter(testSorter: DistributedTestSorter, testName: String): Reporter = { class TestSpecificReporter(testSorter: DistributedTestSorter, testName: String) extends Reporter { def apply(event: Event) { testSorter.apply(testName, event) } } new TestSpecificReporter(testSorter, testName) } }