aboutsummaryrefslogtreecommitdiff
path: root/compiler/test/dotty/tools/vulpix/SummaryReport.scala
blob: 678d888093be5a09d95debbee936c72044582048 (plain)
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package dotty
package tools
package vulpix

import scala.collection.mutable
import dotc.reporting.TestReporter

/** `SummaryReporting` can be used by unit tests by utilizing `@AfterClass` to
 *  call `echoSummary`
 *
 *  This is used in vulpix by passing the companion object's `SummaryReporting`
 *  to each test, the `@AfterClass def` then calls the `SummaryReport`'s
 *  `echoSummary` method in order to dump the summary to both stdout and a log
 *  file
 */
trait SummaryReporting {
  /** Report a failed test */
  def reportFailed(): Unit

  /** Report a test as passing */
  def reportPassed(): Unit

  /** Add the name of the failed test */
  def addFailedTest(msg: String): Unit

  /** Add instructions to reproduce the error */
  def addReproduceInstruction(instr: String): Unit

  /** Add a message that will be issued in the beginning of the summary */
  def addStartingMessage(msg: String): Unit

  /** Add a cleanup hook to be run upon completion */
  def addCleanup(f: () => Unit): Unit

  /** Echo the summary report to the appropriate locations */
  def echoSummary(): Unit

  /** Echoes *immediately* to file */
  def echoToLog(msg: String): Unit

  /** Echoes contents of `it` to file *immediately* then flushes */
  def echoToLog(it: Iterator[String]): Unit
}

/** A summary report that doesn't do anything */
final class NoSummaryReport extends SummaryReporting {
  def reportFailed(): Unit = ()
  def reportPassed(): Unit = ()
  def addFailedTest(msg: String): Unit = ()
  def addReproduceInstruction(instr: String): Unit = ()
  def addStartingMessage(msg: String): Unit = ()
  def addCleanup(f: () => Unit): Unit = ()
  def echoSummary(): Unit = ()
  def echoToLog(msg: String): Unit = ()
  def echoToLog(it: Iterator[String]): Unit = ()
}

/** A summary report that logs to both stdout and the `TestReporter.logWriter`
 *  which outputs to a log file in `./testlogs/`
 */
final class SummaryReport extends SummaryReporting {

  private val startingMessages = mutable.ArrayBuffer.empty[String]
  private val failedTests = mutable.ArrayBuffer.empty[String]
  private val reproduceInstructions = mutable.ArrayBuffer.empty[String]
  private val cleanUps = mutable.ArrayBuffer.empty[() => Unit]

  private[this] var passed = 0
  private[this] var failed = 0

  def reportFailed(): Unit =
    failed += 1

  def reportPassed(): Unit =
    passed += 1

  def addFailedTest(msg: String): Unit =
    failedTests.append(msg)

  def addReproduceInstruction(instr: String): Unit =
    reproduceInstructions.append(instr)

  def addStartingMessage(msg: String): Unit =
    startingMessages.append(msg)

  def addCleanup(f: () => Unit): Unit =
    cleanUps.append(f)

  /** Both echoes the summary to stdout and prints to file */
  def echoSummary(): Unit = {
    import SummaryReport._

    val rep = new StringBuilder
    rep.append(
      s"""|
          |================================================================================
          |Test Report
          |================================================================================
          |
          |$passed passed, $failed failed, ${passed + failed} total
          |""".stripMargin
    )

    startingMessages.foreach(rep.append)

    failedTests.map(x => s"    $x\n").foreach(rep.append)

    // If we're compiling locally, we don't need instructions on how to
    // reproduce failures
    if (isInteractive) {
      println(rep.toString)
      if (failed > 0) println {
        s"""|
            |--------------------------------------------------------------------------------
            |Note - reproduction instructions have been dumped to log file:
            |    ${TestReporter.logPath}
            |--------------------------------------------------------------------------------""".stripMargin
      }
    }

    rep += '\n'

    reproduceInstructions.foreach(rep.append)

    // If we're on the CI, we want everything
    if (!isInteractive) println(rep.toString)

    TestReporter.logPrintln(rep.toString)

    // Perform cleanup callback:
    if (cleanUps.nonEmpty) cleanUps.foreach(_.apply())
  }

  def echoToLog(msg: String): Unit =
    TestReporter.logPrintln(msg)

  def echoToLog(it: Iterator[String]): Unit = {
    it.foreach(TestReporter.logPrint)
    TestReporter.logFlush()
  }
}

object SummaryReport {
  val isInteractive = Properties.testsInteractive && !Properties.isRunByDrone
}