Added support of custom summary message returned from Runner.done method.

This commit is contained in:
cheeseng 2013-04-04 11:30:59 +08:00
parent 1ec2118219
commit f862e64911
3 changed files with 60 additions and 38 deletions

View File

@ -31,7 +31,7 @@ private[sbt] object ForkTests {
val server = new ServerSocket(0)
object Acceptor extends Runnable {
val resultsAcc = mutable.Map.empty[String, SuiteResult]
lazy val result = (overall(resultsAcc.values.map(_.result)), resultsAcc.toMap)
lazy val result = TestOutput(overall(resultsAcc.values.map(_.result)), resultsAcc.toMap, Iterable.empty)
def run: Unit = {
val socket =
try {
@ -76,20 +76,20 @@ 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 -> SuiteResult.Error))
TestOutput(TestResult.Error, Map("Running java with options " + options.mkString(" ") + " failed with exit code " + ec -> SuiteResult.Error), Iterable.empty)
else {
// Need to wait acceptor thread to finish its business
acceptorThread.join()
Acceptor.result
}
testListeners.foreach(_.doComplete(result._1))
testListeners.foreach(_.doComplete(result.overall))
result
} finally {
server.close()
}
} else
(TestResult.Passed, Map.empty[String, SuiteResult])
TestOutput(TestResult.Passed, Map.empty[String, SuiteResult], Iterable.empty)
} tagw (config.tags: _*)
}
}

View File

@ -19,7 +19,8 @@ sealed trait TestOption
object Tests
{
// (overall result, individual results)
type Output = (TestResult.Value, Map[String,SuiteResult])
final case class Output(overall: TestResult.Value, events: Map[String,SuiteResult], summaries: Iterable[Summary])
final case class Summary(name: String, summaryText: String)
final case class Setup(setup: ClassLoader => Unit) extends TestOption
def Setup(setup: () => Unit) = new Setup(_ => setup())
@ -115,7 +116,7 @@ object Tests
makeSerial(runnables, setupTasks, config.tags)
val taggedMainTasks = mainTasks.tagw(config.tags : _*)
taggedMainTasks map processResults flatMap { results =>
val cleanupTasks = fj(partApp(userCleanup) :+ frameworkCleanup(results._1))
val cleanupTasks = fj(partApp(userCleanup) :+ frameworkCleanup(results.overall))
cleanupTasks map { _ => results }
}
}
@ -125,12 +126,12 @@ object Tests
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, SuiteResult)]): (TestResult.Value, Map[String, SuiteResult]) =
(overall(results.map(_._2.result)), results.toMap)
def foldTasks(results: Seq[Task[Output]], parallel: Boolean): Task[Output] =
def processResults(results: Iterable[(String, SuiteResult)]): Output =
Output(overall(results.map(_._2.result)), results.toMap, Iterable.empty)
def foldTasks(results: Seq[Task[Output]], parallel: Boolean): Task[Output] =
if (parallel)
reduced(results.toIndexedSeq, {
case ((v1, m1), (v2, m2)) => (if (v1.id < v2.id) v2 else v1, m1 ++ m2)
case (Output(v1, m1, _), Output(v2, m2, _)) => Output(if (v1.id < v2.id) v2 else v1, m1 ++ m2, Iterable.empty)
})
else {
def sequence(tasks: List[Task[Output]], acc: List[Output]): Task[List[Output]] = tasks match {
@ -138,8 +139,8 @@ object Tests
case hd::tl => hd flatMap { out => sequence(tl, out::acc) }
}
sequence(results.toList, List()) map { ress =>
val (rs, ms) = ress.unzip
(overall(rs), ms reduce (_ ++ _))
val (rs, ms) = ress.unzip { e => (e.overall, e.events) }
Output(overall(rs), ms reduce (_ ++ _), Iterable.empty)
}
}
def overall(results: Iterable[TestResult.Value]): TestResult.Value =
@ -169,27 +170,45 @@ object Tests
(tests, mains.toSet)
}
def showResults(log: Logger, results: (TestResult.Value, Map[String, SuiteResult]), noTestsMessage: =>String): Unit =
def showResults(log: Logger, results: Output, 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)
val multipleFrameworks = results.summaries.size > 1
def printSummary(name: String, message: String)
{
if (multipleFrameworks)
log.info(name)
if (message.size > 0)
log.info(message)
else
log.info("Summary for " + name + " not available.")
}
if (results._2.isEmpty)
for (Summary(name, messages) <- results.summaries)
printSummary(name, messages)
val noSummary = results.summaries.headOption.forall(_.summaryText.size == 0)
val printStandard = multipleFrameworks || noSummary
// Print the standard one-liner statistic if no framework summary is defined, or when > 1 framework is in used.
if (printStandard)
{
val (skippedCount, errorsCount, passedCount, failuresCount) =
results.events.foldLeft((0, 0, 0, 0)) { case (acc, (name, testEvent)) =>
(acc._1 + testEvent.skippedCount, acc._2 + testEvent.errorCount, acc._3 + testEvent.passedCount, acc._4 + testEvent.failureCount)
}
val totalCount = failuresCount + errorsCount + skippedCount + passedCount
val postfix = "Total " + totalCount + ", Failed " + failuresCount + ", Errors " + errorsCount + ", Passed " + passedCount + ", Skipped " + skippedCount
results.overall match {
case TestResult.Error => log.error("Error: " + postfix)
case TestResult.Passed => log.info("Passed: " + postfix)
case TestResult.Failed => log.error("Failed: " + postfix)
}
}
// Let's always print out Failed tests for now
if (results.events.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.events collect { case (name, Tpe) => name }
val failures = select(Failed)
val errors = select(Error)
@ -197,17 +216,19 @@ object Tests
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", ""))
}
{
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)
}
if(!failures.isEmpty || !errors.isEmpty)
throw new TestsFailedException
results.overall match {
case TestResult.Error | TestResult.Failed => throw new TestsFailedException
case TestResult.Passed =>
}
}

View File

@ -446,9 +446,9 @@ object Defaults extends BuildCommon
implicit val display = Project.showContextKey(state.value)
val modifiedOpts = Tests.Filters(filter(selected)) +: Tests.Argument(frameworkOptions : _*) +: config.options
val newConfig = config.copy(options = modifiedOpts)
val groupsTask = allTestGroupsTask(s, loadedTestFrameworks.value, testLoader.value, testGrouping.value, newConfig, fullClasspath.value, javaHome.value)
val output = allTestGroupsTask(s, loadedTestFrameworks.value, testLoader.value, testGrouping.value, newConfig, fullClasspath.value, javaHome.value)
val processed =
for(out <- groupsTask) yield
for(out <- output) yield
Tests.showResults(s.log, out, noTestsMessage(resolvedScoped.value))
Def.value(processed)
}
@ -480,10 +480,11 @@ object Defaults extends BuildCommon
}
val output = Tests.foldTasks(groupTasks, config.parallel)
output map { out =>
runners foreach { case (tf, r) =>
r.done()
}
out
val summaries =
runners map { case (tf, r) =>
Tests.Summary(frameworks(tf).name, r.done())
}
out.copy(summaries = summaries)
}
}