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:
parent
cb231c58c0
commit
51a3453425
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue