@@ -9,6 +9,9 @@ import com.intellij.util.ReflectionUtil
9
9
import jetbrains .buildServer .messages .serviceMessages ._
10
10
import org .jetbrains .plugins .scala .testingSupport .test .{AbstractTestRunConfiguration , ScalaTestFrameworkConsoleProperties }
11
11
12
+ import java .io .PrintStream
13
+ import scala .util .control .NoStackTrace
14
+
12
15
private [zio] class ZTestFrameworkConsoleProperties (configuration : AbstractTestRunConfiguration , executor : Executor )
13
16
extends ScalaTestFrameworkConsoleProperties (configuration, " ZIO Test" , executor)
14
17
with SMCustomMessagesParsing {
@@ -26,6 +29,7 @@ private[zio] class ZTestFrameworkConsoleProperties(configuration: AbstractTestRu
26
29
// All this is needed to emit a custom TestFailedEvent that contains the expected and actual values extracted from
27
30
// the output of a ZIO Test. This allows displaying a clickable hyperlink to see the differences in IDEA's built-in
28
31
// diff viewer, because it assumes a JUnit-style failure reporting ("Expected:", "Actual:") which ZIO Test doesn't do.
32
+ // Unfortunately, the visitor is both private and not overridable, so we have to resort to reflection to get to it.
29
33
private lazy val underlyingTestVisitor = ReflectionUtil
30
34
.findFieldInHierarchy(classOf [OutputToGeneralTestEventsConverter ], _.getName == " myServiceMessageVisitor" )
31
35
.get(self)
@@ -38,20 +42,36 @@ private[zio] class ZTestFrameworkConsoleProperties(configuration: AbstractTestRu
38
42
39
43
}
40
44
41
- // Copied from [[com.intellij.execution.testframework.sm.runner.OutputToGeneralTestEventsConverter#MyServiceMessageVisitor]]
42
45
private class ZTestVisitor (underlying : ServiceMessageVisitor ) extends DefaultServiceMessageVisitor {
46
+ private val regexFromHell =
47
+ raw " \[1m.\[34m([\s\S]*).\[0m.\[0m.*\[31mwas not equal to.*\[1m.\[34m([\s\S]*?).\[0m.\[0m " .r
48
+
43
49
override def visitTestFailed (testFailed : TestFailed ): Unit = {
44
50
val details = testFailed.getStacktrace
51
+ val tf = regexFromHell
52
+ .findFirstMatchIn(details)
53
+ .map { m =>
54
+ val expected = unescapeAnsi(m.group(1 )).trim
55
+ val actual = unescapeAnsi(m.group(2 )).trim
56
+ val ex = new Throwable with NoStackTrace {
57
+ override def printStackTrace (s : PrintStream ): Unit =
58
+ s.println(details)
59
+
60
+ override def toString : String = testFailed.getFailureMessage
61
+ }
62
+ new TestFailed (testFailed.getTestName, ex, actual, expected)
63
+ }
64
+ .getOrElse {
65
+ testFailed
66
+ }
67
+
68
+ underlying.visitTestFailed(tf)
69
+ }
45
70
71
+ private def unescapeAnsi (s : String ): String = {
46
72
val builder = new StringBuilder ()
47
- new AnsiEscapeDecoder ().escapeText(details.trim, ProcessOutputTypes .STDOUT , (text, _) => builder.append(text))
48
- val plain = builder.result()
49
- println(plain)
50
-
51
-
52
-
53
- underlying.visitTestFailed(testFailed)
54
-
73
+ new AnsiEscapeDecoder ().escapeText(s, ProcessOutputTypes .STDOUT , (text, _) => builder.append(text))
74
+ builder.result()
55
75
}
56
76
57
77
override def visitTestSuiteStarted (testSuiteStarted : TestSuiteStarted ): Unit =
0 commit comments