Fix timeout problem in partest

git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@16859 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
This commit is contained in:
phaller 2009-01-08 09:36:09 +00:00
parent cb231c58c0
commit 51a3453425
3 changed files with 161 additions and 164 deletions

View File

@ -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 = {
/* This method returns true iff compilation succeeds.
*/
def shouldCompile(files: List[File], kind: String, log: File): 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
}
}
compiler.compile(None, files, kind, log)
}
/* 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)
}
/* 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)
}
}

View File

@ -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)

View File

@ -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 <code>script</code> 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
if (!succeeded) {
errors += 1
NestUI.verbose("incremented errors: "+errors)
}
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)
}
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) {