summaryrefslogtreecommitdiff
path: root/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala
diff options
context:
space:
mode:
authorTobias Roeser <le.petit.fou@web.de>2019-10-16 13:07:51 +0200
committerTobias Roeser <le.petit.fou@web.de>2019-10-16 13:07:51 +0200
commit6a19be4008426bb4edc137dd756d0a0b7347e0a5 (patch)
tree7add40a2be94061cac8767bb36e987a304077854 /contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala
parent9354e9311118ca23a44f72e463875a2769e53716 (diff)
parent76a8dfe705cafc18836df122d45c51452d4c6de3 (diff)
downloadmill-6a19be4008426bb4edc137dd756d0a0b7347e0a5.tar.gz
mill-6a19be4008426bb4edc137dd756d0a0b7347e0a5.tar.bz2
mill-6a19be4008426bb4edc137dd756d0a0b7347e0a5.zip
Merge pull request https://github.com/lihaoyi/mill/pull/664
Added the contrib.bsp module which contains an implementation of the BuildServer from BSP, thus alowing mill to be used by IDEs which use BSP. The MillBuildServer supports the following BSP features: * retrieving the build targets * compile requests * run requests * test requests * compile published diagnostics * task start/finish notifications for compile and test requests * progress notifications for compile * retrieving the scala main classes, test classes and scalac options Currently these features allow importing and compiling a mill project in IntelliJ IDEA via BSP. Known issues, some of which are being investigated: * can not run main classes from the IntelliJ interface * rarely, a strange NoClassDefFoundException is being thrown upon compiling from intellij * still tweaking the command for starting the server in order to work on all operating systems ( should be fine for linux and macOs so far ) Would be great to get feedback about this integration.
Diffstat (limited to 'contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala')
-rw-r--r--contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala117
1 files changed, 117 insertions, 0 deletions
diff --git a/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala
new file mode 100644
index 00000000..b49c0b7c
--- /dev/null
+++ b/contrib/bsp/src/mill/contrib/bsp/BspTestReporter.scala
@@ -0,0 +1,117 @@
+package mill.contrib.bsp
+
+import java.io.{PrintWriter, StringWriter}
+
+import ch.epfl.scala.bsp4j._
+import mill.api.TestReporter
+import sbt.testing._
+
+
+/**
+ * Context class for BSP, specialized for sending `task-start` and
+ * `task-finish` notifications for every test being ran.
+ *
+ * @param client The client to send notifications to
+ * @param targetId The targetId of the BSP target for which
+ * the test request is being processed
+ * @param taskId The unique taskId associated with the
+ * test task that will trigger this reporter
+ * to log testing events.
+ * @param arguments compilation arguments as part of the BSP context,
+ * in case special arguments need to be passed to
+ * the compiler before running the test task.
+ */
+class BspTestReporter(client: BuildClient,
+ targetId: BuildTargetIdentifier,
+ taskId: TaskId,
+ arguments: Seq[String]) extends TestReporter {
+
+ var passed = 0
+ var failed = 0
+ var cancelled = 0
+ var ignored = 0
+ var skipped = 0
+ var totalTime: Long = 0
+
+ override def logStart(event: Event): Unit = {
+ val taskStartParams = new TaskStartParams(taskId)
+ taskStartParams.setEventTime(System.currentTimeMillis())
+ taskStartParams.setDataKind(TaskDataKind.TEST_START)
+ taskStartParams.setData(new TestStart(getDisplayName(event)))
+ taskStartParams.setMessage("Starting running: " + getDisplayName(event))
+ client.onBuildTaskStart(taskStartParams)
+ }
+
+ // Compute the display name of the test / test suite
+ // to which the given event relates
+ private[this] def getDisplayName(e: Event): String = {
+ 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()
+ }
+ }
+
+ override def logFinish(event: Event): Unit = {
+ totalTime += event.duration()
+ val taskFinishParams = new TaskFinishParams(taskId,
+ event.status() match {
+ case sbt.testing.Status.Canceled => StatusCode.CANCELLED
+ case sbt.testing.Status.Error => StatusCode.ERROR
+ case default => StatusCode.OK
+ })
+ val status = event.status match {
+ case sbt.testing.Status.Success =>
+ passed += 1
+ TestStatus.PASSED
+ case sbt.testing.Status.Canceled =>
+ cancelled += 1
+ TestStatus.CANCELLED
+ case sbt.testing.Status.Error =>
+ failed += 1
+ TestStatus.FAILED
+ case sbt.testing.Status.Failure =>
+ failed += 1
+ TestStatus.FAILED
+ case sbt.testing.Status.Ignored =>
+ ignored += 1
+ TestStatus.IGNORED
+ case sbt.testing.Status.Skipped =>
+ skipped += 1
+ TestStatus.SKIPPED
+ case sbt.testing.Status.Pending =>
+ skipped += 1
+ TestStatus.SKIPPED //TODO: what to do here
+ }
+
+ taskFinishParams.setDataKind(TaskDataKind.TEST_FINISH)
+ taskFinishParams.setData({
+ val testFinish = new TestFinish(getDisplayName(event), status)
+ if (event.throwable.isDefined)
+ testFinish.setMessage(throwableToString(event.throwable().get()))
+ testFinish
+ })
+ taskFinishParams.setEventTime(System.currentTimeMillis())
+ client.onBuildTaskFinish(taskFinishParams)
+ }
+
+ private def throwableToString(t: Throwable): String = {
+ val sw = new StringWriter
+ val pw = new PrintWriter(sw)
+ t.printStackTrace(pw)
+ sw.toString
+ }
+
+ // Compute the test report data structure that will go into
+ // the task finish notification after all tests are ran.
+ def getTestReport: TestReport = {
+ val report = new TestReport(targetId, passed, failed, ignored, cancelled, skipped)
+ report.setTime(totalTime)
+ report
+ }
+
+}
+
+case class TestException(stackTrace: String, message: String, exClass: String)