diff --git a/main/actions/src/main/scala/sbt/ForkTests.scala b/main/actions/src/main/scala/sbt/ForkTests.scala index 17bf0a32e..dcab26ade 100755 --- a/main/actions/src/main/scala/sbt/ForkTests.scala +++ b/main/actions/src/main/scala/sbt/ForkTests.scala @@ -11,7 +11,7 @@ import Tests.{Output => TestOutput, _} import ForkMain._ private[sbt] object ForkTests { - def apply(frameworks: Seq[TestFramework], tests: List[TestDefinition], config: Execution, classpath: Seq[File], fork: ForkOptions, log: Logger): Task[TestOutput] = { + def apply(runners: Map[TestFramework, Runner], tests: List[TestDefinition], config: Execution, classpath: Seq[File], fork: ForkOptions, log: Logger): Task[TestOutput] = { val opts = config.options.toList val listeners = opts flatMap { case Listeners(ls) => ls @@ -25,12 +25,6 @@ private[sbt] object ForkTests { case Filter(f) => Some(f) case _ => None } - val argMap = frameworks.map { - f => f.implClassName -> opts.flatMap { - case Argument(None | Some(`f`), args) => args - case _ => Nil - } - }.toMap std.TaskExtra.task { if (!tests.isEmpty) { @@ -56,10 +50,12 @@ private[sbt] object ForkTests { }.toArray os.writeObject(testsFiltered) - os.writeInt(frameworks.size) - for ((clazz, args) <- argMap) { - os.writeObject(clazz) - os.writeObject(args.toArray) + os.writeInt(runners.size) + for ((testFramework, mainRunner) <- runners) { + val remoteArgs = mainRunner.remoteArgs() + os.writeObject(testFramework.implClassName) + os.writeObject(mainRunner.args) + os.writeObject(remoteArgs) } os.flush() @@ -86,6 +82,7 @@ private[sbt] object ForkTests { acceptorThread.join() Acceptor.result } + testListeners.foreach(_.doComplete(result._1)) result } finally { diff --git a/main/actions/src/main/scala/sbt/Tests.scala b/main/actions/src/main/scala/sbt/Tests.scala index 3d1c6bcdc..23c0c7d63 100644 --- a/main/actions/src/main/scala/sbt/Tests.scala +++ b/main/actions/src/main/scala/sbt/Tests.scala @@ -11,7 +11,7 @@ package sbt import xsbti.api.Definition import ConcurrentRestrictions.Tag - import testing.{AnnotatedFingerprint, Fingerprint, Framework, SubclassFingerprint} + import testing.{AnnotatedFingerprint, Fingerprint, Framework, SubclassFingerprint, Runner} import java.io.File @@ -43,7 +43,7 @@ object Tests final case class Execution(options: Seq[TestOption], parallel: Boolean, tags: Seq[(Tag, Int)]) - def apply(frameworks: Map[TestFramework, Framework], testLoader: ClassLoader, discovered: Seq[TestDefinition], config: Execution, log: Logger): Task[Output] = + def apply(frameworks: Map[TestFramework, Framework], testLoader: ClassLoader, runners: Map[TestFramework, Runner], discovered: Seq[TestDefinition], config: Execution, log: Logger): Task[Output] = { import collection.mutable.{HashSet, ListBuffer, Map, Set} val testFilters = new ListBuffer[String => Boolean] @@ -94,10 +94,10 @@ object Tests val filtered0 = discovered.filter(includeTest).toList.distinct val tests = if(orderedFilters.isEmpty) filtered0 else orderedFilters.flatMap(f => filtered0.filter(d => f(d.name))).toList.distinct val arguments = testArgsByFramework.map { case (k,v) => (k, v.toList) } toMap; - testTask(frameworks.values.toSeq, testLoader, tests, setup.readOnly, cleanup.readOnly, log, testListeners.readOnly, arguments, config) + testTask(testLoader, frameworks, runners, tests, setup.readOnly, cleanup.readOnly, log, testListeners.readOnly, arguments, config) } - def testTask(frameworks: Seq[Framework], loader: ClassLoader, tests: Seq[TestDefinition], + def testTask(loader: ClassLoader, frameworks: Map[TestFramework, Framework], runners: Map[TestFramework, Runner], tests: Seq[TestDefinition], userSetup: Iterable[ClassLoader => Unit], userCleanup: Iterable[ClassLoader => Unit], log: Logger, testListeners: Seq[TestReportListener], arguments: Map[Framework, Seq[String]], config: Execution): Task[Output] = { @@ -105,7 +105,7 @@ object Tests def partApp(actions: Iterable[ClassLoader => Unit]) = actions.toSeq map {a => () => a(loader) } val (frameworkSetup, runnables, frameworkCleanup) = - TestFramework.testTasks(frameworks, loader, tests, log, testListeners, arguments) + TestFramework.testTasks(frameworks, runners, loader, tests, log, testListeners, arguments) val setupTasks = fj(partApp(userSetup) :+ frameworkSetup) val mainTasks = diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 990adf54e..1dcbe9b3b 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -454,14 +454,28 @@ object Defaults extends BuildCommon } } + def createTestRunners(frameworks: Map[TestFramework,Framework], loader: ClassLoader, config: Tests.Execution) = { + import Tests.Argument + val opts = config.options.toList + frameworks.map { case (tf, f) => + val args = opts.flatMap { + case Argument(None | Some(`tf`), args) => args + case _ => Nil + } + val mainRunner = f.runner(args.toArray, Array.empty[String], loader) + tf -> mainRunner + } + } + def allTestGroupsTask(s: TaskStreams, frameworks: Map[TestFramework,Framework], loader: ClassLoader, groups: Seq[Tests.Group], config: Tests.Execution, cp: Classpath, javaHome: Option[File]): Task[Tests.Output] = { + val runners = createTestRunners(frameworks, loader, config) val groupTasks = groups map { case Tests.Group(name, tests, runPolicy) => runPolicy match { case Tests.SubProcess(opts) => - ForkTests(frameworks.keys.toSeq, tests.toList, config, cp.files, opts, s.log) tag Tags.ForkedTestGroup + ForkTests(runners, tests.toList, config, cp.files, opts, s.log) tag Tags.ForkedTestGroup case Tests.InProcess => - Tests(frameworks, loader, tests, config, s.log) + Tests(frameworks, loader, runners, tests, config, s.log) } } Tests.foldTasks(groupTasks, config.parallel) diff --git a/sbt/src/sbt-test/tests/single-runner/src/main/scala/custom/CustomReporter.scala b/sbt/src/sbt-test/tests/single-runner/src/main/scala/custom/CustomReporter.scala index f9552f01e..39c7085ac 100644 --- a/sbt/src/sbt-test/tests/single-runner/src/main/scala/custom/CustomReporter.scala +++ b/sbt/src/sbt-test/tests/single-runner/src/main/scala/custom/CustomReporter.scala @@ -21,7 +21,6 @@ class CustomReporter extends Reporter { def apply(event: Event) { event match { case runStarting: RunStarting => writeFile("target/RunStarting", "RunStarting") - case runCompleted: RunCompleted => writeFile("target/RunCompleted", "RunCompleted") case _ => } } diff --git a/testing/agent/src/main/java/sbt/ForkMain.java b/testing/agent/src/main/java/sbt/ForkMain.java index 6a9b1fe45..4e3931ffc 100755 --- a/testing/agent/src/main/java/sbt/ForkMain.java +++ b/testing/agent/src/main/java/sbt/ForkMain.java @@ -189,6 +189,7 @@ public class ForkMain { for (int i = 0; i < nFrameworks; i++) { final String implClassName = (String) is.readObject(); final String[] frameworkArgs = (String[]) is.readObject(); + final String[] remoteFrameworkArgs = (String[]) is.readObject(); final Framework framework; try { @@ -208,7 +209,7 @@ public class ForkMain { if (matches(testFingerprint, test.fingerprint)) filteredTests.add(test); } } - final Runner runner = framework.runner(frameworkArgs, new String[0], getClass().getClassLoader()); + final Runner runner = framework.runner(frameworkArgs, remoteFrameworkArgs, getClass().getClassLoader()); for (ForkTestDefinition test : filteredTests) runTestSafe(test, runner, loggers, os); } diff --git a/testing/src/main/scala/sbt/TestFramework.scala b/testing/src/main/scala/sbt/TestFramework.scala index 311ba4044..bd85f5741 100644 --- a/testing/src/main/scala/sbt/TestFramework.scala +++ b/testing/src/main/scala/sbt/TestFramework.scala @@ -54,8 +54,7 @@ final class TestDefinition(val name: String, val fingerprint: Fingerprint) override def hashCode: Int = (name.hashCode, TestFramework.hashCode(fingerprint)).hashCode } -final class TestRunner(framework: Framework, loader: ClassLoader, args: Array[String], listeners: Seq[TestReportListener], log: Logger) { - val delegate = framework.runner(args, Array.empty, loader) +final class TestRunner(delegate: Runner, listeners: Seq[TestReportListener], log: Logger) { final def run(testDefinition: TestDefinition): TestResult.Value = { @@ -133,7 +132,8 @@ object TestFramework case _ => f.toString } - def testTasks(frameworks: Seq[Framework], + def testTasks(frameworks: Map[TestFramework, Framework], + runners: Map[TestFramework, Runner], testLoader: ClassLoader, tests: Seq[TestDefinition], log: Logger, @@ -142,11 +142,11 @@ object TestFramework (() => Unit, Seq[(String, () => TestResult.Value)], TestResult.Value => () => Unit) = { val arguments = testArgsByFramework withDefaultValue Nil - val mappedTests = testMap(frameworks, tests, arguments) + val mappedTests = testMap(frameworks.values.toSeq, tests, arguments) if(mappedTests.isEmpty) (() => (), Nil, _ => () => () ) else - createTestTasks(testLoader, mappedTests, tests, log, listeners) + createTestTasks(testLoader, runners.map { case (tf, r) => (frameworks(tf), new TestRunner(r, listeners, log))}, mappedTests, tests, log, listeners) } private[this] def order(mapped: Map[String, () => TestResult.Value], inputs: Seq[TestDefinition]): Seq[(String, () => TestResult.Value)] = @@ -180,24 +180,19 @@ object TestFramework new TestDefinition(name, pickOne(defs.map(_.fingerprint))) uniqueDefs.toSet } - - private def createTestTasks(loader: ClassLoader, tests: Map[Framework, (Set[TestDefinition], Seq[String])], ordered: Seq[TestDefinition], log: Logger, listeners: Seq[TestReportListener]) = + + private def createTestTasks(loader: ClassLoader, runners: Map[Framework, TestRunner], tests: Map[Framework, (Set[TestDefinition], Seq[String])], ordered: Seq[TestDefinition], log: Logger, listeners: Seq[TestReportListener]) = { val testsListeners = listeners collect { case tl: TestsListener => tl } - val runnerMap = - tests map { case (framework, (testDefinitions, testArgs)) => - (framework, new TestRunner(framework, loader, testArgs.toArray, listeners, log)) - } - def foreachListenerSafe(f: TestsListener => Unit): () => Unit = () => safeForeach(testsListeners, log)(f) - - import TestResult.{Error,Passed,Failed} + + import TestResult.{Error,Passed,Failed} val startTask = foreachListenerSafe(_.doInit) val testTasks = tests flatMap { case (framework, (testDefinitions, testArgs)) => - val runner = runnerMap(framework) + val runner = runners(framework) for(testDefinition <- testDefinitions) yield { val runTest = () => withContextLoader(loader) { runner.run(testDefinition) }