Fixes after review, take 2.

This commit is contained in:
Eugene Vigdorchik 2012-04-02 12:08:18 +04:00
parent e3a7a331d5
commit 4d5effcb28
6 changed files with 60 additions and 55 deletions

View File

@ -286,26 +286,29 @@ object Defaults extends BuildCommon
testOptions in GlobalScope :== Nil,
testFilter in testOnly :== (selectedFilter _),
testFilter in testQuick <<= testQuickFilter,
executeTests <<= (streams in test, loadedTestFrameworks, testLoader, testGrouping in test, fullClasspath in test, javaOptions in test, javaHome in test, resolvedScoped, state) flatMap {
(s, frameworkMap, loader, groups, cp, javaOpts, javaHome, scoped, st) =>
implicit val display = Project.showContextKey(st)
executeTests <<= (streams in test, loadedTestFrameworks, testLoader, testGrouping in test, fullClasspath in test, javaOptions in test, javaHome in test) flatMap {
(s, frameworkMap, loader, groups, cp, javaOpts, javaHome) =>
val results = groups map {
case Tests.TestGroup(name, tests, config) =>
case Tests.Group(name, tests, config) =>
config.subproc match {
case Tests.Fork(extraJvm) =>
ForkTests(frameworkMap.keys.toSeq, tests.toList, config, cp.files, javaHome, javaOpts, s.log)
case Tests.InProcess =>
Tests(frameworkMap, loader, tests, config, noTestsMessage(scoped, name), s.log)
Tests(frameworkMap, loader, tests, config, s.log)
}
}
Tests.reduce(results)
Tests.flatten(results)
},
test <<= (executeTests, streams, resolvedScoped, state) map {
(results, s, scoped, st) =>
implicit val display = Project.showContextKey(st)
Tests.showResults(s.log, results, noTestsMessage(scoped))
},
test <<= (executeTests, streams) map { (results, s) => Tests.showResults(s.log, results) },
testOnly <<= inputTests(testOnly),
testQuick <<= inputTests(testQuick)
)
private[this] def noTestsMessage(scoped: ScopedKey[_], group: String)(implicit display: Show[ScopedKey[_]]): String =
"No tests to run for group " + group + " in " + display(scoped)
private[this] def noTestsMessage(scoped: ScopedKey[_])(implicit display: Show[ScopedKey[_]]): String =
"No tests to run for " + display(scoped)
lazy val TaskGlobal: Scope = ThisScope.copy(task = Global)
lazy val ConfigGlobal: Scope = ThisScope.copy(config = Global)
@ -316,7 +319,7 @@ object Defaults extends BuildCommon
testOptions <<= (testOptions in TaskGlobal, testListeners) map { (options, ls) => Tests.Listeners(ls) +: options },
testExecution <<= testExecutionTask(key),
testGrouping <<= ((definedTests, testExecution) map {
(tests, exec) => Seq(new Tests.TestGroup("<default>", tests, exec))
(tests, exec) => Seq(new Tests.Group("<default>", tests, exec))
})
) )
def testLogger(manager: Streams, baseKey: Scoped)(tdef: TestDefinition): Logger =
@ -372,17 +375,17 @@ object Defaults extends BuildCommon
case (s, frameworks, filter, groups, loader, scoped, (selected, frameworkOptions), cp, javaOpts, javaHome, st) =>
implicit val display = Project.showContextKey(st)
val results = groups map {
case Tests.TestGroup(name, tests, config) =>
case Tests.Group(name, tests, config) =>
val modifiedOpts = Tests.Filter(filter(selected)) +: Tests.Argument(frameworkOptions : _*) +: config.options
val newConfig = config.copy(options = modifiedOpts)
newConfig.subproc match {
case Tests.Fork(extraJvm) =>
ForkTests(frameworks.keys.toSeq, tests.toList, newConfig, cp.files, javaHome, javaOpts, s.log)
case Tests.InProcess =>
Tests(frameworks, loader, tests, newConfig, noTestsMessage(scoped, name), s.log)
Tests(frameworks, loader, tests, newConfig, s.log)
}
}
Tests.reduce(results) map (Tests.showResults(s.log, _))
Tests.flatten(results) map (Tests.showResults(s.log, _, noTestsMessage(scoped)))
}
}

View File

@ -198,7 +198,7 @@ object Keys
val testListeners = TaskKey[Seq[TestReportListener]]("test-listeners", "Defines test listeners.")
val testExecution = TaskKey[Tests.Execution]("test-execution", "Settings controlling test execution")
val testFilter = TaskKey[Seq[String] => String => Boolean]("test-filter", "Filter controlling whether the test is executed")
val testGrouping = TaskKey[Seq[Tests.TestGroup]]("test-grouping", "Groups discovered tests into groups. Groups are run sequentially.")
val testGrouping = TaskKey[Seq[Tests.Group]]("test-grouping", "Groups discovered tests into groups. Groups are run sequentially.")
val isModule = AttributeKey[Boolean]("is-module", "True if the target is a module.")
// Classpath/Dependency Management Keys

View File

@ -58,12 +58,13 @@ private[sbt] object ForkTests {
os.writeObject(args.toArray)
}
import Tags._
@annotation.tailrec def react: Unit = is.readObject match {
case `TestsDone` => os.writeObject(TestsDone);
case Array(`ErrorTag`, s: String) => log.error(s); react
case Array(`WarnTag`, s: String) => log.warn(s); react
case Array(`InfoTag`, s: String) => log.info(s); react
case Array(`DebugTag`, s: String) => log.debug(s); react
case `Done` => os.writeObject(Done);
case Array(`Error`, s: String) => log.error(s); react
case Array(`Warn`, s: String) => log.warn(s); react
case Array(`Info`, s: String) => log.info(s); react
case Array(`Debug`, s: String) => log.debug(s); react
case t: Throwable => log.trace(t); react
case tEvents: Array[Event] =>
for (first <- tEvents.headOption) listeners.foreach(_ startGroup first.testName)

View File

@ -45,7 +45,7 @@ object Tests
final case class Execution(options: Seq[TestOption], parallel: Boolean, subproc: SubProcessPolicy, tags: Seq[(Tag, Int)])
def apply(frameworks: Map[TestFramework, Framework], testLoader: ClassLoader, discovered: Seq[TestDefinition], config: Execution, noTestsMessage: => String, log: Logger): Task[Output] =
def apply(frameworks: Map[TestFramework, Framework], testLoader: ClassLoader, discovered: Seq[TestDefinition], config: Execution, log: Logger): Task[Output] =
{
import collection.mutable.{HashSet, ListBuffer, Map, Set}
val testFilters = new ListBuffer[String => Boolean]
@ -93,10 +93,10 @@ object Tests
def includeTest(test: TestDefinition) = !excludeTestsSet.contains(test.name) && testFilters.forall(filter => filter(test.name))
val tests = discovered.filter(includeTest).toSet.toSeq
val arguments = testArgsByFramework.map { case (k,v) => (k, v.toList) } toMap;
testTask(frameworks.values.toSeq, testLoader, tests, noTestsMessage, setup.readOnly, cleanup.readOnly, log, testListeners.readOnly, arguments, config)
testTask(frameworks.values.toSeq, testLoader, tests, setup.readOnly, cleanup.readOnly, log, testListeners.readOnly, arguments, config)
}
def testTask(frameworks: Seq[Framework], loader: ClassLoader, tests: Seq[TestDefinition], noTestsMessage: => String,
def testTask(frameworks: Seq[Framework], loader: ClassLoader, 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] =
{
@ -104,7 +104,7 @@ object Tests
def partApp(actions: Iterable[ClassLoader => Unit]) = actions.toSeq map {a => () => a(loader) }
val (frameworkSetup, runnables, frameworkCleanup) =
TestFramework.testTasks(frameworks, loader, tests, noTestsMessage, log, testListeners, arguments)
TestFramework.testTasks(frameworks, loader, tests, log, testListeners, arguments)
val setupTasks = fj(partApp(userSetup) :+ frameworkSetup)
val mainTasks =
@ -126,7 +126,7 @@ object Tests
def processResults(results: Iterable[(String, TestResult.Value)]): (TestResult.Value, Map[String, TestResult.Value]) =
(overall(results.map(_._2)), results.toMap)
def reduce(results: Seq[Task[Output]]): Task[Output] =
def flatten(results: Seq[Task[Output]]): Task[Output] =
reduced(results.toIndexedSeq, {
case ((v1, m1), (v2, m2)) => (if (v1.id < v2.id) v2 else v1, m1 ++ m2)
})
@ -157,31 +157,35 @@ object Tests
(tests, mains.toSet)
}
def showResults(log: Logger, results: (TestResult.Value, Map[String, TestResult.Value])): Unit =
def showResults(log: Logger, results: (TestResult.Value, Map[String, TestResult.Value]), noTestsMessage: =>String): Unit =
{
if (results._2.isEmpty)
log.info(noTestsMessage)
else {
import TestResult.{Error, Failed, Passed}
def select(Tpe: TestResult.Value) = results._2 collect { case (name, Tpe) => name }
def select(Tpe: TestResult.Value) = results._2 collect { case (name, Tpe) => name }
val failures = select(Failed)
val errors = select(Error)
val passed = select(Passed)
val failures = select(Failed)
val errors = select(Error)
val passed = select(Passed)
def show(label: String, level: Level.Value, tests: Iterable[String]): Unit =
if(!tests.isEmpty)
{
log.log(level, label)
log.log(level, tests.mkString("\t", "\n\t", ""))
}
def show(label: String, level: Level.Value, tests: Iterable[String]): Unit =
if(!tests.isEmpty)
{
log.log(level, label)
log.log(level, tests.mkString("\t", "\n\t", ""))
}
show("Passed tests:", Level.Debug, passed )
show("Failed tests:", Level.Error, failures)
show("Error during tests:", Level.Error, errors)
show("Passed tests:", Level.Debug, passed )
show("Failed tests:", Level.Error, failures)
show("Error during tests:", Level.Error, errors)
if(!failures.isEmpty || !errors.isEmpty)
error("Tests unsuccessful")
if(!failures.isEmpty || !errors.isEmpty)
error("Tests unsuccessful")
}
}
final case class TestGroup(name: String, tests: Seq[TestDefinition], config: Execution)
final case class Group(name: String, tests: Seq[TestDefinition], config: Execution)
}

View File

@ -129,7 +129,6 @@ object TestFramework
def testTasks(frameworks: Seq[Framework],
testLoader: ClassLoader,
tests: Seq[TestDefinition],
noTestsMessage: => String,
log: Logger,
listeners: Seq[TestReportListener],
testArgsByFramework: Map[Framework, Seq[String]]):
@ -138,7 +137,7 @@ object TestFramework
val arguments = testArgsByFramework withDefaultValue Nil
val mappedTests = testMap(frameworks, tests, arguments)
if(mappedTests.isEmpty)
(() => (), Nil, _ => () => log.info(noTestsMessage) )
(() => (), Nil, _ => () => () )
else
createTestTasks(testLoader, mappedTests, log, listeners)
}

View File

@ -15,11 +15,9 @@ import java.util.ArrayList;
import java.util.List;
public class ForkMain {
public static final String TestsDone = "TestsDone";
public static final String ErrorTag = "[error]";
public static final String WarnTag = "[warn]";
public static final String InfoTag = "[info]";
public static final String DebugTag = "[debug]";
public static enum Tags {
Error, Warn, Info, Debug, Done;
}
static class SubclassFingerscan implements TestFingerprint, Serializable {
private boolean isModule;
@ -95,18 +93,18 @@ public class ForkMain {
Logger[] loggers = {
new Logger() {
public boolean ansiCodesSupported() { return false; }
void print(Object obj) {
void write(Object obj) {
try {
os.writeObject(obj);
} catch (IOException e) {
System.err.println("Cannot write to socket");
}
}
public void error(String s) { print(new String[]{ErrorTag, s}); }
public void warn(String s) { print(new String[]{WarnTag, s}); }
public void info(String s) { print(new String[]{InfoTag, s}); }
public void debug(String s) { print(new String[]{DebugTag, s}); }
public void trace(Throwable t) { print(t); }
public void error(String s) { write(new Object[]{Tags.Error, s}); }
public void warn(String s) { write(new Object[]{Tags.Warn, s}); }
public void info(String s) { write(new Object[]{Tags.Info, s}); }
public void debug(String s) { write(new Object[]{Tags.Debug, s}); }
public void trace(Throwable t) { write(t); }
}
};
@ -144,7 +142,7 @@ public class ForkMain {
os.writeObject(events.toArray(new ForkEvent[events.size()]));
}
}
os.writeObject(TestsDone);
os.writeObject(Tags.Done);
is.readObject();
}
}