diff options
author | Paul Phillips <paulp@improving.org> | 2012-06-10 20:41:14 -0700 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-07-11 06:27:48 -0700 |
commit | 883371e18ebb644d6601c0847640a36a8c39f302 (patch) | |
tree | 778fda4546ed31f10194ab7b7eda434ad2daf882 /src/partest | |
parent | 783013672cb96579c7e10573acb2c18dda707580 (diff) | |
download | scala-883371e18ebb644d6601c0847640a36a8c39f302.tar.gz scala-883371e18ebb644d6601c0847640a36a8c39f302.tar.bz2 scala-883371e18ebb644d6601c0847640a36a8c39f302.zip |
Eliminated more logic.
Why hand out big blocks of tests to workers. Just put them
all in a queue and let the threads consume them when they can.
This speeds up partest a fair bit in those not uncommon
situations where one worker would end up lagging well behind
the others, and drops another 150ish lines of code.
Test suite time on my 8-core machine goes from 28:21 to 22:34.
Diffstat (limited to 'src/partest')
-rw-r--r-- | src/partest/scala/tools/partest/PartestDefaults.scala | 7 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/DirectRunner.scala | 42 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/RunnerManager.scala (renamed from src/partest/scala/tools/partest/nest/Worker.scala) | 504 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/utils/Properties.scala | 2 |
4 files changed, 206 insertions, 349 deletions
diff --git a/src/partest/scala/tools/partest/PartestDefaults.scala b/src/partest/scala/tools/partest/PartestDefaults.scala index f20823d0d0..73a7b92778 100644 --- a/src/partest/scala/tools/partest/PartestDefaults.scala +++ b/src/partest/scala/tools/partest/PartestDefaults.scala @@ -4,6 +4,7 @@ package partest import nsc.io.{ File, Path, Directory } import util.{ PathResolver } import nsc.Properties.{ propOrElse, propOrNone, propOrEmpty } +import java.lang.Runtime.getRuntime object PartestDefaults { import nsc.Properties._ @@ -22,9 +23,9 @@ object PartestDefaults { def javaOpts = propOrElse("partest.java_opts", "") def scalacOpts = propOrElse("partest.scalac_opts", "-deprecation") - def testBuild = propOrNone("partest.build") - def errorCount = propOrElse("partest.errors", "0").toInt - def numActors = propOrElse("partest.actors", "6").toInt + def testBuild = propOrNone("partest.build") + def errorCount = propOrElse("partest.errors", "0").toInt + def numThreads = propOrNone("partest.threads") map (_.toInt) getOrElse getRuntime.availableProcessors def timeout = "1200000" } diff --git a/src/partest/scala/tools/partest/nest/DirectRunner.scala b/src/partest/scala/tools/partest/nest/DirectRunner.scala index 94e3d917d1..2bb373d9c5 100644 --- a/src/partest/scala/tools/partest/nest/DirectRunner.scala +++ b/src/partest/scala/tools/partest/nest/DirectRunner.scala @@ -19,10 +19,9 @@ import scala.collection.convert.decorateAll._ case class TestRunParams(val scalaCheckParentClassLoader: ScalaClassLoader) trait DirectRunner { - def fileManager: FileManager - import PartestDefaults.numActors + import PartestDefaults.numThreads def denotesTestFile(arg: String) = Path(arg).hasExtension("scala", "res", "xml") def denotesTestDir(arg: String) = Path(arg).ifDirectory(_.files.nonEmpty) exists (x => x) @@ -37,41 +36,34 @@ trait DirectRunner { false }) } - - private def timeoutResult = { - NestUI.verbose("worker timed out; adding failed test") - Map("worker timed out; adding failed test" -> TestState.Timeout) - } def runTestsForFiles(_kindFiles: List[File], kind: String): immutable.Map[String, TestState] = { - val kindFiles = onlyValidTestPaths(_kindFiles) - val groupSize = (kindFiles.length / numActors) + 1 - // @partest maintainer: we cannot create a fresh file manager here // since the FM must respect --buildpath and --classpath from the command line // for example, see how it's done in ReflectiveRunner //val consFM = new ConsoleFileManager //import consFM.{ latestCompFile, latestLibFile, latestPartestFile } - val latestCompFile = new File(fileManager.LATEST_COMP) + val latestCompFile = new File(fileManager.LATEST_COMP) val latestReflectFile = new File(fileManager.LATEST_REFLECT) - val latestLibFile = new File(fileManager.LATEST_LIB) + val latestLibFile = new File(fileManager.LATEST_LIB) val latestPartestFile = new File(fileManager.LATEST_PARTEST) - val latestActorsFile = new File(fileManager.LATEST_ACTORS) - val latestActMigFile = new File(fileManager.LATEST_ACTORS_MIGRATION) - val scalacheckURL = PathSettings.scalaCheck.toURL + val latestActorsFile = new File(fileManager.LATEST_ACTORS) + val latestActMigFile = new File(fileManager.LATEST_ACTORS_MIGRATION) + val scalacheckURL = PathSettings.scalaCheck.toURL val scalaCheckParentClassLoader = ScalaClassLoader.fromURLs( scalacheckURL :: (List(latestCompFile, latestReflectFile, latestLibFile, latestActorsFile, latestActMigFile, latestPartestFile).map(_.toURI.toURL)) ) - Output.init() - val pool = Executors.newCachedThreadPool() - val fileSets = kindFiles grouped groupSize toList; - val workers = fileSets map (_ => new Worker(kind, fileManager, TestRunParams(scalaCheckParentClassLoader))) - val callables = (fileSets, workers).zipped map ((xs, w) => callable(w actDirect xs)) - val results = pool.invokeAll(callables.asJava, 1, TimeUnit.HOURS).asScala + val kindFiles = onlyValidTestPaths(_kindFiles) + val pool = Executors.newFixedThreadPool(numThreads) + val manager = new RunnerManager(kind, fileManager, TestRunParams(scalaCheckParentClassLoader)) + val futures = kindFiles map (f => (f, pool submit callable(manager runTest f))) toMap + + pool.shutdown() + pool.awaitTermination(1, TimeUnit.HOURS) - results flatMap { r => - if (r.isCancelled) timeoutResult - else for ((file, status) <- r.get) yield (file.getAbsolutePath, status) - } toMap + for ((file, future) <- futures) yield { + val state = if (future.isCancelled) TestState.Timeout else future.get + (file.getAbsolutePath, state) + } } } diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/RunnerManager.scala index f453d56b7e..feca40a159 100644 --- a/src/partest/scala/tools/partest/nest/Worker.scala +++ b/src/partest/scala/tools/partest/nest/RunnerManager.scala @@ -22,6 +22,7 @@ import scala.collection.{ mutable, immutable } import scala.tools.nsc.interactive.{ BuildManager, RefinedBuildManager } import scala.sys.process._ import java.util.concurrent.{ Executors, TimeUnit, TimeoutException } +import PartestDefaults.{ javaCmd, javacCmd } class LogContext(val file: File, val writers: Option[(StringWriter, PrintWriter)]) @@ -34,10 +35,11 @@ object LogContext { } object Output { - def init() { - System.setOut(outRedirect) - System.setErr(errRedirect) - } + 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 @@ -55,10 +57,6 @@ object Output { override def close = withStream(_.close) }) - object outRedirect extends Redirecter(out) - - object errRedirect extends Redirecter(err) - // this supports thread-safe nested output redirects def withRedirected[T](newstream: PrintStream)(func: => T): T = { // note down old redirect destination @@ -76,71 +74,16 @@ object Output { } } -class Worker(kind: String, val fileManager: FileManager, params: TestRunParams) { +class RunnerManager(kind: String, val fileManager: FileManager, params: TestRunParams) { import fileManager._ - private val executor = Executors.newSingleThreadExecutor() - - /** Formerly deeper inside, these next few things are now promoted outside so - * I can see what they're doing when the world comes to a premature stop. - */ - private val toDelete = mutable.HashSet[File]() - val failDiffs = mutable.HashMap[File, String]() - val timer = new Timer - val javaCmd = propOrElse("partest.javacmd", Path(javaHome) / "bin" / "java" path) - val javacCmd = propOrElse("partest.javac_cmd", Path(jdkHome) / "bin" / "javac" path) - - def cancelTimerTask() = if (currentTimerTask != null) currentTimerTask.cancel() - def updateTimerTask(body: => Unit) = { - cancelTimerTask() - currentTimerTask = new KickableTimerTask(body) - timer.schedule(currentTimerTask, fileManager.oneTestTimeout) - } - - class KickableTimerTask(body: => Unit) extends TimerTask { - def run() = body - def kick() = { - cancel() - body - } - } - - private var currentTimerTask: KickableTimerTask = _ - private var currentFileStart: Long = System.currentTimeMillis - private var currentTestFile: File = _ - private def fileBase = basename(currentTestFile.getName) + val compileMgr = new CompileManager(fileManager) + fileManager.CLASSPATH += File.pathSeparator + PathSettings.scalaCheck private def compareFiles(f1: File, f2: File): String = try fileManager.compareFiles(f1, f2) catch { case t => t.toString } - private def cleanup() { - // keep output directories under debug - if (!isPartestDebug) - toDelete foreach (_.deleteRecursively()) - - toDelete.clear() - } - sys addShutdownHook cleanup() - - private def resetAll() { - cancelTimerTask() - cleanup() - currentTestFile = null - currentTimerTask = null - } - - def currentFileElapsed = (System.currentTimeMillis - currentFileStart) / 1000 - def forceTimeout() = { - println("Let's see what them threads are doing before I kill that test.") - sys.allThreads foreach { t => - println(t) - t.getStackTrace foreach println - println("") - } - currentTimerTask.kick() - } - /** This does something about absolute paths and file separator * chars before diffing. */ @@ -151,25 +94,8 @@ class Worker(kind: String, val fileManager: FileManager, params: TestRunParams) s.replace('\\', '/').replaceAll(regex, "") } - private def currentFileString = { - "Current test file is: %s\n Started: %s (%s seconds ago)\n Current time: %s".format( - currentTestFile, - new java.util.Date(currentFileStart), - currentFileElapsed, - new java.util.Date() - ) - } - - override def toString = "Partest Worker" - private def workerError(msg: String): Unit = System.err.println("Error: " + msg) - def actDirect(files: List[File]): Map[File, TestState] = { - NestUI.verbose("actDirect(%s files)".format(files.size)) - - try runTests(files) finally resetAll() - } - private def printInfoStart(file: File, printer: PrintWriter) { NestUI.outline("testing: ", printer) val filesdir = file.getAbsoluteFile.getParentFile.getParentFile @@ -198,15 +124,6 @@ class Worker(kind: String, val fileManager: FileManager, params: TestRunParams) NestUI.normal("]\n", printer) } - private def createLogFile(file: File) = fileManager.getLogFile(file, kind) - - private def createOutputDir(dir: File): File = { - val outDir = Path(dir) / Directory("%s-%s.obj".format(fileBase, kind)) - outDir.createDirectory() - toDelete += outDir.jfile - outDir.jfile - } - private def javac(outDir: File, files: List[File], output: File): CompilationOutcome = { // compile using command-line javac compiler val args = Seq( @@ -229,87 +146,6 @@ class Worker(kind: String, val fileManager: FileManager, params: TestRunParams) (Process(args) #> outFile !) == 0 } - private def execTest(outDir: File, logFile: File, classpathPrefix: String = ""): Boolean = { - // check whether there is a ".javaopts" file - val argsFile = new File(logFile.getParentFile, fileBase + ".javaopts") - val argString = file2String(argsFile) - if (argString != "") - NestUI.verbose("Found javaopts file '%s', using options: '%s'".format(argsFile, argString)) - - val testFullPath = { - val d = new File(logFile.getParentFile, fileBase) - if (d.isDirectory) d.getAbsolutePath - else { - val f = new File(logFile.getParentFile, fileBase + ".scala") - if (f.isFile) f.getAbsolutePath - else "" - } - } - - // 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 (classpathPrefix != "") join(classpathPrefix, CLASSPATH) else CLASSPATH - val cmd = javaCmd +: ( - (JAVA_OPTS.split(' ') ++ argString.split(' ')).map(_.trim).filter(_ != "") ++ Seq( - "-classpath", - join(outDir.toString, classpath) - ) ++ propertyOptions ++ Seq( - "scala.tools.nsc.MainGenericRunner", - "-usejavacp", - "Test", - "jvm" - ) - ) - - runCommand(cmd, logFile) - } - - private def getCheckFilePath(dir: File, suffix: String = "") = { - def chkFile(s: String) = (Directory(dir) / "%s%s.check".format(fileBase, s)).toFile - - if (chkFile("").isFile || suffix == "") chkFile("") - else chkFile("-" + suffix) - } - private def getCheckFile(dir: File) = Some(getCheckFilePath(dir, kind)) filter (_.canRead) - - private def compareOutput(dir: File, logFile: File): String = { - val checkFile = getCheckFilePath(dir, kind) - val diff = - if (checkFile.canRead) compareFiles(logFile, checkFile.jfile) - else file2String(logFile) - - // if check file exists, compare with log file - if (diff != "" && fileManager.updateCheck) { - NestUI.verbose("Updating checkfile " + checkFile.jfile) - val toWrite = if (checkFile.exists) checkFile else getCheckFilePath(dir, "") - toWrite writeAll file2String(logFile) - "" - } - else diff - } - @inline private def isJava(f: File) = SFile(f) hasExtension "java" @inline private def isScala(f: File) = SFile(f) hasExtension "scala" @inline private def isJavaOrScala(f: File) = isJava(f) || isScala(f) @@ -331,17 +167,104 @@ class Worker(kind: String, val fileManager: FileManager, params: TestRunParams) case e: Exception => logStackTrace(logFile, e, msg) ; value } - /** Runs a list of tests. - * - * @param files The list of test files - */ - private def runTests(files: List[File]): Map[File, TestState] = { - val compileMgr = new CompileManager(fileManager) - fileManager.CLASSPATH += File.pathSeparator + PathSettings.scalaCheck + class Runner(testFile: File) { + var testDiff: String = "" + var passed: Option[Boolean] = None + + val fileBase = basename(testFile.getName) + val logFile = fileManager.getLogFile(testFile, kind) + val parent = testFile.getParentFile + val outDir = new File(parent, "%s-%s.obj".format(fileBase, kind)) + def toDelete = if (isPartestDebug) Nil else List( + if (passed exists (x => x)) Some(logFile) else None, + if (outDir.isDirectory) Some(outDir) else None + ).flatten + + private def createOutputDir(): File = { + outDir.mkdirs() + outDir + } + + private def execTest(outDir: File, logFile: File, classpathPrefix: String = ""): Boolean = { + // check whether there is a ".javaopts" file + val argsFile = new File(logFile.getParentFile, fileBase + ".javaopts") + val argString = file2String(argsFile) + if (argString != "") + NestUI.verbose("Found javaopts file '%s', using options: '%s'".format(argsFile, argString)) + + val testFullPath = { + val d = new File(logFile.getParentFile, fileBase) + if (d.isDirectory) d.getAbsolutePath + else { + val f = new File(logFile.getParentFile, fileBase + ".scala") + if (f.isFile) f.getAbsolutePath + else "" + } + } + + // 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 (classpathPrefix != "") join(classpathPrefix, CLASSPATH) else CLASSPATH + val cmd = javaCmd +: ( + (JAVA_OPTS.split(' ') ++ argString.split(' ')).map(_.trim).filter(_ != "") ++ Seq( + "-classpath", + join(outDir.toString, classpath) + ) ++ propertyOptions ++ Seq( + "scala.tools.nsc.MainGenericRunner", + "-usejavacp", + "Test", + "jvm" + ) + ) + + runCommand(cmd, logFile) + } - // You don't default "succeeded" to true. - var succeeded = false - var errors = 0 + private def getCheckFilePath(dir: File, suffix: String = "") = { + def chkFile(s: String) = (Directory(dir) / "%s%s.check".format(fileBase, s)).toFile + + if (chkFile("").isFile || suffix == "") chkFile("") + else chkFile("-" + suffix) + } + private def getCheckFile(dir: File) = Some(getCheckFilePath(dir, kind)) filter (_.canRead) + + private def compareOutput(dir: File, logFile: File): String = { + val checkFile = getCheckFilePath(dir, kind) + val diff = + if (checkFile.canRead) compareFiles(logFile, checkFile.jfile) + else file2String(logFile) + + // if check file exists, compare with log file + if (diff != "" && fileManager.updateCheck) { + NestUI.verbose("Updating checkfile " + checkFile.jfile) + val toWrite = if (checkFile.exists) checkFile else getCheckFilePath(dir, "") + toWrite writeAll file2String(logFile) + "" + } + else diff + } def newTestWriters() = { val swr = new StringWriter @@ -355,41 +278,34 @@ class Worker(kind: String, val fileManager: FileManager, params: TestRunParams) NestUI.verbose("scalac: compilation of "+what+" failed\n") false } - def diffCheck(testFile: File, diff: String) = - (diff == "") || ( try false finally failDiffs(testFile) = diff ) + def diffCheck(testFile: File, diff: String) = { + testDiff = diff + testDiff == "" + } /** 1. Creates log file and output directory. * 2. Runs script function, providing log file and output directory as arguments. */ def runInContext(file: File, script: (File, File) => Boolean): (Boolean, LogContext) = { - // When option "--failed" is provided, execute test only if log file is present - // (which means it failed before) - val logFile = createLogFile(file) - - if (fileManager.failed && !logFile.canRead) - (true, LogContext(logFile)) - else { - val (swr, wr) = newTestWriters() - printInfoStart(file, wr) - - NestUI.verbose(this+" running test "+fileBase) - val dir = file.getParentFile - val outDir = createOutputDir(dir) - NestUI.verbose("output directory: "+outDir) - - // run test-specific code - val succeeded = try { - if (isPartestDebug) { - val (result, millis) = timed(script(logFile, outDir)) - fileManager.recordTestTiming(file.getPath, millis) - result - } - else script(logFile, outDir) + val (swr, wr) = newTestWriters() + printInfoStart(file, wr) + + NestUI.verbose(this+" running test "+fileBase) + val outDir = createOutputDir() + NestUI.verbose("output directory: "+outDir) + + // run test-specific code + val succeeded = try { + if (isPartestDebug) { + val (result, millis) = timed(script(logFile, outDir)) + fileManager.recordTestTiming(file.getPath, millis) + result } - catch exHandler(logFile, "", false) - - (succeeded, LogContext(logFile, swr, wr)) + else script(logFile, outDir) } + catch exHandler(logFile, "", false) + + (succeeded, LogContext(logFile, swr, wr)) } def groupedFiles(dir: File): List[List[File]] = { @@ -475,33 +391,29 @@ class Worker(kind: String, val fileManager: FileManager, params: TestRunParams) } def runAntTest(file: File): (Boolean, LogContext) = { - val logFile = createLogFile(file) - if (!fileManager.failed || logFile.canRead) { - val (swr, wr) = newTestWriters() - printInfoStart(file, wr) + val (swr, wr) = newTestWriters() + printInfoStart(file, wr) - NestUI.verbose(this+" running test "+fileBase) + NestUI.verbose(this+" running test "+fileBase) - 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.path, "-file", file.path) - NestUI.verbose("ant "+args.mkString(" ")) - ant(args, logFile) && diffCheck(file, compareOutput(file.getParentFile, logFile)) - } - catch { // *catch-all* - case e: Exception => - NestUI.verbose("caught "+e) - false - } - - (succeeded, LogContext(logFile, swr, wr)) + 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.path, "-file", file.path) + NestUI.verbose("ant "+args.mkString(" ")) + ant(args, logFile) && diffCheck(file, compareOutput(file.getParentFile, logFile)) } - else (true, LogContext(logFile)) + catch { // *catch-all* + case e: Exception => + NestUI.verbose("caught "+e) + false + } + + (succeeded, LogContext(logFile, swr, wr)) } def runSpecializedTest(file: File): (Boolean, LogContext) = @@ -530,11 +442,7 @@ class Worker(kind: String, val fileManager: FileManager, params: TestRunParams) NestUI.verbose(file2String(logFile)) // obviously this must be improved upon val lines = SFile(logFile).lines map (_.trim) filterNot (_ == "") toBuffer; - if (lines forall (x => !x.startsWith("!"))) { - NestUI.verbose("test for '" + file + "' success: " + succeeded) - true - } - else { + lines.forall(x => !x.startsWith("!")) || { NestUI.normal("ScalaCheck test failed. Output:\n") lines foreach (x => NestUI.normal(x + "\n")) false @@ -574,10 +482,6 @@ class Worker(kind: String, val fileManager: FileManager, params: TestRunParams) runAntTest(file) case "buildmanager" => - val logFile = createLogFile(file) - if (fileManager.failed && !logFile.canRead) - return (true, LogContext(logFile)) - val (swr, wr) = newTestWriters() printInfoStart(file, wr) val (outDir, testFile, changesDir) = { @@ -585,7 +489,7 @@ class Worker(kind: String, val fileManager: FileManager, params: TestRunParams) (null, null, null) else { NestUI.verbose(this+" running test "+fileBase) - val outDir = createOutputDir(file) + val outDir = createOutputDir() val testFile = new File(file, fileBase + ".test") val changesDir = new File(file, fileBase + ".changes") @@ -696,19 +600,12 @@ class Worker(kind: String, val fileManager: FileManager, params: TestRunParams) // simulate resident compiler loop val prompt = "\nnsc> " - // when option "--failed" is provided - // execute test only if log file is present - // (which means it failed before) - val logFile = createLogFile(file) - if (fileManager.failed && !logFile.canRead) - return (true, LogContext(logFile)) - val (swr, wr) = newTestWriters() printInfoStart(file, wr) NestUI.verbose(this+" running test "+fileBase) val dir = file.getParentFile - val outDir = createOutputDir(dir) + val outDir = createOutputDir() val resFile = new File(dir, fileBase + ".res") NestUI.verbose("outDir: "+outDir) NestUI.verbose("logFile: "+logFile) @@ -771,19 +668,11 @@ class Worker(kind: String, val fileManager: FileManager, params: TestRunParams) } case "shootout" => - // when option "--failed" is provided - // execute test only if log file is present - // (which means it failed before) - val logFile = createLogFile(file) - if (fileManager.failed && !logFile.canRead) - return (true, LogContext(logFile)) - val (swr, wr) = newTestWriters() printInfoStart(file, wr) NestUI.verbose(this+" running test "+fileBase) - val dir = file.getParentFile - val outDir = createOutputDir(dir) + val outDir = createOutputDir() // 2. define file {outDir}/test.scala that contains code to compile/run val testFile = new File(outDir, "test.scala") @@ -792,8 +681,8 @@ class Worker(kind: String, val fileManager: FileManager, params: TestRunParams) NestUI.verbose("testFile: "+testFile) // 3. cat {test}.scala.runner {test}.scala > testFile - val runnerFile = new File(dir, fileBase+".scala.runner") - val bodyFile = new File(dir, fileBase+".scala") + val runnerFile = new File(parent, fileBase+".scala.runner") + val bodyFile = new File(parent, fileBase+".scala") SFile(testFile).writeAll( file2String(runnerFile), file2String(bodyFile) @@ -805,7 +694,7 @@ class Worker(kind: String, val fileManager: FileManager, params: TestRunParams) val result = ok && { execTest(outDir, logFile) && { NestUI.verbose(this+" finished running "+fileBase) - diffCheck(file, compareOutput(dir, logFile)) + diffCheck(file, compareOutput(parent, logFile)) } } @@ -847,13 +736,6 @@ class Worker(kind: String, val fileManager: FileManager, params: TestRunParams) }) case "script" => - // when option "--failed" is provided - // execute test only if log file is present - // (which means it failed before) - val logFile = createLogFile(file) - if (fileManager.failed && !logFile.canRead) - return (true, LogContext(logFile)) - val (swr, wr) = newTestWriters() printInfoStart(file, wr) @@ -880,70 +762,52 @@ class Worker(kind: String, val fileManager: FileManager, params: TestRunParams) (succeeded, LogContext(logFile, swr, wr)) } - def reportResult(testFile: File, state: TestState, logFile: File, writers: Option[(StringWriter, PrintWriter)]) { - if (state.isOk) { - // add logfile from deletion list if test passed - if (logFile != null) - toDelete += logFile - } - else { - errors += 1 - NestUI.verbose("incremented errors: "+errors) + private def crashContext(t: Throwable): LogContext = { + try { + logStackTrace(logFile, t, "Possible compiler crash during test of: " + testFile + "\n") + LogContext(logFile) } + catch { case t => LogContext(null) } + } + + def run(): (Boolean, LogContext) = { + val result = try processSingleFile(testFile) catch { case t => (false, crashContext(t)) } + passed = Some(result._1) + result + } + def reportResult(writers: Option[(StringWriter, PrintWriter)]) { writers foreach { case (swr, wr) => - if (state.isTimeout) printInfoTimeout(wr) - else printInfoEnd(state.isOk, wr) + if (passed.isEmpty) printInfoTimeout(wr) + else printInfoEnd(passed.get, wr) wr.flush() swr.flush() NestUI.normal(swr.toString) - if (state.isFail) { + if (passed exists (x => !x)) { if (fileManager.showDiff || isPartestDebug) - failDiffs get testFile foreach (NestUI normal _) + NestUI.normal(testDiff) else if (fileManager.showLog) showLog(logFile) } } - cleanup() + toDelete foreach (_.deleteRecursively()) } + } - def runNext(testFile: File): (Boolean, LogContext) = { - // System.out.println("runNext(" + testFile + ")") - currentTestFile = testFile - - try processSingleFile(testFile) - catch { case t => - val ctx = try { - val logFile = createLogFile(testFile) - logStackTrace(logFile, t, "Possible compiler crash during test of: " + testFile + "\n") - LogContext(logFile) - } - catch { case t => LogContext(null) } - - (false, ctx) - } - } + def runTest(testFile: File): TestState = { + val runner = new Runner(testFile) + // when option "--failed" is provided execute test only if log + // is present (which means it failed before) + if (fileManager.failed && !runner.logFile.canRead) + return TestState.Ok - val statuses = files map { file => - def dummyCtx = { - val swr = new StringWriter - val wr = new PrintWriter(swr, true) - printInfoStart(file, wr) - new LogContext(null, Some((swr, wr))) - } - val future = executor submit callable(timed(runNext(file))) - val result = try future.get(10, TimeUnit.MINUTES) catch { case _: TimeoutException => null } - val timedOut = result == null - val ((success, ctx), elapsed) = if (timedOut) ((false, dummyCtx), 1000 * 60 * 10) else future.get - val state = if (timedOut) TestState.Timeout else if (success) TestState.Ok else TestState.Fail - - reportResult(file, state, ctx.file, ctx.writers) - file -> state - } + // sys addShutdownHook cleanup() + val ((success, ctx), elapsed) = timed(runner.run()) + val state = if (success) TestState.Ok else TestState.Fail - timer.cancel() - statuses.toMap + runner.reportResult(ctx.writers) + state } private def filesToSet(pre: String, fs: List[String]): Set[AbstractFile] = diff --git a/src/partest/scala/tools/partest/utils/Properties.scala b/src/partest/scala/tools/partest/utils/Properties.scala index b8a6ec8007..1e10ffb874 100644 --- a/src/partest/scala/tools/partest/utils/Properties.scala +++ b/src/partest/scala/tools/partest/utils/Properties.scala @@ -13,5 +13,5 @@ package utils /** Loads partest.properties from the jar. */ object Properties extends scala.util.PropertiesTrait { protected def propCategory = "partest" - protected def pickJarBasedOn = classOf[nest.Worker] + protected def pickJarBasedOn = classOf[nest.RunnerManager] } |