From 0313e1c018d89fc4e4474f42308e81cfc60fdfb6 Mon Sep 17 00:00:00 2001 From: Philipp Haller Date: Thu, 8 Jan 2009 09:36:09 +0000 Subject: Fix timeout problem in partest --- .../scala/tools/partest/nest/CompileManager.scala | 70 ++---- .../scala/tools/partest/nest/ConsoleRunner.scala | 6 +- src/partest/scala/tools/partest/nest/Worker.scala | 247 ++++++++++++--------- 3 files changed, 160 insertions(+), 163 deletions(-) (limited to 'src') diff --git a/src/partest/scala/tools/partest/nest/CompileManager.scala b/src/partest/scala/tools/partest/nest/CompileManager.scala index a8eb044629..d61e0af51b 100644 --- a/src/partest/scala/tools/partest/nest/CompileManager.scala +++ b/src/partest/scala/tools/partest/nest/CompileManager.scala @@ -155,10 +155,6 @@ class ReflectiveCompiler(val fileManager: ConsoleFileManager) extends SimpleComp } class CompileManager(val fileManager: FileManager) { - - import scala.actors.Actor._ - import scala.actors.{Actor, Exit, TIMEOUT} - var compiler: SimpleCompiler = new /*ReflectiveCompiler*/ DirectCompiler(fileManager) var numSeparateCompilers = 1 @@ -167,71 +163,37 @@ class CompileManager(val fileManager: FileManager) { compiler = new /*ReflectiveCompiler*/ DirectCompiler(fileManager) } - val delay = fileManager.timeout.toLong - - def withTimeout(files: List[File])(thunk: => Boolean): Boolean = { - createSeparateCompiler() - - val parent = self - self.trapExit = true - val child = link { - parent ! (self, thunk) - } - - receiveWithin(delay) { - case TIMEOUT => - println("compilation timed out") - false - case Exit(from, reason) if from == child => - val From = from - reason match { - case 'normal => - receive { - case (From, result: Boolean) => result - } - case t: Throwable => - NestUI.verbose("while invoking compiler ("+files+"):") - NestUI.verbose("caught "+t) - t.printStackTrace - if (t.getCause != null) - t.getCause.printStackTrace - false - } - } - } - /* This method returns true iff compilation succeeds. */ - def shouldCompile(files: List[File], kind: String, log: File): Boolean = - withTimeout(files) { - compiler.compile(None, files, kind, log) - } + def shouldCompile(files: List[File], kind: String, log: File): Boolean = { + createSeparateCompiler() + compiler.compile(None, files, kind, log) + } /* This method returns true iff compilation succeeds. */ - def shouldCompile(out: File, files: List[File], kind: String, log: File): Boolean = - withTimeout(files) { - compiler.compile(Some(out), files, kind, log) - } + def shouldCompile(out: File, files: List[File], kind: String, log: File): Boolean = { + createSeparateCompiler() + compiler.compile(Some(out), files, kind, log) + } /* This method returns true iff compilation fails * _and_ the compiler does _not_ crash or loop. * * If the compiler crashes, this method returns false. */ - def shouldFailCompile(files: List[File], kind: String, log: File): Boolean = - withTimeout(files) { - !compiler.compile(None, files, kind, log) - } + def shouldFailCompile(files: List[File], kind: String, log: File): Boolean = { + createSeparateCompiler() + !compiler.compile(None, files, kind, log) + } /* This method returns true iff compilation fails * _and_ the compiler does _not_ crash or loop. * * If the compiler crashes, this method returns false. */ - def shouldFailCompile(out: File, files: List[File], kind: String, log: File): Boolean = - withTimeout(files) { - !compiler.compile(Some(out), files, kind, log) - } - + def shouldFailCompile(out: File, files: List[File], kind: String, log: File): Boolean = { + createSeparateCompiler() + !compiler.compile(Some(out), files, kind, log) + } } diff --git a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala index 3e08a2ffff..4872a61437 100644 --- a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala +++ b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala @@ -90,7 +90,7 @@ class ConsoleRunner extends DirectRunner with RunnerUtils { runAll = true var enabled = List[TestSet]() - + var readTimeout = false for (arg <- args) { (testSets find { set => arg == "--" + set.loc }) match { case Some(set) => enabled = set :: enabled @@ -102,6 +102,10 @@ class ConsoleRunner extends DirectRunner with RunnerUtils { case "--failed" => fileManager.failed = true case "--version" => //todo: printVersion case "--ansi" => NestUI.initialize(NestUI.MANY) + case "--timeout" => readTimeout = true + case s: String if readTimeout => + fileManager.timeout = s + readTimeout = false case _ => if (denotesTestFile(arg) || denotesTestDir(arg)) { val file = new File(arg) diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/Worker.scala index ef7f81f475..34dbdd7a3a 100644 --- a/src/partest/scala/tools/partest/nest/Worker.scala +++ b/src/partest/scala/tools/partest/nest/Worker.scala @@ -11,6 +11,7 @@ import java.io.{File, FileInputStream, FileOutputStream, PrintStream, FileReader, OutputStreamWriter, BufferedReader} import java.net.URL +import java.util.{Timer, TimerTask} import scala.tools.nsc.{ObjectRunner, GenericRunnerCommand} @@ -19,6 +20,7 @@ import scala.actors.Actor._ case class RunTests(kind: String, files: List[File]) case class Results(succ: Int, fail: Int, logs: List[LogFile], outdirs: List[File]) +case class LogContext(file: LogFile, writers: Option[(StringWriter, PrintWriter)]) class LogFile(parent: File, child: String) extends File(parent, child) { var toDelete = false @@ -31,6 +33,7 @@ class Worker(val fileManager: FileManager) extends Actor { import scala.tools.nsc.util.FakePos var reporter: ConsoleReporter = _ + val timer = new Timer def error(msg: String) { reporter.error(FakePos("scalac"), @@ -41,8 +44,10 @@ class Worker(val fileManager: FileManager) extends Actor { react { case RunTests(kind, files) => NestUI.verbose("received "+files.length+" to test") - val (succ, fail) = runTests(kind, files) - sender ! Results(succ, fail, createdLogFiles, createdOutputDirs) + val master = sender + runTests(kind, files, (succ: Int, fail: Int) => { + master ! Results(succ, fail, createdLogFiles, createdOutputDirs) + }) } } @@ -78,6 +83,12 @@ class Worker(val fileManager: FileManager) extends Actor { NestUI.normal("]\n", printer) } + def printInfoTimeout(printer: PrintWriter) { + NestUI.normal("[", printer) + NestUI.failure("TIMOUT", printer) + NestUI.normal("]\n", printer) + } + var log = "" var createdLogFiles: List[LogFile] = List() var createdOutputDirs: List[File] = List() @@ -317,7 +328,7 @@ class Worker(val fileManager: FileManager) extends Actor { * @param kind The test kind (pos, neg, run, etc.) * @param files The list of test files */ - def runTests(kind: String, files: List[File]): (Int, Int) = { + def runTests(kind: String, files: List[File], topcont: (Int, Int) => Unit): Unit = { val compileMgr = new CompileManager(fileManager) var errors = 0 var succeeded = true @@ -327,10 +338,8 @@ class Worker(val fileManager: FileManager) extends Actor { /** 1. Creates log file and output directory. * 2. Runs script function, providing log file and * output directory as arguments. - * 3. Prints test result. - * 4. Shows log/diff if enabled. */ - def runInContext(file: File, kind: String, script: (File, File) => Unit) { + def runInContext(file: File, kind: String, script: (File, File) => Unit): LogContext = { // when option "--failed" is provided // execute test only if log file is present // (which means it failed before) @@ -360,23 +369,9 @@ class Worker(val fileManager: FileManager) extends Actor { succeeded = false } - if (!succeeded) { - errors += 1 - NestUI.verbose("incremented errors: "+errors) - } else { - // delete log file only if test was successful - logFile.toDelete = true - } - - printInfoEnd(succeeded, wr) - wr.flush() - swr.flush() //TODO: needed? - NestUI.normal(swr.toString) - if (!succeeded && fileManager.showDiff && diff != "") - NestUI.normal(diff) - else if (!succeeded && fileManager.showLog) - showLog(logFile) - } + LogContext(logFile, Some((swr, wr))) + } else + LogContext(logFile, None) } def compileFilesIn(dir: File, kind: String, logFile: File, outDir: File) { @@ -412,7 +407,7 @@ class Worker(val fileManager: FileManager) extends Actor { } } - def runJvmTest(file: File, kind: String) { + def runJvmTest(file: File, kind: String): LogContext = runInContext(file, kind, (logFile: File, outDir: File) => { if (file.isDirectory) { compileFilesIn(file, kind, logFile, outDir) @@ -440,10 +435,9 @@ class Worker(val fileManager: FileManager) extends Actor { } } }) - } - kind match { - case "scalacheck" => for (file <- files) + def processSingleFile(file: File): LogContext = kind match { + case "scalacheck" => runInContext(file, kind, (logFile: File, outDir: File) => { if (file.isDirectory) { compileFilesIn(file, kind, logFile, outDir) @@ -480,7 +474,8 @@ class Worker(val fileManager: FileManager) extends Actor { } } }) - case "pos" => for (file <- files) + + case "pos" => runInContext(file, kind, (logFile: File, outDir: File) => { if (file.isDirectory) { compileFilesIn(file, kind, logFile, outDir) @@ -489,44 +484,40 @@ class Worker(val fileManager: FileManager) extends Actor { succeeded = false } }) + case "neg" => - for (file <- files) { - runInContext(file, kind, (logFile: File, outDir: File) => { - if (file.isDirectory) { - failCompileFilesIn(file, kind, logFile, outDir) - } else if (!compileMgr.shouldFailCompile(List(file), kind, logFile)) { + runInContext(file, kind, (logFile: File, outDir: File) => { + if (file.isDirectory) { + failCompileFilesIn(file, kind, logFile, outDir) + } else if (!compileMgr.shouldFailCompile(List(file), kind, logFile)) { + succeeded = false + } + if (succeeded) { // compare log file to check file + val fileBase = basename(file.getName) + val dir = file.getParentFile + if (!existsCheckFile(dir, fileBase, kind)) { + // diff is contents of logFile + diff = file2String(logFile) + } else + diff = compareOutput(dir, fileBase, kind, logFile) + + if (!diff.equals("")) { + NestUI.verbose("output differs from log file\n") succeeded = false } - if (succeeded) { // compare log file to check file - val fileBase = basename(file.getName) - val dir = file.getParentFile - if (!existsCheckFile(dir, fileBase, kind)) { - // diff is contents of logFile - diff = file2String(logFile) - } else - diff = compareOutput(dir, fileBase, kind, logFile) + } + }) - if (!diff.equals("")) { - NestUI.verbose("output differs from log file\n") - succeeded = false - } - } - }) - } case "run" => - for (file <- files) { - runJvmTest(file, kind) - } + runJvmTest(file, kind) + case "jvm" => - for (file <- files) { - runJvmTest(file, kind) - } + runJvmTest(file, kind) + case "jvm5" => - for (file <- files) { - runJvmTest(file, kind) - } + runJvmTest(file, kind) + case "res" => { - for (file <- files) { // when option "--failed" is provided // execute test only if log file is present // (which means it failed before) @@ -596,6 +587,7 @@ class Worker(val fileManager: FileManager) extends Actor { logWriter.print(prompt) val line = resReader.readLine() if ((line ne null) && line.length() > 0) { +/* val parent = self self.trapExit = true val child = link { @@ -617,7 +609,8 @@ class Worker(val fileManager: FileManager) extends Actor { false } } - +*/ + action(line) loop(action) } } @@ -676,26 +669,12 @@ class Worker(val fileManager: FileManager) extends Actor { succeeded = false } - // delete log file only if test was successful - if (succeeded) - logFile.toDelete = true - - if (!succeeded) { - errors += 1 - NestUI.verbose("incremented errors: "+errors) - } - printInfoEnd(succeeded, wr) - wr.flush() - swr.flush() - NestUI.normal(swr.toString) - - if (!succeeded && fileManager.showDiff) NestUI.normal(diff) - if (!succeeded && fileManager.showLog) showLog(logFile) - } + LogContext(logFile, Some((swr, wr))) + } else + LogContext(logFile, None) } - } + case "shootout" => { - for (file <- files) { // when option "--failed" is provided // execute test only if log file is present // (which means it failed before) @@ -756,27 +735,13 @@ class Worker(val fileManager: FileManager) extends Actor { succeeded = false } - // delete log file only if test was successful - if (succeeded) - logFile.toDelete = true - - if (!succeeded) { - errors += 1 - NestUI.verbose("incremented errors: "+errors) - } - printInfoEnd(succeeded, wr) - wr.flush() - swr.flush() - NestUI.normal(swr.toString) - - if (!succeeded && fileManager.showDiff) NestUI.normal(diff) - if (!succeeded && fileManager.showLog) showLog(logFile) - } + LogContext(logFile, Some((swr, wr))) + } else + LogContext(logFile, None) } - } + case "script" => { val osName = System.getProperty("os.name", "") - for (file <- files) { // when option "--failed" is provided // execute test only if log file is present // (which means it failed before) @@ -835,28 +800,94 @@ class Worker(val fileManager: FileManager) extends Actor { succeeded = false } - // delete log file only if test was successful - if (succeeded) - logFile.toDelete = true + LogContext(logFile, Some((swr, wr))) + } else + LogContext(logFile, None) + } + } + + def reportAll(cont: (Int, Int) => Unit) { + NestUI.verbose("finished testing "+kind+" with "+errors+" errors") + NestUI.verbose("created "+compileMgr.numSeparateCompilers+" separate compilers") + timer.cancel() + cont(files.length-errors, errors) + } - if (!succeeded) { - errors += 1 - NestUI.verbose("incremented errors: "+errors) - } + def reportResult(logs: Option[LogContext]) { + // delete log file only if test was successful + if (succeeded && !logs.isEmpty) + logs.get.file.toDelete = true + + if (!succeeded) { + errors += 1 + NestUI.verbose("incremented errors: "+errors) + } + + if (!logs.isEmpty) + logs.get.writers match { + case Some((swr, wr)) => printInfoEnd(succeeded, wr) wr.flush() swr.flush() NestUI.normal(swr.toString) + if (!succeeded && fileManager.showDiff && diff != "") + NestUI.normal(diff) + if (!succeeded && fileManager.showLog) + showLog(logs.get.file) + case None => + } + } - if (!succeeded && fileManager.showDiff) NestUI.normal(diff) - if (!succeeded && fileManager.showLog) showLog(logFile) - } + val numFiles = files.size + if (numFiles == 0) + reportAll(topcont) + + var fileCnt = 1 + Actor.loopWhile(fileCnt <= numFiles) { + val parent = self + val ontimeout = new TimerTask { + def run() { + parent ! 'timeout } } + timer.schedule(ontimeout, fileManager.timeout.toLong) + + actor { + val result = try { + processSingleFile(files(fileCnt-1)) + } catch { + case t: Throwable => + NestUI.verbose("while invoking compiler ("+files+"):") + NestUI.verbose("caught "+t) + t.printStackTrace + if (t.getCause != null) + t.getCause.printStackTrace + null + } + parent ! result + } + + react { + case 'timeout => + val swr = new StringWriter + val wr = new PrintWriter(swr) + printInfoStart(files(fileCnt-1), wr) + printInfoTimeout(wr) + wr.flush() + swr.flush() + NestUI.normal(swr.toString) + succeeded = false + reportResult(None) + if (fileCnt == numFiles) + reportAll(topcont) + fileCnt += 1 + case logs: LogContext => + reportResult(if (logs != null) Some(logs) else None) + if (fileCnt == numFiles) + reportAll(topcont) + fileCnt += 1 + } } - NestUI.verbose("finished testing "+kind+" with "+errors+" errors") - NestUI.verbose("created "+compileMgr.numSeparateCompilers+" separate compilers") - (files.length-errors, errors) } def showLog(logFile: File) { -- cgit v1.2.3