summaryrefslogtreecommitdiff
path: root/src/partest/scala/tools/partest/nest/Runner.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/partest/scala/tools/partest/nest/Runner.scala')
-rw-r--r--src/partest/scala/tools/partest/nest/Runner.scala883
1 files changed, 0 insertions, 883 deletions
diff --git a/src/partest/scala/tools/partest/nest/Runner.scala b/src/partest/scala/tools/partest/nest/Runner.scala
deleted file mode 100644
index 470a2188de..0000000000
--- a/src/partest/scala/tools/partest/nest/Runner.scala
+++ /dev/null
@@ -1,883 +0,0 @@
-/* NEST (New Scala Test)
- * Copyright 2007-2013 LAMP/EPFL
- * @author Paul Phillips
- */
-package scala.tools.partest
-package nest
-
-import java.io.{ Console => _, _ }
-import java.net.URL
-import java.nio.charset.{ Charset, CharsetDecoder, CharsetEncoder, CharacterCodingException, CodingErrorAction => Action }
-import java.util.concurrent.Executors
-import java.util.concurrent.TimeUnit.NANOSECONDS
-import scala.collection.mutable.ListBuffer
-import scala.concurrent.duration.Duration
-import scala.io.Codec
-import scala.reflect.internal.FatalError
-import scala.sys.process.{ Process, ProcessLogger }
-import scala.tools.nsc.Properties.{ envOrElse, isWin, jdkHome, javaHome, propOrElse, propOrEmpty, setProp }
-import scala.tools.nsc.{ Settings, CompilerCommand, Global }
-import scala.tools.nsc.reporters.ConsoleReporter
-import scala.tools.nsc.util.{ Exceptional, ScalaClassLoader, stackTraceString }
-import scala.tools.scalap.Main.decompileScala
-import scala.tools.scalap.scalasig.ByteCode
-import scala.util.{ Try, Success, Failure }
-import ClassPath.{ join, split }
-import PartestDefaults.{ javaCmd, javacCmd }
-import TestState.{ Pass, Fail, Crash, Uninitialized, Updated }
-
-trait PartestRunSettings {
- def gitPath: Path
- def reportPath: Path
- def logPath: Path
-
- def testPaths: List[Path]
-
- def gitDiffOptions: List[String]
- def extraScalacOptions: List[String]
- def extraJavaOptions: List[String]
-}
-
-class TestTranscript {
- import NestUI.color._
- private val buf = ListBuffer[String]()
- private def pass(s: String) = bold(green("% ")) + s
- private def fail(s: String) = bold(red("% ")) + s
-
- def add(action: String): this.type = { buf += action ; this }
- def append(text: String) { val s = buf.last ; buf.trimEnd(1) ; buf += (s + text) }
-
- // Colorize prompts according to pass/fail
- def fail: List[String] = buf.toList match {
- case Nil => Nil
- case xs => (xs.init map pass) :+ fail(xs.last)
- }
-}
-
-/** Run a single test. Rubber meets road. */
-class Runner(val testFile: File, fileManager: FileManager, val testRunParams: TestRunParams) {
- import fileManager._
-
- // Override to true to have the outcome of this test displayed
- // whether it passes or not; in general only failures are reported,
- // except for a . per passing test to show progress.
- def isEnumeratedTest = false
-
- private var _lastState: TestState = null
- private var _transcript = new TestTranscript
-
- def lastState = if (_lastState == null) Uninitialized(testFile) else _lastState
- def setLastState(s: TestState) = _lastState = s
- def transcript: List[String] = _transcript.fail ++ logFile.fileLines
- def pushTranscript(msg: String) = _transcript add msg
-
- val parentFile = testFile.getParentFile
- val kind = parentFile.getName
- val fileBase = basename(testFile.getName)
- val logFile = new File(parentFile, s"$fileBase-$kind.log")
- val outFile = logFile changeExtension "obj"
- val checkFile = testFile changeExtension "check"
- val flagsFile = testFile changeExtension "flags"
- val testIdent = testFile.testIdent // e.g. pos/t1234
-
- lazy val outDir = { outFile.mkdirs() ; outFile }
-
- type RanOneTest = (Boolean, LogContext)
-
- def showCrashInfo(t: Throwable) {
- System.err.println(s"Crashed running test $testIdent: $t")
- if (!isPartestTerse)
- System.err.println(stackTraceString(t))
- }
- protected def crashHandler: PartialFunction[Throwable, TestState] = {
- case t: InterruptedException =>
- genTimeout()
- case t: Throwable =>
- showCrashInfo(t)
- logFile.appendAll(stackTraceString(t))
- genCrash(t)
- }
-
- def genPass() = Pass(testFile)
- def genFail(reason: String) = Fail(testFile, reason, _transcript.fail)
- def genTimeout() = Fail(testFile, "timed out", _transcript.fail)
- def genCrash(caught: Throwable) = Crash(testFile, caught, _transcript.fail)
- def genUpdated() = Updated(testFile)
-
- def speclib = PathSettings.srcSpecLib.toString // specialization lib
- def codelib = PathSettings.srcCodeLib.toString // reify lib
-
- // Prepend to a classpath, but without incurring duplicate entries
- def prependTo(classpath: String, path: String): String = {
- val segments = ClassPath split classpath
-
- if (segments startsWith path) classpath
- else ClassPath.join(path :: segments distinct: _*)
- }
-
- def prependToJavaClasspath(path: String) {
- val jcp = sys.props.getOrElse("java.class.path", "")
- prependTo(jcp, path) match {
- case `jcp` =>
- case cp => sys.props("java.class.path") = cp
- }
- }
- def prependToClasspaths(s: Settings, path: String) {
- prependToJavaClasspath(path)
- val scp = s.classpath.value
- prependTo(scp, path) match {
- case `scp` =>
- case cp => s.classpath.value = cp
- }
- }
-
- private def workerError(msg: String): Unit = System.err.println("Error: " + msg)
-
- def javac(files: List[File]): TestState = {
- // compile using command-line javac compiler
- val args = Seq(
- javacCmd,
- "-d",
- outDir.getAbsolutePath,
- "-classpath",
- join(outDir.toString, CLASSPATH)
- ) ++ files.map(_.getAbsolutePath)
-
- pushTranscript(args mkString " ")
- val captured = StreamCapture(runCommand(args, logFile))
- if (captured.result) genPass() else {
- logFile appendAll captured.stderr
- genFail("java compilation failed")
- }
- }
-
- def testPrompt = kind match {
- case "res" => "nsc> "
- case _ => "% "
- }
-
- /** Evaluate an action body and update the test state.
- * @param failFn optionally map a result to a test state.
- */
- def nextTestAction[T](body: => T)(failFn: PartialFunction[T, TestState]): T = {
- val result = body
- setLastState( if (failFn isDefinedAt result) failFn(result) else genPass() )
- result
- }
- def nextTestActionExpectTrue(reason: String, body: => Boolean): Boolean = (
- nextTestAction(body) { case false => genFail(reason) }
- )
- def nextTestActionFailing(reason: String): Boolean = nextTestActionExpectTrue(reason, false)
-
- private def assembleTestCommand(outDir: File, logFile: File): List[String] = {
- // check whether there is a ".javaopts" file
- val argsFile = testFile changeExtension "javaopts"
- val argString = file2String(argsFile)
- if (argString != "")
- NestUI.verbose("Found javaopts file '%s', using options: '%s'".format(argsFile, argString))
-
- val testFullPath = testFile.getAbsolutePath
-
- // Note! As this currently functions, JAVA_OPTS must precede argString
- // because when an option is repeated to java only the last one wins.
- // That means until now all the .javaopts files were being ignored because
- // they all attempt to change options which are also defined in
- // partest.java_opts, leading to debug output like:
- //
- // debug: Found javaopts file 'files/shootout/message.scala-2.javaopts', using options: '-Xss32k'
- // debug: java -Xss32k -Xss2m -Xms256M -Xmx1024M -classpath [...]
- val extras = if (isPartestDebug) List("-Dpartest.debug=true") else Nil
- val propertyOptions = List(
- "-Dfile.encoding=UTF-8",
- "-Djava.library.path="+logFile.getParentFile.getAbsolutePath,
- "-Dpartest.output="+outDir.getAbsolutePath,
- "-Dpartest.lib="+LATEST_LIB,
- "-Dpartest.reflect="+LATEST_REFLECT,
- "-Dpartest.cwd="+outDir.getParent,
- "-Dpartest.test-path="+testFullPath,
- "-Dpartest.testname="+fileBase,
- "-Djavacmd="+javaCmd,
- "-Djavaccmd="+javacCmd,
- "-Duser.language=en",
- "-Duser.country=US"
- ) ++ extras
-
- val classpath = if (extraClasspath != "") join(extraClasspath, CLASSPATH) else CLASSPATH
-
- javaCmd +: (
- (JAVA_OPTS.split(' ') ++ extraJavaOptions.split(' ') ++ argString.split(' ')).map(_.trim).filter(_ != "").toList ++ Seq(
- "-classpath",
- join(outDir.toString, classpath)
- ) ++ propertyOptions ++ Seq(
- "scala.tools.nsc.MainGenericRunner",
- "-usejavacp",
- "Test",
- "jvm"
- )
- )
- }
-
- /** Runs command redirecting standard out and
- * error out to output file.
- */
- private def runCommand(args: Seq[String], outFile: File): Boolean = {
- //(Process(args) #> outFile !) == 0 or (Process(args) ! pl) == 0
- val pl = ProcessLogger(outFile)
- val nonzero = 17 // rounding down from 17.3
- def run: Int = {
- val p = Process(args) run pl
- try p.exitValue
- catch {
- case e: InterruptedException =>
- NestUI verbose s"Interrupted waiting for command to finish (${args mkString " "})"
- p.destroy
- nonzero
- case t: Throwable =>
- NestUI verbose s"Exception waiting for command to finish: $t (${args mkString " "})"
- p.destroy
- throw t
- }
- finally pl.close()
- }
- (pl buffer run) == 0
- }
-
- private def execTest(outDir: File, logFile: File): Boolean = {
- val cmd = assembleTestCommand(outDir, logFile)
-
- pushTranscript((cmd mkString s" \\$EOL ") + " > " + logFile.getName)
- nextTestAction(runCommand(cmd, logFile)) {
- case false =>
- _transcript append EOL + logFile.fileContents
- genFail("non-zero exit code")
- }
- }
-
- override def toString = s"""Test($testIdent, lastState = $lastState)"""
-
- // result is unused
- def newTestWriters() = {
- val swr = new StringWriter
- val wr = new PrintWriter(swr, true)
- // diff = ""
-
- ((swr, wr))
- }
-
- def fail(what: Any) = {
- NestUI.verbose("scalac: compilation of "+what+" failed\n")
- false
- }
-
- /** Filter the check file for conditional blocks.
- * The check file can contain lines of the form:
- * `#partest java7`
- * where the line contains a conventional flag name.
- * If the flag tests true, succeeding lines are retained
- * (removed on false) until the next #partest flag.
- * A missing flag evaluates the same as true.
- */
- def filteredCheck: Seq[String] = {
- import scala.util.Properties.{javaVersion, isAvian}
- // use lines in block so labeled? Default to sorry, Charlie.
- def retainOn(expr: String) = {
- val f = expr.trim
- def flagWasSet(f: String) = fileManager.SCALAC_OPTS contains f
- val (invert, token) =
- if (f startsWith "!") (true, f drop 1) else (false, f)
- val cond = token.trim match {
- case "java7" => javaVersion startsWith "1.7"
- case "java6" => javaVersion startsWith "1.6"
- case "avian" => isAvian
- case "true" => true
- case "-optimise" | "-optimize"
- => flagWasSet("-optimise") || flagWasSet("-optimize")
- case flag if flag startsWith "-"
- => flagWasSet(flag)
- case rest => rest.isEmpty
- }
- if (invert) !cond else cond
- }
- val prefix = "#partest"
- val b = new ListBuffer[String]()
- var on = true
- for (line <- file2String(checkFile).lines) {
- if (line startsWith prefix) {
- on = retainOn(line stripPrefix prefix)
- } else if (on) {
- b += line
- }
- }
- b.toList
- }
-
- def currentDiff = {
- val logged = augmentString(file2String(logFile)).lines.toList
- val (other, othername) =
- if (checkFile.canRead) (filteredCheck, checkFile.getName) else (Nil, "empty")
- compareContents(logged, other, logFile.getName, othername)
- }
-
- val gitRunner = List("/usr/local/bin/git", "/usr/bin/git") map (f => new java.io.File(f)) find (_.canRead)
- val gitDiffOptions = "--ignore-space-at-eol --no-index " + propOrEmpty("partest.git_diff_options")
- // --color=always --word-diff
-
- def gitDiff(f1: File, f2: File): Option[String] = {
- try gitRunner map { git =>
- val cmd = s"$git diff $gitDiffOptions $f1 $f2"
- val diff = Process(cmd).lines_!.drop(4).map(_ + "\n").mkString
-
- "\n" + diff
- }
- catch { case t: Exception => None }
- }
-
- /** Normalize the log output by applying test-specific filters
- * and fixing filesystem-specific paths.
- *
- * Line filters are picked up from `filter: pattern` at the top of sources.
- * The filtered line is detected with a simple "contains" test,
- * and yes, "filter" means "filter out" in this context.
- *
- * File paths are detected using the absolute path of the test root.
- * A string that looks like a file path is normalized by replacing
- * the leading segments (the root) with "$ROOT" and by replacing
- * any Windows backslashes with the one true file separator char.
- */
- def normalizeLog() {
- // Apply judiciously; there are line comments in the "stub implementations" error output.
- val slashes = """[/\\]+""".r
- def squashSlashes(s: String) = slashes replaceAllIn (s, "/")
-
- // this string identifies a path and is also snipped from log output.
- // to preserve more of the path, could use fileManager.testRootPath
- val elided = parentFile.getAbsolutePath
-
- // something to mark the elision in the log file (disabled)
- val ellipsis = "" //".../" // using * looks like a comment
-
- // no spaces in test file paths below root, because otherwise how to detect end of path string?
- val pathFinder = raw"""(?i)\Q${elided}${File.separator}\E([\${File.separator}\S]*)""".r
- def canonicalize(s: String): String = (
- pathFinder replaceAllIn (s, m => ellipsis + squashSlashes(m group 1))
- )
-
- def masters = {
- val files = List(new File(parentFile, "filters"), new File(PathSettings.srcDir.path, "filters"))
- files filter (_.exists) flatMap (_.fileLines) map (_.trim) filter (s => !(s startsWith "#"))
- }
- val filters = toolArgs("filter", split = false) ++ masters
- val elisions = ListBuffer[String]()
- //def lineFilter(s: String): Boolean = !(filters exists (s contains _))
- def lineFilter(s: String): Boolean = (
- filters map (_.r) forall { r =>
- val res = (r findFirstIn s).isEmpty
- if (!res) elisions += s
- res
- }
- )
-
- logFile.mapInPlace(canonicalize)(lineFilter)
- if (isPartestVerbose && elisions.nonEmpty) {
- import NestUI.color._
- val emdash = bold(yellow("--"))
- pushTranscript(s"filtering ${logFile.getName}$EOL${elisions mkString (emdash, EOL + emdash, EOL)}")
- }
- }
-
- def diffIsOk: Boolean = {
- // always normalize the log first
- normalizeLog()
- val diff = currentDiff
- // if diff is not empty, is update needed?
- val updating: Option[Boolean] = (
- if (diff == "") None
- else Some(fileManager.updateCheck)
- )
- pushTranscript(s"diff $logFile $checkFile")
- nextTestAction(updating) {
- case Some(true) =>
- NestUI.verbose("Updating checkfile " + checkFile)
- checkFile writeAll file2String(logFile)
- genUpdated()
- case Some(false) =>
- // Get a word-highlighted diff from git if we can find it
- val bestDiff = if (updating.isEmpty) "" else {
- if (checkFile.canRead)
- gitDiff(logFile, checkFile) getOrElse {
- s"diff $logFile $checkFile\n$diff"
- }
- else diff
- }
- _transcript append bestDiff
- genFail("output differs")
- // TestState.fail("output differs", "output differs",
- // genFail("output differs")
- // TestState.Fail("output differs", bestDiff)
- case None => genPass() // redundant default case
- } getOrElse true
- }
-
- /** 1. Creates log file and output directory.
- * 2. Runs script function, providing log file and output directory as arguments.
- * 2b. or, just run the script without context and return a new context
- */
- def runInContext(body: => Boolean): (Boolean, LogContext) = {
- val (swr, wr) = newTestWriters()
- val succeeded = body
- (succeeded, LogContext(logFile, swr, wr))
- }
-
- /** Grouped files in group order, and lex order within each group. */
- def groupedFiles(sources: List[File]): List[List[File]] = (
- if (sources.tail.nonEmpty) {
- val grouped = sources groupBy (_.group)
- grouped.keys.toList.sorted map (k => grouped(k) sortBy (_.getName))
- }
- else List(sources)
- )
-
- /** Source files for the given test file. */
- def sources(file: File): List[File] = (
- if (file.isDirectory)
- file.listFiles.toList filter (_.isJavaOrScala)
- else
- List(file)
- )
-
- def newCompiler = new DirectCompiler(fileManager)
-
- def attemptCompile(sources: List[File]): TestState = {
- val state = newCompiler.compile(this, flagsForCompilation(sources), sources)
- if (!state.isOk)
- _transcript append ("\n" + file2String(logFile))
-
- state
- }
-
- // snort or scarf all the contributing flags files
- def flagsForCompilation(sources: List[File]): List[String] = {
- def argsplitter(s: String) = words(s) filter (_.nonEmpty)
- val perTest = argsplitter(flagsFile.fileContents)
- val perGroup = if (testFile.isDirectory) {
- sources flatMap { f => SFile(Path(f) changeExtension "flags").safeSlurp map argsplitter getOrElse Nil }
- } else Nil
- perTest ++ perGroup
- }
-
- def toolArgs(tool: String, split: Boolean = true): List[String] = {
- def argsplitter(s: String) = if (split) words(s) filter (_.nonEmpty) else List(s)
- def argsFor(f: File): List[String] = {
- import scala.util.matching.Regex
- val p = new Regex(s"(?:.*\\s)?${tool}:(?:\\s*)(.*)?", "args")
- val max = 10
- val src = Path(f).toFile.chars(codec)
- val args = try {
- src.getLines take max collectFirst {
- case s if (p findFirstIn s).nonEmpty => for (m <- p findFirstMatchIn s) yield m group "args"
- }
- } finally src.close()
- args.flatten map argsplitter getOrElse Nil
- }
- sources(testFile) flatMap argsFor
- }
-
- abstract class CompileRound {
- def fs: List[File]
- def result: TestState
- def description: String
-
- def fsString = fs map (_.toString stripPrefix parentFile.toString + "/") mkString " "
- def isOk = result.isOk
- def mkScalacString(): String = s"""scalac $fsString"""
- override def toString = description + ( if (result.isOk) "" else "\n" + result.status )
- }
- case class OnlyJava(fs: List[File]) extends CompileRound {
- def description = s"""javac $fsString"""
- lazy val result = { pushTranscript(description) ; javac(fs) }
- }
- case class OnlyScala(fs: List[File]) extends CompileRound {
- def description = mkScalacString()
- lazy val result = { pushTranscript(description) ; attemptCompile(fs) }
- }
- case class ScalaAndJava(fs: List[File]) extends CompileRound {
- def description = mkScalacString()
- lazy val result = { pushTranscript(description) ; attemptCompile(fs) }
- }
-
- def compilationRounds(file: File): List[CompileRound] = (
- (groupedFiles(sources(file)) map mixedCompileGroup).flatten
- )
- def mixedCompileGroup(allFiles: List[File]): List[CompileRound] = {
- val (scalaFiles, javaFiles) = allFiles partition (_.isScala)
- val isMixed = javaFiles.nonEmpty && scalaFiles.nonEmpty
- val round1 = if (scalaFiles.isEmpty) None else Some(ScalaAndJava(allFiles))
- val round2 = if (javaFiles.isEmpty) None else Some(OnlyJava(javaFiles))
- val round3 = if (!isMixed) None else Some(OnlyScala(scalaFiles))
-
- List(round1, round2, round3).flatten
- }
-
- def runNegTest() = runInContext {
- val rounds = compilationRounds(testFile)
-
- // failing means Does Not Compile
- val failing = rounds find (x => nextTestActionExpectTrue("compilation failed", x.isOk) == false)
-
- // which means passing if it checks and didn't crash the compiler
- // or, OK, we'll let you crash the compiler with a FatalError if you supply a check file
- def checked(r: CompileRound) = r.result match {
- case Crash(_, t, _) if !checkFile.canRead || !t.isInstanceOf[FatalError] => false
- case _ => diffIsOk
- }
-
- failing map (checked) getOrElse nextTestActionFailing("expected compilation failure")
- }
-
- def runTestCommon(andAlso: => Boolean): (Boolean, LogContext) = runInContext {
- compilationRounds(testFile).forall(x => nextTestActionExpectTrue("compilation failed", x.isOk)) && andAlso
- }
-
- // Apache Ant 1.6 or newer
- def ant(args: Seq[String], output: File): Boolean = {
- val antDir = Directory(envOrElse("ANT_HOME", "/opt/ant/"))
- val antLibDir = Directory(antDir / "lib")
- val antLauncherPath = SFile(antLibDir / "ant-launcher.jar").path
- val antOptions =
- if (NestUI._verbose) List("-verbose", "-noinput")
- else List("-noinput")
- val cmd = javaCmd +: (
- JAVA_OPTS.split(' ').map(_.trim).filter(_ != "") ++ Seq(
- "-classpath",
- antLauncherPath,
- "org.apache.tools.ant.launch.Launcher"
- ) ++ antOptions ++ args
- )
-
- runCommand(cmd, output)
- }
-
- def runAntTest(): (Boolean, LogContext) = {
- val (swr, wr) = newTestWriters()
-
- val succeeded = try {
- val binary = "-Dbinary="+(
- if (fileManager.LATEST_LIB endsWith "build/quick/classes/library") "quick"
- else if (fileManager.LATEST_LIB endsWith "build/pack/lib/scala-library.jar") "pack"
- else if (fileManager.LATEST_LIB endsWith "dists/latest/lib/scala-library.jar/") "latest"
- else "installed"
- )
- val args = Array(binary, "-logfile", logFile.getPath, "-file", testFile.getPath)
- NestUI.verbose("ant "+args.mkString(" "))
-
- pushTranscript(s"ant ${args.mkString(" ")}")
- nextTestActionExpectTrue("ant failed", ant(args, logFile)) && diffIsOk
- }
- catch { // *catch-all*
- case e: Exception =>
- NestUI.warning("caught "+e)
- false
- }
-
- (succeeded, LogContext(logFile, swr, wr))
- }
-
- def extraClasspath = kind match {
- case "specialized" => PathSettings.srcSpecLib.toString
- case _ => ""
- }
- def extraJavaOptions = kind match {
- case "instrumented" => "-javaagent:"+PathSettings.instrumentationAgentLib
- case _ => ""
- }
-
- def runScalacheckTest() = runTestCommon {
- NestUI verbose f"compilation of $testFile succeeded%n"
-
- // this classloader is test specific: its parent contains library classes and others
- val loader = {
- import PathSettings.scalaCheck
- val locations = List(outDir, scalaCheck.jfile) map (_.getAbsoluteFile.toURI.toURL)
- ScalaClassLoader.fromURLs(locations, getClass.getClassLoader)
- }
- val logWriter = new PrintStream(new FileOutputStream(logFile), true)
-
- def runInFramework(): Boolean = {
- import org.scalatools.testing._
- val f: Framework = loader.instantiate[Framework]("org.scalacheck.ScalaCheckFramework")
- val logger = new Logger {
- def ansiCodesSupported = false //params.env.isSet("colors")
- def error(msg: String) = logWriter println msg
- def warn(msg: String) = logWriter println msg
- def info(msg: String) = logWriter println msg
- def debug(msg: String) = logWriter println msg
- def trace(t: Throwable) = t printStackTrace logWriter
- }
- var bad = 0
- val handler = new EventHandler {
- // testName, description, result, error
- // Result = Success, Failure, Error, Skipped
- def handle(event: Event): Unit = event.result match {
- case Result.Success =>
- //case Result.Skipped => // an exhausted test is skipped, therefore bad
- case _ => bad += 1
- }
- }
- val loggers = Array(logger)
- val r = f.testRunner(loader, loggers).asInstanceOf[Runner2] // why?
- val claas = "Test"
- val fingerprint = f.tests collectFirst { case x: SubclassFingerprint if x.isModule => x }
- val args = toolArgs("scalacheck")
- vlog(s"Run $testFile with args $args")
- // set the context class loader for scaladoc/scalacheck tests (FIX ME)
- ScalaClassLoader(testRunParams.scalaCheckParentClassLoader).asContext {
- r.run(claas, fingerprint.get, handler, args.toArray) // synchronous?
- }
- val ok = (bad == 0)
- if (!ok) _transcript append logFile.fileContents
- ok
- }
- try nextTestActionExpectTrue("ScalaCheck test failed", runInFramework()) finally logWriter.close()
- }
-
- def runResidentTest() = {
- // simulate resident compiler loop
- val prompt = "\nnsc> "
- val (swr, wr) = newTestWriters()
-
- NestUI.verbose(this+" running test "+fileBase)
- val dir = parentFile
- val resFile = new File(dir, fileBase + ".res")
-
- // run compiler in resident mode
- // $SCALAC -d "$os_dstbase".obj -Xresident -sourcepath . "$@"
- val sourcedir = logFile.getParentFile.getAbsoluteFile
- val sourcepath = sourcedir.getAbsolutePath+File.separator
- NestUI.verbose("sourcepath: "+sourcepath)
-
- val argList = List(
- "-d", outDir.getAbsoluteFile.getPath,
- "-Xresident",
- "-sourcepath", sourcepath)
-
- // configure input/output files
- val logOut = new FileOutputStream(logFile)
- val logWriter = new PrintStream(logOut, true)
- val resReader = new BufferedReader(new FileReader(resFile))
- val logConsoleWriter = new PrintWriter(new OutputStreamWriter(logOut), true)
-
- // create compiler
- val settings = new Settings(workerError)
- settings.sourcepath.value = sourcepath
- settings.classpath.value = fileManager.CLASSPATH
- val reporter = new ConsoleReporter(settings, scala.Console.in, logConsoleWriter)
- val command = new CompilerCommand(argList, settings)
- object compiler extends Global(command.settings, reporter)
-
- def resCompile(line: String): Boolean = {
- // NestUI.verbose("compiling "+line)
- val cmdArgs = (line split ' ').toList map (fs => new File(dir, fs).getAbsolutePath)
- // NestUI.verbose("cmdArgs: "+cmdArgs)
- val sett = new Settings(workerError)
- sett.sourcepath.value = sourcepath
- val command = new CompilerCommand(cmdArgs, sett)
- // "scalac " + command.files.mkString(" ")
- pushTranscript("scalac " + command.files.mkString(" "))
- nextTestActionExpectTrue(
- "compilation failed",
- command.ok && {
- (new compiler.Run) compile command.files
- !reporter.hasErrors
- }
- )
- }
- def loop(): Boolean = {
- logWriter.print(prompt)
- resReader.readLine() match {
- case null | "" => logWriter.close() ; true
- case line => resCompile(line) && loop()
- }
- }
- // res/t687.res depends on ignoring its compilation failure
- // and just looking at the diff, so I made them all do that
- // because this is long enough.
- if (!Output.withRedirected(logWriter)(try loop() finally resReader.close()))
- setLastState(genPass())
-
- (diffIsOk, LogContext(logFile, swr, wr))
- }
-
- def run(): TestState = {
- // javac runner, for one, would merely append to an existing log file, so just delete it before we start
- logFile.delete()
-
- if (kind == "neg" || (kind endsWith "-neg")) runNegTest()
- else kind match {
- case "pos" => runTestCommon(true)
- case "ant" => runAntTest()
- case "scalacheck" => runScalacheckTest()
- case "res" => runResidentTest()
- case "scalap" => runScalapTest()
- case "script" => runScriptTest()
- case _ => runTestCommon(execTest(outDir, logFile) && diffIsOk)
- }
-
- lastState
- }
-
- def runScalapTest() = runTestCommon {
- val isPackageObject = testFile.getName startsWith "package"
- val className = testFile.getName.stripSuffix(".scala").capitalize + (if (!isPackageObject) "" else ".package")
- val loader = ScalaClassLoader.fromURLs(List(outDir.toURI.toURL), this.getClass.getClassLoader)
- val byteCode = ByteCode forClass (loader loadClass className)
- val result = decompileScala(byteCode.bytes, isPackageObject)
-
- logFile writeAll result
- diffIsOk
- }
- def runScriptTest() = {
- import scala.sys.process._
- val (swr, wr) = newTestWriters()
-
- val args = file2String(testFile changeExtension "args")
- val cmdFile = if (isWin) testFile changeExtension "bat" else testFile
- val succeeded = (((cmdFile + " " + args) #> logFile !) == 0) && diffIsOk
-
- (succeeded, LogContext(logFile, swr, wr))
- }
-
- def cleanup() {
- if (lastState.isOk)
- logFile.delete()
- if (!isPartestDebug)
- Directory(outDir).deleteRecursively()
- }
-}
-
-case class TestRunParams(val scalaCheckParentClassLoader: ScalaClassLoader)
-
-/** Extended by Ant- and ConsoleRunner for running a set of tests. */
-trait DirectRunner {
- def fileManager: FileManager
-
- import PartestDefaults.{ numThreads, waitTime }
-
- setUncaughtHandler
-
- def runTestsForFiles(kindFiles: List[File], kind: String): List[TestState] = {
-
- NestUI.resetTestNumber(kindFiles.size)
-
- // this special class loader is for the benefit of scaladoc tests, which need a class path
- import PathSettings.{ testInterface, scalaCheck }
- val allUrls = scalaCheck.toURL :: testInterface.toURL :: fileManager.latestUrls
- val parentClassLoader = ScalaClassLoader fromURLs allUrls
- // add scalacheck.jar to a special classloader, but use our loader as parent with test-interface
- //val parentClassLoader = ScalaClassLoader fromURLs (List(scalaCheck.toURL), getClass().getClassLoader)
- val pool = Executors newFixedThreadPool numThreads
- val manager = new RunnerManager(kind, fileManager, TestRunParams(parentClassLoader))
- val futures = kindFiles map (f => pool submit callable(manager runTest f.getAbsoluteFile))
-
- pool.shutdown()
- Try (pool.awaitTermination(waitTime) {
- throw TimeoutException(waitTime)
- }) match {
- case Success(_) => futures map (_.get)
- case Failure(e) =>
- e match {
- case TimeoutException(d) =>
- NestUI warning "Thread pool timeout elapsed before all tests were complete!"
- case ie: InterruptedException =>
- NestUI warning "Thread pool was interrupted"
- ie.printStackTrace()
- }
- pool.shutdownNow() // little point in continuing
- // try to get as many completions as possible, in case someone cares
- val results = for (f <- futures) yield {
- try {
- Some(f.get(0, NANOSECONDS))
- } catch {
- case _: Throwable => None
- }
- }
- results.flatten
- }
- }
-}
-
-case class TimeoutException(duration: Duration) extends RuntimeException
-
-class LogContext(val file: File, val writers: Option[(StringWriter, PrintWriter)])
-
-object LogContext {
- def apply(file: File, swr: StringWriter, wr: PrintWriter): LogContext = {
- require (file != null)
- new LogContext(file, Some((swr, wr)))
- }
- def apply(file: File): LogContext = new LogContext(file, None)
-}
-
-object Output {
- object outRedirect extends Redirecter(out)
- object errRedirect extends Redirecter(err)
-
- System.setOut(outRedirect)
- System.setErr(errRedirect)
-
- import scala.util.DynamicVariable
- private def out = java.lang.System.out
- private def err = java.lang.System.err
- private val redirVar = new DynamicVariable[Option[PrintStream]](None)
-
- class Redirecter(stream: PrintStream) extends PrintStream(new OutputStream {
- def write(b: Int) = withStream(_ write b)
-
- private def withStream(f: PrintStream => Unit) = f(redirVar.value getOrElse stream)
-
- override def write(b: Array[Byte]) = withStream(_ write b)
- override def write(b: Array[Byte], off: Int, len: Int) = withStream(_.write(b, off, len))
- override def flush = withStream(_.flush)
- override def close = withStream(_.close)
- })
-
- // this supports thread-safe nested output redirects
- def withRedirected[T](newstream: PrintStream)(func: => T): T = {
- // note down old redirect destination
- // this may be None in which case outRedirect and errRedirect print to stdout and stderr
- val saved = redirVar.value
- // set new redirecter
- // this one will redirect both out and err to newstream
- redirVar.value = Some(newstream)
-
- try func
- finally {
- newstream.flush()
- redirVar.value = saved
- }
- }
-}
-
-/** Use a Runner to run a test. */
-class RunnerManager(kind: String, fileManager: FileManager, params: TestRunParams) {
- fileManager.CLASSPATH += File.pathSeparator + PathSettings.scalaCheck
- fileManager.CLASSPATH += File.pathSeparator + PathSettings.diffUtils // needed to put diffutils on test/partest's classpath
-
- def runTest(testFile: File): TestState = {
- val runner = new Runner(testFile, fileManager, params)
-
- // when option "--failed" is provided execute test only if log
- // is present (which means it failed before)
- if (fileManager.failed && !runner.logFile.canRead)
- runner.genPass()
- else {
- val (state, _) =
- try timed(runner.run())
- catch {
- case t: Throwable => throw new RuntimeException(s"Error running $testFile", t)
- }
- NestUI.reportTest(state)
- runner.cleanup()
- state
- }
- }
-}