From 65062d459f24a79a0cf0483a393d54590992de4b Mon Sep 17 00:00:00 2001 From: Philipp Haller Date: Wed, 27 Aug 2008 16:49:39 +0000 Subject: Added support for multi-file tests. --- .../scala/tools/partest/nest/CompileManager.scala | 66 +++++++++------ .../tools/partest/nest/ConsoleFileManager.scala | 24 ++++-- .../scala/tools/partest/nest/ConsoleRunner.scala | 4 +- src/partest/scala/tools/partest/nest/NestUI.scala | 34 +++++--- .../scala/tools/partest/nest/TestFile.scala | 27 +++--- src/partest/scala/tools/partest/nest/Worker.scala | 97 ++++++++++++++++------ 6 files changed, 168 insertions(+), 84 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 7c96bb0ea0..bcd0d3961b 100644 --- a/src/partest/scala/tools/partest/nest/CompileManager.scala +++ b/src/partest/scala/tools/partest/nest/CompileManager.scala @@ -21,7 +21,7 @@ class ExtConsoleReporter(override val settings: Settings, reader: BufferedReader abstract class SimpleCompiler { def compile(file: File, kind: String): Boolean - def compile(file: File, kind: String, log: File): Boolean + def compile(out: Option[File], files: List[File], kind: String, log: File): Boolean } class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { @@ -48,7 +48,7 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { Console.in, new PrintWriter(new StringWriter)) - def compile(file: File, kind: String, log: File): Boolean = { + def compile(out: Option[File], files: List[File], kind: String, log: File): Boolean = { val testSettings = newSettings val logWriter = new FileWriter(log) val args = List.fromArray(fileManager.SCALAC_OPTS.split("\\s")) @@ -57,17 +57,26 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { val testRep: ExtConsoleReporter = global.reporter.asInstanceOf[ExtConsoleReporter] val test: TestFile = kind match { - case "pos" => PosTestFile(file, fileManager) - case "neg" => NegTestFile(file, fileManager) - case "run" => RunTestFile(file, fileManager) - case "jvm" => JvmTestFile(file, fileManager) - case "jvm5" => Jvm5TestFile(file, fileManager) - case "shootout" => ShootoutTestFile(file, fileManager) + case "pos" => PosTestFile(files(0), fileManager, out.isEmpty) + case "neg" => NegTestFile(files(0), fileManager, out.isEmpty) + case "run" => RunTestFile(files(0), fileManager, out.isEmpty) + case "jvm" => JvmTestFile(files(0), fileManager, out.isEmpty) + case "jvm5" => Jvm5TestFile(files(0), fileManager, out.isEmpty) + case "shootout" => ShootoutTestFile(files(0), fileManager, out.isEmpty) } test.defineSettings(testSettings) + out match { + case Some(outDir) => + testSettings.outdir.value = outDir.getAbsolutePath + testSettings.classpath.value = testSettings.classpath.value+ + File.pathSeparator+outDir.getAbsolutePath + case None => + // do nothing + } - val toCompile = List(file.getPath) + val toCompile = files.map(_.getPath) try { + NestUI.verbose("compiling "+toCompile) (new global.Run) compile toCompile testRep.printSummary testRep.writer.flush @@ -90,12 +99,12 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { val global = newGlobal(command.settings, testRep) val test: TestFile = kind match { - case "pos" => PosTestFile(file, fileManager) - case "neg" => NegTestFile(file, fileManager) - case "run" => RunTestFile(file, fileManager) - case "jvm" => JvmTestFile(file, fileManager) - case "jvm5" => Jvm5TestFile(file, fileManager) - case "shootout" => ShootoutTestFile(file, fileManager) + case "pos" => PosTestFile(file, fileManager, true) + case "neg" => NegTestFile(file, fileManager, true) + case "run" => RunTestFile(file, fileManager, true) + case "jvm" => JvmTestFile(file, fileManager, true) + case "jvm5" => Jvm5TestFile(file, fileManager, true) + case "shootout" => ShootoutTestFile(file, fileManager, true) } test.defineSettings(testSettings) @@ -150,8 +159,8 @@ class ReflectiveCompiler(val fileManager: ConsoleFileManager) extends SimpleComp * This exception is handled in the shouldCompile and shouldFailCompile * methods of class CompileManager. */ - def compile(file: File, kind: String, log: File): Boolean = { - val fileArgs: Array[AnyRef] = Array(file, kind, log) + def compile(out: Option[File], files: List[File], kind: String, log: File): Boolean = { + val fileArgs: Array[AnyRef] = Array(out, files, kind, log) val res = sepCompileMethod2.invoke(sepCompiler, fileArgs: _*).asInstanceOf[java.lang.Boolean] res.booleanValue() } @@ -172,7 +181,7 @@ class CompileManager(val fileManager: FileManager) { val delay = fileManager.timeout.toLong - def withTimeout(file: File)(thunk: => Boolean): Boolean = { + def withTimeout(files: List[File])(thunk: => Boolean): Boolean = { createSeparateCompiler() val parent = self @@ -193,7 +202,7 @@ class CompileManager(val fileManager: FileManager) { case (From, result: Boolean) => result } case t: Throwable => - NestUI.verbose("while invoking compiler ("+file+"):") + NestUI.verbose("while invoking compiler ("+files+"):") NestUI.verbose("caught "+t) t.printStackTrace if (t.getCause != null) @@ -205,9 +214,16 @@ class CompileManager(val fileManager: FileManager) { /* This method returns true iff compilation succeeds. */ - def shouldCompile(file: File, kind: String, log: File): Boolean = - withTimeout(file) { - compiler.compile(file, kind, log) + 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) } /* This method returns true iff compilation fails @@ -215,8 +231,8 @@ class CompileManager(val fileManager: FileManager) { * * If the compiler crashes, this method returns false. */ - def shouldFailCompile(file: File, kind: String, log: File): Boolean = - withTimeout(file) { - !compiler.compile(file, kind, log) + def shouldFailCompile(files: List[File], kind: String, log: File): Boolean = + withTimeout(files) { + !compiler.compile(None, files, kind, log) } } diff --git a/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala b/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala index cb0f87cb88..c8ac780f79 100644 --- a/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala +++ b/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala @@ -219,19 +219,27 @@ else var testFiles: List[File] = List() - def getFiles(kind: String, doCheck: Boolean, ending: String): List[File] = { - val filter = new FilenameFilter { - def accept(dir: File, name: String): Boolean = name endsWith ending - } + def getFiles(kind: String, doCheck: Boolean, filter: Option[(String, Boolean)]): List[File] = { val dir = new File(srcDir, kind) NestUI.verbose("look in "+dir+" for tests") if (dir.isDirectory) { if (!testFiles.isEmpty) { val dirpath = dir.getAbsolutePath testFiles filter { _.getParentFile.getAbsolutePath == dirpath } - } else if (doCheck) - dir.listFiles(filter).toList - else // skip + } else if (doCheck) filter match { + case Some((ending, enableDirs)) => + val filter = new FilenameFilter { + def accept(dir: File, name: String) = + name.endsWith(ending) || + (enableDirs && (name != ".svn") && (new File(dir, name)).isDirectory) + } + dir.listFiles(filter).toList + case None => + val filter = new FilenameFilter { + def accept(dir: File, name: String) = name != ".svn" + } + dir.listFiles(filter).toList + } else // skip Nil } else { NestUI.failure("Directory \"" + dir.getPath + "\" not found") @@ -240,6 +248,6 @@ else } def getFiles(kind: String, doCheck: Boolean): List[File] = - getFiles(kind, doCheck, ".scala") + getFiles(kind, doCheck, Some((".scala", false))) } diff --git a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala index 1ffb992661..af89778a7f 100644 --- a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala +++ b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala @@ -179,7 +179,9 @@ class ConsoleRunner extends DirectRunner with RunnerUtils { def runTests(kind: String, check: Boolean, msg: String): (Int, Int) = { if (check) { val kindFiles = if (kind == "res") //TODO: is there a nicer way? - fileManager.getFiles(kind, check, ".res") + fileManager.getFiles(kind, check, Some((".res", false))) + else if (kind == "pos") + fileManager.getFiles(kind, check, Some((".scala", true))) else fileManager.getFiles(kind, check) if (!kindFiles.isEmpty) { diff --git a/src/partest/scala/tools/partest/nest/NestUI.scala b/src/partest/scala/tools/partest/nest/NestUI.scala index e9c0aeebd6..85147ae880 100644 --- a/src/partest/scala/tools/partest/nest/NestUI.scala +++ b/src/partest/scala/tools/partest/nest/NestUI.scala @@ -66,19 +66,27 @@ object NestUI { println(" : list of files ending in '.scala'") println(" : a file not ending in '.scala'") println(" :") - println(" --all run all tests") - println(" --pos next files test a compilation success") - println(" --neg next files test a compilation failure") - println(" --run next files test the interpreter and all backends") - println(" --jvm next files test the JVM backend") - println(" --jvm5 next files test the JVM backend") - println(" --res next files test the resident compiler") - println(" --shootout next files are shootout tests") - println(" --script next files test the script runner") - println(" --pack pick compiler / library in build/pack, and run all tests") - println(" --four pick compiler / library in build/four-pack, and run all tests") - println(" --verbose display progress information") - //println(" --version display version information") + println + println(" Test categories:") + println(" --all run all tests") + println(" --pos run compilation tests (success)") + println(" --neg run compilation tests (failure)") + println(" --run run interpreter and backend tests") + println(" --jvm run JVM backend tests") + println(" --jvm5 run JVM backend tests (-target:jvm-1.5)") + println(" --res run resident compiler tests") + println(" --script run script runner tests") + println(" --shootout run shootout tests") + println + println(" Other options:") + println(" --pack pick compiler/library in build/pack, and run all tests") + println(" --four pick compiler/library in build/four-pack, and run all tests") + println(" --show-log show log") + println(" --show-diff show diff between log and check file") + println(" --failed run only those tests that failed during the last run") + println(" --verbose show progress information") + println(" --buildpath set (relative) path to build jars") + println(" --classpath set (absolute) path to build classes") println println("version 0.9.1") println("maintained by Philipp Haller (EPFL)") diff --git a/src/partest/scala/tools/partest/nest/TestFile.scala b/src/partest/scala/tools/partest/nest/TestFile.scala index 10b12aae29..6cc574c1b6 100644 --- a/src/partest/scala/tools/partest/nest/TestFile.scala +++ b/src/partest/scala/tools/partest/nest/TestFile.scala @@ -10,7 +10,7 @@ package scala.tools.partest.nest import java.io.{File, BufferedReader, FileReader} import scala.tools.nsc.Settings -class TestFile(kind: String, val file: File, val fileManager: FileManager) { +class TestFile(kind: String, val file: File, val fileManager: FileManager, createOutDir: Boolean) { val dir = file.getParentFile val dirpath = dir.getAbsolutePath val fileBase: String = basename(file.getName) @@ -18,12 +18,13 @@ class TestFile(kind: String, val file: File, val fileManager: FileManager) { // @mutates settings protected def baseSettings(settings: Settings) { settings.classpath.value = dirpath - settings.outdir.value = { - val outDir = new File(dir, fileBase + "-" + kind + ".obj") - if (!outDir.exists) - outDir.mkdir() - outDir.toString - } + if (createOutDir) + settings.outdir.value = { + val outDir = new File(dir, fileBase + "-" + kind + ".obj") + if (!outDir.exists) + outDir.mkdir() + outDir.toString + } // add additional flags found in 'testname.flags' val flagsFile = new File(dir, fileBase + ".flags") @@ -47,7 +48,7 @@ class TestFile(kind: String, val file: File, val fileManager: FileManager) { override def toString(): String = kind+" "+file } -case class PosTestFile(override val file: File, override val fileManager: FileManager) extends TestFile("pos", file, fileManager) { +case class PosTestFile(override val file: File, override val fileManager: FileManager, createOutDir: Boolean) extends TestFile("pos", file, fileManager, createOutDir) { override def defineSettings(settings: Settings) { baseSettings(settings) settings.classpath.value = fileManager.CLASSPATH @@ -55,7 +56,7 @@ case class PosTestFile(override val file: File, override val fileManager: FileMa } } -case class NegTestFile(override val file: File, override val fileManager: FileManager) extends TestFile("neg", file, fileManager) { +case class NegTestFile(override val file: File, override val fileManager: FileManager, createOutDir: Boolean) extends TestFile("neg", file, fileManager, createOutDir) { override def defineSettings(settings: Settings) { baseSettings(settings) settings.classpath.value = fileManager.CLASSPATH @@ -63,14 +64,14 @@ case class NegTestFile(override val file: File, override val fileManager: FileMa } } -case class RunTestFile(override val file: File, override val fileManager: FileManager) extends TestFile("run", file, fileManager) { +case class RunTestFile(override val file: File, override val fileManager: FileManager, createOutDir: Boolean) extends TestFile("run", file, fileManager, createOutDir) { override def defineSettings(settings: Settings) { baseSettings(settings) settings.classpath.value = fileManager.CLASSPATH } } -case class JvmTestFile(override val file: File, override val fileManager: FileManager) extends TestFile("jvm", file, fileManager) { +case class JvmTestFile(override val file: File, override val fileManager: FileManager, createOutDir: Boolean) extends TestFile("jvm", file, fileManager, createOutDir) { override def defineSettings(settings: Settings) { baseSettings(settings) settings.classpath.value = fileManager.CLASSPATH @@ -78,7 +79,7 @@ case class JvmTestFile(override val file: File, override val fileManager: FileMa } } -case class Jvm5TestFile(override val file: File, override val fileManager: FileManager) extends TestFile("jvm5", file, fileManager) { +case class Jvm5TestFile(override val file: File, override val fileManager: FileManager, createOutDir: Boolean) extends TestFile("jvm5", file, fileManager, createOutDir) { override def defineSettings(settings: Settings) { baseSettings(settings) settings.classpath.value = fileManager.CLASSPATH @@ -87,7 +88,7 @@ case class Jvm5TestFile(override val file: File, override val fileManager: FileM } } -case class ShootoutTestFile(override val file: File, override val fileManager: FileManager) extends TestFile("shootout", file, fileManager) { +case class ShootoutTestFile(override val file: File, override val fileManager: FileManager, createOutDir: Boolean) extends TestFile("shootout", file, fileManager, createOutDir) { override def defineSettings(settings: Settings) { baseSettings(settings) settings.classpath.value = fileManager.CLASSPATH diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/Worker.scala index 86df4abbde..c5138a593c 100644 --- a/src/partest/scala/tools/partest/nest/Worker.scala +++ b/src/partest/scala/tools/partest/nest/Worker.scala @@ -96,6 +96,8 @@ class Worker(val fileManager: FileManager) extends Actor { def createOutputDir(dir: File, fileBase: String, kind: String): File = { val outDir = new File(dir, fileBase + "-" + kind + ".obj") + if (!outDir.exists) + outDir.mkdir() createdOutputDirs = outDir :: createdOutputDirs outDir } @@ -163,6 +165,41 @@ class Worker(val fileManager: FileManager) extends Actor { } } + def javac(outDir: File, files: List[File], output: File) { + // compile using command-line javac compiler + // (assumed to be in PATH) + + val cmd = "javac"+ + " -d "+outDir.getAbsolutePath+ + " -classpath "+outDir+File.pathSeparator+CLASSPATH+ + " -Djava.library.path="+output.getParentFile.getAbsolutePath+ + " -Dscalatest.output="+outDir.getAbsolutePath+ + " -Dscalatest.lib="+LATEST_LIB+ + " -Dscalatest.cwd="+outDir.getParent+ + " "+files.mkString(" ") + + runCommand(cmd, output) + } + + /** Runs command redirecting standard out and + * error out to output file. + */ + def runCommand(command: String, output: File) { + val proc = Runtime.getRuntime.exec(command) + val in = proc.getInputStream + val err = proc.getErrorStream + val writer = new PrintWriter(new FileWriter(output), true) + val inApp = new StreamAppender(new BufferedReader(new InputStreamReader(in)), + writer) + val errApp = new StreamAppender(new BufferedReader(new InputStreamReader(err)), + writer) + val async = new Thread(errApp) + async.start() + inApp.run() + async.join() + writer.close() + } + def execTest(outDir: File, logFile: File, fileBase: String) { // check whether there is a ".javaopts" file val argsFile = new File(logFile.getParentFile, fileBase+".javaopts") @@ -191,19 +228,7 @@ class Worker(val fileManager: FileManager) extends Actor { " Test jvm" NestUI.verbose(cmd) - val proc = Runtime.getRuntime.exec(cmd) - val in = proc.getInputStream - val err = proc.getErrorStream - val writer = new PrintWriter(new FileWriter(logFile), true) - val inApp = new StreamAppender(new BufferedReader(new InputStreamReader(in)), - writer) - val errApp = new StreamAppender(new BufferedReader(new InputStreamReader(err)), - writer) - val async = new Thread(errApp) - async.start() - inApp.run() - async.join() - writer.close() + runCommand(cmd, logFile) if (fileManager.showLog) { // produce log as string in `log` @@ -269,6 +294,12 @@ class Worker(val fileManager: FileManager) extends Actor { var diff = "" var log = "" + /** 1. Creates log file and output directory. + * 2. Runs script 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) { // when option "--failed" is provided // execute test only if log file is present @@ -286,6 +317,7 @@ class Worker(val fileManager: FileManager) extends Actor { NestUI.verbose(this+" running test "+fileBase) val dir = file.getParentFile val outDir = createOutputDir(dir, fileBase, kind) + NestUI.verbose("output directory: "+outDir) // run test-specific code try { @@ -317,7 +349,7 @@ class Worker(val fileManager: FileManager) extends Actor { def runJvmTest(file: File, kind: String) { runInContext(file, kind, (logFile: File, outDir: File) => { - if (!compileMgr.shouldCompile(file, kind, logFile)) { + if (!compileMgr.shouldCompile(List(file), kind, logFile)) { NestUI.verbose("compilation of "+file+" failed\n") succeeded = false } else { // run test @@ -343,19 +375,36 @@ class Worker(val fileManager: FileManager) extends Actor { } kind match { - case "pos" => - for (file <- files) { - runInContext(file, kind, (logFile: File, outDir: File) => { - if (!compileMgr.shouldCompile(file, kind, logFile)) { - NestUI.verbose("compilation of "+file+" failed\n") + case "pos" => for (file <- files) + runInContext(file, kind, (logFile: File, outDir: File) => { + if (file.isDirectory) { + val testFiles = file.listFiles.toList + // 1. compile all '.java' files using javac + val javaFiles = testFiles.filter(_.getName.endsWith(".java")) + if (!javaFiles.isEmpty) + javac(outDir, javaFiles, logFile) + // 2. compile all '.scala' files together + val scalaFiles = testFiles.filter(_.getName.endsWith(".scala")) + if (!compileMgr.shouldCompile(outDir, scalaFiles, kind, logFile)) { + NestUI.verbose("compilation of "+scalaFiles+" failed\n") succeeded = false } - }) - } + // 3. compile each '.scala' file separately + scalaFiles foreach { scalaFile => + if (!compileMgr.shouldCompile(outDir, List(scalaFile), kind, logFile)) { + NestUI.verbose("compilation of "+scalaFile+" failed\n") + succeeded = false + } + } + } else if (!compileMgr.shouldCompile(List(file), kind, logFile)) { + NestUI.verbose("compilation of "+file+" failed\n") + succeeded = false + } + }) case "neg" => for (file <- files) { runInContext(file, kind, (logFile: File, outDir: File) => { - if (!compileMgr.shouldFailCompile(file, kind, logFile)) { + if (!compileMgr.shouldFailCompile(List(file), kind, logFile)) { succeeded = false } else { // compare log file to check file val fileBase = basename(file.getName) @@ -493,7 +542,7 @@ class Worker(val fileManager: FileManager) extends Actor { System.setOut(oldStdOut) System.setErr(oldStdErr) - val tempLogFile = new File(dir, ".temp.log") + val tempLogFile = new File(dir, fileBase+".temp.log") val logFileReader = new BufferedReader(new FileReader(logFile)) val tempLogFilePrinter = new PrintWriter(new FileWriter(tempLogFile)) val appender = @@ -588,7 +637,7 @@ class Worker(val fileManager: FileManager) extends Actor { try { // *catch-all* // 4. compile testFile - if (!compileMgr.shouldCompile(testFile, kind, logFile)) { + if (!compileMgr.shouldCompile(List(testFile), kind, logFile)) { NestUI.verbose("compilation of "+file+" failed\n") succeeded = false } else { -- cgit v1.2.3