From 48fb0c4ed6ed38283506a2960c02508603885b7d Mon Sep 17 00:00:00 2001 From: cheeseng Date: Wed, 3 Apr 2013 17:04:40 +0800 Subject: [PATCH] Initial working version of makeParallel that support nested tasks. --- main/actions/src/main/scala/sbt/Tests.scala | 33 ++++++++++--- .../tests/nested-inproc-par/build.sbt | 7 +++ .../main/scala/custom/CustomReporter.scala | 30 ++++++++++++ .../src/test/scala/com/test/NestedSpecs.scala | 17 +++++++ sbt/src/sbt-test/tests/nested-inproc-par/test | 48 +++++++++++++++++++ 5 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 sbt/src/sbt-test/tests/nested-inproc-par/build.sbt create mode 100644 sbt/src/sbt-test/tests/nested-inproc-par/src/main/scala/custom/CustomReporter.scala create mode 100644 sbt/src/sbt-test/tests/nested-inproc-par/src/test/scala/com/test/NestedSpecs.scala create mode 100644 sbt/src/sbt-test/tests/nested-inproc-par/test diff --git a/main/actions/src/main/scala/sbt/Tests.scala b/main/actions/src/main/scala/sbt/Tests.scala index 9da8786dc..4a7eda96a 100644 --- a/main/actions/src/main/scala/sbt/Tests.scala +++ b/main/actions/src/main/scala/sbt/Tests.scala @@ -112,7 +112,7 @@ object Tests val setupTasks = fj(partApp(userSetup) :+ frameworkSetup) val mainTasks = if(config.parallel) - makeParallel(runnables, setupTasks, config.tags).toSeq.join + makeParallel(loader, runnables, setupTasks, config.tags)//.toSeq.join else makeSerial(loader, runnables, setupTasks, config.tags) val taggedMainTasks = mainTasks.tagw(config.tags : _*) @@ -122,8 +122,30 @@ 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()._1) } tagw(tags : _*) tag(test.tags map (ConcurrentRestrictions.Tag(_)) : _*) dependsOn setupTasks named name } + + private def createNestedRunnables(name: String, loader: ClassLoader, testFun: TestFunction, nestedTasks: Seq[TestTask]): Seq[(String, TestFunction)] = + nestedTasks.view.zipWithIndex map { case (nt, idx) => + (name, TestFramework.createTestFunction(loader, new TestDefinition(testFun.testDefinition.name + "-" + idx, testFun.testDefinition.fingerprint), testFun.runner, nt)) + } + + def makeParallel(loader: ClassLoader, runnables: Iterable[TestRunnable], setupTasks: Task[Unit], tags: Seq[(Tag,Int)]): Task[Map[String,SuiteResult]] = + toTasks(loader, runnables.toSeq, tags).dependsOn(setupTasks) + + def toTasks(loader: ClassLoader, runnables: Seq[TestRunnable], tags: Seq[(Tag,Int)]): Task[Map[String, SuiteResult]] = { + val tasks = runnables.map { case (name, test) => toTask(loader, name, test, tags) } + tasks.join.map( _.foldLeft(Map.empty[String, SuiteResult]) { case (sum, e) => + sum ++ e + } ) + } + + def toTask(loader: ClassLoader, name: String, fun: TestFunction, tags: Seq[(Tag,Int)]): Task[Map[String, SuiteResult]] = { + val base = task { (name, fun.apply()) } + val taggedBase = base.tagw(tags : _*).tag(fun.tags.map(ConcurrentRestrictions.Tag(_)) : _*) + taggedBase flatMap { case (name, (result, nested)) => + val nestedRunnables = createNestedRunnables(fun.testDefinition.name, loader, fun, nested) + toTasks(loader, nestedRunnables, tags).map( _.updated(name, result) ) + } + } def makeSerial(loader: ClassLoader, runnables: Seq[TestRunnable], setupTasks: Task[Unit], tags: Seq[(Tag,Int)]): Task[List[(String, SuiteResult)]] = { @@ -133,10 +155,7 @@ object Tests 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)) - } + val nestedRunnables = createNestedRunnables(testFun.testDefinition.name, loader, testFun, nestedTasks) processRunnable(nestedRunnables.toList ::: rst, (hd._1, result) :: acc) case Nil => acc } diff --git a/sbt/src/sbt-test/tests/nested-inproc-par/build.sbt b/sbt/src/sbt-test/tests/nested-inproc-par/build.sbt new file mode 100644 index 000000000..0895fe82a --- /dev/null +++ b/sbt/src/sbt-test/tests/nested-inproc-par/build.sbt @@ -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 := true \ No newline at end of file diff --git a/sbt/src/sbt-test/tests/nested-inproc-par/src/main/scala/custom/CustomReporter.scala b/sbt/src/sbt-test/tests/nested-inproc-par/src/main/scala/custom/CustomReporter.scala new file mode 100644 index 000000000..f21595450 --- /dev/null +++ b/sbt/src/sbt-test/tests/nested-inproc-par/src/main/scala/custom/CustomReporter.scala @@ -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 _ => + } + } +} \ No newline at end of file diff --git a/sbt/src/sbt-test/tests/nested-inproc-par/src/test/scala/com/test/NestedSpecs.scala b/sbt/src/sbt-test/tests/nested-inproc-par/src/test/scala/com/test/NestedSpecs.scala new file mode 100644 index 000000000..b9d5a838f --- /dev/null +++ b/sbt/src/sbt-test/tests/nested-inproc-par/src/test/scala/com/test/NestedSpecs.scala @@ -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 ` {} +} \ No newline at end of file diff --git a/sbt/src/sbt-test/tests/nested-inproc-par/test b/sbt/src/sbt-test/tests/nested-inproc-par/test new file mode 100644 index 000000000..dedaa5c47 --- /dev/null +++ b/sbt/src/sbt-test/tests/nested-inproc-par/test @@ -0,0 +1,48 @@ +#This test that the framework will execute ScalaTest nested suites as parallel 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 \ No newline at end of file