summaryrefslogtreecommitdiff
path: root/src/partest-alternative/scala/tools/partest/io/Logging.scala
blob: 52239ffb2cdd9e588e7ea89faf78d6bee5d76daa (plain) (blame)
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
package scala.tools
package partest
package io

import java.io.{ StringWriter, PrintWriter, Writer }
import scala.tools.nsc.io._
import scala.util.control.ControlThrowable

trait Logging {
  universe: Universe =>

  class PartestANSIWriter extends ANSIWriter(Console.out) {
    override def colorful: Int = ANSIWriter(universe.isAnsi)
    private def printIf(cond: Boolean, msg: String) =
      if (cond) { outline("debug: ") ; println(msg) }

    val verbose = printIf(isVerbose || isDebug, _: String)
    val debug   = printIf(isDebug, _: String)
  }

  lazy val NestUI = new PartestANSIWriter()

  import NestUI.{ _outline, _success, _failure, _warning, _default }

  def markOutline(msg: String) = _outline + msg + _default
  def markSuccess(msg: String) = _success + msg + _default
  def markFailure(msg: String) = _failure + msg + _default
  def markWarning(msg: String) = _warning + msg + _default
  def markNormal(msg: String)  = _default + msg

  def outline(msg: String) = NestUI outline msg
  def success(msg: String) = NestUI success msg
  def failure(msg: String) = NestUI failure msg
  def warning(msg: String) = NestUI warning msg
  def  normal(msg: String) = NestUI normal msg

  def verbose(msg: String) = NestUI verbose msg
  def   debug(msg: String) = NestUI debug msg

  trait EntityLogging {
    self: TestEntity =>

    lazy val logWriter = new LogWriter(logFile)

    /** Redirect stdout and stderr to logFile, run body, return result.
     */
    def loggingOutAndErr[T](body: => T): T = {
      val log = logFile.printStream(append = true)

      try Console.withOut(log) {
        Console.withErr(log) {
          body
        }
      }
      finally log.close()
    }

    /** What to print in a failure summary.
     */
    def failureMessage() = if (diffOutput != "") diffOutput else safeSlurp(logFile)

    /** For tracing.  Outputs a line describing the next action.  tracePath
     *  is a path wrapper which prints name or full path depending on verbosity.
     */
    def trace(msg: String) = if (isTrace || isDryRun) System.err.println(">> [%s] %s".format(label, msg))

    def tracePath(path: Path): String   = if (isVerbose) path.path else path.name
    def tracePath(path: String): String = tracePath(Path(path))

    /** v == verbose.
     */
    def vtrace(msg: String)   = if (isVerbose) trace(msg)

    /** Run body, writes result to logFile.  Any throwable is
     *  caught, stringified, and written to the log.
     */
    def loggingResult(body: => String) =
      try returning(true)(_ => logFile writeAll body)
      catch {
        case x: ControlThrowable      => throw x
        case x: InterruptedException  => debug(this + " received interrupt, failing.\n") ; false
        case x: Throwable             => logException(x)
      }

    def throwableToString(x: Throwable): String = {
      val w = new StringWriter
      x.printStackTrace(new PrintWriter(w))
      w.toString
    }

    def warnAndLog(str: String) = {
      warning(toStringTrunc(str, 800))
      logWriter append str
    }

    def warnAndLogException(msg: String, ex: Throwable) =
      warnAndLog(msg + throwableToString(ex))

    def deleteLog(force: Boolean = false) =
      if (universe.isNoCleanup && !force) debug("Not cleaning up " + logFile)
      else logFile.deleteIfExists()

    def onException(x: Throwable)    { logException(x) }
    def logException(x: Throwable) = {
      val msg = throwableToString(x)
      if (!isTerse)
        normal(msg)

      logWriter append msg
      false
    }
  }

  /** A writer which doesn't create the file until a write comes in.
   */
  class LazilyCreatedWriter(log: File) extends Writer {
    @volatile private var isCreated = false
    private lazy val underlying = {
      isCreated = true
      log.bufferedWriter()
    }

    def flush() = if (isCreated) underlying.flush()
    def close() = if (isCreated) underlying.close()
    def write(chars: Array[Char], off: Int, len: Int) = {
      underlying.write(chars, off, len)
      underlying.flush()
    }
  }

  class LogWriter(log: File) extends PrintWriter(new LazilyCreatedWriter(log), true) {
    override def print(s: String) = {
      super.print(s)
      flush()
    }
  }
}