-Moved code that count test results from TestLogger to SuiteResult.

-Print the results in Tests.showResult.
This commit is contained in:
cheeseng 2013-04-10 15:28:50 +08:00
parent a76523a5f6
commit 1ec2118219
6 changed files with 65 additions and 57 deletions

View File

@ -30,8 +30,8 @@ private[sbt] object ForkTests {
if (!tests.isEmpty) {
val server = new ServerSocket(0)
object Acceptor extends Runnable {
val resultsAcc = mutable.Map.empty[String, TestResult.Value]
lazy val result = (overall(resultsAcc.values), resultsAcc.toMap)
val resultsAcc = mutable.Map.empty[String, SuiteResult]
lazy val result = (overall(resultsAcc.values.map(_.result)), resultsAcc.toMap)
def run: Unit = {
val socket =
try {
@ -76,7 +76,7 @@ private[sbt] object ForkTests {
val ec = Fork.java(fork, options)
val result =
if (ec != 0)
(TestResult.Error, Map("Running java with options " + options.mkString(" ") + " failed with exit code " + ec -> TestResult.Error))
(TestResult.Error, Map("Running java with options " + options.mkString(" ") + " failed with exit code " + ec -> SuiteResult.Error))
else {
// Need to wait acceptor thread to finish its business
acceptorThread.join()
@ -89,11 +89,11 @@ private[sbt] object ForkTests {
server.close()
}
} else
(TestResult.Passed, Map.empty[String, TestResult.Value])
(TestResult.Passed, Map.empty[String, SuiteResult])
} tagw (config.tags: _*)
}
}
private final class React(is: ObjectInputStream, os: ObjectOutputStream, log: Logger, listeners: Seq[TestReportListener], results: mutable.Map[String, TestResult.Value])
private final class React(is: ObjectInputStream, os: ObjectOutputStream, log: Logger, listeners: Seq[TestReportListener], results: mutable.Map[String, SuiteResult])
{
import ForkTags._
@annotation.tailrec def react(): Unit = is.readObject match {
@ -107,9 +107,9 @@ private final class React(is: ObjectInputStream, os: ObjectOutputStream, log: Lo
listeners.foreach(_ startGroup group)
val event = TestEvent(tEvents)
listeners.foreach(_ testEvent event)
val result = event.result getOrElse TestResult.Passed
results += group -> result
listeners.foreach(_ endGroup (group, result))
val suiteResult = SuiteResult(tEvents)
results += group -> suiteResult
listeners.foreach(_ endGroup (group, suiteResult.result))
react()
}
}

View File

@ -19,7 +19,7 @@ sealed trait TestOption
object Tests
{
// (overall result, individual results)
type Output = (TestResult.Value, Map[String,TestResult.Value])
type Output = (TestResult.Value, Map[String,SuiteResult])
final case class Setup(setup: ClassLoader => Unit) extends TestOption
def Setup(setup: () => Unit) = new Setup(_ => setup())
@ -119,14 +119,14 @@ object Tests
cleanupTasks map { _ => results }
}
}
type TestRunnable = (String, () => TestResult.Value)
type TestRunnable = (String, () => SuiteResult)
def makeParallel(runnables: Iterable[TestRunnable], setupTasks: Task[Unit], tags: Seq[(Tag,Int)]) =
runnables map { case (name, test) => task { (name, test()) } tagw(tags : _*) dependsOn setupTasks named name }
def makeSerial(runnables: Seq[TestRunnable], setupTasks: Task[Unit], tags: Seq[(Tag,Int)]) =
task { runnables map { case (name, test) => (name, test()) } } dependsOn(setupTasks)
def processResults(results: Iterable[(String, TestResult.Value)]): (TestResult.Value, Map[String, TestResult.Value]) =
(overall(results.map(_._2)), results.toMap)
def processResults(results: Iterable[(String, SuiteResult)]): (TestResult.Value, Map[String, SuiteResult]) =
(overall(results.map(_._2.result)), results.toMap)
def foldTasks(results: Seq[Task[Output]], parallel: Boolean): Task[Output] =
if (parallel)
reduced(results.toIndexedSeq, {
@ -169,8 +169,21 @@ object Tests
(tests, mains.toSet)
}
def showResults(log: Logger, results: (TestResult.Value, Map[String, TestResult.Value]), noTestsMessage: =>String): Unit =
def showResults(log: Logger, results: (TestResult.Value, Map[String, SuiteResult]), noTestsMessage: =>String): Unit =
{
val (skipped, errors, passed, failures) =
results._2.foldLeft((0, 0, 0, 0)) { case (acc, entry) =>
val suiteResult = entry._2
(acc._1 + suiteResult.skippedCount, acc._2 + suiteResult.errorCount, acc._3 + suiteResult.passedCount, acc._4 + suiteResult.failureCount)
}
val totalCount = failures + errors + skipped + passed
val postfix = "Total " + totalCount + ", Failed " + failures + ", Errors " + errors + ", Passed " + passed + ", Skipped " + skipped
results._1 match {
case TestResult.Error => log.error("Error: " + postfix)
case TestResult.Passed => log.info("Passed: " + postfix)
case TestResult.Failed => log.error("Failed: " + postfix)
}
if (results._2.isEmpty)
log.info(noTestsMessage)
else {

View File

@ -479,10 +479,12 @@ object Defaults extends BuildCommon
}
}
val output = Tests.foldTasks(groupTasks, config.parallel)
runners foreach { case (tf, r) =>
r.done()
output map { out =>
runners foreach { case (tf, r) =>
r.done()
}
out
}
output
}
def selectedFilter(args: Seq[String]): Seq[String => Boolean] =

View File

@ -14,10 +14,10 @@ object Ticket543Test extends Build {
fork := true,
testListeners += new TestReportListener {
def testEvent(event: TestEvent) {
for (e <- event.detail.filter(_.result == org.scalatools.testing.Result.Failure)) {
if (e.error ne null) {
for (e <- event.detail.filter(_.status == sbt.testing.Status.Failure)) {
if (e.throwable ne null) {
val caw = new CharArrayWriter
e.error.printStackTrace(new PrintWriter(caw))
e.throwable.printStackTrace(new PrintWriter(caw))
if (caw.toString.contains("Test.scala:"))
marker.createNewFile()
}

View File

@ -65,7 +65,7 @@ final class TestDefinition(val name: String, val fingerprint: Fingerprint)
final class TestRunner(delegate: Runner, listeners: Seq[TestReportListener], log: Logger) {
final def run(testDefinition: TestDefinition): TestResult.Value =
final def run(testDefinition: TestDefinition): SuiteResult =
{
log.debug("Running " + testDefinition)
val name = testDefinition.name
@ -82,21 +82,21 @@ final class TestRunner(delegate: Runner, listeners: Seq[TestReportListener], log
finally loggers.foreach( _.flush() )
val event = TestEvent(results)
safeListenersCall(_.testEvent( event ))
event.result
SuiteResult(results)
}
safeListenersCall(_.startGroup(name))
try
{
val result = runTest().getOrElse(TestResult.Passed)
safeListenersCall(_.endGroup(name, result))
result
val suiteResult = runTest()
safeListenersCall(_.endGroup(name, suiteResult.result))
suiteResult
}
catch
{
case e: Throwable =>
safeListenersCall(_.endGroup(name, e))
TestResult.Error
SuiteResult.Error
}
}
@ -148,7 +148,7 @@ object TestFramework
log: Logger,
listeners: Seq[TestReportListener],
testArgsByFramework: Map[Framework, Seq[String]]):
(() => Unit, Seq[(String, () => TestResult.Value)], TestResult.Value => () => Unit) =
(() => Unit, Seq[(String, () => SuiteResult)], TestResult.Value => () => Unit) =
{
val arguments = testArgsByFramework withDefaultValue Nil
val mappedTests = testMap(frameworks.values.toSeq, tests, arguments)
@ -158,7 +158,7 @@ object TestFramework
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)] =
private[this] def order(mapped: Map[String, () => SuiteResult], inputs: Seq[TestDefinition]): Seq[(String, () => SuiteResult)] =
for( d <- inputs; act <- mapped.get(d.name) ) yield (d.name, act)
private[this] def testMap(frameworks: Seq[Framework], tests: Seq[TestDefinition], args: Map[Framework, Seq[String]]):

View File

@ -28,6 +28,24 @@ trait TestsListener extends TestReportListener
def doComplete(finalResult: TestResult.Value)
}
final class SuiteResult(val result: TestResult.Value, val passedCount: Int, val failureCount: Int, val errorCount: Int, val skippedCount: Int)
object SuiteResult
{
def apply(events: Seq[TEvent]): SuiteResult =
{
def count(status: TStatus) = events.count(_.status == status)
val overallResult = (TestResult.Passed /: events) { (sum, event) =>
val status = event.status
if(sum == TestResult.Error || status == TStatus.Error) TestResult.Error
else if(sum == TestResult.Failed || status == TStatus.Failure) TestResult.Failed
else TestResult.Passed
}
new SuiteResult (overallResult, count(TStatus.Success), count(TStatus.Failure), count(TStatus.Error), count(TStatus.Skipped))
}
val Error: SuiteResult = new SuiteResult(TestResult.Error, 0, 0, 0, 0)
val Empty: SuiteResult = new SuiteResult(TestResult.Passed, 0, 0, 0, 0)
}
abstract class TestEvent extends NotNull
{
def result: Option[TestResult.Value]
@ -77,43 +95,18 @@ final class TestLogging(val global: TLogger, val logTest: TestDefinition => Cont
final class ContentLogger(val log: TLogger, val flush: () => Unit)
class TestLogger(val logging: TestLogging) extends TestsListener
{
import logging.{global => log, logTest}
import java.util.concurrent.atomic.AtomicInteger
protected val skippedCount, errorsCount, passedCount, failuresCount = new AtomicInteger
import logging.{global => log, logTest}
def startGroup(name: String) {}
def testEvent(event: TestEvent): Unit = event.detail.foreach(count)
def testEvent(event: TestEvent): Unit = {}
def endGroup(name: String, t: Throwable)
{
log.trace(t)
log.error("Could not run test " + name + ": " + t.toString)
}
def endGroup(name: String, result: TestResult.Value) {}
protected def count(event: TEvent): Unit =
{
val count = event.status match {
case TStatus.Error => errorsCount
case TStatus.Success => passedCount
case TStatus.Failure => failuresCount
case TStatus.Skipped => skippedCount
}
count.incrementAndGet()
}
def doInit
{
for (count <- List(skippedCount, errorsCount, passedCount, failuresCount)) count.set(0)
}
/** called once, at end. */
def doComplete(finalResult: TestResult.Value): Unit =
{
val (skipped, errors, passed, failures) = (skippedCount.get, errorsCount.get, passedCount.get, failuresCount.get)
val totalCount = failures + errors + skipped + passed
val postfix = ": Total " + totalCount + ", Failed " + failures + ", Errors " + errors + ", Passed " + passed + ", Skipped " + skipped
finalResult match {
case TestResult.Error => log.error("Error" + postfix)
case TestResult.Passed => log.info("Passed: " + postfix)
case TestResult.Failed => log.error("Failed: " + postfix)
}
}
def doInit {}
/** called once, at end of test group. */
def doComplete(finalResult: TestResult.Value): Unit = {}
override def contentLogger(test: TestDefinition): Option[ContentLogger] = Some(logTest(test))
}