Skip to content

Commit

Permalink
Reluctant surgery on partest.
Browse files Browse the repository at this point in the history
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 ecbf895
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 112 deletions.
98 changes: 23 additions & 75 deletions src/partest/scala/tools/partest/nest/CompileManager.scala
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ import io.Path
import java.io.{ File, BufferedReader, PrintWriter, FileReader, Writer, FileWriter, StringWriter } import java.io.{ File, BufferedReader, PrintWriter, FileReader, Writer, FileWriter, StringWriter }
import File.pathSeparator 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) { class ExtConsoleReporter(settings: Settings, val writer: PrintWriter) extends ConsoleReporter(settings, Console.in, writer) {
shortname = true shortname = true
} }
Expand All @@ -32,7 +47,7 @@ class TestSettings(cp: String, error: String => Unit) extends Settings(error) {
} }


abstract class SimpleCompiler { 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 { class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler {
Expand Down Expand Up @@ -68,7 +83,7 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler {
(opt2 ::: pluginOption) mkString " " (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 { val testSettings = out match {
case Some(f) => newSettings(f.getAbsolutePath) case Some(f) => newSettings(f.getAbsolutePath)
case _ => newSettings() case _ => newSettings()
Expand Down Expand Up @@ -118,88 +133,21 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler {
catch { catch {
case FatalError(msg) => case FatalError(msg) =>
testRep.error(null, "fatal error: " + msg) testRep.error(null, "fatal error: " + msg)
return CompilerCrashed
} }


testRep.printSummary() testRep.printSummary()
testRep.writer.close() testRep.writer.close()
} }
finally logWriter.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) { class CompileManager(val fileManager: FileManager) {
var compiler: SimpleCompiler = new DirectCompiler(fileManager) private def newCompiler = new DirectCompiler(fileManager)

def attemptCompile(outdir: Option[File], sources: List[File], kind: String, log: File): CompilationOutcome =
var numSeparateCompilers = 1 newCompiler.compile(outdir, sources, kind, log)
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)
}
} }
71 changes: 34 additions & 37 deletions src/partest/scala/tools/partest/nest/Worker.scala
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor
outDir.jfile 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 // compile using command-line javac compiler
val args = Seq( val args = Seq(
javacCmd, javacCmd,
Expand All @@ -279,8 +279,8 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor
join(outDir.toString, CLASSPATH) join(outDir.toString, CLASSPATH)
) ++ files.map("" + _) ) ++ files.map("" + _)


try runCommand(args, output) try if (runCommand(args, output)) CompileSuccess else CompileFailed
catch exHandler(output, "javac command failed:\n" + args.map(" " + _ + "\n").mkString + "\n") catch exHandler(output, "javac command failed:\n" + args.map(" " + _ + "\n").mkString + "\n", CompilerCrashed)
} }


/** Runs command redirecting standard out and /** Runs command redirecting standard out and
Expand Down Expand Up @@ -363,7 +363,7 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor
else file2String(logFile) else file2String(logFile)


if (diff != "" && fileManager.updateCheck) { 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, "") val toWrite = if (checkFile.exists) checkFile else getCheckFilePath(dir, "")
toWrite writeAll file2String(logFile) toWrite writeAll file2String(logFile)
"" ""
Expand All @@ -388,10 +388,8 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor
false false
} }


private def exHandler(logFile: File): PartialFunction[Throwable, Boolean] = private def exHandler[T](logFile: File, msg: String, value: T): PartialFunction[Throwable, T] = {
exHandler(logFile, "") case e: Exception => logStackTrace(logFile, e, msg) ; value
private def exHandler(logFile: File, msg: String): PartialFunction[Throwable, Boolean] = {
case e: Exception => logStackTrace(logFile, e, msg)
} }


/** Runs a list of tests. /** Runs a list of tests.
Expand Down Expand Up @@ -464,55 +462,53 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor
} }
else script(logFile, outDir) else script(logFile, outDir)
} }
catch exHandler(logFile) catch exHandler(logFile, "", false)


LogContext(logFile, swr, wr) 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 val testFiles = dir.listFiles.toList filter isJavaOrScala


def isInGroup(f: File, num: Int) = SFile(f).stripExtension endsWith ("_" + num) 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 groups = (0 to 9).toList map (num => testFiles filter (f => isInGroup(f, num)))
val noGroupSuffix = testFiles filterNot (groups.flatten contains) 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 (scalaFiles, javaFiles) = g partition isScala
val allFiles = javaFiles ++ scalaFiles val allFiles = javaFiles ++ scalaFiles


// scala+java, then java, then scala List(1, 2, 3).foldLeft(CompileSuccess: CompilationOutcome) {
(scalaFiles.isEmpty || compileMgr.shouldCompile(outDir, allFiles, kind, logFile) || fail(g)) && { case (CompileSuccess, 1) if scalaFiles.nonEmpty => compileMgr.attemptCompile(Some(outDir), allFiles, kind, logFile) // java + scala
(javaFiles.isEmpty || javac(outDir, javaFiles, logFile)) && { case (CompileSuccess, 2) if javaFiles.nonEmpty => javac(outDir, javaFiles, logFile) // java
(scalaFiles.isEmpty || compileMgr.shouldCompile(outDir, scalaFiles, kind, logFile) || fail(scalaFiles)) case (CompileSuccess, 3) if scalaFiles.nonEmpty => compileMgr.attemptCompile(Some(outDir), scalaFiles, kind, logFile) // scala
} case (outcome, _) => outcome
} }
} }

groupedFiles(dir).foldLeft(CompileSuccess: CompilationOutcome) {
(noGroupSuffix.isEmpty || compileGroup(noGroupSuffix)) && (groups forall compileGroup) case (CompileSuccess, files) => compileGroup(files)
} case (outcome, _) => outcome

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


def runTestCommon(file: File, expectFailure: Boolean)( def runTestCommon(file: File, expectFailure: Boolean)(
onSuccess: (File, File) => Boolean, onSuccess: (File, File) => Boolean,
onFail: (File, File) => Unit = (_, _) => ()): LogContext = onFail: (File, File) => Unit = (_, _) => ()): LogContext =
{ {
runInContext(file, (logFile: File, outDir: File) => { runInContext(file, (logFile: File, outDir: File) => {
val result = val outcome = (
if (file.isDirectory) { if (file.isDirectory) compileFilesIn(file, logFile, outDir)
if (expectFailure) failCompileFilesIn(file, logFile, outDir) else compileMgr.attemptCompile(None, List(file), kind, logFile)
else compileFilesIn(file, logFile, outDir) )
} val result = (
else { if (expectFailure) outcome.isNegative
if (expectFailure) compileMgr.shouldFailCompile(List(file), kind, logFile) else outcome.isPositive
else compileMgr.shouldCompile(List(file), kind, logFile) )
}


if (result) onSuccess(logFile, outDir) if (result) onSuccess(logFile, outDir)
else { onFail(logFile, outDir) ; false } else { onFail(logFile, outDir) ; false }
Expand Down Expand Up @@ -551,7 +547,7 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor
) )


try runCommand(cmd, output) 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 = { def runAntTest(file: File): LogContext = {
Expand Down Expand Up @@ -884,7 +880,7 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor
) )


// 4. compile testFile // 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")) NestUI.verbose("compilation of " + testFile + (if (ok) "succeeded" else "failed"))
if (ok) { if (ok) {
execTest(outDir, logFile) && { execTest(outDir, logFile) && {
Expand All @@ -911,7 +907,8 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor
else { else {
val resFile = results.head val resFile = results.head
// 2. Compile source file // 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)) NestUI.normal("compilerMgr failed to compile %s to %s".format(sources mkString ", ", outDir))
false false
} }
Expand Down

0 comments on commit ecbf895

Please sign in to comment.