summaryrefslogtreecommitdiff
path: root/scalaplugin/src
diff options
context:
space:
mode:
authorLi Haoyi <haoyi.sg@gmail.com>2017-12-30 18:00:18 -0800
committerLi Haoyi <haoyi.sg@gmail.com>2017-12-30 18:14:18 -0800
commit282d9a667b69863fefbddb83ade4b9efbc98b994 (patch)
tree06ef80cf9cfd11b80bde22e1c6cffa61905e139b /scalaplugin/src
parent476640d8b78a2d1bc902fcdd15fcef996b73ca2c (diff)
downloadmill-282d9a667b69863fefbddb83ade4b9efbc98b994.tar.gz
mill-282d9a667b69863fefbddb83ade4b9efbc98b994.tar.bz2
mill-282d9a667b69863fefbddb83ade4b9efbc98b994.zip
Make use of `T.command`s `T.ctx().dest` in `ScalaPlugin#test.test` and `forkTest`, and allow users to dump the structured JSON test results via `--show`
Diffstat (limited to 'scalaplugin/src')
-rw-r--r--scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala32
-rw-r--r--scalaplugin/src/main/scala/mill/scalaplugin/TestRunner.scala70
2 files changed, 79 insertions, 23 deletions
diff --git a/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala b/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala
index 13e91c2a..4fe3ebc2 100644
--- a/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala
+++ b/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala
@@ -8,8 +8,18 @@ import mill.define.Task.{Module, TaskModule}
import mill.eval.{PathRef, Result}
import mill.modules.Jvm
import mill.modules.Jvm.{createAssembly, createJar, interactiveSubprocess, subprocess}
-
import Lib._
+import sbt.testing.Status
+object TestScalaModule{
+ def handleResults(doneMsg: String, results: Seq[TestRunner.Result]) = {
+ if (results.count(Set(Status.Error, Status.Failure)) == 0) Result.Success((doneMsg, results))
+ else {
+ val grouped = results.map(_.status).groupBy(x => x).mapValues(_.length).filter(_._2 != 0).toList.sorted
+
+ Result.Failure(grouped.map{case (k, v) => k + ": " + v}.mkString(","))
+ }
+ }
+}
trait TestScalaModule extends ScalaModule with TaskModule {
override def defaultCommandName() = "test"
def testFramework: T[String]
@@ -17,7 +27,8 @@ trait TestScalaModule extends ScalaModule with TaskModule {
def forkWorkingDir = ammonite.ops.pwd
def forkArgs = T{ Seq.empty[String] }
def forkTest(args: String*) = T.command{
- val outputPath = tmp.dir()/"out.json"
+ mkdir(T.ctx().dest)
+ val outputPath = T.ctx().dest/"out.json"
Jvm.subprocess(
mainClass = "mill.scalaplugin.TestRunner",
@@ -32,21 +43,20 @@ trait TestScalaModule extends ScalaModule with TaskModule {
),
workingDir = forkWorkingDir
)
- upickle.default.read[Option[String]](ammonite.ops.read(outputPath)) match{
- case Some(errMsg) => Result.Failure(errMsg)
- case None => Result.Success(())
- }
+
+ val jsonOutput = upickle.json.read(outputPath.toIO)
+ val (doneMsg, results) = upickle.default.readJs[(String, Seq[TestRunner.Result])](jsonOutput)
+ TestScalaModule.handleResults(doneMsg, results)
+
}
def test(args: String*) = T.command{
- TestRunner(
+ val (doneMsg, results) = TestRunner(
testFramework(),
runDepClasspath().map(_.path) :+ compile().classes.path,
Seq(compile().classes.path),
args
- ) match{
- case Some(errMsg) => Result.Failure(errMsg)
- case None => Result.Success(())
- }
+ )
+ TestScalaModule.handleResults(doneMsg, results)
}
}
diff --git a/scalaplugin/src/main/scala/mill/scalaplugin/TestRunner.scala b/scalaplugin/src/main/scala/mill/scalaplugin/TestRunner.scala
index bc36d9c7..8819d452 100644
--- a/scalaplugin/src/main/scala/mill/scalaplugin/TestRunner.scala
+++ b/scalaplugin/src/main/scala/mill/scalaplugin/TestRunner.scala
@@ -9,7 +9,8 @@ import ammonite.ops.{Path, ls, pwd}
import mill.util.Ctx.LogCtx
import mill.util.PrintLogger
import sbt.testing._
-
+import upickle.Js
+import mill.util.JsonFormatters._
import scala.collection.mutable
object TestRunner {
@@ -49,6 +50,7 @@ object TestRunner {
def log = new PrintLogger(true)
})
val outputPath = args(4)
+
ammonite.ops.write(Path(outputPath), upickle.default.write(result))
// Tests are over, kill the JVM whether or not anyone's threads are still running
@@ -60,7 +62,7 @@ object TestRunner {
entireClasspath: Seq[Path],
testClassfilePath: Seq[Path],
args: Seq[String])
- (implicit ctx: LogCtx): Option[String] = {
+ (implicit ctx: LogCtx): (String, Seq[Result]) = {
val outerClassLoader = getClass.getClassLoader
val cl = new URLClassLoader(
entireClasspath.map(_.toIO.toURI.toURL).toArray,
@@ -88,11 +90,11 @@ object TestRunner {
new TaskDef(cls.getName.stripSuffix("$"), fingerprint, true, Array())
}
)
- val events = mutable.Buffer.empty[Status]
+ val events = mutable.Buffer.empty[Event]
for(t <- tasks){
t.execute(
new EventHandler {
- def handle(event: Event) = events.append(event.status())
+ def handle(event: Event) = events.append(event)
},
Array(
new Logger {
@@ -111,14 +113,58 @@ object TestRunner {
)
}
val doneMsg = runner.done()
- val msg =
- if (doneMsg.trim.nonEmpty) doneMsg
- else{
- val grouped = events.groupBy(x => x).mapValues(_.length).filter(_._2 != 0).toList.sorted
- grouped.map{case (k, v) => k + ": " + v}.mkString(",")
+
+ val results = for(e <- events) yield {
+ val ex = if (e.throwable().isDefined) Some(e.throwable().get) else None
+ Result(
+ e.fullyQualifiedName(),
+ e.selector() match{
+ case s: NestedSuiteSelector => s.suiteId()
+ case s: NestedTestSelector => s.suiteId() + "." + s.testName()
+ case s: SuiteSelector => s.toString
+ case s: TestSelector => s.testName()
+ case s: TestWildcardSelector => s.testWildcard()
+ },
+ e.duration(),
+ e.status(),
+ ex.map(_.getClass.getName),
+ ex.map(_.getMessage),
+ ex.map(_.getStackTrace)
+ )
+ }
+ (doneMsg, results)
+ }
+
+ case class Result(fullyQualifiedName: String,
+ selector: String,
+ duration: Long,
+ status: Status,
+ exceptionName: Option[String],
+ exceptionMsg: Option[String],
+ exceptionTrace: Option[Seq[StackTraceElement]])
+
+ object Result{
+ implicit def resultRW: upickle.default.ReadWriter[Result] = upickle.default.macroRW[Result]
+ implicit def statusRW: upickle.default.ReadWriter[Status] = upickle.default.ReadWriter[Status](
+ {
+ case Status.Success => Js.Str("Success")
+ case Status.Error => Js.Str("Error")
+ case Status.Failure => Js.Str("Failure")
+ case Status.Skipped => Js.Str("Skipped")
+ case Status.Ignored => Js.Str("Ignored")
+ case Status.Canceled => Js.Str("Canceled")
+ case Status.Pending => Js.Str("Pending")
+ },
+ {
+ case Js.Str("Success") => Status.Success
+ case Js.Str("Error") => Status.Error
+ case Js.Str("Failure") => Status.Failure
+ case Js.Str("Skipped") => Status.Skipped
+ case Js.Str("Ignored") => Status.Ignored
+ case Js.Str("Canceled") => Status.Canceled
+ case Js.Str("Pending") => Status.Pending
}
- ctx.log.info(msg)
- if (events.count(Set(Status.Error, Status.Failure)) == 0) None
- else Some(msg)
+ )
}
+
}