Permalink
Browse files

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.
  • Loading branch information...
paulp committed May 5, 2012
1 parent 015cb86 commit ecbf89552666ebba974188ef3c98b5f4855d0cea
@@ -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,88 +133,21 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler {
catch {
case FatalError(msg) =>
testRep.error(null, "fatal error: " + msg)
+ return CompilerCrashed
}
testRep.printSummary()
testRep.writer.close()
}
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)
}
@@ -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,55 +462,53 @@ 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)(
onSuccess: (File, File) => Boolean,
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
}

0 comments on commit ecbf895

Please sign in to comment.