summaryrefslogtreecommitdiff
path: root/src/partest/scala/tools/partest/package.scala
blob: 77400b1b9cb5a33fbc96a194954ee299165c7e72 (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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/* NEST (New Scala Test)
 * Copyright 2007-2013 LAMP/EPFL
 */

package scala.tools

import java.util.concurrent.{ Callable, ExecutorService }
import scala.concurrent.duration.Duration
import scala.sys.process.javaVmArguments
import scala.tools.partest.nest.NestUI
import scala.tools.nsc.util.{ ScalaClassLoader, Exceptional }

package object partest {
  type File         = java.io.File
  type SFile        = scala.reflect.io.File
  type Directory    = scala.reflect.io.Directory
  type Path         = scala.reflect.io.Path
  type PathResolver = scala.tools.util.PathResolver
  type ClassPath[T] = scala.tools.nsc.util.ClassPath[T]
  type StringWriter = java.io.StringWriter

  val SFile        = scala.reflect.io.File
  val Directory    = scala.reflect.io.Directory
  val Path         = scala.reflect.io.Path
  val PathResolver = scala.tools.util.PathResolver
  val ClassPath    = scala.tools.nsc.util.ClassPath

  val space                       = "\u0020"
  val EOL                         = scala.compat.Platform.EOL
  def onull(s: String)            = if (s == null) "" else s
  def oempty(xs: String*)         = xs filterNot (x => x == null || x == "")
  def ojoin(xs: String*): String  = oempty(xs: _*) mkString space
  def nljoin(xs: String*): String = oempty(xs: _*) mkString EOL

  implicit val codec = scala.io.Codec.UTF8

  def setUncaughtHandler() = {
    Thread.setDefaultUncaughtExceptionHandler(
      new Thread.UncaughtExceptionHandler {
        def uncaughtException(thread: Thread, t: Throwable) {
          val t1 = Exceptional unwrap t
          System.err.println(s"Uncaught exception on thread $thread: $t1")
          t1.printStackTrace()
        }
      }
    )
  }

  /** Sources have a numerical group, specified by name_7 and so on. */
  private val GroupPattern = """.*_(\d+)""".r

  implicit class FileOps(val f: File) {
    private def sf = SFile(f)

    def testIdent = {
      f.toString split """[/\\]+""" takeRight 2 mkString "/" // e.g. pos/t1234
    }

    def mapInPlace(mapFn: String => String)(filterFn: String => Boolean = _ => true): Unit =
      writeAll(fileLines filter filterFn map (x => mapFn(x) + EOL): _*)

    def appendAll(strings: String*): Unit = sf.appendAll(strings: _*)
    def writeAll(strings: String*): Unit = sf.writeAll(strings: _*)
    def absolutePathSegments: List[String] = f.getAbsolutePath split """[/\\]+""" toList

    def isJava        = f.isFile && (sf hasExtension "java")
    def isScala       = f.isFile && (sf hasExtension "scala")
    def isJavaOrScala = isJava || isScala

    def extension = sf.extension
    def hasExtension(ext: String) = sf hasExtension ext
    def changeExtension(ext: String): File = (sf changeExtension ext).jfile

    /** The group number for this source file, or -1 for no group. */
    def group: Int =
      sf.stripExtension match {
        case GroupPattern(g) if g.toInt >= 0 => g.toInt
        case _                               => -1
      }

    def fileContents: String    = try sf.slurp() catch { case _: java.io.FileNotFoundException => "" }
    def fileLines: List[String] = augmentString(fileContents).lines.toList
  }

  implicit class PathOps(p: Path) extends FileOps(p.jfile) { }

  implicit class Copier(val f: SFile) extends AnyVal {
    def copyTo(dest: Path): Unit = dest.toFile writeAll f.slurp(scala.io.Codec.UTF8)
  }

  implicit class LoaderOps(val loader: ClassLoader) extends AnyVal {
    import scala.util.control.Exception.catching
    /** Like ScalaClassLoader.create for the case where the result type is
     *  available to the current class loader, implying that the current
     *  loader is a parent of `loader`.
     */
    def instantiate[A >: Null](name: String): A = (
      catching(classOf[ClassNotFoundException], classOf[SecurityException]) opt
      (loader loadClass name).newInstance.asInstanceOf[A] orNull
    )
  }

  implicit class ExecutorOps(val executor: ExecutorService) {
    def awaitTermination[A](wait: Duration)(failing: => A = ()): Option[A] = (
      if (executor awaitTermination (wait.length, wait.unit)) None
      else Some(failing)
    )
  }

  implicit def temporaryPath2File(x: Path): File = x.jfile
  implicit def stringPathToJavaFile(path: String): File = new File(path)

  implicit lazy val postfixOps = scala.language.postfixOps
  implicit lazy val implicitConversions = scala.language.implicitConversions

  def fileSeparator = java.io.File.separator
  def pathSeparator = java.io.File.pathSeparator

  def pathToTestIdent(path: Path) = path.jfile.testIdent

  def canonicalizeSlashes(line: String) = line.replaceAll("""[/\\]+""", "/")

  def words(s: String): List[String] = (s.trim split "\\s+").toList

  def timed[T](body: => T): (T, Long) = {
    val t1 = System.currentTimeMillis
    val result = body
    val t2 = System.currentTimeMillis

    (result, t2 - t1)
  }

  def callable[T](body: => T): Callable[T] = new Callable[T] { override def call() = body }

  def file2String(f: File): String = f.fileContents

  def basename(name: String): String = Path(name).stripExtension

  /** In order to allow for spaces in flags/options, this
   *  parses .flags, .javaopts, javacopts etc files as follows:
   *  If it is exactly one line, it is split (naively) on spaces.
   *  If it contains more than one line, each line is its own
   *  token, spaces and all.
   */
  def readOptionsFile(file: File): List[String] = {
    file.fileLines match {
      case x :: Nil   => words(x)
      case xs         => xs map (_.trim)
    }
  }

  def findProgram(name: String): Option[File] = {
    val pathDirs = sys.env("PATH") match {
      case null => List("/usr/local/bin", "/usr/bin", "/bin")
      case path => path split "[:;]" filterNot (_ == "") toList
    }
    pathDirs.iterator map (d => new File(d, name)) find (_.canExecute)
  }

  def now = (new java.util.Date).toString
  def elapsedString(millis: Long): String = {
    val elapsedSecs = millis/1000
    val elapsedMins = elapsedSecs/60
    val elapsedHrs  = elapsedMins/60
    val dispMins    = elapsedMins - elapsedHrs  * 60
    val dispSecs    = elapsedSecs - elapsedMins * 60

    "%02d:%02d:%02d".format(elapsedHrs, dispMins, dispSecs)
  }

  def vmArgString = javaVmArguments.mkString(
    "Java VM started with arguments: '",
    " ",
    "'"
  )

  def allPropertiesString = {
    import scala.collection.JavaConversions._
    System.getProperties.toList.sorted map { case (k, v) => "%s -> %s\n".format(k, v) } mkString ""
  }

  def showAllJVMInfo() {
    vlog(vmArgString)
    vlog(allPropertiesString)
  }

  import scala.language.experimental.macros

  /**
   * `trace("".isEmpty)` will return `true` and as a side effect print the following to standard out.
   * {{{
   *   trace> "".isEmpty
   *   res: Boolean = true
   *
   * }}}
   *
   * An alternative to [[scala.tools.partest.ReplTest]] that avoids the inconvenience of embedding
   * test code in a string.
   */
  def trace[A](a: A) = macro traceImpl[A]

  import scala.reflect.macros.Context
  def traceImpl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A]): c.Expr[A] = {
    import c.universe._
    import definitions._

    // xeno.by: reify shouldn't be used explicitly before the final release of 2.10.0,
    // because this impairs reflection refactorings
    //
    // val exprCode = c.literal(show(a.tree))
    // val exprType = c.literal(show(a.actualType))
    // reify {
    //   println(s"trace> ${exprCode.splice}\nres: ${exprType.splice} = ${a.splice}\n")
    //   a.splice
    // }

    c.Expr(Block(
      List(Apply(
        Select(Ident(PredefModule), TermName("println")),
        List(Apply(
          Select(Apply(
            Select(Ident(ScalaPackage), TermName("StringContext")),
            List(
              Literal(Constant("trace> ")),
              Literal(Constant("\\nres: ")),
              Literal(Constant(" = ")),
              Literal(Constant("\\n")))),
          TermName("s")),
          List(
            Literal(Constant(show(a.tree))),
            Literal(Constant(show(a.actualType))),
            a.tree))))),
      a.tree))
  }

  def isPartestTerse   = NestUI.isTerse
  def isPartestDebug   = NestUI.isDebug
  def isPartestVerbose = NestUI.isVerbose

  def vlog(msg: => String) = if (isPartestVerbose) System.err.println(msg)
}