diff options
author | Li Haoyi <haoyi.sg@gmail.com> | 2017-12-30 18:00:18 -0800 |
---|---|---|
committer | Li Haoyi <haoyi.sg@gmail.com> | 2017-12-30 18:14:18 -0800 |
commit | 282d9a667b69863fefbddb83ade4b9efbc98b994 (patch) | |
tree | 06ef80cf9cfd11b80bde22e1c6cffa61905e139b /scalaplugin/src | |
parent | 476640d8b78a2d1bc902fcdd15fcef996b73ca2c (diff) | |
download | mill-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.scala | 32 | ||||
-rw-r--r-- | scalaplugin/src/main/scala/mill/scalaplugin/TestRunner.scala | 70 |
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) + ) } + } |