mirror of https://github.com/sbt/sbt.git
buffered, separate loggers for each test
This commit is contained in:
parent
e653517c8d
commit
c8fe1a3c1d
|
|
@ -10,6 +10,7 @@ package sbt
|
||||||
import Configurations.{Compile, CompilerPlugin, IntegrationTest, Runtime, Test}
|
import Configurations.{Compile, CompilerPlugin, IntegrationTest, Runtime, Test}
|
||||||
import complete._
|
import complete._
|
||||||
import std.TaskExtra._
|
import std.TaskExtra._
|
||||||
|
import org.scalatools.testing.{AnnotatedFingerprint, SubclassFingerprint}
|
||||||
|
|
||||||
import scala.xml.{Node => XNode,NodeSeq}
|
import scala.xml.{Node => XNode,NodeSeq}
|
||||||
import org.apache.ivy.core.module.{descriptor, id}
|
import org.apache.ivy.core.module.{descriptor, id}
|
||||||
|
|
@ -44,6 +45,9 @@ object Defaults
|
||||||
))
|
))
|
||||||
def globalCore: Seq[Setting[_]] = inScope(GlobalScope)(Seq(
|
def globalCore: Seq[Setting[_]] = inScope(GlobalScope)(Seq(
|
||||||
pollInterval :== 500,
|
pollInterval :== 500,
|
||||||
|
logBuffered :== false,
|
||||||
|
logBuffered in testOnly :== true,
|
||||||
|
logBuffered in test :== true,
|
||||||
autoCompilerPlugins :== true,
|
autoCompilerPlugins :== true,
|
||||||
internalConfigurationMap :== Configurations.internalMap _,
|
internalConfigurationMap :== Configurations.internalMap _,
|
||||||
initialize :== (),
|
initialize :== (),
|
||||||
|
|
@ -219,9 +223,24 @@ object Defaults
|
||||||
)
|
)
|
||||||
|
|
||||||
def testTaskOptions(key: Scoped): Seq[Setting[_]] = inTask(key)( Seq(
|
def testTaskOptions(key: Scoped): Seq[Setting[_]] = inTask(key)( Seq(
|
||||||
testListeners <<= (streams, testListeners) map { (s, ls) => TestLogger(s.log) +: ls },
|
testListeners <<= (streams, resolvedScoped, streamsManager, logBuffered in key, testListeners) map { (s, sco, sm, buff, ls) =>
|
||||||
|
TestLogger(s.log, testLogger(sm, test in sco.scope), buff) +: ls
|
||||||
|
},
|
||||||
testOptions <<= (testOptions, testListeners) map { (options, ls) => Tests.Listeners(ls) +: options }
|
testOptions <<= (testOptions, testListeners) map { (options, ls) => Tests.Listeners(ls) +: options }
|
||||||
) )
|
) )
|
||||||
|
def testLogger(manager: Streams, baseKey: Scoped)(tdef: TestDefinition): Logger =
|
||||||
|
{
|
||||||
|
val scope = baseKey.scope
|
||||||
|
val extra = scope.extra match { case Select(x) => x; case _ => AttributeMap.empty }
|
||||||
|
val key = ScopedKey(scope.copy(extra = Select(testExtra(extra, tdef))), baseKey.key)
|
||||||
|
manager(key).log
|
||||||
|
}
|
||||||
|
def buffered(log: Logger): Logger = new BufferedLogger(FullLogger(log))
|
||||||
|
def testExtra(extra: AttributeMap, tdef: TestDefinition): AttributeMap =
|
||||||
|
{
|
||||||
|
val mod = tdef.fingerprint match { case f: SubclassFingerprint => f.isModule; case f: AnnotatedFingerprint => f.isModule; case _ => false }
|
||||||
|
extra.put(name.key, tdef.name).put(isModule, mod)
|
||||||
|
}
|
||||||
|
|
||||||
def testOnlyTask =
|
def testOnlyTask =
|
||||||
InputTask( TaskData(definedTests)(testOnlyParser)(Nil) ) { result =>
|
InputTask( TaskData(definedTests)(testOnlyParser)(Nil) ) { result =>
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ object Keys
|
||||||
val showTiming = SettingKey[Boolean]("show-timing", "If true, the command success message includes the completion time.")
|
val showTiming = SettingKey[Boolean]("show-timing", "If true, the command success message includes the completion time.")
|
||||||
val timingFormat = SettingKey[java.text.DateFormat]("timing-format", "The format used for displaying the completion time.")
|
val timingFormat = SettingKey[java.text.DateFormat]("timing-format", "The format used for displaying the completion time.")
|
||||||
val logManager = SettingKey[LogManager]("log-manager", "The log manager, which creates Loggers for different contexts.")
|
val logManager = SettingKey[LogManager]("log-manager", "The log manager, which creates Loggers for different contexts.")
|
||||||
|
val logBuffered = SettingKey[Boolean]("log-buffered", "True if logging should be buffered until work completes.")
|
||||||
|
|
||||||
// Project keys
|
// Project keys
|
||||||
val projectCommand = AttributeKey[Boolean]("project-command", "Marks Commands that were registered for the current Project.")
|
val projectCommand = AttributeKey[Boolean]("project-command", "Marks Commands that were registered for the current Project.")
|
||||||
|
|
@ -154,6 +155,7 @@ object Keys
|
||||||
val testOptions = TaskKey[Seq[TestOption]]("test-options", "Options for running tests.")
|
val testOptions = TaskKey[Seq[TestOption]]("test-options", "Options for running tests.")
|
||||||
val testFrameworks = SettingKey[Seq[TestFramework]]("test-frameworks", "Registered, although not necessarily present, test frameworks.")
|
val testFrameworks = SettingKey[Seq[TestFramework]]("test-frameworks", "Registered, although not necessarily present, test frameworks.")
|
||||||
val testListeners = TaskKey[Seq[TestReportListener]]("test-listeners", "Defines test listeners.")
|
val testListeners = TaskKey[Seq[TestReportListener]]("test-listeners", "Defines test listeners.")
|
||||||
|
val isModule = AttributeKey[Boolean]("is-module", "True if the target is a module.")
|
||||||
|
|
||||||
// Classpath/Dependency Management Keys
|
// Classpath/Dependency Management Keys
|
||||||
type Classpath = Seq[Attributed[File]]
|
type Classpath = Seq[Attributed[File]]
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ object LogManager
|
||||||
|
|
||||||
def withScreenLogger(mk: => AbstractLogger): LogManager = withLoggers(mk)
|
def withScreenLogger(mk: => AbstractLogger): LogManager = withLoggers(mk)
|
||||||
|
|
||||||
def withLoggers(screen: => AbstractLogger = defaultScreen, backed: PrintWriter => AbstractLogger = defaultBacked(false)): LogManager =
|
def withLoggers(screen: => AbstractLogger = defaultScreen, backed: PrintWriter => AbstractLogger = defaultBacked(true)): LogManager =
|
||||||
new LogManager {
|
new LogManager {
|
||||||
def apply(data: Settings[Scope], task: ScopedKey[_], to: PrintWriter): Logger =
|
def apply(data: Settings[Scope], task: ScopedKey[_], to: PrintWriter): Logger =
|
||||||
defaultLogger(data, task, screen, backed(to))
|
defaultLogger(data, task, screen, backed(to))
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ object TestFrameworks
|
||||||
val SpecsCompat = new TestFramework("sbt.impl.SpecsFramework")
|
val SpecsCompat = new TestFramework("sbt.impl.SpecsFramework")
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestFramework(val implClassName: String) extends NotNull
|
class TestFramework(val implClassName: String)
|
||||||
{
|
{
|
||||||
def create(loader: ClassLoader, log: Logger): Option[Framework] =
|
def create(loader: ClassLoader, log: Logger): Option[Framework] =
|
||||||
{
|
{
|
||||||
|
|
@ -34,7 +34,7 @@ class TestFramework(val implClassName: String) extends NotNull
|
||||||
catch { case e: ClassNotFoundException => log.debug("Framework implementation '" + implClassName + "' not present."); None }
|
catch { case e: ClassNotFoundException => log.debug("Framework implementation '" + implClassName + "' not present."); None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final class TestDefinition(val name: String, val fingerprint: Fingerprint) extends NotNull
|
final class TestDefinition(val name: String, val fingerprint: Fingerprint)
|
||||||
{
|
{
|
||||||
override def toString = "Test " + name + " : " + TestFramework.toString(fingerprint)
|
override def toString = "Test " + name + " : " + TestFramework.toString(fingerprint)
|
||||||
override def equals(t: Any) =
|
override def equals(t: Any) =
|
||||||
|
|
@ -47,8 +47,14 @@ final class TestDefinition(val name: String, val fingerprint: Fingerprint) exten
|
||||||
|
|
||||||
final class TestRunner(framework: Framework, loader: ClassLoader, listeners: Seq[TestReportListener], log: Logger)
|
final class TestRunner(framework: Framework, loader: ClassLoader, listeners: Seq[TestReportListener], log: Logger)
|
||||||
{
|
{
|
||||||
private[this] val delegate = framework.testRunner(loader, listeners.flatMap(_.contentLogger).toArray)
|
|
||||||
private[this] def run(testDefinition: TestDefinition, handler: EventHandler, args: Array[String]): Unit =
|
private[this] def run(testDefinition: TestDefinition, handler: EventHandler, args: Array[String]): Unit =
|
||||||
|
{
|
||||||
|
val loggers = listeners.flatMap(_.contentLogger(testDefinition))
|
||||||
|
val delegate = framework.testRunner(loader, loggers.map(_.log).toArray)
|
||||||
|
try { delegateRun(delegate, testDefinition, handler, args) }
|
||||||
|
finally { loggers.foreach( _.flush() ) }
|
||||||
|
}
|
||||||
|
private[this] def delegateRun(delegate: Runner, testDefinition: TestDefinition, handler: EventHandler, args: Array[String]): Unit =
|
||||||
(testDefinition.fingerprint, delegate) match
|
(testDefinition.fingerprint, delegate) match
|
||||||
{
|
{
|
||||||
case (simple: TestFingerprint, _) => delegate.run(testDefinition.name, simple, handler, args)
|
case (simple: TestFingerprint, _) => delegate.run(testDefinition.name, simple, handler, args)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ trait TestReportListener
|
||||||
/** called if test completed */
|
/** called if test completed */
|
||||||
def endGroup(name: String, result: TestResult.Value)
|
def endGroup(name: String, result: TestResult.Value)
|
||||||
/** Used by the test framework for logging test results*/
|
/** Used by the test framework for logging test results*/
|
||||||
def contentLogger: Option[TLogger] = None
|
def contentLogger(test: TestDefinition): Option[ContentLogger] = None
|
||||||
}
|
}
|
||||||
|
|
||||||
trait TestsListener extends TestReportListener
|
trait TestsListener extends TestReportListener
|
||||||
|
|
@ -52,7 +52,15 @@ object TestEvent
|
||||||
|
|
||||||
object TestLogger
|
object TestLogger
|
||||||
{
|
{
|
||||||
def apply(logger: sbt.Logger): TestLogger = new TestLogger(wrap(logger))
|
def apply(logger: sbt.Logger, logTest: TestDefinition => sbt.Logger, buffered: Boolean): TestLogger =
|
||||||
|
new TestLogger(new TestLogging(wrap(logger), tdef => contentLogger(logTest(tdef), buffered)) )
|
||||||
|
|
||||||
|
def contentLogger(log: sbt.Logger, buffered: Boolean): ContentLogger =
|
||||||
|
{
|
||||||
|
val blog = new BufferedLogger(FullLogger(log))
|
||||||
|
if(buffered) blog.record()
|
||||||
|
new ContentLogger(wrap(blog), () => blog.stopQuietly())
|
||||||
|
}
|
||||||
def wrap(logger: sbt.Logger): TLogger =
|
def wrap(logger: sbt.Logger): TLogger =
|
||||||
new TLogger
|
new TLogger
|
||||||
{
|
{
|
||||||
|
|
@ -62,11 +70,14 @@ object TestLogger
|
||||||
def debug(s: String) = log(Level.Debug, s)
|
def debug(s: String) = log(Level.Debug, s)
|
||||||
def trace(t: Throwable) = logger.trace(t)
|
def trace(t: Throwable) = logger.trace(t)
|
||||||
private def log(level: Level.Value, s: String) = logger.log(level, s)
|
private def log(level: Level.Value, s: String) = logger.log(level, s)
|
||||||
def ansiCodesSupported() = logger.ansiCodesSupported
|
def ansiCodesSupported() = true//logger.ansiCodesSupported
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class TestLogger(val log: TLogger) extends TestsListener
|
final class TestLogging(val global: TLogger, val logTest: TestDefinition => ContentLogger)
|
||||||
|
final class ContentLogger(val log: TLogger, val flush: () => Unit)
|
||||||
|
class TestLogger(val logging: TestLogging) extends TestsListener
|
||||||
{
|
{
|
||||||
|
import logging.{global => log, logTest}
|
||||||
protected var skipped, errors, passed, failures = 0
|
protected var skipped, errors, passed, failures = 0
|
||||||
|
|
||||||
def startGroup(name: String) {}
|
def startGroup(name: String) {}
|
||||||
|
|
@ -106,5 +117,5 @@ class TestLogger(val log: TLogger) extends TestsListener
|
||||||
case TestResult.Failed => log.error("Failed: " + postfix)
|
case TestResult.Failed => log.error("Failed: " + postfix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override def contentLogger: Option[TLogger] = Some(log)
|
override def contentLogger(test: TestDefinition): Option[ContentLogger] = Some(logTest(test))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue