1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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)
|