From ecbf89552666ebba974188ef3c98b5f4855d0cea Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 4 May 2012 17:38:55 -0700 Subject: Reluctant surgery on partest. Now neg tests are treated like all the other tests in terms of grouping, so if you have a negative test which requires more than one pass (like the one enclosed with the next commit) you can actually test it. --- .../scala/tools/partest/nest/CompileManager.scala | 98 +++++----------------- src/partest/scala/tools/partest/nest/Worker.scala | 71 ++++++++-------- 2 files changed, 57 insertions(+), 112 deletions(-) (limited to 'src/partest') diff --git a/src/partest/scala/tools/partest/nest/CompileManager.scala b/src/partest/scala/tools/partest/nest/CompileManager.scala index 7aaa7bab00..c674e21482 100644 --- a/src/partest/scala/tools/partest/nest/CompileManager.scala +++ b/src/partest/scala/tools/partest/nest/CompileManager.scala @@ -18,6 +18,21 @@ import io.Path import java.io.{ File, BufferedReader, PrintWriter, FileReader, Writer, FileWriter, StringWriter } import File.pathSeparator +sealed abstract class CompilationOutcome { + def merge(other: CompilationOutcome): CompilationOutcome + def isPositive = this eq CompileSuccess + def isNegative = this eq CompileFailed +} +case object CompileSuccess extends CompilationOutcome { + def merge(other: CompilationOutcome) = other +} +case object CompileFailed extends CompilationOutcome { + def merge(other: CompilationOutcome) = if (other eq CompileSuccess) this else other +} +case object CompilerCrashed extends CompilationOutcome { + def merge(other: CompilationOutcome) = this +} + class ExtConsoleReporter(settings: Settings, val writer: PrintWriter) extends ConsoleReporter(settings, Console.in, writer) { shortname = true } @@ -32,7 +47,7 @@ class TestSettings(cp: String, error: String => Unit) extends Settings(error) { } abstract class SimpleCompiler { - def compile(out: Option[File], files: List[File], kind: String, log: File): Boolean + def compile(out: Option[File], files: List[File], kind: String, log: File): CompilationOutcome } class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { @@ -68,7 +83,7 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { (opt2 ::: pluginOption) mkString " " } - def compile(out: Option[File], files: List[File], kind: String, log: File): Boolean = { + def compile(out: Option[File], files: List[File], kind: String, log: File): CompilationOutcome = { val testSettings = out match { case Some(f) => newSettings(f.getAbsolutePath) case _ => newSettings() @@ -118,6 +133,7 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { catch { case FatalError(msg) => testRep.error(null, "fatal error: " + msg) + return CompilerCrashed } testRep.printSummary() @@ -125,81 +141,13 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { } finally logWriter.close() - !testRep.hasErrors + if (testRep.hasErrors) CompileFailed + else CompileSuccess } } -// class ReflectiveCompiler(val fileManager: ConsoleFileManager) extends SimpleCompiler { -// import fileManager.{latestCompFile, latestPartestFile} -// -// val sepUrls = Array(latestCompFile.toURI.toURL, latestPartestFile.toURI.toURL) -// //NestUI.verbose("constructing URLClassLoader from URLs "+latestCompFile+" and "+latestPartestFile) -// -// val sepLoader = new java.net.URLClassLoader(sepUrls, null) -// -// val sepCompilerClass = -// sepLoader.loadClass("scala.tools.partest.nest.DirectCompiler") -// val sepCompiler = sepCompilerClass.newInstance() -// -// // needed for reflective invocation -// val fileClass = Class.forName("java.io.File") -// val stringClass = Class.forName("java.lang.String") -// val sepCompileMethod = -// sepCompilerClass.getMethod("compile", fileClass, stringClass) -// val sepCompileMethod2 = -// sepCompilerClass.getMethod("compile", fileClass, stringClass, fileClass) -// -// /* This method throws java.lang.reflect.InvocationTargetException -// * if the compiler crashes. -// * This exception is handled in the shouldCompile and shouldFailCompile -// * methods of class CompileManager. -// */ -// def compile(out: Option[File], files: List[File], kind: String, log: File): Boolean = { -// val res = sepCompileMethod2.invoke(sepCompiler, out, files, kind, log).asInstanceOf[java.lang.Boolean] -// res.booleanValue() -// } -// } - class CompileManager(val fileManager: FileManager) { - var compiler: SimpleCompiler = new DirectCompiler(fileManager) - - var numSeparateCompilers = 1 - def createSeparateCompiler() = { - numSeparateCompilers += 1 - compiler = new /*ReflectiveCompiler*/ DirectCompiler(fileManager) - } - - /* This method returns true iff compilation succeeds. - */ - 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 = { - 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 = { - 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 = { - createSeparateCompiler() - !compiler.compile(Some(out), files, kind, log) - } + private def newCompiler = new DirectCompiler(fileManager) + def attemptCompile(outdir: Option[File], sources: List[File], kind: String, log: File): CompilationOutcome = + newCompiler.compile(outdir, sources, kind, log) } diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/Worker.scala index 00ee8ba857..c69609ec58 100644 --- a/src/partest/scala/tools/partest/nest/Worker.scala +++ b/src/partest/scala/tools/partest/nest/Worker.scala @@ -269,7 +269,7 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor outDir.jfile } - private def javac(outDir: File, files: List[File], output: File): Boolean = { + private def javac(outDir: File, files: List[File], output: File): CompilationOutcome = { // compile using command-line javac compiler val args = Seq( javacCmd, @@ -279,8 +279,8 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor join(outDir.toString, CLASSPATH) ) ++ files.map("" + _) - try runCommand(args, output) - catch exHandler(output, "javac command failed:\n" + args.map(" " + _ + "\n").mkString + "\n") + try if (runCommand(args, output)) CompileSuccess else CompileFailed + catch exHandler(output, "javac command failed:\n" + args.map(" " + _ + "\n").mkString + "\n", CompilerCrashed) } /** Runs command redirecting standard out and @@ -363,7 +363,7 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor else file2String(logFile) if (diff != "" && fileManager.updateCheck) { - NestUI.verbose("output differs from log file: updating checkfile\n") + NestUI.verbose("Updating checkfile " + checkFile.jfile) val toWrite = if (checkFile.exists) checkFile else getCheckFilePath(dir, "") toWrite writeAll file2String(logFile) "" @@ -388,10 +388,8 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor false } - private def exHandler(logFile: File): PartialFunction[Throwable, Boolean] = - exHandler(logFile, "") - private def exHandler(logFile: File, msg: String): PartialFunction[Throwable, Boolean] = { - case e: Exception => logStackTrace(logFile, e, msg) + private def exHandler[T](logFile: File, msg: String, value: T): PartialFunction[Throwable, T] = { + case e: Exception => logStackTrace(logFile, e, msg) ; value } /** Runs a list of tests. @@ -464,39 +462,38 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor } else script(logFile, outDir) } - catch exHandler(logFile) + catch exHandler(logFile, "", false) LogContext(logFile, swr, wr) } } - def compileFilesIn(dir: File, logFile: File, outDir: File): Boolean = { + def groupedFiles(dir: File): List[List[File]] = { val testFiles = dir.listFiles.toList filter isJavaOrScala def isInGroup(f: File, num: Int) = SFile(f).stripExtension endsWith ("_" + num) val groups = (0 to 9).toList map (num => testFiles filter (f => isInGroup(f, num))) val noGroupSuffix = testFiles filterNot (groups.flatten contains) - def compileGroup(g: List[File]): Boolean = { + noGroupSuffix :: groups filterNot (_.isEmpty) + } + + def compileFilesIn(dir: File, logFile: File, outDir: File): CompilationOutcome = { + def compileGroup(g: List[File]): CompilationOutcome = { val (scalaFiles, javaFiles) = g partition isScala val allFiles = javaFiles ++ scalaFiles - // scala+java, then java, then scala - (scalaFiles.isEmpty || compileMgr.shouldCompile(outDir, allFiles, kind, logFile) || fail(g)) && { - (javaFiles.isEmpty || javac(outDir, javaFiles, logFile)) && { - (scalaFiles.isEmpty || compileMgr.shouldCompile(outDir, scalaFiles, kind, logFile) || fail(scalaFiles)) - } + List(1, 2, 3).foldLeft(CompileSuccess: CompilationOutcome) { + case (CompileSuccess, 1) if scalaFiles.nonEmpty => compileMgr.attemptCompile(Some(outDir), allFiles, kind, logFile) // java + scala + case (CompileSuccess, 2) if javaFiles.nonEmpty => javac(outDir, javaFiles, logFile) // java + case (CompileSuccess, 3) if scalaFiles.nonEmpty => compileMgr.attemptCompile(Some(outDir), scalaFiles, kind, logFile) // scala + case (outcome, _) => outcome } } - - (noGroupSuffix.isEmpty || compileGroup(noGroupSuffix)) && (groups forall compileGroup) - } - - def failCompileFilesIn(dir: File, logFile: File, outDir: File): Boolean = { - val testFiles = dir.listFiles.toList - val sourceFiles = testFiles filter isJavaOrScala - - sourceFiles.isEmpty || compileMgr.shouldFailCompile(outDir, sourceFiles, kind, logFile) || fail(testFiles filter isScala) + groupedFiles(dir).foldLeft(CompileSuccess: CompilationOutcome) { + case (CompileSuccess, files) => compileGroup(files) + case (outcome, _) => outcome + } } def runTestCommon(file: File, expectFailure: Boolean)( @@ -504,15 +501,14 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor onFail: (File, File) => Unit = (_, _) => ()): LogContext = { runInContext(file, (logFile: File, outDir: File) => { - val result = - if (file.isDirectory) { - if (expectFailure) failCompileFilesIn(file, logFile, outDir) - else compileFilesIn(file, logFile, outDir) - } - else { - if (expectFailure) compileMgr.shouldFailCompile(List(file), kind, logFile) - else compileMgr.shouldCompile(List(file), kind, logFile) - } + val outcome = ( + if (file.isDirectory) compileFilesIn(file, logFile, outDir) + else compileMgr.attemptCompile(None, List(file), kind, logFile) + ) + val result = ( + if (expectFailure) outcome.isNegative + else outcome.isPositive + ) if (result) onSuccess(logFile, outDir) else { onFail(logFile, outDir) ; false } @@ -551,7 +547,7 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor ) try runCommand(cmd, output) - catch exHandler(output, "ant command '" + cmd + "' failed:\n") + catch exHandler(output, "ant command '" + cmd + "' failed:\n", false) } def runAntTest(file: File): LogContext = { @@ -884,7 +880,7 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor ) // 4. compile testFile - val ok = compileMgr.shouldCompile(List(testFile), kind, logFile) + val ok = compileMgr.attemptCompile(None, List(testFile), kind, logFile) eq CompileSuccess NestUI.verbose("compilation of " + testFile + (if (ok) "succeeded" else "failed")) if (ok) { execTest(outDir, logFile) && { @@ -911,7 +907,8 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor else { val resFile = results.head // 2. Compile source file - if (!compileMgr.shouldCompile(outDir, sources, kind, logFile)) { + + if (!compileMgr.attemptCompile(Some(outDir), sources, kind, logFile).isPositive) { NestUI.normal("compilerMgr failed to compile %s to %s".format(sources mkString ", ", outDir)) false } -- cgit v1.2.3