diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/Global.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala | 10 | ||||
-rw-r--r-- | src/partest/README | 1 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/PartestTask.scala | 23 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/ConsoleRunner.scala | 10 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/FileManager.scala | 43 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/NestUI.scala | 17 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/TestFile.scala | 8 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/Worker.scala | 277 |
9 files changed, 286 insertions, 105 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 3cde4698ff..08671526e6 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -130,7 +130,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable import util.NoPosition def error(msg: String) = reporter.error(NoPosition, msg) def warning(msg: String) = reporter.warning(NoPosition, msg) - def inform(msg: String) = Console.err.println(msg) + def inform(msg: String) = reporter.info(NoPosition, msg, true) def inform[T](msg: String, value: T): T = { inform(msg+value); value } //reporter.info(null, msg, true) diff --git a/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala b/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala index 5e1729a515..7268c6c8c7 100644 --- a/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala +++ b/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala @@ -102,6 +102,7 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana private def update(files: Set[AbstractFile]) = { val coll: mutable.Map[AbstractFile, immutable.Set[AbstractFile]] = mutable.HashMap[AbstractFile, immutable.Set[AbstractFile]]() + compiler.reporter.reset // See if we really have coresponding symbols, not just those // which share the name @@ -117,7 +118,6 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana run.compileFiles(files.toList) if (compiler.reporter.hasErrors) { - compiler.reporter.reset return } @@ -158,7 +158,7 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana } } } - println("Changes: " + changesOf) + compiler.inform("Changes: " + changesOf) updateDefinitions(files) val invalid = invalidated(files, changesOf, additionalDefs) update0(checkCycles(invalid, files, coll)) @@ -206,7 +206,7 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana compiler.dependencyAnalysis.dependencies.dependentFiles(1, files) def invalidate(file: AbstractFile, reason: String, change: Change) = { - println("invalidate " + file + " because " + reason + " [" + change + "]") + compiler.inform("invalidate " + file + " because " + reason + " [" + change + "]") buf += file directDeps -= file for (syms <- definitions(file)) // fixes #2557 @@ -217,7 +217,7 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana for ((oldSym, changes) <- changesOf; change <- changes) { def checkParents(cls: Symbol, file: AbstractFile) { val parentChange = cls.info.parents.exists(_.typeSymbol.fullNameString == oldSym.fullNameString) - // println("checkParents " + cls + " oldSym: " + oldSym + " parentChange: " + parentChange + " " + cls.info.parents) + // compiler.inform("checkParents " + cls + " oldSym: " + oldSym + " parentChange: " + parentChange + " " + cls.info.parents) change match { case Changed(Class(_)) if parentChange => invalidate(file, "parents have changed", change) @@ -249,7 +249,7 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana } def checkReferences(file: AbstractFile) { - // println(file + ":" + references(file)) + // compiler.inform(file + ":" + references(file)) val refs = references(file) if (refs.isEmpty) invalidate(file, "it is a direct dependency and we don't yet have finer-grained dependency information", change) diff --git a/src/partest/README b/src/partest/README index 430a2987f8..27159ca078 100644 --- a/src/partest/README +++ b/src/partest/README @@ -24,6 +24,7 @@ Other arguments: * --run next files test the interpreter and all backends * --jvm next files test the JVM backend * --res next files test the resident compiler + * --buildmanager next files test the build manager * --shootout next files are shootout tests * --script next files test the script runner * ''-Dscalatest.scalac_opts=...'' -> add compiler options diff --git a/src/partest/scala/tools/partest/PartestTask.scala b/src/partest/scala/tools/partest/PartestTask.scala index 59781b0aa2..d97adfa166 100644 --- a/src/partest/scala/tools/partest/PartestTask.scala +++ b/src/partest/scala/tools/partest/PartestTask.scala @@ -40,6 +40,10 @@ class PartestTask extends Task { residentFiles = Some(input) } + def addConfiguredBuildManagerTests(input: FileSet) { + buildManagerFiles = Some(input) + } + def addConfiguredScriptTests(input: FileSet) { scriptFiles = Some(input) } @@ -111,6 +115,7 @@ class PartestTask extends Task { private var runFiles: Option[FileSet] = None private var jvmFiles: Option[FileSet] = None private var residentFiles: Option[FileSet] = None + private var buildManagerFiles: Option[FileSet] = None private var scriptFiles: Option[FileSet] = None private var shootoutFiles: Option[FileSet] = None private var scalapFiles: Option[FileSet] = None @@ -144,14 +149,15 @@ class PartestTask extends Task { else Array() - private def getPosFiles = getFilesAndDirs(posFiles) - private def getNegFiles = getFilesAndDirs(negFiles) - private def getRunFiles = getFiles(runFiles) - private def getJvmFiles = getFilesAndDirs(jvmFiles) - private def getResidentFiles = getFiles(residentFiles) - private def getScriptFiles = getFiles(scriptFiles) - private def getShootoutFiles = getFiles(shootoutFiles) - private def getScalapFiles = getFiles(scalapFiles) + private def getPosFiles = getFilesAndDirs(posFiles) + private def getNegFiles = getFilesAndDirs(negFiles) + private def getRunFiles = getFiles(runFiles) + private def getJvmFiles = getFilesAndDirs(jvmFiles) + private def getResidentFiles = getFiles(residentFiles) + private def getBuildManagerFiles = getFiles(buildManagerFiles) + private def getScriptFiles = getFiles(scriptFiles) + private def getShootoutFiles = getFiles(shootoutFiles) + private def getScalapFiles = getFiles(scalapFiles) override def execute() { if (debug) @@ -220,6 +226,7 @@ class PartestTask extends Task { (getRunFiles, "run", "Compiling and running files"), (getJvmFiles, "jvm", "Compiling and running files"), (getResidentFiles, "res", "Running resident compiler scenarii"), + (getBuildManagerFiles, "buildmanager", "Running Build Manager scenarii"), (getScriptFiles, "script", "Running script files"), (getShootoutFiles, "shootout", "Running shootout tests"), (getScalapFiles, "scalap", "Running scalap tests") diff --git a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala index 574cc70762..94b1d6c684 100644 --- a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala +++ b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala @@ -24,12 +24,13 @@ class ConsoleRunner extends DirectRunner with RunnerUtils { List( TestSet("pos", fileFilter, "pos", "Testing compiler (on files whose compilation should succeed)"), - TestSet("neg", fileFilter, "neg", + TestSet("neg", fileFilter, "neg", "Testing compiler (on files whose compilation should fail)"), - TestSet("run", fileFilter, "run", "Testing JVM backend"), - TestSet("jvm", fileFilter, "jvm", "Testing JVM backend"), - TestSet("res", Some((".res", false)), "res", + TestSet("run", fileFilter, "run", "Testing JVM backend"), + TestSet("jvm", fileFilter, "jvm", "Testing JVM backend"), + TestSet("res", Some((".res", false)), "res", "Testing resident compiler"), + TestSet("buildmanager", Some((".nothing", true)), "buildmanager", "Testing Build Manager"), TestSet("shootout", fileFilter, "shootout", "Testing shootout tests"), TestSet("script", fileFilter, "script", "Testing script tests"), TestSet("scalacheck", fileFilter, "scalacheck", "Testing ScalaCheck tests"), @@ -210,6 +211,7 @@ class ConsoleRunner extends DirectRunner with RunnerUtils { case "sho" => "shootout" case "scr" => "script" case "sca" => "scalacheck" + case "bui" => "buildmanager" } } } diff --git a/src/partest/scala/tools/partest/nest/FileManager.scala b/src/partest/scala/tools/partest/nest/FileManager.scala index 637999cc36..df7a8e129c 100644 --- a/src/partest/scala/tools/partest/nest/FileManager.scala +++ b/src/partest/scala/tools/partest/nest/FileManager.scala @@ -8,7 +8,9 @@ package scala.tools.partest package nest -import java.io.{File, FilenameFilter, IOException, StringWriter} +import java.io.{File, FilenameFilter, IOException, StringWriter, + FileInputStream, FileOutputStream, BufferedReader, + FileReader, PrintWriter, FileWriter} import java.net.URI import scala.tools.nsc.io.Directory @@ -76,4 +78,43 @@ trait FileManager { val logFile = getLogFile(file, kind) logFile.exists && logFile.canRead } + + def overwriteFileWith(dest: File, file: File): Boolean = + if (dest.exists && dest.isFile) + copyFile(file, dest) + else + false + + def copyFile(from: File, to: File): Boolean = + try { + val fromReader = new BufferedReader(new FileReader(from)) + val toWriter = new PrintWriter(new FileWriter(to)) + + new StreamAppender(fromReader, toWriter).run() + + fromReader.close() + toWriter.close() + true + } catch { + case _:IOException => false + } + + def mapFile(file: File, suffix: String, dir: File, replace: String => String) { + val tmpFile = File.createTempFile("tmp", suffix, dir) // prefix required by API + val fileReader = new BufferedReader(new FileReader(file)) + val tmpFilePrinter = new PrintWriter(new FileWriter(tmpFile)) + val appender = new StreamAppender(fileReader, tmpFilePrinter) + + appender.runAndMap(replace) + + fileReader.close() + tmpFilePrinter.close() + + val tmpFileReader = new BufferedReader(new FileReader(tmpFile)) + val filePrinter= new PrintWriter(new FileWriter(file), true) + (new StreamAppender(tmpFileReader, filePrinter)).run + tmpFileReader.close() + filePrinter.close() + tmpFile.delete() + } } diff --git a/src/partest/scala/tools/partest/nest/NestUI.scala b/src/partest/scala/tools/partest/nest/NestUI.scala index b8d77bc704..5fe10ce271 100644 --- a/src/partest/scala/tools/partest/nest/NestUI.scala +++ b/src/partest/scala/tools/partest/nest/NestUI.scala @@ -70,14 +70,15 @@ object NestUI { println(" <options>:") 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(" --res run resident compiler tests") - println(" --script run script runner tests") - println(" --shootout run shootout tests") + 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(" --res run resident compiler tests") + println(" --buildmanager run Build Manager 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") diff --git a/src/partest/scala/tools/partest/nest/TestFile.scala b/src/partest/scala/tools/partest/nest/TestFile.scala index 7ffe11e5b6..5a62beb48d 100644 --- a/src/partest/scala/tools/partest/nest/TestFile.scala +++ b/src/partest/scala/tools/partest/nest/TestFile.scala @@ -74,6 +74,14 @@ case class RunTestFile(override val file: File, override val fileManager: FileMa } } +case class BuildManagerTestFile(override val file: File, override val fileManager: FileManager, createOutDir: Boolean) extends TestFile("bm", file, fileManager, createOutDir) { + override def defineSettings(settings: Settings) { + baseSettings(settings) + settings.classpath.value = settings.classpath.value+ + File.pathSeparator+fileManager.CLASSPATH + } +} + case class ScalaCheckTestFile(override val file: File, override val fileManager: FileManager, createOutDir: Boolean) extends TestFile("scalacheck", file, fileManager, createOutDir) { override def defineSettings(settings: Settings) { baseSettings(settings) diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/Worker.scala index 37a98860ad..be0a2c606d 100644 --- a/src/partest/scala/tools/partest/nest/Worker.scala +++ b/src/partest/scala/tools/partest/nest/Worker.scala @@ -19,8 +19,12 @@ import scala.actors.{Actor, Exit, TIMEOUT} import scala.actors.Actor._ import scala.tools.scalap.scalax.rules.scalasig.{ByteCode, ClassFileParser, ScalaSigAttributeParsers} +import io.{AbstractFile, PlainFile} + import scala.collection.mutable.HashMap +import scala.tools.nsc.interactive.{BuildManager, RefinedBuildManager} + case class RunTests(kind: String, files: List[File]) case class Results(succ: Int, fail: Int, logs: List[LogFile], outdirs: List[File]) case class LogContext(file: LogFile, writers: Option[(StringWriter, PrintWriter)]) @@ -136,32 +140,13 @@ class Worker(val fileManager: FileManager) extends Actor { // grab global lock fileManager.synchronized { - - val oldStdOut = System.out - val oldStdErr = System.err - System.setOut(logWriter) - System.setErr(logWriter) - - /* - " -Djava.library.path="+logFile.getParentFile.getAbsolutePath+ - " -Dscalatest.output="+outDir.getAbsolutePath+ - " -Dscalatest.lib="+LATEST_LIB+ - " -Dscalatest.cwd="+outDir.getParent+ - " -Djavacmd="+JAVACMD+ - */ - - System.setProperty("java.library.path", logFile.getParentFile.getCanonicalFile.getAbsolutePath) - System.setProperty("scalatest.output", outDir.getCanonicalFile.getAbsolutePath) - System.setProperty("scalatest.lib", LATEST_LIB) - System.setProperty("scalatest.cwd", outDir.getParent) - - ObjectRunner.run(classpath, "Test", List("jvm")) - - logWriter.flush() - logWriter.close() - - System.setOut(oldStdOut) - System.setErr(oldStdErr) + withOutputRedirected(logWriter) { + System.setProperty("java.library.path", logFile.getParentFile.getCanonicalFile.getAbsolutePath) + System.setProperty("scalatest.output", outDir.getCanonicalFile.getAbsolutePath) + System.setProperty("scalatest.lib", LATEST_LIB) + System.setProperty("scalatest.cwd", outDir.getParent) + ObjectRunner.run(classpath, "Test", List("jvm")) + } } /*val out = new FileOutputStream(logFile, true) @@ -475,20 +460,11 @@ class Worker(val fileManager: FileManager) extends Actor { List(outURL, scalacheckURL, latestCompFile.toURI.toURL, latestLibFile.toURI.toURL, latestActFile.toURI.toURL, latestPartestFile.toURI.toURL).removeDuplicates - // XXX this is a big cut-and-paste mess, but the revamp is coming - val logOut = new FileOutputStream(logFile) - val logWriter = new PrintStream(logOut) - val oldStdOut = System.out - val oldStdErr = System.err - System.setOut(logWriter) - System.setErr(logWriter) - - ObjectRunner.run(classpath, "Test", Nil) + val logWriter = new PrintStream(new FileOutputStream(logFile)) - logWriter.flush() - logWriter.close() - System.setOut(oldStdOut) - System.setErr(oldStdErr) + withOutputRedirected(logWriter) { + ObjectRunner.run(classpath, "Test", Nil) + } NestUI.verbose(io.File(logFile).slurp()) // obviously this must be improved upon @@ -535,6 +511,143 @@ class Worker(val fileManager: FileManager) extends Actor { case "jvm" => runJvmTest(file, kind) + case "buildmanager" => + val logFile = createLogFile(file, kind) + if (!fileManager.failed || (logFile.exists && logFile.canRead)) { + val swr = new StringWriter + val wr = new PrintWriter(swr) + succeeded = true; diff = "" + printInfoStart(file, wr) + val (outDir, testFile, changesDir, fileBase) = + + if (!file.isDirectory) { + succeeded = false + (null, null, null, null) + } else { + val fileBase: String = basename(file.getName) + NestUI.verbose(this+" running test "+fileBase) + val outDir = createOutputDir(file, fileBase, kind) + if (!outDir.exists) outDir.mkdir() + val testFile = new File(file, fileBase + ".test") + val changesDir = new File(file, fileBase + ".changes") + if (changesDir.exists && !changesDir.isDirectory) { + // if changes exists then it has to be a dir + succeeded = false + (null, null, null, null) + } else { + copyTestFiles(file, outDir) + NestUI.verbose("outDir: "+outDir) + NestUI.verbose("logFile: "+logFile) + (outDir, testFile, changesDir, fileBase) + } + } + + if (succeeded) { + // Pre-conditions satisfied + + try { + val sourcepath = outDir.getAbsolutePath+File.separator + + // configure input/output files + val logWriter = new PrintStream(new FileOutputStream(logFile)) + val testReader = new BufferedReader(new FileReader(testFile)) + //val logConsoleWriter = new PrintWriter(new OutputStreamWriter(logOut)) + val logConsoleWriter = new PrintWriter(logWriter) + + // create proper settings for the compiler + val settings = new Settings(error) + settings.outdir.value = outDir.getCanonicalFile.getAbsolutePath + settings.sourcepath.value = sourcepath + settings.classpath.value = fileManager.CLASSPATH + + // simulate Build Manager loop + val prompt = "builder > " + reporter = new ConsoleReporter(settings, scala.Console.in, logConsoleWriter) + val bM: BuildManager = + new RefinedBuildManager(settings) { + override protected def newCompiler(settings: Settings) = + new BuilderGlobal(settings, reporter) + } + + val testCompile = (line: String) => { + NestUI.verbose("compiling " + line) + val args = (line split ' ').toList + val command = new CompilerCommand(args, settings, error, true) + bM.update(filesToSet(settings.sourcepath.value, command.files), Set.empty) + !reporter.hasErrors + } + + val updateFiles = (line: String) => { + NestUI.verbose("updating " + line) + val res = + ((line split ' ').toList).forall(u => { + (u split "=>").toList match { + case origFileName::(newFileName::Nil) => + val newFile = new File(changesDir, newFileName) + if (newFile.exists && newFile.isFile) { + val v = overwriteFileWith(new File(outDir, origFileName), newFile) + if (!v) + NestUI.verbose("'update' operation on " + u + " failed") + v + } else { + NestUI.verbose("File " + newFile + " is invalid") + false + } + case a => + NestUI.verbose("Other =: " + a) + false + } + }) + if (!res) + NestUI.verbose("updating failed") + else + NestUI.verbose("updating succeeded") + res + } + + def loop() { + val command = testReader.readLine() + if ((command ne null) && command.length() > 0) { + val commandResult = command match { + case s if (s.startsWith(">>update ")) => + updateFiles(s.stripPrefix(">>update ")) + case s if (s.startsWith(">>compile ")) => + val files = s.stripPrefix(">>compile ") + logWriter.println(prompt + files) + testCompile(files) // In the end, it can finish with an error + case _ => + NestUI.verbose("wrong command in test file: " + command) + false + } + + if (commandResult) loop() + + } else { + NestUI.verbose("finished") + succeeded = true + } + } + + withOutputRedirected(logWriter) { + loop() + testReader.close() + } + + fileManager.mapFile(logFile, "tmp", file, {s => s.replace(sourcepath, "")}) + + diff = compareOutput(file, fileBase, kind, logFile) + if (!diff.equals("")) { + NestUI.verbose("output differs from log file\n") + succeeded = false + } + + } + LogContext(logFile, Some((swr, wr))) + } else + LogContext(logFile, None) + } else + LogContext(logFile, None) + case "res" => { // when option "--failed" is provided // execute test only if log file is present @@ -632,48 +745,25 @@ class Worker(val fileManager: FileManager) extends Actor { loop(action) } } - val oldStdOut = System.out - val oldStdErr = System.err - System.setOut(logWriter) - System.setErr(logWriter) - loop(resCompile) - resReader.close() - logWriter.flush() - logWriter.close() - - System.setOut(oldStdOut) - System.setErr(oldStdErr) - - val tempLogFile = new File(dir, fileBase+".temp.log") - val logFileReader = new BufferedReader(new FileReader(logFile)) - val tempLogFilePrinter = new PrintWriter(new FileWriter(tempLogFile)) - val appender = - new StreamAppender(logFileReader, tempLogFilePrinter) - - // function that removes a given string from another string - def removeFrom(line: String, path: String): String = { - // find `path` in `line` - val index = line.indexOf(path) - if (index != -1) { - line.substring(0, index) + line.substring(index + path.length, line.length) - } else line - } - appender.runAndMap({ s => - val woPath = removeFrom(s, dir.getAbsolutePath/*.replace(File.separatorChar,'/')*/+File.separator) - // now replace single '\' with '/' - woPath.replace('\\', '/') - }) - logFileReader.close() - tempLogFilePrinter.close() + withOutputRedirected(logWriter) { + loop(resCompile) + resReader.close() + } - val tempLogFileReader = new BufferedReader(new FileReader(tempLogFile)) - val logFilePrinter= new PrintWriter(new FileWriter(logFile), true) - (new StreamAppender(tempLogFileReader, logFilePrinter)).run - tempLogFileReader.close() - logFilePrinter.close() + def replaceSlashes(s: String): String = { + val path = dir.getAbsolutePath/*.replace(File.separatorChar,'/')*/+ + File.separator + // find `path` in `line` + val index = s.indexOf(path) + val line = + if (index != -1) + s.substring(0, index) + s.substring(index + path.length, s.length) + else s + line.replace('\\', '/') + } - tempLogFile.delete() + fileManager.mapFile(logFile, "tmp", dir, replaceSlashes) diff = compareOutput(dir, fileBase, kind, logFile) if (!diff.equals("")) { @@ -982,6 +1072,37 @@ class Worker(val fileManager: FileManager) extends Actor { } } + private def withOutputRedirected(out: PrintStream)(func: => Unit) { + val oldStdOut = System.out + val oldStdErr = System.err + + try { + System.setOut(out) + System.setErr(out) + func + out.flush() + out.close() + } finally { + System.setOut(oldStdOut) + System.setErr(oldStdErr) + } + } + + private def filesToSet(pre: String, fs: List[String]): Set[AbstractFile] = { + Set.empty ++ fs.map( s => AbstractFile.getFile(pre + s)).filter(_ != null) + } + + private def copyTestFiles(testDir: File, destDir: File) { + val filter = + new FilenameFilter { + def accept(dir: File, name: String) = + (name.endsWith(".scala") || name.endsWith(".java")) && + ((new File(dir, name).isFile)) + } + (testDir.listFiles(filter)).foreach( f => + fileManager.copyFile(f, new File(destDir, f.getName)) ) + } + def showLog(logFile: File) { try { val logReader = new BufferedReader(new FileReader(logFile)) |