mirror of https://github.com/sbt/sbt.git
Added support of nested test tasks when tests are executed InProcess and sequentially.
This commit is contained in:
parent
df9a475158
commit
3109912d00
|
|
@ -12,6 +12,7 @@ package sbt
|
|||
import ConcurrentRestrictions.Tag
|
||||
|
||||
import testing.{AnnotatedFingerprint, Fingerprint, Framework, SubclassFingerprint, Runner, Task => TestTask}
|
||||
import scala.annotation.tailrec
|
||||
|
||||
import java.io.File
|
||||
|
||||
|
|
@ -113,7 +114,7 @@ object Tests
|
|||
if(config.parallel)
|
||||
makeParallel(runnables, setupTasks, config.tags).toSeq.join
|
||||
else
|
||||
makeSerial(runnables, setupTasks, config.tags)
|
||||
makeSerial(loader, runnables, setupTasks, config.tags)
|
||||
val taggedMainTasks = mainTasks.tagw(config.tags : _*)
|
||||
taggedMainTasks map processResults flatMap { results =>
|
||||
val cleanupTasks = fj(partApp(userCleanup) :+ frameworkCleanup(results.overall))
|
||||
|
|
@ -122,10 +123,26 @@ object Tests
|
|||
}
|
||||
type TestRunnable = (String, TestFunction)
|
||||
def makeParallel(runnables: Iterable[TestRunnable], setupTasks: Task[Unit], tags: Seq[(Tag,Int)]) =
|
||||
runnables map { case (name, test) => task { (name, test.apply()) } tagw(tags : _*) tag(test.tags map (ConcurrentRestrictions.Tag(_)) : _*) dependsOn setupTasks named name }
|
||||
runnables map { case (name, test) => task { (name, test.apply()._1) } tagw(tags : _*) tag(test.tags map (ConcurrentRestrictions.Tag(_)) : _*) dependsOn setupTasks named name }
|
||||
|
||||
def makeSerial(runnables: Seq[TestRunnable], setupTasks: Task[Unit], tags: Seq[(Tag,Int)]) =
|
||||
task { runnables map { case (name, test) => (name, test.apply()) } } dependsOn(setupTasks)
|
||||
def makeSerial(loader: ClassLoader, runnables: Seq[TestRunnable], setupTasks: Task[Unit], tags: Seq[(Tag,Int)]): Task[List[(String, SuiteResult)]] =
|
||||
{
|
||||
@tailrec
|
||||
def processRunnable(runnableList: List[TestRunnable], acc: List[(String, SuiteResult)]): List[(String, SuiteResult)] =
|
||||
runnableList match {
|
||||
case hd :: rst =>
|
||||
val testFun = hd._2
|
||||
val (result, nestedTasks) = testFun.apply()
|
||||
val nestedRunnables =
|
||||
nestedTasks.view.zipWithIndex map { case (nt, idx) =>
|
||||
(hd._1, TestFramework.createTestFunction(loader, new TestDefinition(testFun.testDefinition.name + "-" + idx, testFun.testDefinition.fingerprint), testFun.runner, nt))
|
||||
}
|
||||
processRunnable(nestedRunnables.toList ::: rst, (hd._1, result) :: acc)
|
||||
case Nil => acc
|
||||
}
|
||||
|
||||
task { processRunnable(runnables.toList, List.empty) } dependsOn(setupTasks)
|
||||
}
|
||||
|
||||
def processResults(results: Iterable[(String, SuiteResult)]): Output =
|
||||
Output(overall(results.map(_._2.result)), results.toMap, Iterable.empty)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
scalaVersion := "2.10.1"
|
||||
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15"
|
||||
|
||||
testOptions in Test += Tests.Argument("-r", "custom.CustomReporter")
|
||||
|
||||
parallelExecution in Test := false
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package custom
|
||||
|
||||
import java.io._
|
||||
import org.scalatest._
|
||||
import events._
|
||||
|
||||
class CustomReporter extends Reporter {
|
||||
|
||||
private def writeFile(filePath: String, content: String) {
|
||||
val file = new File(filePath)
|
||||
val writer =
|
||||
if (!file.exists)
|
||||
new FileWriter(new File(filePath))
|
||||
else
|
||||
new FileWriter(new File(filePath + "-2"))
|
||||
writer.write(content)
|
||||
writer.flush()
|
||||
writer.close()
|
||||
}
|
||||
|
||||
def apply(event: Event) {
|
||||
event match {
|
||||
case SuiteStarting(_, suiteName, _, _, _, _, _, _, _, _) => writeFile("target/SuiteStarting-" + suiteName, suiteName)
|
||||
case SuiteCompleted(_, suiteName, _, _, _, _, _, _, _, _, _) => writeFile("target/SuiteCompleted-" + suiteName, suiteName)
|
||||
case TestStarting(_, _, _, _, testName, _, _, _, _, _, _, _) => writeFile("target/TestStarting-" + testName, testName)
|
||||
case TestSucceeded(_, _, _, _, testName, _, _, _, _, _, _, _, _, _) => writeFile("target/TestSucceeded-" + testName, testName)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.test
|
||||
|
||||
import org.scalatest._
|
||||
|
||||
class NestedSpecs extends Suites (
|
||||
new TestSpec
|
||||
)
|
||||
|
||||
@DoNotDiscover
|
||||
class TestSpec extends Spec {
|
||||
|
||||
def `TestSpec-test-1 ` {}
|
||||
|
||||
def `TestSpec-test-2 ` {}
|
||||
|
||||
def `TestSpec-test-3 ` {}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
#This test that the framework will execute ScalaTest nested suites as sequential nested task (InProcess) properly.
|
||||
#A CustomReporter is used to report expected ScalaTest's events by writing out to files in target/,
|
||||
#it is then used to check for their existence, and if the expected event is fired > 1 (which is unexpected),
|
||||
#a xxxx-2 file will be written, thus here we also check for 'absent' of such file.
|
||||
|
||||
> clean
|
||||
|
||||
> test
|
||||
|
||||
$ exists target/SuiteStarting-NestedSpecs
|
||||
|
||||
$ absent target/SuiteStarting-NestedSpecs-2
|
||||
|
||||
$ exists target/SuiteCompleted-NestedSpecs
|
||||
|
||||
$ absent target/SuiteCompleted-NestedSpecs-2
|
||||
|
||||
$ exists target/SuiteStarting-TestSpec
|
||||
|
||||
$ absent target/SuiteStarting-TestSpec-2
|
||||
|
||||
$ exists target/SuiteCompleted-TestSpec
|
||||
|
||||
$ absent target/SuiteCompleted-TestSpec-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-1
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-1-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-1
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-1-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-2
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-2-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-2
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-2-2
|
||||
|
||||
$ exists target/TestStarting-TestSpec-test-3
|
||||
|
||||
$ absent target/TestStarting-TestSpec-test-3-2
|
||||
|
||||
$ exists target/TestSucceeded-TestSpec-test-3
|
||||
|
||||
$ absent target/TestSucceeded-TestSpec-test-3-2
|
||||
|
|
@ -68,7 +68,7 @@ final class TestRunner(delegate: Runner, listeners: Seq[TestReportListener], log
|
|||
final def task(testDefinition: TestDefinition): TestTask =
|
||||
delegate.task(testDefinition.name, testDefinition.fingerprint, false, Array(new SuiteSelector)) // TODO: To pass in correct explicitlySpecified and selectors
|
||||
|
||||
final def run(testDefinition: TestDefinition, testTask: TestTask): SuiteResult =
|
||||
final def run(testDefinition: TestDefinition, testTask: TestTask): (SuiteResult, Seq[TestTask]) =
|
||||
{
|
||||
log.debug("Running " + testDefinition)
|
||||
val name = testDefinition.name
|
||||
|
|
@ -78,25 +78,26 @@ final class TestRunner(delegate: Runner, listeners: Seq[TestReportListener], log
|
|||
val results = new scala.collection.mutable.ListBuffer[Event]
|
||||
val handler = new EventHandler { def handle(e:Event){ results += e } }
|
||||
val loggers = listeners.flatMap(_.contentLogger(testDefinition))
|
||||
try testTask.execute(handler, loggers.map(_.log).toArray)
|
||||
finally loggers.foreach( _.flush() )
|
||||
val nestedTasks =
|
||||
try testTask.execute(handler, loggers.map(_.log).toArray)
|
||||
finally loggers.foreach( _.flush() )
|
||||
val event = TestEvent(results)
|
||||
safeListenersCall(_.testEvent( event ))
|
||||
SuiteResult(results)
|
||||
(SuiteResult(results), nestedTasks.toSeq)
|
||||
}
|
||||
|
||||
safeListenersCall(_.startGroup(name))
|
||||
try
|
||||
{
|
||||
val suiteResult = runTest()
|
||||
val (suiteResult, nestedTasks) = runTest()
|
||||
safeListenersCall(_.endGroup(name, suiteResult.result))
|
||||
suiteResult
|
||||
(suiteResult, nestedTasks)
|
||||
}
|
||||
catch
|
||||
{
|
||||
case e: Throwable =>
|
||||
safeListenersCall(_.endGroup(name, e))
|
||||
SuiteResult.Error
|
||||
(SuiteResult.Error, Seq.empty[TestTask])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -204,8 +205,8 @@ object TestFramework
|
|||
val runner = runners(framework)
|
||||
for(testDefinition <- testDefinitions) yield
|
||||
{
|
||||
val task = withContextLoader(loader) { runner.task(testDefinition) }
|
||||
val testFunction = new TestFunction(() => withContextLoader(loader) { runner.run(testDefinition, task) }) { def tags = task.tags }
|
||||
val testTask = withContextLoader(loader) { runner.task(testDefinition) }
|
||||
val testFunction = createTestFunction(loader, testDefinition, runner, testTask)
|
||||
(testDefinition.name, testFunction)
|
||||
}
|
||||
}
|
||||
|
|
@ -228,8 +229,13 @@ object TestFramework
|
|||
val main = ClasspathUtilities.makeLoader(classpath, dual, scalaInstance, tempDir)
|
||||
ClasspathUtilities.filterByClasspath(interfaceJar +: classpath, main)
|
||||
}
|
||||
def createTestFunction(loader: ClassLoader, testDefinition: TestDefinition, runner:TestRunner, testTask: TestTask): TestFunction =
|
||||
new TestFunction(testDefinition, runner, (r: TestRunner) => withContextLoader(loader) { r.run(testDefinition, testTask) }) { def tags = testTask.tags }
|
||||
}
|
||||
|
||||
abstract class TestFunction(val apply: () => SuiteResult) {
|
||||
def tags: Array[String]
|
||||
abstract class TestFunction(val testDefinition: TestDefinition, val runner: TestRunner, fun: (TestRunner) => (SuiteResult, Seq[TestTask])) {
|
||||
|
||||
def apply(): (SuiteResult, Seq[TestTask]) = fun(runner)
|
||||
|
||||
def tags: Seq[String]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue