Skip to content
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.
You can’t perform that action at this time.