diff options
Diffstat (limited to 'src/partest')
47 files changed, 2751 insertions, 2372 deletions
diff --git a/src/partest/README b/src/partest/README index c7673fe2f8..81876fc810 100644 --- a/src/partest/README +++ b/src/partest/README @@ -1,50 +1,32 @@ -If you're looking for something to read, I suggest running ../test/partest -with no arguments, which at this moment prints this: +How partest choses the compiler / library: -Usage: partest [<options>] [<test> <test> ...] - <test>: a path to a test designator, typically a .scala file or a directory. - Examples: files/pos/test1.scala, files/res/bug785 + * ''-Dpartest.build=build/four-pack'' -> will search for libraries in + ''lib'' directory of given path + * ''--pack'' -> will set ''partest.build=build/pack'', and run all tests. + add ''--[kind]'' to run a selected set of tests. + * auto detection: + - partest.build property -> ''bin'' / ''lib'' directories + - distribution (''dists/latest'') + - supersabbus pack (''build/pack'') + - sabbus quick (''build/quick'') + - installed dist (test files in ''misc/scala-test/files'') - Test categories: - --all run all tests (default, unless no options given) - --pos Compile files that are expected to build - --neg Compile files that are expected to fail - --run Test JVM backend - --jvm Test JVM backend - --res Run resident compiler scenarii - --buildmanager Run Build Manager scenarii - --scalacheck Run Scalacheck tests - --script Run script files - --shootout Run shootout tests - --scalap Run scalap tests +How partest choses test files: the test files must be accessible from +the directory on which partest is run. So the test files must be either +at: + * ./test/files + * ./files (cwd is "test") + * ./misc/scala-test/files (installed scala distribution) - Test "smart" categories: - --grep run all tests with a source file containing <expr> - --failed run all tests which failed on the last run - - Specifying paths and additional flags, ~ means repository root: - --rootdir path from ~ to partest (default: test) - --builddir path from ~ to test build (default: build/pack) - --srcdir path from --rootdir to sources (default: files) - --javaopts flags to java on all runs (overrides JAVA_OPTS) - --scalacopts flags to scalac on all tests (overrides SCALAC_OPTS) - --pack alias for --builddir build/pack - --quick alias for --builddir build/quick - - Options influencing output: - --trace show the individual steps taken by each test - --show-diff show diff between log and check file - --show-log show log on failures - --dry-run do not run tests, only show their traces. - --terse be less verbose (almost silent except for failures) - --verbose be more verbose (additive with --trace) - --debug maximum debugging output - --ansi print output in color - - Other options: - --timeout Timeout in seconds - --cleanup delete all stale files and dirs before run - --nocleanup do not delete any logfiles or object dirs - --stats collect and print statistics about the tests - --validate examine test filesystem for inconsistencies - --version print version +Other arguments: + * --pos next files test a compilation success + * --neg next files test a compilation failure + * --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 + * ''-Dpartest.scalac_opts=...'' -> add compiler options + * ''--verbose'' -> print verbose messages + * ''-Dpartest.debug=true'' -> print debug messages diff --git a/src/partest/scala/tools/partest/Actions.scala b/src/partest/scala/tools/partest/Actions.scala deleted file mode 100644 index 5cbe64ce23..0000000000 --- a/src/partest/scala/tools/partest/Actions.scala +++ /dev/null @@ -1,205 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala Parallel Testing ** -** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.tools -package partest - -import util._ -import nsc.io._ - -trait Actions { - partest: Universe => - - class TestSequence(val actions: List[TestStep]) extends AbsTestSequence { - } - - implicit def createSequence(xs: List[TestStep]) = new TestSequence(xs) - - trait ExecSupport { - self: TestEntity => - - def execEnv: Map[String, String] = { - val map = assembleEnvironment() - val cwd = execCwd.toList map ("CWD" -> _.path) - - map ++ cwd - } - def execCwd = if (commandFile.isFile) Some(sourcesDir) else None - - def runExec(args: List[String]): Boolean = { - val cmd = fromArgs(args) - - if (isVerbose) { - trace("runExec: " + execEnv.mkString("ENV(", "\n", "\n)")) - execCwd foreach (x => trace("CWD(" + x + ")")) - } - - trace("runExec: " + cmd) - isDryRun || execAndLog(cmd) - } - - /** Exec a process to run a command. Assumes 0 exit value is success. - * Of necessity, also treats no available exit value as success. - */ - protected def execAndLog(cmd: String): Boolean = { - var proc: Process = null - - val result = interruptMeIn(cmd, testTimeout) { - loggingResult { - proc = Process.exec(toArgs(cmd), execEnv, execCwd.orNull, true) - proc.slurp() - } - proc != null && (proc.waitFor() == 0) - } - result getOrElse { - warning("Process never terminated: '%s'" format cmd) - if (proc != null) - proc.destroy() - - false - } - } - } - - trait ScriptableTest { - self: TestEntity => - - /** Translates a line from a .cmds file into a teststep. - */ - def customTestStep(line: String): TestStep = { - trace("customTestStep: " + line) - val (cmd, rest) = line span (x => !Character.isWhitespace(x)) - def qualify(name: String) = sourcesDir / name path - val args = toArgs(rest) map qualify - def fail: TestStep = (_: TestEntity) => error("Parse error: did not understand '%s'" format line) - - val f: TestEntity => Boolean = cmd match { - case "scalac" => _ scalac args - case "javac" => _ javac args - case "scala" => _ runScala args - case _ => fail - } - f - } - } - - trait CompilableTest extends CompileExecSupport { - self: TestEntity => - - def sourceFiles = location.walk collect { case f: File if isJavaOrScala(f) => f } toList - def allSources = sourceFiles map (_.path) - def scalaSources = sourceFiles filter isScala map (_.path) - def javaSources = sourceFiles filter isJava map (_.path) - - /** If there are mixed java and scala files, the standard compilation - * sequence is: - * - * scalac with all files - * javac with only java files - * scalac with only scala files - * - * This should be expanded to encompass other strategies so we know how - * well they're working or not working - notably, it would be very useful - * to know exactly when and how two-pass compilation fails. - */ - def compile() = { - trace("compile: " + sourceFiles) - - def compileJava() = javac(javaSources) - def compileScala() = scalac(scalaSources) - def compileAll() = scalac(allSources) - def compileMixed() = compileAll() && compileJava() && compileScala() - - if (scalaSources.nonEmpty && javaSources.nonEmpty) compileMixed() - else compileScala() - } - } - - trait DiffableTest { - self: TestEntity => - - def checkFile: File = withExtension("check").toFile - def checkFileRequired = - returning(checkFile.isFile)(res => if (!res) warnAndLog("A checkFile at '%s' is mandatory.\n" format checkFile.path)) - - lazy val sourceFileNames = sourceFiles map (_.name) - - /** Given the difficulty of verifying that any selective approach works - * everywhere, the algorithm now is to look for the name of any known - * source file for this test, and if seen, remove all the non-whitespace - * preceding it. (Paths with whitespace don't work anyway.) This should - * wipe out all slashes, backslashes, C:\, cygwin/windows differences, - * and whatever else makes a simple diff not simple. - * - * The log and check file are both transformed, which I don't think is - * correct -- only the log should be -- but doing it this way until I - * can clarify martin's comments in #3283. - */ - def normalizePaths(s: String) = - sourceFileNames.foldLeft(s)((res, name) => res.replaceAll("""\S+\Q%s\E""" format name, name)) - - /** The default cleanup normalizes paths relative to sourcesDir, - * absorbs line terminator differences by going to lines and back, - * and trims leading or trailing whitespace. - */ - def diffCleanup(f: File) = safeLines(f) map normalizePaths mkString "\n" trim - - /** diffFiles requires actual Files as arguments but the output we want - * is the post-processed versions of log/check, so we resort to tempfiles. - */ - lazy val diffOutput = { - if (!checkFile.exists) "" else { - val input = diffCleanup(checkFile) - val output = diffCleanup(logFile) - def asFile(s: String) = returning(File.makeTemp("partest-diff"))(_ writeAll s) - - if (input == output) "" - else diffFiles(asFile(input), asFile(output)) - } - } - private def checkTraceName = tracePath(checkFile) - private def logTraceName = tracePath(logFile) - private def isDiffConfirmed = checkFile.exists && (diffOutput == "") - - private def sendTraceMsg() { - def result = - if (isDryRun) "" - else if (isDiffConfirmed) " [passed]" - else if (checkFile.exists) " [failed]" - else " [unchecked]" - - trace("diff %s %s%s".format(checkTraceName, logTraceName, result)) - } - - /** If optional is true, a missing check file is considered - * a successful diff. Necessary since many categories use - * checkfiles in an ad hoc manner. - */ - def runDiff() = { - sendTraceMsg() - - def updateCheck = ( - isUpdateCheck && { - val formatStr = "** diff %s %s: " + ( - if (checkFile.exists) "failed, updating '%s' and marking as passed." - else if (diffOutput == "") "not creating checkFile at '%s' as there is no output." - else "was unchecked, creating '%s' for future tests." - ) + "\n" - - normal(formatStr.format(checkTraceName, logTraceName, checkFile.path)) - if (diffOutput != "") normal(diffOutput) - - checkFile.writeAll(diffCleanup(logFile), "\n") - true - } - ) - - isDryRun || isDiffConfirmed || (updateCheck || !checkFile.exists) - } - } -} diff --git a/src/partest/scala/tools/partest/Alarms.scala b/src/partest/scala/tools/partest/Alarms.scala deleted file mode 100644 index f38d8d6268..0000000000 --- a/src/partest/scala/tools/partest/Alarms.scala +++ /dev/null @@ -1,86 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools -package partest - -import java.util.{ Timer, TimerTask } - -trait Alarms { - self: Universe => - - def interruptMeIn[T](debugMsg: String, seconds: Int)(body: => T): Option[T] = { - val thisThread = currentThread - val alarm = new SimpleAlarm(seconds * 1000) set thisThread.interrupt() - debug("interruptMeIn(%d) '%s'".format(seconds, debugMsg)) - - try { Some(body) } - catch { case _: InterruptedException => debug("Received interrupted exception.") ; None } - finally { debug("Cancelling interruptMeIn '%s'" format debugMsg) ; alarm.cancel() ; Thread.interrupted() } - } - - case class AlarmerAction(secs: Int, action: () => Unit) extends Runnable { - override def run() = action() - } - - /** Set any number of alarms up with tuples of the form: - * seconds to alarm -> Function0[Unit] to execute - */ - class Alarmer(alarms: AlarmerAction*) { - import java.util.concurrent._ - - val exec = Executors.newSingleThreadScheduledExecutor() - alarms foreach (x => exec.schedule(x, x.secs, TimeUnit.SECONDS)) - exec.shutdown() - - def cancelAll() = exec.shutdownNow() - } - - class SimpleAlarm(timeout: Long) { - private val alarm = new Timer - - /** Start a timer, running the given body if it goes off. - */ - def set(body: => Unit) = returning(new TimerTask { def run() = body })(alarm.schedule(_, timeout)) - - /** Cancel the timer. - */ - def cancel() = alarm.cancel() - } - - trait TestAlarms { - test: TestEntity => - - private def warning1 = AlarmerAction(testWarning, () => warning( - """|I've been waiting %s seconds for this to complete: - | %s - |It may be stuck, or if not, it should be broken into smaller tests. - |""".stripMargin.format(testWarning, test)) - ) - private def warning2 = AlarmerAction(testWarning * 2, () => warning( - """|Now I've been waiting %s seconds for this to complete: - | %s - |If partest seems hung it would be a good place to look. - |""".stripMargin.format(testWarning * 2, test)) - ) - - def startAlarms(onTimeout: => Unit) = - if (isNoAlarms) new Alarmer() // for alarm debugging - else new Alarmer(Seq(warning1, warning2, AlarmerAction(testTimeout, () => onTimeout)): _*) - } - - // Thread.setDefaultUncaughtExceptionHandler(new UncaughtException) - // class UncaughtException extends Thread.UncaughtExceptionHandler { - // def uncaughtException(t: Thread, e: Throwable) { - // Console.println("Uncaught in %s: %s".format(t, e)) - // } - // } - // - // lazy val logger = File("/tmp/partest.log").bufferedWriter() - // def flog(msg: String) = logger synchronized { - // logger write (msg + "\n") - // logger.flush() - // } -} diff --git a/src/partest/scala/tools/partest/BuildContributors.scala b/src/partest/scala/tools/partest/BuildContributors.scala deleted file mode 100644 index 64c7e07bc3..0000000000 --- a/src/partest/scala/tools/partest/BuildContributors.scala +++ /dev/null @@ -1,102 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest - -import nsc.io._ -import nsc.util.ClassPath - -trait BuildContributors { - universe: Universe => - - /** A trait mixed into types which contribute a portion of the values. - * The basic mechanism is the TestBuild, TestCategory, and TestEntity - * can each contribute to each value. They are assembled at the last - * moment by the ContributorAssembler (presently the TestEntity.) - */ - trait BuildContributor { - def javaFlags: List[String] - def scalacFlags: List[String] - def classpathPaths: List[Path] - def buildProperties: List[(String, Any)] - def buildEnvironment: Map[String, String] - } - - trait ContributorAssembler { - def contributors: List[BuildContributor] - def assemble[T](what: BuildContributor => List[T]): List[T] = contributors flatMap what - - /** !!! This will need work if we want to achieve real composability, - * but it can wait for the demand. - */ - def assembleScalacArgs(args: List[String]) = assemble(_.scalacFlags) ++ args - def assembleJavaArgs(args: List[String]) = assemble(_.javaFlags) ++ args - def assembleProperties() = assemble(_.buildProperties) - def assembleClasspaths(paths: List[Path]) = assemble(_.classpathPaths) ++ paths - def assembleEnvironment() = assemble(_.buildEnvironment.toList).toMap - - def createClasspathString() = ClassPath fromPaths (assembleClasspaths(Nil) : _*) - def createPropertyString() = assembleProperties() map { case (k, v) => "-D%s=%s".format(k, v.toString) } - } - - trait BuildContribution extends BuildContributor { - self: TestBuild => - - /** The base classpath and system properties. - * !!! TODO - this should adjust itself depending on the build - * being tested, because pack and quick at least need different jars. - */ - def classpathPaths = List[Path](library, compiler, partest, fjbg) ++ forkJoinPath - def buildProperties = List( - "scala.home" -> testBuildDir, - "partest.lib" -> library, // used in jvm/inner - "java.awt.headless" -> true, - "user.language" -> "en", - "user.country" -> "US", - "partest.debug" -> isDebug, - "partest.verbose" -> isVerbose - // Disabled because there are no natives tests. - // "java.library.path" -> srcLibDir - ) - def javaFlags: List[String] = toArgs(javaOpts) - def scalacFlags: List[String] = toArgs(scalacOpts) - - /** We put the build being tested's /bin directory in the front of the - * path so the scripts and such written to execute "scala" will use this - * build and not whatever happens to be on their path. - */ - private def modifiedPath = ClassPath.join(scalaBin.path, Properties.envOrElse("PATH", "")) - def buildEnvironment = Map("PATH" -> modifiedPath) - } - - trait CategoryContribution extends BuildContributor { - self: DirBasedCategory => - - /** Category-wide classpath additions placed in <category>/lib. */ - private def libContents = root / "lib" ifDirectory (_.list.toList) - - def classpathPaths = libContents getOrElse Nil - def buildProperties = Nil - def javaFlags = Nil - def scalacFlags = Nil - def buildEnvironment = Map() - } - - trait TestContribution extends BuildContributor with ContributorAssembler { - self: TestEntity => - - def jarsInTestDir = location.walk collect { case f: File if f hasExtension "jar" => f } toList - - def contributors = List(build, category, self) - def javaFlags = safeArgs(javaOptsFile) - def scalacFlags = safeArgs(scalaOptsFile) - def classpathPaths = jarsInTestDir :+ outDir - def buildProperties = List( - "partest.output" -> outDir.toAbsolute, // used in jvm/inner - "partest.cwd" -> outDir.parent.toAbsolute // used in shootout tests - ) - def buildEnvironment = Map("JAVA_OPTS" -> fromArgs(assembleJavaArgs(Nil))) - } -}
\ No newline at end of file diff --git a/src/partest/scala/tools/partest/Categories.scala b/src/partest/scala/tools/partest/Categories.scala deleted file mode 100644 index 172cca74b4..0000000000 --- a/src/partest/scala/tools/partest/Categories.scala +++ /dev/null @@ -1,70 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala Parallel Testing ** -** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.tools -package partest - -import nsc.Settings -import nsc.io._ -import nsc.util.{ ClassPath } - -trait Categories { - self: Universe => - - trait TestCategory extends AbsTestCategory { - def kind: String - def startMessage: String = "Executing test group" - def testSequence: TestSequence - - class TestSettings(entity: TestEntity, error: String => Unit) extends Settings(error) { - def this(entity: TestEntity) = this(entity, Console println _) - - deprecation.value = false - encoding.value = "ISO-8859-1" - classpath.value = entity.testClasspath - outdir.value = entity.outDir.path - } - - def createSettings(entity: TestEntity): TestSettings = new TestSettings(entity) - def createTest(location: Path): TestEntity = - if (location.isFile) TestFile(this, location.toFile) - else if (location.isDirectory) TestDirectory(this, location.toDirectory) - else error("Failed to create test at '%s'" format location) - - /** Category test identification. - */ - def denotesTestFile(p: Path) = p.isFile && (p hasExtension "scala") - def denotesTestDir(p: Path) = p.isDirectory && !ignorePath(p) - def denotesTest(p: Path) = denotesTestDir(p) || denotesTestFile(p) - - /** This should verify that all necessary files are present. - * By default it delegates to denotesTest. - */ - def denotesValidTest(p: Path) = denotesTest(p) - } - - abstract class DirBasedCategory(val kind: String) extends TestCategory with CategoryContribution { - lazy val root = Directory(src / kind).normalize - def enumerate = root.list filter denotesTest map createTest toList - - /** Standard actions. These can be overridden either on the - * Category level or by individual tests. - */ - def compile: TestStep = (_: TestEntity).compile() - def checkFileRequired: TestStep = (_: TestEntity).checkFileRequired - def diff: TestStep = (_: TestEntity).diff() - def run: TestStep = (_: TestEntity).run() - def exec: TestStep = (_: TestEntity).exec() - - /** Combinators. - */ - def not(f: TestStep): TestStep = !f(_: TestEntity) - - override def toString = kind - } -}
\ No newline at end of file diff --git a/src/partest/scala/tools/partest/Compilable.scala b/src/partest/scala/tools/partest/Compilable.scala deleted file mode 100644 index ddaa277842..0000000000 --- a/src/partest/scala/tools/partest/Compilable.scala +++ /dev/null @@ -1,106 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest - -import scala.tools.nsc.io._ -import scala.tools.nsc.{ Global, Settings, CompilerCommand, FatalError } -import scala.tools.nsc.util.{ ClassPath } -import scala.tools.nsc.reporters.{ Reporter, ConsoleReporter } - -trait PartestCompilation { - self: Universe => - - trait CompileExecSupport extends ExecSupport { - self: TestEntity => - - def javacpArg = "-classpath " + testClasspath - def scalacpArg = "-usejavacp" - - /** Not used, requires tools.jar. - */ - // def javacInternal(args: List[String]) = { - // import com.sun.tools.javac.Main - // Main.compile(args.toArray, logWriter) - // } - - def javac(args: List[String]): Boolean = { - val allArgString = fromArgs(javacpArg :: javacOpts :: args) - - // javac -d outdir -classpath <basepath> <files> - val cmd = "%s -d %s %s".format(javacCmd, outDir, allArgString) - def traceMsg = - if (isVerbose) cmd - else "%s -d %s %s".format(tracePath(Path(javacCmd)), tracePath(outDir), fromArgs(args)) - - trace(traceMsg) - - isDryRun || execAndLog(cmd) - } - - def scalac(args: List[String]): Boolean = { - val allArgs = assembleScalacArgs(args) - val (global, files) = newGlobal(allArgs) - def nonFileArgs = if (isVerbose) global.settings.recreateArgs else assembleScalacArgs(Nil) - def traceArgs = fromArgs(nonFileArgs ++ (files map tracePath)) - def traceMsg = "scalac " + traceArgs - - trace(traceMsg) - isDryRun || global.partestCompile(files, true) - } - - /** Actually running the test, post compilation. - * Normally args will be List("Test", "jvm"), main class and arg to it. - */ - def runScala(args: List[String]): Boolean = { - val scalaRunnerClass = "scala.tools.nsc.MainGenericRunner" - - // java $JAVA_OPTS <javaopts> -classpath <cp> - val javaCmdAndOptions = javaCmd +: assembleJavaArgs(List(javacpArg)) - // MainGenericRunner -usejavacp <scalacopts> Test jvm - val scalaCmdAndOptions = List(scalaRunnerClass, scalacpArg) ++ assembleScalacArgs(args) - // Assembled - val cmd = fromArgs(javaCmdAndOptions ++ createPropertyString() ++ scalaCmdAndOptions) - - def traceMsg = if (isVerbose) cmd else fromArgs(javaCmd :: args) - trace("runScala: " + traceMsg) - - isDryRun || execAndLog(cmd) - } - - def newReporter(settings: Settings) = new ConsoleReporter(settings, Console.in, logWriter) - - class PartestGlobal(settings: Settings, val creporter: ConsoleReporter) extends Global(settings, creporter) { - def partestCompile(files: List[String], printSummary: Boolean): Boolean = { - try { new Run compile files } - catch { - case FatalError(msg) => creporter.error(null, "fatal error: " + msg) - case ae: AssertionError => creporter.error(null, ""+ae) - case te: TypeError => creporter.error(null, ""+te) - case ex => - creporter.error(null, ""+ex) - throw ex - } - - if (printSummary) - creporter.printSummary - - creporter.flush() - !creporter.hasErrors - } - } - - def newGlobal(args: List[String]): (PartestGlobal, List[String]) = { - val settings = category createSettings self - val command = new CompilerCommand(args, settings) - val reporter = newReporter(settings) - - if (!command.ok) - debug("Error parsing arguments: '%s'".format(args mkString ", ")) - - (new PartestGlobal(command.settings, reporter), command.files) - } - } -} diff --git a/src/partest/scala/tools/partest/Config.scala b/src/partest/scala/tools/partest/Config.scala deleted file mode 100644 index 288a3034e9..0000000000 --- a/src/partest/scala/tools/partest/Config.scala +++ /dev/null @@ -1,129 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest - -import io._ -import nsc.io._ -import Properties._ - -trait Config { - universe: Universe => - - lazy val src = absolutize(srcDir).toDirectory - lazy val build = new TestBuild() - - def javaHomeEnv = envOrElse("JAVA_HOME", null) - def javaCmd = envOrElse("JAVACMD", "java") - def javacCmd = Option(javaHomeEnv) map (x => Path(x) / "bin" / "javac" path) getOrElse "javac" - - /** Values related to actors. The timeouts are in seconds. On a dry - * run we only allocate one worker so the output isn't interspersed. - */ - def workerTimeout = 3600 // 1 hour, probably overly generous - def numWorkers = if (isDryRun) 1 else propOrElse("partest.actors", "8").toInt - def expectedErrors = propOrElse("partest.errors", "0").toInt - def poolSize = (wrapAccessControl(propOrNone("actors.corePoolSize")) getOrElse "16").toInt - - def allScalaFiles = src.deepFiles filter (_ hasExtension "scala") - def allObjDirs = src.deepDirs filter (_ hasExtension "obj") - def allLogFiles = src.deepFiles filter (_ hasExtension "log") - def allClassFiles = src.deepFiles filter (_ hasExtension "class") - - class TestBuild() extends BuildContribution { - import nsc.util.ClassPath - - /** Scala core libs. - */ - val library = pathForComponent("library") - val compiler = pathForComponent("compiler") - val partest = pathForComponent("partest") - val scalap = pathForComponent("scalap", "%s.jar") - - /** Scala supplementary libs - these are not all needed for all build targets, - * and some of them are copied inside other jars in later targets. However quick - * for instance cannot be run without some of these. - */ - val fjbg = pathForLibrary("fjbg") - val msil = pathForLibrary("msil") - val forkjoin = pathForLibrary("forkjoin") - val scalacheck = pathForLibrary("scalacheck") - - /** Other interesting paths. - */ - val scalaBin = testBuildDir / "bin" - - /** A hack for now to get quick running. - */ - def needsForkJoin = { - val loader = nsc.util.ScalaClassLoader.fromURLs(List(library.toURL)) - val fjMarker = "scala.concurrent.forkjoin.ForkJoinTask" - val clazz = loader.tryToLoadClass(fjMarker) - - if (clazz.isDefined) debug("Loaded ForkJoinTask OK, don't need jar.") - else debug("Could not load ForkJoinTask, putting jar on classpath.") - - clazz.isEmpty - } - lazy val forkJoinPath: List[Path] = if (needsForkJoin) List(forkjoin) else Nil - - /** Internal **/ - private def repo = partestDir.parent.normalize - // XXX - is this needed? Where? - // - // private val pluginOptionString = "-Xplugin:" - // private def updatedPluginPath(options: String): String = { - // val (pluginArgs, rest) = toArgs(options) partition (_ startsWith pluginOptionString) - // // join all plugin paths as one classpath - // val pluginPaths = ClassPath.join(pluginArgs map (_ stripPrefix pluginOptionString): _*) - // // map all paths to absolute - // val newPath = ClassPath.map(pluginPaths, x => absolutize(x).path) - // // recreate option - // val pluginOption = if (newPath == "") None else Some(pluginOptionString + newPath) - // - // fromArgs(rest ::: pluginOption.toList) - // } - - private def pathForComponent(what: String, jarFormat: String = "scala-%s.jar"): Path = { - def asDir = testBuildDir / "classes" / what - def asJar = testBuildDir / "lib" / jarFormat.format(what) - - if (asDir.isDirectory) asDir - else if (asJar.isFile) asJar - else "" - } - private def pathForLibrary(what: String) = File(repo / "lib" / (what + ".jar")) - } - - def printConfigBanner() = { - debug("Java VM started with arguments: '%s'" format fromArgs(Process.javaVmArguments)) - debug("System Properties:\n" + util.allPropertiesString()) - - normal(configBanner()) - } - - /** Treat an access control failure as None. */ - private def wrapAccessControl[T](body: => Option[T]): Option[T] = - try body catch { case _: java.security.AccessControlException => None } - - private def configBanner() = { - val javaBin = Path(javaHome) / "bin" - val javaInfoString = "%s (build %s, %s)".format(javaVmName, javaVmVersion, javaVmInfo) - - List( - "Scala compiler classes in: " + testBuildDir, - "Scala version is: " + nsc.Properties.versionMsg, - "Scalac options are: " + universe.scalacOpts, - "Java binaries in: " + javaBin, - "Java runtime is: " + javaInfoString, - "Java runtime options: " + (Process.javaVmArguments mkString " "), - "Javac options are: " + universe.javacOpts, - "Java options are: " + universe.javaOpts, - "Source directory is: " + src, - "Selected categories: " + (selectedCategories mkString " "), - "" - ) mkString "\n" - } -} diff --git a/src/partest/scala/tools/partest/Dispatcher.scala b/src/partest/scala/tools/partest/Dispatcher.scala deleted file mode 100644 index 2a9d99ab60..0000000000 --- a/src/partest/scala/tools/partest/Dispatcher.scala +++ /dev/null @@ -1,162 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - * @author Philipp Haller - */ - -package scala.tools -package partest - -import scala.tools.nsc.io._ -import scala.actors.{ Actor, TIMEOUT } -import scala.actors.Actor._ -import scala.collection.immutable -import scala.util.control.Exception.ultimately - -/** The machinery for concurrent execution of tests. Each Worker - * is given a bundle of tests, which it runs sequentially and then - * sends a report back to the dispatcher. - */ -trait Dispatcher { - partest: Universe => - - /** The public entry point. The given filter narrows down the list of - * tests to run. - */ - def runSelection(categories: List[TestCategory], filt: TestEntity => Boolean = _ => true): CombinedTestResults = { - // Setting scala.home informs tests where to obtain their jars. - setProp("scala.home", testBuildDir.path) - - val allTests = allCategories flatMap (_.enumerate) - val selected = allTests filter filt - val groups = selected groupBy (_.category) - val count = selected.size - - if (count == 0) return CombinedTestResults(0, 0, 0, Nil) - else if (count == allTests.size) verbose("Running all %d tests." format count) - else verbose("Running %d/%d tests: %s".format(count, allTests.size, toStringTrunc(selected map (_.label) mkString ", "))) - - allCategories collect { case x if groups contains x => runCategory(x, groups(x)) } reduceLeft (_ ++ _) - } - - private def parallelizeTests(tests: List[TestEntity]): immutable.Map[TestEntity, TestResult] = { - // propagate verbosity - if (isDebug) scala.actors.Debug.level = 3 - - // "If elected, I guarantee a slice of tests for every worker!" - val groups = tests grouped ((tests.size / numWorkers) + 1) toList - - // "Workers, line up for assignments!" - val workers = - for ((slice, workerNum) <- groups.zipWithIndex) yield { - returning(new Worker(workerNum)) { worker => - worker.start() - worker ! TestsToRun(slice) - } - } - - normal("Started %d workers with ~%d tests each.\n".format(groups.size, groups.head.size)) - - /** Listening for news from the proletariat. - */ - (workers map { w => - receiveWithin(workerTimeout * 1000) { - case ResultsOfRun(resultMap) => resultMap - case TIMEOUT => - warning("Worker %d timed out." format w.workerNum) - // mark all the worker's tests as having timed out - should be hard to miss - // immutable.Map[TestEntity, TestResult]() - groups(w.workerNum) map (x => (x -> new Timeout(x))) toMap - } - }) reduceLeft (_ ++ _) - } - - private def runCategory(category: TestCategory, tests: List[TestEntity]): CombinedTestResults = { - val kind = category.kind - normal("%s (%s tests in %s)\n".format(category.startMessage, tests.size, category)) - - val (milliSeconds, resultMap) = timed2(parallelizeTests(tests)) - val (passed, failed) = resultsToStatistics(resultMap mapValues (_.state)) - val failures = resultMap.values filterNot (_.passed) toList - - CombinedTestResults(passed, failed, milliSeconds, failures) - } - - /** A Worker is given a bundle of tests and runs them all sequentially. - */ - class Worker(val workerNum: Int) extends Actor { - def act() { - react { case TestsToRun(tests) => - val master = sender - runTests(tests)(results => master ! ResultsOfRun(results)) - } - } - - /** Runs the tests. Passes the result Map to onCompletion when done. - */ - private def runTests(tests: List[TestEntity])(onCompletion: immutable.Map[TestEntity, TestResult] => Unit) { - var results = new immutable.HashMap[TestEntity, TestResult] // maps tests to results - val numberOfTests = tests.size - val testIterator = tests.iterator - def processed = results.size - def isComplete = testIterator.isEmpty - - def atThreshold(num: Double) = { - require(num >= 0 && num <= 1.0) - ((processed - 1).toDouble / numberOfTests <= num) && (processed.toDouble / numberOfTests >= num) - } - - def extraMessage = { - // for now quiet for normal people - if (isVerbose || isTrace || isDebug) { - if (isComplete) "(#%d 100%%)" format workerNum - else if (isVerbose) "(#%d %d/%d)".format(workerNum, processed, numberOfTests) - else if (isTrace && atThreshold(0.5)) "(#%d 50%%)" format workerNum - else "" - } - else "" - } - - def countAndReport(result: TestResult) { - val TestResult(test, state) = result - // refuse to count an entity twice - if (results contains test) - return warning("Received duplicate result for %s: was %s, now %s".format(test, results(test), state)) - - // increment the counter for this result state - results += (test -> result) - - // show on screen - if (isDryRun) normal("\n") // blank line between dry run traces - else result show extraMessage - - // remove log if successful - if (result.passed) - test.deleteLog() - - // Respond to master if this Worker is complete - if (isComplete) - onCompletion(results) - } - - Actor.loopWhile(testIterator.hasNext) { - val parent = self - // pick a test and set some alarms - val test = testIterator.next - val alarmer = test startAlarms (parent ! new Timeout(test)) - - actor { - ultimately(alarmer.cancelAll()) { - // Calling isSuccess forces the lazy val "process" inside the test, running it. - val res = test.isSuccess - // Cancel the alarms and alert the media. - parent ! TestResult(test, res) - } - } - - react { - case x: TestResult => countAndReport(x) - } - } - } - } -}
\ No newline at end of file diff --git a/src/partest/scala/tools/partest/Entities.scala b/src/partest/scala/tools/partest/Entities.scala deleted file mode 100644 index bea505b594..0000000000 --- a/src/partest/scala/tools/partest/Entities.scala +++ /dev/null @@ -1,74 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - * @author Philipp Haller - */ - -package scala.tools -package partest - -import nsc.io._ - -trait Entities { - self: Universe => - - abstract class TestEntity extends AbsTestEntity - with TestContribution - with TestHousekeeping - with TestAlarms - with EntityLogging - with CompilableTest - with ScriptableTest - with DiffableTest { - def location: Path - def category: TestCategory - - lazy val label = location.stripExtension - lazy val testClasspath = returning(createClasspathString())(x => vtrace("testClasspath: " + x)) - - /** Was this test successful? Calling this for the first time forces - * lazy val "process" which actually runs the test. - */ - def isSuccess = process - - /** Some standard files, which may or may not be present. - */ - def scalaOptsFile = withExtension("flags").toFile // opts to scalac - def javaOptsFile = withExtension("javaopts").toFile // opts to java (but not javac) - def commandFile = withExtension("cmds").toFile // sequence of commands to execute - def logFile = withExtension("log").toFile // collected output - - /** Some standard directories. - */ - def outDir = withExtension("obj").toDirectory // output dir, e.g. files/pos/t14.obj - def categoryDir = location.parent.normalize // category dir, e.g. files/pos/ - def sourcesDir = location ifDirectory (_.normalize) getOrElse categoryDir - - /** Standard arguments for run, exec, diff. - */ - def argumentsToRun = List("Test", "jvm") - def argumentsToExec = List(location.path) - - /** Using a .cmds file for a custom test sequence. - */ - def commandList = safeLines(commandFile) - def testSequence = - if (commandFile.isFile && commandList.nonEmpty) commandList map customTestStep - else category.testSequence - - def run() = runScala(argumentsToRun) - def exec() = runExec(argumentsToExec) - def diff() = runDiff() // checkFile, logFile - - /** The memoized result of the test run. - */ - private lazy val process = { - val outcome = runWrappers(testSequence.actions forall (f => f(this))) - - // an empty outcome means we've been interrupted and are shutting down. - outcome getOrElse false - } - } - - case class TestDirectory(category: TestCategory, location: Directory) extends TestEntity { } - case class TestFile(category: TestCategory, location: File) extends TestEntity { } -} diff --git a/src/partest/scala/tools/partest/Housekeeping.scala b/src/partest/scala/tools/partest/Housekeeping.scala deleted file mode 100644 index a624ca8adb..0000000000 --- a/src/partest/scala/tools/partest/Housekeeping.scala +++ /dev/null @@ -1,187 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest - -import scala.util.control.Exception.catching -import util._ -import nsc.io._ -import Process.runtime -import Properties._ - -/** An agglomeration of code which is low on thrills. Hopefully - * it operates so quietly in the background that you never have to - * look at this file. - */ -trait Housekeeping { - self: Universe => - - /** Orderly shutdown on ctrl-C. */ - @volatile private var _shuttingDown = false - protected def setShuttingDown() = { - /** Whatever we want to do as shutdown begins goes here. */ - if (!_shuttingDown) { - warning("Received shutdown signal, partest is cleaning up...\n") - _shuttingDown = true - } - } - def isShuttingDown = _shuttingDown - - /** Execute some code with a shutdown hook in place. This is - * motivated by the desire not to leave the filesystem full of - * junk when someone ctrl-Cs a test run. - */ - def withShutdownHook[T](hook: => Unit)(body: => T): Option[T] = - /** Java doesn't like it if you keep adding and removing shutdown - * hooks after shutdown has begun, so we trap the failure. - */ - catching(classOf[IllegalStateException]) opt { - val t = new Thread() { - override def run() = { - setShuttingDown() - hook - } - } - runtime addShutdownHook t - - try body - finally runtime removeShutdownHook t - } - - /** Search for a directory, possibly given only a name, by starting - * at the current dir and walking upward looking for it at each level. - */ - protected def searchForDir(name: String): Directory = { - val result = Path(name) ifDirectory (x => x.normalize) orElse { - val cwd = Directory.Current getOrElse error("user.dir property not set") - val dirs = cwd :: cwd.parents map (_ / name) - - Path onlyDirs dirs map (_.normalize) headOption - } - - result getOrElse error("Fatal: could not find directory '%s'" format name) - } - - /** Paths we ignore for most purposes. - */ - def ignorePath(x: Path) = { - (x.name startsWith ".") || - (x.isDirectory && ((x.name == "lib") || x.hasExtension("obj", "svn"))) - } - /** Make a possibly relative path absolute using partestDir as the base. - */ - def absolutize(path: String) = Path(path) toAbsoluteWithRoot partestDir - - /** Go on a deleting binge. - */ - def cleanupAll() { - if (isNoCleanup) - return - - val (dirCount, fileCount) = (cleanupObjDirs(), cleanupLogs() + cleanupJunk()) - if (dirCount + fileCount > 0) - normal("Cleaned up %d directories and %d files.\n".format(dirCount, fileCount)) - } - - def cleanupObjDirs() = countTrue(allObjDirs collect { case x if x.exists => x.deleteRecursively() }) - def cleanupJunk() = countTrue(allClassFiles collect { case x if x.exists => x.delete() }) - def cleanupLogs() = countTrue(allLogFiles collect { case x if x.exists => x.delete() }) - - /** Look through every file in the partest directory and ask around - * to make sure someone knows him. Complain about strangers. - */ - def validateAll() { - def denotesTest(p: Path) = allCategories exists (_ denotesTest p) - def isMSILcheck(p: Path) = p.name endsWith "-msil.check" - - def analyzeCategory(cat: DirBasedCategory) = { - val allTests = cat.enumerate - val otherPaths = cat.root walkFilter (x => !ignorePath(x)) filterNot (cat denotesTest _) filterNot isMSILcheck toList - val count = otherPaths.size - - println("Validating %d non-test paths in %s.".format(count, cat.kind)) - - for (path <- otherPaths) { - (allTests find (_ acknowledges path)) match { - case Some(test) => if (isVerbose) println(" OK: '%s' is claimed by '%s'".format(path, test.label)) - case _ => println(">> Unknown path '%s'" format path) - } - } - } - - allCategories collect { case x: DirBasedCategory => analyzeCategory(x) } - } - - trait TestHousekeeping { - self: TestEntity => - - /** Calculating derived files. Given a test like - * files/run/foo.scala or files/run/foo/ - * This creates paths like foo.check, foo.flags, etc. - */ - def withExtension(extension: String) = categoryDir / "%s.%s".format(label, extension) - - /** True for a path if this test acknowledges it belongs to this test. - * Overridden by some categories. - */ - def acknowledges(path: Path): Boolean = { - val loc = location.normalize - val knownPaths = List(scalaOptsFile, javaOptsFile, commandFile, logFile, checkFile) ++ jarsInTestDir - def isContainedSource = location.isDirectory && isJavaOrScala(path) && (path.normalize startsWith loc) - - (knownPaths exists (_ isSame path)) || isContainedSource - } - - /** This test "responds to" this String. This could mean anything -- it's a - * way of specifying ad-hoc collections of tests to exercise only a subset of tests. - * At present it looks for the given String in all the test sources. - */ - def respondsToString(str: String) = containsString(str) - def containsString(str: String) = { - debug("Checking %s for \"%s\"".format(sourceFiles mkString ", ", str)) - sourceFiles map safeSlurp exists (_ contains str) - } - - def possiblyTimed[T](body: => T): T = { - if (isStats) timed(recordTestTiming(label, _))(body) - else body - } - - private def prepareForTestRun() = { - // make sure we have a clean slate - deleteLog(force = true) - if (outDir.exists) - outDir.deleteRecursively() - - // recreate object dir - outDir createDirectory true - } - def deleteOutDir() = outDir.deleteRecursively() - def deleteShutdownHook() = { debug("Shutdown hook deleting " + outDir) ; deleteOutDir() } - - protected def runWrappers[T](body: => T): Option[T] = { - prepareForTestRun() - - withShutdownHook(deleteShutdownHook()) { - loggingOutAndErr { - val result = possiblyTimed { body } - if (!isNoCleanup) - deleteOutDir() - - result - } - } - } - - override def toString = location.path - override def equals(other: Any) = other match { - case x: TestEntity => location.normalize == x.location.normalize - case _ => false - } - override def hashCode = location.normalize.hashCode - } - - private def countTrue(f: => Iterator[Boolean]) = f filter (_ == true) length -}
\ No newline at end of file diff --git a/src/partest/scala/tools/partest/Partest.scala b/src/partest/scala/tools/partest/Partest.scala deleted file mode 100644 index b3fe9a98ef..0000000000 --- a/src/partest/scala/tools/partest/Partest.scala +++ /dev/null @@ -1,81 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest - -import nsc.io._ -import nsc.util._ -import category.AllCategories - -/** Global object for a Partest run. It is completely configured by the list - * of arguments passed to the constructor (although there are a few properties - * and environment variables which can influence matters.) See PartestSpec.scala - * for the complete list. - */ -class Partest(args: List[String]) extends { - val parsed = PartestSpec(args: _*) -} with Universe with PartestSpec with cmd.Instance with AllCategories { - - if (parsed.propertyArgs.nonEmpty) - debug("Partest property args: " + fromArgs(parsed.propertyArgs)) - - debug("Partest created with args: " + fromArgs(args)) - - def helpMsg = PartestSpec.helpMsg - - // The abstract values from Universe. - lazy val testBuildDir = searchForDir(buildDir) - lazy val partestDir = searchForDir(rootDir) - lazy val allCategories = List(Pos, Neg, Run, Jvm, Res, Shootout, Scalap, Scalacheck, BuildManager, Script) - lazy val selectedCategories = if (isAllImplied) allCategories else specifiedCats - - def specifiedTests = parsed.residualArgs map (x => Path(x).normalize) - def specifiedKinds = testKinds filter (x => isSet(x) || (runSets contains x)) - def specifiedCats = specifiedKinds flatMap (x => allCategories find (_.kind == x)) - def isAllImplied = isAll || (specifiedTests.isEmpty && specifiedKinds.isEmpty) - - /** Assembles a filter based on command line options which restrict the test set - * --grep limits to only matching tests - * --failed limits to only recently failed tests (log file is present) - * --<category> limits to only the given tests and categories (but --all overrides) - * path/to/Test limits to only the given tests and categories - */ - lazy val filter = { - def indivFilter(test: TestEntity) = specifiedTests contains test.location.normalize - def categoryFilter(test: TestEntity) = specifiedCats contains test.category - def indivOrCat(test: TestEntity) = isAllImplied || indivFilter(test) || categoryFilter(test) // combines previous two - - def failedFilter(test: TestEntity) = !isFailed || (test.logFile exists) - def grepFilter(test: TestEntity) = grepExpr.isEmpty || (test containsString grepExpr.get) - def combinedFilter(x: TestEntity) = indivOrCat(x) && failedFilter(x) && grepFilter(x) // combines previous three - - combinedFilter _ - } - - def launchTestSuite() = { - def onTimeout() = { - warning("Partest test run timed out after " + timeout + " seconds.\n") - System.exit(-1) - } - val alarm = new Alarmer(AlarmerAction(timeout, () => onTimeout())) - - try runSelection(selectedCategories, filter) - finally alarm.cancelAll() - } -} - -object Partest { - def fromBuild(dir: String, args: String*): Partest = apply("--builddir" +: dir +: args: _*) - def apply(args: String*): Partest = new Partest(args.toList) - - // builds without partest jars won't actually work - def starr() = fromBuild("") - def locker() = fromBuild("build/locker") - def quick() = fromBuild("build/quick") - def pack() = fromBuild("build/pack") - def strap() = fromBuild("build/strap") - def dist() = fromBuild("dists/latest") -} - diff --git a/src/partest/scala/tools/partest/PartestDefaults.scala b/src/partest/scala/tools/partest/PartestDefaults.scala new file mode 100644 index 0000000000..139c54dedd --- /dev/null +++ b/src/partest/scala/tools/partest/PartestDefaults.scala @@ -0,0 +1,30 @@ +package scala.tools +package partest + +import nsc.io.{ File, Path, Process, Directory } +import util.{ PathResolver } +import nsc.Properties.{ propOrElse, propOrNone, propOrEmpty } + +object PartestDefaults { + import nsc.Properties._ + private def wrapAccessControl[T](body: => Option[T]): Option[T] = + try body catch { case _: java.security.AccessControlException => None } + + def testRootName = propOrNone("partest.root") + def srcDirName = propOrElse("partest.srcdir", "files") + def testRootDir = testRootName map (x => Directory(x)) + + def classPath = PathResolver.Environment.javaUserClassPath // XXX + + def javaCmd = propOrElse("partest.javacmd", "java") + def javacCmd = propOrElse("partest.javac_cmd", "javac") + def javaOpts = propOrElse("partest.java_opts", "") + def scalacOpts = propOrElse("partest.scalac_opts", "-deprecation") + + def testBuild = propOrNone("partest.build") + def errorCount = propOrElse("partest.errors", "0").toInt + def numActors = propOrElse("partest.actors", "8").toInt + def poolSize = wrapAccessControl(propOrNone("actors.corePoolSize")) + + def timeout = "1200000" +} diff --git a/src/partest/scala/tools/partest/PartestSpec.scala b/src/partest/scala/tools/partest/PartestSpec.scala deleted file mode 100644 index c25119b3af..0000000000 --- a/src/partest/scala/tools/partest/PartestSpec.scala +++ /dev/null @@ -1,104 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools -package partest - -import nsc.io._ -import cmd._ - -/** This takes advantage of bits of scala goodness to fully define a command - * line program with a minimum of duplicated code. When the specification object - * is created, the vals are evaluated in order and each of them side effects - * a private accumulator. What emerges is a full list of the valid unary - * and binary arguments, as well as autogenerated help. - */ -trait PartestSpec extends Spec with Meta.StdOpts with Interpolation { - def referenceSpec = PartestSpec - def programInfo = Spec.Info("partest", "", "scala.tools.partest.Runner") - private val kind = new Spec.Accumulator[String]() - protected def testKinds = kind.get - - private implicit val tokenizeString = FromString.ArgumentsFromString // String => List[String] - - help(""" - |# Pro Tip! Instant bash completion: `partest --bash` (note backticks) - |Usage: partest [<options>] [<test> <test> ...] - | <test>: a path to a test designator, typically a .scala file or a directory. - | Examples: files/pos/test1.scala, files/res/bug785 - | - | Test categories:""".stripMargin) - - val isAll = ("all" / "run all tests (default, unless no options given)" --?) - (kind("pos") / "Compile files that are expected to build" --?) - (kind("neg") / "Compile files that are expected to fail" --?) - (kind("run") / "Test JVM backend" --?) - (kind("jvm") / "Test JVM backend" --?) - (kind("res") / "Run resident compiler scenarii" --?) - (kind("buildmanager") / "Run Build Manager scenarii" --?) - (kind("scalacheck") / "Run Scalacheck tests" --?) - (kind("script") / "Run script files" --?) - (kind("shootout") / "Run shootout tests" --?) - (kind("scalap") / "Run scalap tests" --?) - - heading ("""Test "smart" categories:""") - val grepExpr = "grep" / "run all tests with a source file containing <expr>" --| - val isFailed = "failed" / "run all tests which failed on the last run" --? - - heading ("Specifying paths and additional flags, ~ means repository root:") - - val rootDir = "rootdir" / "path from ~ to partest" defaultTo "test" - val buildDir = "builddir" / "path from ~ to test build" defaultTo "build/pack" - val srcDir = "srcdir" / "path from --rootdir to sources" defaultTo "files" - val javaOpts = "javaopts" / "flags to java on all runs" defaultToEnv "JAVA_OPTS" - val javacOpts = "javacopts" / "flags to javac on all runs" defaultToEnv "JAVAC_OPTS" - val scalacOpts = "scalacopts" / "flags to scalac on all tests" defaultToEnv "SCALAC_OPTS" - - "pack" / "" expandTo ("--builddir", "build/pack") - "quick" / "" expandTo ("--builddir", "build/quick") - - heading ("Options influencing output:") - val isTrace = "trace" / "show the individual steps taken by each test" --? - val isShowDiff = "show-diff" / "show diff between log and check file" --? - val isShowLog = "show-log" / "show log on failures" --? - val isDryRun = "dry-run" / "do not run tests, only show their traces." --? - val isTerse = "terse" / "be less verbose (almost silent except for failures)" --? - val isVerbose = "verbose" / "be more verbose (additive with --trace)" --? - val isDebug = "debug" / "maximum debugging output" --? - val isAnsi = "ansi" / "print output in color" --? - - heading ("Other options:") - val timeout = "timeout" / "Overall timeout in seconds" defaultTo 7200 - val testWarning = "test-warning" / "Test warning in seconds" defaultTo 90 - val testTimeout = "test-timeout" / "Test timeout in seconds" defaultTo 900 - val isCleanup = "cleanup" / "delete all stale files and dirs before run" --? - val isNoCleanup = "nocleanup" / "do not delete any logfiles or object dirs" --? - val isStats = "stats" / "collect and print statistics about the tests" --? - val isValidate = "validate" / "examine test filesystem for inconsistencies" --? - val isUpdateCheck = "update-check" / "overwrite checkFile if diff fails" --? - - "version" / "print version" --> runAndExit(println(Properties.versionMsg)) - - // no help for anything below this line - secret options - // mostly intended for property configuration. - val runSets = ("runsets" --^) getOrElse Nil - val isNoAlarms = "noalarms" --? - val isInsideAnt = "is-in-ant" --? -} - -object PartestSpec extends PartestSpec with Property { - lazy val propMapper = new PropertyMapper(PartestSpec) { - override def isPassThrough(key: String) = key == "partest.options" - } - - type ThisCommandLine = PartestCommandLine - class PartestCommandLine(args: List[String]) extends SpecCommandLine(args) { - override def errorFn(msg: String) = printAndExit("Error: " + msg) - - def propertyArgs = PartestSpec.propertyArgs - } - - override def creator(args: List[String]): PartestCommandLine = new PartestCommandLine(args) -} diff --git a/src/partest/scala/tools/partest/PartestTask.scala b/src/partest/scala/tools/partest/PartestTask.scala new file mode 100644 index 0000000000..230a6f73ec --- /dev/null +++ b/src/partest/scala/tools/partest/PartestTask.scala @@ -0,0 +1,287 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.tools +package partest + +import scala.actors.Actor._ +import scala.util.Properties.setProp +import scala.tools.nsc.io.{ Directory, Path => SPath } +import nsc.Settings +import nsc.util.ClassPath +import util.PathResolver +import scala.tools.ant.sabbus.CompilationPathProperty + +import java.io.File +import java.net.URLClassLoader +import java.lang.reflect.Method + +import org.apache.tools.ant.Task +import org.apache.tools.ant.types.{Path, Reference, FileSet} + +class PartestTask extends Task with CompilationPathProperty { + + def addConfiguredPosTests(input: FileSet) { + posFiles = Some(input) + } + + def addConfiguredNegTests(input: FileSet) { + negFiles = Some(input) + } + + def addConfiguredRunTests(input: FileSet) { + runFiles = Some(input) + } + + def addConfiguredJvmTests(input: FileSet) { + jvmFiles = Some(input) + } + + def addConfiguredResidentTests(input: FileSet) { + residentFiles = Some(input) + } + + def addConfiguredBuildManagerTests(input: FileSet) { + buildManagerFiles = Some(input) + } + + def addConfiguredScalacheckTests(input: FileSet) { + scalacheckFiles = Some(input) + } + + def addConfiguredScriptTests(input: FileSet) { + scriptFiles = Some(input) + } + + def addConfiguredShootoutTests(input: FileSet) { + shootoutFiles = Some(input) + } + + def addConfiguredScalapTests(input: FileSet) { + scalapFiles = Some(input) + } + + def setSrcDir(input: String) { + srcDir = Some(input) + } + + def setClasspath(input: Path) { + if (classpath.isEmpty) + classpath = Some(input) + else + classpath.get.append(input) + } + + def createClasspath(): Path = { + if (classpath.isEmpty) classpath = Some(new Path(getProject())) + classpath.get.createPath() + } + + def setClasspathref(input: Reference) { + createClasspath().setRefid(input) + } + + def setShowLog(input: Boolean) { + showLog = input + } + + def setShowDiff(input: Boolean) { + showDiff = input + } + + def setErrorOnFailed(input: Boolean) { + errorOnFailed = input + } + + def setJavaCmd(input: File) { + javacmd = Some(input) + } + + def setJavacCmd(input: File) { + javaccmd = Some(input) + } + + def setScalacOpts(opts: String) { + scalacOpts = Some(opts) + } + + def setTimeout(delay: String) { + timeout = Some(delay) + } + + def setDebug(input: Boolean) { + debug = input + } + + def setJUnitReportDir(input: File) { + jUnitReportDir = Some(input) + } + + private var classpath: Option[Path] = None + private var srcDir: Option[String] = None + private var javacmd: Option[File] = None + private var javaccmd: Option[File] = None + private var showDiff: Boolean = false + private var showLog: Boolean = false + private var runFailed: Boolean = false + private var posFiles: Option[FileSet] = None + private var negFiles: Option[FileSet] = None + 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 scalacheckFiles: Option[FileSet] = None + private var scriptFiles: Option[FileSet] = None + private var shootoutFiles: Option[FileSet] = None + private var scalapFiles: Option[FileSet] = None + private var errorOnFailed: Boolean = false + private var scalacOpts: Option[String] = None + private var timeout: Option[String] = None + private var jUnitReportDir: Option[File] = None + private var debug = false + + def fileSetToDir(fs: FileSet) = Directory(fs getDir getProject) + def fileSetToArray(fs: FileSet): Array[SPath] = { + val root = fileSetToDir(fs) + (fs getDirectoryScanner getProject).getIncludedFiles map (root / _) + } + + private def getFiles(fileSet: Option[FileSet]): Array[File] = fileSet match { + case None => Array() + case Some(fs) => fileSetToArray(fs) filterNot (_ hasExtension "log") map (_.jfile) + } + + private def getFilesAndDirs(fileSet: Option[FileSet]): Array[File] = fileSet match { + case None => Array() + case Some(fs) => + def shouldExclude(name: String) = (name endsWith ".obj") || (name startsWith ".") + + val fileTests = getFiles(Some(fs)) filterNot (x => shouldExclude(x.getName)) + val dirTests: Iterator[SPath] = fileSetToDir(fs).dirs filterNot (x => shouldExclude(x.name)) + val dirResult = dirTests.toList.toArray map (_.jfile) + + dirResult ++ fileTests + } + + 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 = getFilesAndDirs(buildManagerFiles) + private def getScalacheckFiles = getFiles(scalacheckFiles) + private def getScriptFiles = getFiles(scriptFiles) + private def getShootoutFiles = getFiles(shootoutFiles) + private def getScalapFiles = getFiles(scalapFiles) + + override def execute() { + if (isPartestDebug) + setProp("partest.debug", "true") + + srcDir foreach (x => setProp("partest.srcdir", x)) + + val classpath = this.compilationPath getOrElse error("Mandatory attribute 'compilationPath' is not set.") + + val scalaLibrary = { + (classpath.list map { fs => new File(fs) }) find { f => + f.getName match { + case "scala-library.jar" => true + case "library" if (f.getParentFile.getName == "classes") => true + case _ => false + } + } + } getOrElse error("Provided classpath does not contain a Scala library.") + + val antRunner = new scala.tools.partest.nest.AntRunner + val antFileManager = antRunner.fileManager + + antFileManager.showDiff = showDiff + antFileManager.showLog = showLog + antFileManager.failed = runFailed + antFileManager.CLASSPATH = ClassPath.join(classpath.list: _*) + antFileManager.LATEST_LIB = scalaLibrary.getAbsolutePath + + javacmd foreach (x => antFileManager.JAVACMD = x.getAbsolutePath) + javaccmd foreach (x => antFileManager.JAVAC_CMD = x.getAbsolutePath) + scalacOpts foreach (antFileManager.SCALAC_OPTS = _) + timeout foreach (antFileManager.timeout = _) + + type TFSet = (Array[File], String, String) + val testFileSets = List( + (getPosFiles, "pos", "Compiling files that are expected to build"), + (getNegFiles, "neg", "Compiling files that are expected to fail"), + (getRunFiles, "run", "Compiling and running files"), + (getJvmFiles, "jvm", "Compiling and running files"), + (getResidentFiles, "res", "Running resident compiler scenarii"), + (getBuildManagerFiles, "buildmanager", "Running Build Manager scenarii"), + (getScalacheckFiles, "scalacheck", "Running scalacheck tests"), + (getScriptFiles, "script", "Running script files"), + (getShootoutFiles, "shootout", "Running shootout tests"), + (getScalapFiles, "scalap", "Running scalap tests") + ) + + def runSet(set: TFSet): (Int, Int, Iterable[String]) = { + val (files, name, msg) = set + if (files.isEmpty) (0, 0, List()) + else { + log(msg) + val results: Iterable[(String, Int)] = antRunner.reflectiveRunTestsForFiles(files, name) + val (succs, fails) = resultsToStatistics(results) + + val failed: Iterable[String] = results collect { + case (path, 1) => path + " [FAILED]" + case (path, 2) => path + " [TIMOUT]" + } + + // create JUnit Report xml files if directory was specified + jUnitReportDir foreach { d => + d.mkdir + + val report = testReport(name, results, succs, fails) + scala.xml.XML.save(d.getAbsolutePath+"/"+name+".xml", report) + } + + (succs, fails, failed) + } + } + + val _results = testFileSets map runSet + val allSuccesses = _results map (_._1) sum + val allFailures = _results map (_._2) sum + val allFailedPaths = _results flatMap (_._3) + + def f = if (errorOnFailed && allFailures > 0) error(_) else log(_: String) + def s = if (allFailures > 1) "s" else "" + val msg = + if (allFailures > 0) + "Test suite finished with %d case%s failing:\n".format(allFailures, s)+ + allFailedPaths.mkString("\n") + else if (allSuccesses == 0) "There were no tests to run." + else "Test suite finished with no failures." + + f(msg) + } + def oneResult(res: (String, Int)) = + <testcase name={res._1}>{ + res._2 match { + case 0 => scala.xml.NodeSeq.Empty + case 1 => <failure message="Test failed"/> + case 2 => <failure message="Test timed out"/> + } + }</testcase> + + def testReport(kind: String, results: Iterable[(String, Int)], succs: Int, fails: Int) = + <testsuite name={kind} tests={(succs + fails).toString} failures={fails.toString}> + <properties/> + { + results.map(oneResult(_)) + } + </testsuite> +} diff --git a/src/partest/scala/tools/partest/Results.scala b/src/partest/scala/tools/partest/Results.scala deleted file mode 100644 index 5d0e300136..0000000000 --- a/src/partest/scala/tools/partest/Results.scala +++ /dev/null @@ -1,121 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest - -import scala.collection.immutable - -trait Results { - self: Universe => - - /** A collection of tests for a Worker. - */ - case class TestsToRun(entities: List[TestEntity]) - - /** The response from a Worker who has been given TestsToRun. - */ - case class ResultsOfRun(results: immutable.Map[TestEntity, TestResult]) - - /** The result of a single test. (0: OK, 1: FAILED, 2: TIMEOUT) - */ - sealed abstract class TestResult(val state: Int, val description: String) { - def entity: TestEntity - - def passed = state == 0 - def colorize(s: String): String - def show(msg: String) = - if (!isShuttingDown) - showResult(colorize(description), msg) - - private def outputPrefix = if (isInsideAnt) "" else markNormal("partest: ") - private def name = src relativize entity.location // e.g. "neg/test.scala" - private def showResult(status: String, extraMsg: String) = - normal(outputPrefix + "[...]/%-40s [%s] %s\n".format(name, status, extraMsg)) - - override def equals(other: Any) = other match { - case x: TestResult => entity == x.entity - case _ => false - } - override def hashCode = entity.hashCode - override def toString = "%s [%s]".format(entity, description) - } - - class Success(val entity: TestEntity) extends TestResult(0, " OK ") { - def colorize(s: String) = markSuccess(s) - override def show(msg: String) = if (!isTerse) super.show(msg) - } - class Failure(val entity: TestEntity) extends TestResult(1, " FAILED ") { - def colorize(s: String) = markFailure(s) - - override def show(msg: String) = { - super.show(msg) - - if (isShowDiff || isTrace) - normal(entity.diffOutput) - - if (isShowLog || isTrace) - normal(toStringTrunc(entity.failureMessage(), 1600)) - } - override def toString = List(super.toString, toStringTrunc(entity.failureMessage(), 400)) mkString "\n" - } - class Timeout(val entity: TestEntity) extends TestResult(2, "TIME OUT") { - def colorize(s: String) = markFailure(s) - } - - object TestResult { - def apply(entity: TestEntity, success: Boolean) = - if (success) new Success(entity) - else new Failure(entity) - - def apply(entity: TestEntity, state: Int) = state match { - case 0 => new Success(entity) - case 1 => new Failure(entity) - case 2 => new Timeout(entity) - } - def unapply(x: Any) = x match { - case x: TestResult => Some((x.entity, x.state)) - case _ => None - } - } - - /** The combined results of any number of tests. - */ - case class CombinedTestResults( - passed: Int, - failed: Int, - elapsedMilliseconds: Long, - failures: List[TestResult] - ) { - // housekeeping - val elapsedSecs = elapsedMilliseconds / 1000 - val elapsedMins = elapsedSecs / 60 - val elapsedHrs = elapsedMins / 60 - val dispMins = elapsedMins - elapsedHrs * 60 - val dispSecs = elapsedSecs - elapsedMins * 60 - - def total = passed + failed - def hasFailures = failed > 0 - def exitCode = if (expectedErrors == failed) 0 else 1 - - def ++(x: CombinedTestResults) = CombinedTestResults( - passed + x.passed, - failed + x.failed, - elapsedMilliseconds + x.elapsedMilliseconds, - failures ::: x.failures - ) - - def elapsedString = "%02d:%02d:%02d".format(elapsedHrs, dispMins, dispSecs) - def failuresString = { - if (failures.isEmpty) "" - else "Summary of failures:" :: failures mkString ("\n", "\n", "") - } - - override def toString = - if (total == 0) "There were no tests to run." - else if (isDryRun) "%d tests would be run." format total - else if (hasFailures) "%d of %d tests failed (elapsed time: %s)".format(failed, total, elapsedString) + failuresString - else "All %d tests were successful (elapsed time: %s)".format(total, elapsedString) - } -}
\ No newline at end of file diff --git a/src/partest/scala/tools/partest/Runner.scala b/src/partest/scala/tools/partest/Runner.scala deleted file mode 100644 index 1a28e60896..0000000000 --- a/src/partest/scala/tools/partest/Runner.scala +++ /dev/null @@ -1,36 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - * @author Philipp Haller - */ - -package scala.tools -package partest - -import nsc.io._ - -object Runner { - def main(args: Array[String]) { - val runner = Partest(args: _*) - import runner._ - - if (args.isEmpty) return println(helpMsg) - if (isValidate) return validateAll() - - printConfigBanner() - - if (isCleanup) - cleanupAll() - - val result = launchTestSuite() - val exitCode = result.exitCode - val message = "\n" + result + "\n" - - if (exitCode == 0) success(message) - else failure(message) - - if (isStats) - showTestStatistics() - - System exit exitCode - } -} diff --git a/src/partest/scala/tools/partest/Statistics.scala b/src/partest/scala/tools/partest/Statistics.scala deleted file mode 100644 index 2ea3c6e8f0..0000000000 --- a/src/partest/scala/tools/partest/Statistics.scala +++ /dev/null @@ -1,46 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - * @author Philipp Haller - */ - -package scala.tools -package partest - -import scala.collection.mutable.HashMap - -trait Statistics { - /** Only collected when --stats is given. */ - lazy val testStatistics = new HashMap[String, Long] - - /** Given function and block of code, evaluates code block, - * calls function with milliseconds elapsed, and returns block result. - */ - def timed[T](f: Long => Unit)(body: => T): T = { - val start = System.currentTimeMillis - val result = body - val end = System.currentTimeMillis - - f(end - start) - result - } - /** Times body and returns both values. - */ - def timed2[T](body: => T): (Long, T) = { - var milliSeconds = 0L - val result = timed(x => milliSeconds = x)(body) - - (milliSeconds, result) - } - - def resultsToStatistics(results: Iterable[(_, Int)]): (Int, Int) = - (results partition (_._2 == 0)) match { - case (winners, losers) => (winners.size, losers.size) - } - - def recordTestTiming(name: String, milliseconds: Long) = - synchronized { testStatistics(name) = milliseconds } - - def showTestStatistics() { - testStatistics.toList sortBy (-_._2) foreach { case (k, v) => println("%s: %.2f seconds".format(k, (v.toDouble / 1000))) } - } -} diff --git a/src/partest/scala/tools/partest/Universe.scala b/src/partest/scala/tools/partest/Universe.scala deleted file mode 100644 index 942fc1a8be..0000000000 --- a/src/partest/scala/tools/partest/Universe.scala +++ /dev/null @@ -1,96 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala Parallel Testing ** -** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.tools -package partest - -import nsc.io._ -import category.AllCategories -import io.Logging - -/** The high level view of the partest infrastructure. - */ -abstract class Universe - extends Entities - with BuildContributors - with Logging - with Dispatcher - with Statistics - with Housekeeping - with Results - with PartestCompilation - with PartestSpec - with Config - with Alarms - with Actions - with Categories { - - /** The abstract values from which all else is derived. */ - def partestDir: Directory - def testBuildDir: Directory - def allCategories: List[TestCategory] - def selectedCategories: List[TestCategory] - - /** Some plausibly abstract types. */ - type TestBuild <: BuildContributor // e.g. quick, pack - type TestCategory <: AbsTestCategory // e.g. pos, neg, run - type TestEntity <: AbsTestEntity // e.g. files/pos/test25.scala - type TestSequence <: AbsTestSequence // e.g. compile, run, diff - - /** Although TestStep isn't much more than Function1 right now, - * it exists this way so it can become more capable. - */ - implicit def f1ToTestStep(f: TestEntity => Boolean): TestStep = - new TestStep { def apply(test: TestEntity) = f(test) } - - abstract class TestStep extends (TestEntity => Boolean) { - def apply(test: TestEntity): Boolean - } - - /** An umbrella category of tests, such as "pos" or "run". - */ - trait AbsTestCategory extends BuildContributor { - type TestSettings - - def kind: String - def testSequence: TestSequence - def denotesTest(location: Path): Boolean - - def createTest(location: Path): TestEntity - def createSettings(entity: TestEntity): TestSettings - def enumerate: List[TestEntity] - } - - /** A single test. It may involve multiple files, but only a - * single path is used to designate it. - */ - trait AbsTestEntity extends BuildContributor { - def category: TestCategory - def location: Path - def onException(x: Throwable): Unit - def testClasspath: String - - /** Most tests will use the sequence defined by the category, - * but the test can override and define a custom sequence. - */ - def testSequence: TestSequence - - /** True if this test recognizes the given path as a piece of it. - * For validation purposes. - */ - def acknowledges(path: Path): Boolean - } - - /** Every TestEntity is partly characterized by a series of actions - * which are applied to the TestEntity in the given order. The test - * passes if all those actions return true, fails otherwise. - */ - trait AbsTestSequence { - def actions: List[TestStep] - } -}
\ No newline at end of file diff --git a/src/partest/scala/tools/partest/ant/JavaTask.scala b/src/partest/scala/tools/partest/ant/JavaTask.scala deleted file mode 100644 index 6740554dd8..0000000000 --- a/src/partest/scala/tools/partest/ant/JavaTask.scala +++ /dev/null @@ -1,57 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala Parallel Testing ** -** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.tools -package partest -package ant - -import org.apache.tools.ant.Task -import org.apache.tools.ant.taskdefs.Java -import org.apache.tools.ant.types.Environment - -import scala.tools.nsc.io._ -import scala.tools.nsc.util.ClassPath -import cmd.Spec._ - -class JavaTask extends Java { - override def getTaskName() = "partest" - private val scalaRunnerClass = "scala.tools.nsc.MainGenericRunner" - private val partestRunnerClass = "scala.tools.partest.Runner" - def defaultJvmArgs = "-Xms64M -Xmx768M -Xss768K -XX:MaxPermSize=96M" - - protected def rootDir = prop("partest.rootdir") getOrElse (baseDir / "test").path - protected def partestJVMArgs = prop("partest.jvm.args") getOrElse defaultJvmArgs - protected def runnerArgs = List("-usejavacp", partestRunnerClass, "--javaopts", partestJVMArgs) - - private def baseDir = Directory(getProject.getBaseDir) - private def prop(s: String) = Option(getProject getProperty s) - private def jvmline(s: String) = returning(createJvmarg())(_ setLine s) - private def addArg(s: String) = returning(createArg())(_ setValue s) - - private def newKeyValue(key: String, value: String) = - returning(new Environment.Variable)(x => { x setKey key ; x setValue value }) - - def setDefaults() { - setFork(true) - setFailonerror(true) - getProject.setSystemProperties() - setClassname(scalaRunnerClass) - addSysproperty(newKeyValue("partest.is-in-ant", "true")) - jvmline(partestJVMArgs) - runnerArgs foreach addArg - - // do we want basedir or rootDir to be the cwd? - // setDir(Path(rootDir).jfile) - } - - override def init() = { - super.init() - setDefaults() - } -} - diff --git a/src/partest/scala/tools/partest/antlib.xml b/src/partest/scala/tools/partest/antlib.xml index af36f11368..b3b98e853f 100644 --- a/src/partest/scala/tools/partest/antlib.xml +++ b/src/partest/scala/tools/partest/antlib.xml @@ -1,3 +1,4 @@ <antlib> - <taskdef name="partest" classname="scala.tools.partest.ant.JavaTask"/> + <taskdef name="partest" + classname="scala.tools.partest.PartestTask"/> </antlib> diff --git a/src/partest/scala/tools/partest/category/AllCategories.scala b/src/partest/scala/tools/partest/category/AllCategories.scala deleted file mode 100644 index 953f80324b..0000000000 --- a/src/partest/scala/tools/partest/category/AllCategories.scala +++ /dev/null @@ -1,20 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala Parallel Testing ** -** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.tools -package partest -package category - -trait AllCategories extends Compiler with Analysis with Runner { - self: Universe => - - object Pos extends DirBasedCategory("pos") { lazy val testSequence: TestSequence = List(compile) } - object Neg extends DirBasedCategory("neg") { lazy val testSequence: TestSequence = List(checkFileRequired, not(compile), diff) } - object Run extends DirBasedCategory("run") { lazy val testSequence: TestSequence = List(compile, run, diff) } - object Jvm extends DirBasedCategory("jvm") { lazy val testSequence: TestSequence = List(compile, run, diff) } -} diff --git a/src/partest/scala/tools/partest/category/Analysis.scala b/src/partest/scala/tools/partest/category/Analysis.scala deleted file mode 100644 index 2c6c208ee5..0000000000 --- a/src/partest/scala/tools/partest/category/Analysis.scala +++ /dev/null @@ -1,64 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest -package category - -import java.lang.{ ClassLoader => JavaClassLoader } -import java.net.URL -import nsc.util.ScalaClassLoader -import nsc.io._ - -class PartestClassLoader(urls: Array[URL], parent: JavaClassLoader) extends ScalaClassLoader.URLClassLoader(urls, parent) { - def this(urls: Array[URL]) = this(urls, null) - def bytes(path: String) = findBytesForClassName(path) - def singleton(path: String) = tryToInitializeClass(path).get getField "MODULE$" get null - - /** Calls a method in an object via reflection. - */ - def apply[T](className: String, methodName: String)(args: Any*): T = { - def fail = error("Reflection failed on %s.%s".format(className, methodName)) - val clazz = tryToLoadClass(className) getOrElse fail - val obj = singleton(className) - val m = clazz.getMethods find (x => x.getName == methodName && x.getParameterTypes.size == args.size) getOrElse fail - - m.invoke(obj, args map (_.asInstanceOf[AnyRef]): _*).asInstanceOf[T] - } -} - -trait Analysis { - self: Universe => - - object Scalap extends DirBasedCategory("scalap") { - val testSequence: TestSequence = List(checkFileRequired, compile, run, diff) - override def denotesTest(p: Path) = p.isDirectory && (p.toDirectory.files exists (_.name == "result.test")) - override def createTest(location: Path) = new ScalapTest(location) - - class ScalapTest(val location: Path) extends TestEntity { - val category = Scalap - val scalapMain = "scala.tools.scalap.Main$" - val scalapMethod = "decompileScala" - - override def classpathPaths = super.classpathPaths :+ build.scalap - override def checkFile = File(location / "result.test") - - private def runnerURLs = build.classpathPaths ::: classpathPaths map (_.toURL) - private def createClassLoader = new PartestClassLoader(runnerURLs.toArray, this.getClass.getClassLoader) - - val isPackageObject = containsString("package object") - val suffix = if (isPackageObject) ".package" else "" - val className = location.name.capitalize + suffix - - override def run() = loggingResult { - def loader = createClassLoader - def bytes = loader.bytes(className) - - trace("scalap %s".format(className)) - if (isDryRun) "" - else loader[String](scalapMain, scalapMethod)(bytes, isPackageObject) - } - } - } -} diff --git a/src/partest/scala/tools/partest/category/Compiler.scala b/src/partest/scala/tools/partest/category/Compiler.scala deleted file mode 100644 index 49775d5031..0000000000 --- a/src/partest/scala/tools/partest/category/Compiler.scala +++ /dev/null @@ -1,140 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest -package category - -import nsc.io._ -import nsc.reporters._ -import nsc.{ Settings, CompilerCommand } -import scala.tools.nsc.interactive.RefinedBuildManager -import util.copyPath - -trait Compiler { - self: Universe => - - /** Resident Compiler. - * $SCALAC -d dir.obj -Xresident -sourcepath . "$@" - */ - object Res extends DirBasedCategory("res") { - lazy val testSequence: TestSequence = List(checkFileRequired, compile, diff) - - override def denotesTest(p: Path) = p.isDirectory && resFile(p).isFile - override def createTest(location: Path) = new ResidentTest(location.toDirectory) - - override def createSettings(entity: TestEntity): TestSettings = - returning(super.createSettings(entity)) { settings => - settings.resident.value = true - settings.sourcepath.value = entity.sourcesDir.path - } - - class ResidentTest(val location: Directory) extends TestEntity { - val category = Res - override def sourcesDir = categoryDir - - override def acknowledges(p: Path) = - super.acknowledges(p) || (resFile(location) isSame p) - - private def residentCompilerCommands = safeLines(resFile(location)) - private def compileResident(global: PartestGlobal, lines: List[String]) = { - def printPrompt = global inform "nsc> " - val results = - lines map { line => - printPrompt - trace("compile " + line) - isDryRun || global.partestCompile(toArgs(line) map (categoryDir / _ path), false) - } - - printPrompt - - /** Note - some res tests are really "neg" style tests, so we can't - * use the return value of the compile. The diff catches failures. - */ - true // results forall (_ == true) - } - - override def compile() = compileResident(newGlobal(Nil)._1, residentCompilerCommands) - } - private[Res] def resFile(p: Path) = p.toFile addExtension "res" - } - - object BuildManager extends DirBasedCategory("buildmanager") { - lazy val testSequence: TestSequence = List(checkFileRequired, compile, diff) - override def denotesTest(p: Path) = p.isDirectory && testFile(p).isFile - override def createTest(location: Path) = new BuildManagerTest(location.toDirectory) - - override def createSettings(entity: TestEntity): TestSettings = - returning[TestSettings](super.createSettings(entity)) { settings => - settings.Ybuildmanagerdebug.value = true - settings.sourcepath.value = entity.sourcesDir.path - } - - class PartestBuildManager(settings: Settings, val reporter: ConsoleReporter) extends RefinedBuildManager(settings) { - def errorFn(msg: String) = Console println msg - - override protected def newCompiler(newSettings: Settings) = - new BuilderGlobal(newSettings, reporter) - - private def filesToSet(pre: String, fs: List[String]): Set[AbstractFile] = - fs flatMap (s => Option(AbstractFile getFile (Path(settings.sourcepath.value) / s path))) toSet - - def buildManagerCompile(line: String): Boolean = { - val prompt = "builder > " - reporter printMessage (prompt + line) - val command = new CompilerCommand(toArgs(line), settings) - val files = filesToSet(settings.sourcepath.value, command.files) - - update(files, Set.empty) - true - } - } - - private[BuildManager] def testFile(p: Path) = (p / p.name addExtension "test").toFile - - class BuildManagerTest(val location: Directory) extends TestEntity { - val category = BuildManager - - override def sourcesDir = outDir - override def sourceFiles = Path onlyFiles (location walkFilter (_ != changesDir) filter isJavaOrScala toList) - override def checkFile = File(location / location.name addExtension "check") - - override def acknowledges(p: Path) = super.acknowledges(p) || (p isSame testFile(location)) - - def buildManagerCommands = safeLines(testFile(location)) - def changesDir = Directory(location / (location.name + ".changes")) - - override def compile() = { - val settings = createSettings(this) - val pbm = new PartestBuildManager(settings, newReporter(settings)) - - // copy files - for (source <- sourceFiles) { - val target = outDir / (location.normalize relativize source) - copyPath(source, target.toFile) - } - - def runUpdate(line: String) = { - val Array(srcName, replacement) = line split "=>" - copyPath(File(changesDir / replacement), File(outDir / srcName)) - } - - def sendCommand(line: String): Boolean = { - val compileRegex = """^>>compile (.*)$""".r - val updateRegex = """^>>update\s+(.*)""".r - trace("send: " + (line drop 2)) - - isDryRun || (line match { - case compileRegex(xs) => pbm.buildManagerCompile(xs) - case updateRegex(line) => runUpdate(line) - }) - } - - // send each line to the build manager - buildManagerCommands forall sendCommand - } - } - } -} - diff --git a/src/partest/scala/tools/partest/category/Runner.scala b/src/partest/scala/tools/partest/category/Runner.scala deleted file mode 100644 index 10bf5794a9..0000000000 --- a/src/partest/scala/tools/partest/category/Runner.scala +++ /dev/null @@ -1,108 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala Parallel Testing ** -** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.tools -package partest -package category - -import nsc.io._ - -trait Runner { - self: Universe => - - /** Shootout. - */ - object Shootout extends DirBasedCategory("shootout") { - lazy val testSequence: TestSequence = List(compile, run, diff) - - override def denotesTest(p: Path) = isScala(p) && runner(p).isFile - override def createTest(location: Path) = new ShootoutTest(location.toFile) - - class ShootoutTest(val location: File) extends TestEntity { - val category = Shootout - // The files in shootout are very free form, so acknowledge anything close. - override def acknowledges(p: Path) = - (p.parent.normalize isSame Shootout.root) && (p.name startsWith label) - - private def generated = File(outDir / "test.scala") - private def runnerFile = runner(location) - override def sourceFiles = List(generated) - - override def compile() = { - trace("generate %s from %s, %s".format(tracePath(generated), tracePath(location), tracePath(runnerFile))) - // generate source file (even on dry run, we need the path) - generated.writeAll(location.slurp(), runnerFile.slurp()) - - // compile generated file - super.compile() - } - } - - private[Shootout] def runner(p: Path) = p addExtension "runner" toFile - } - - object Scalacheck extends DirBasedCategory("scalacheck") { - lazy val testSequence: TestSequence = List(compile, run) - override def createTest(location: Path) = new ScalacheckTest(location) - - class ScalacheckTest(val location: Path) extends TestEntity { - val category = Scalacheck - - import build.{ scalacheck, forkjoin } - import org.scalacheck.Properties - import org.scalacheck.Test.{ checkProperties, defaultParams, Result } - - override def classpathPaths = super.classpathPaths ::: List(scalacheck, forkjoin) - private def arrayURLs = Array(scalacheck, outDir) map (_.toURL) - - /** For reasons I'm not entirely clear on, I've written all this - * to avoid a source dependency on scalacheck. - */ - class ScalacheckClassLoader extends PartestClassLoader(arrayURLs, this.getClass.getClassLoader) { - type ScalacheckResult = { def passed: Boolean } - - def propCallback(name: String, passed: Int, discarded: Int): Unit = () - def testCallback(name: String, result: AnyRef): Unit = () - - val test = singleton("Test$") - val params = apply[AnyRef]("org.scalacheck.Test$", "defaultParams")() - val result = apply[Seq[(String, AnyRef)]]("org.scalacheck.Test$", "checkProperties")(test, params, propCallback _, testCallback _) - - def allResults() = - for ((prop, res) <- result) yield { - ScalacheckTest.this.trace("%s: %s".format(prop, res)) - res.asInstanceOf[ScalacheckResult].passed - } - - def check() = allResults forall (_ == true) - } - - override def run() = { - trace("scalacheck runs via classloader with: %s".format(arrayURLs mkString ", ")) - isDryRun || (new ScalacheckClassLoader check) - } - } - } - - object Script extends DirBasedCategory("script") { - val testSequence: TestSequence = List(exec, diff) - override def createTest(location: Path) = new ScriptTest(location) - - class ScriptTest(val location: Path) extends TestEntity { - val category = Script - val scriptFile = if (location.isDirectory) location / (label + ".scala") else location - val argsFile = withExtension("args").toFile - def batFile = scriptFile changeExtension "bat" - def script = if (Properties.isWin) batFile else scriptFile - - override def acknowledges(p: Path) = super.acknowledges(p) || (List(argsFile, batFile) exists (_ isSame p)) - override def execCwd = Some(sourcesDir) - override def argumentsToExec = script.path :: safeArgs(argsFile) - } - } -}
\ No newline at end of file diff --git a/src/partest/scala/tools/partest/io/ANSIWriter.scala b/src/partest/scala/tools/partest/io/ANSIWriter.scala deleted file mode 100644 index 0ddcd97a5f..0000000000 --- a/src/partest/scala/tools/partest/io/ANSIWriter.scala +++ /dev/null @@ -1,58 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - * @author Philipp Haller - */ - -package scala.tools -package partest -package io - -import java.io.{ Writer, PrintWriter, OutputStream, OutputStreamWriter } - -object ANSIWriter { - val NONE = 0 - val SOME = 1 - val MANY = 2 - - def apply(isAnsi: Boolean) = if (isAnsi) MANY else NONE -} -import ANSIWriter._ - -class ANSIWriter(writer: Writer) extends PrintWriter(writer, true) { - def this(out: OutputStream) = this(new OutputStreamWriter(out)) - def colorful: Int = NONE - - protected val manyColors = List( - Console.BOLD + Console.BLACK, - Console.BOLD + Console.GREEN, - Console.BOLD + Console.RED, - Console.BOLD + Console.YELLOW, - Console.RESET - ) - protected val someColors = List( - Console.BOLD + Console.BLACK, - Console.RESET, - Console.BOLD + Console.BLACK, - Console.BOLD + Console.BLACK, - Console.RESET - ) - protected val noColors = List("", "", "", "", "") - - lazy val List(_outline, _success, _failure, _warning, _default) = colorful match { - case NONE => noColors - case SOME => someColors - case MANY => manyColors - case _ => noColors - } - - private def wrprint(msg: String): Unit = synchronized { - print(msg) - flush() - } - - def outline(msg: String) = wrprint(_outline + msg + _default) - def success(msg: String) = wrprint(_success + msg + _default) - def failure(msg: String) = wrprint(_failure + msg + _default) - def warning(msg: String) = wrprint(_warning + msg + _default) - def normal(msg: String) = wrprint(_default + msg) -} diff --git a/src/partest/scala/tools/partest/io/JUnitReport.scala b/src/partest/scala/tools/partest/io/JUnitReport.scala deleted file mode 100644 index 63ae200020..0000000000 --- a/src/partest/scala/tools/partest/io/JUnitReport.scala +++ /dev/null @@ -1,38 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest -package io - -/** This is disabled for the moment but I can fix it up if anyone - * is using it. - */ -class JUnitReport { - // create JUnit Report xml files if directory was specified - // def junitReport(dir: Directory) = { - // dir.mkdir() - // val report = testReport(set.kind, results, succs, fails) - // XML.save("%s/%s.xml".format(d.toAbsolute.path, set.kind), report) - // } - - // def oneResult(res: (TestEntity, Int)) = - // <testcase name={res._1.path}>{ - // res._2 match { - // case 0 => scala.xml.NodeSeq.Empty - // case 1 => <failure message="Test failed"/> - // case 2 => <failure message="Test timed out"/> - // } - // }</testcase> - // - // def testReport(kind: String, results: Iterable[(TestEntity, Int)], succs: Int, fails: Int) = { - // <testsuite name={kind} tests={(succs + fails).toString} failures={fails.toString}> - // <properties/> - // { - // results.map(oneResult(_)) - // } - // </testsuite> - // } - // -}
\ No newline at end of file diff --git a/src/partest/scala/tools/partest/io/Logging.scala b/src/partest/scala/tools/partest/io/Logging.scala deleted file mode 100644 index 52239ffb2c..0000000000 --- a/src/partest/scala/tools/partest/io/Logging.scala +++ /dev/null @@ -1,137 +0,0 @@ -package scala.tools -package partest -package io - -import java.io.{ StringWriter, PrintWriter, Writer } -import scala.tools.nsc.io._ -import scala.util.control.ControlThrowable - -trait Logging { - universe: Universe => - - class PartestANSIWriter extends ANSIWriter(Console.out) { - override def colorful: Int = ANSIWriter(universe.isAnsi) - private def printIf(cond: Boolean, msg: String) = - if (cond) { outline("debug: ") ; println(msg) } - - val verbose = printIf(isVerbose || isDebug, _: String) - val debug = printIf(isDebug, _: String) - } - - lazy val NestUI = new PartestANSIWriter() - - import NestUI.{ _outline, _success, _failure, _warning, _default } - - def markOutline(msg: String) = _outline + msg + _default - def markSuccess(msg: String) = _success + msg + _default - def markFailure(msg: String) = _failure + msg + _default - def markWarning(msg: String) = _warning + msg + _default - def markNormal(msg: String) = _default + msg - - def outline(msg: String) = NestUI outline msg - def success(msg: String) = NestUI success msg - def failure(msg: String) = NestUI failure msg - def warning(msg: String) = NestUI warning msg - def normal(msg: String) = NestUI normal msg - - def verbose(msg: String) = NestUI verbose msg - def debug(msg: String) = NestUI debug msg - - trait EntityLogging { - self: TestEntity => - - lazy val logWriter = new LogWriter(logFile) - - /** Redirect stdout and stderr to logFile, run body, return result. - */ - def loggingOutAndErr[T](body: => T): T = { - val log = logFile.printStream(append = true) - - try Console.withOut(log) { - Console.withErr(log) { - body - } - } - finally log.close() - } - - /** What to print in a failure summary. - */ - def failureMessage() = if (diffOutput != "") diffOutput else safeSlurp(logFile) - - /** For tracing. Outputs a line describing the next action. tracePath - * is a path wrapper which prints name or full path depending on verbosity. - */ - def trace(msg: String) = if (isTrace || isDryRun) System.err.println(">> [%s] %s".format(label, msg)) - - def tracePath(path: Path): String = if (isVerbose) path.path else path.name - def tracePath(path: String): String = tracePath(Path(path)) - - /** v == verbose. - */ - def vtrace(msg: String) = if (isVerbose) trace(msg) - - /** Run body, writes result to logFile. Any throwable is - * caught, stringified, and written to the log. - */ - def loggingResult(body: => String) = - try returning(true)(_ => logFile writeAll body) - catch { - case x: ControlThrowable => throw x - case x: InterruptedException => debug(this + " received interrupt, failing.\n") ; false - case x: Throwable => logException(x) - } - - def throwableToString(x: Throwable): String = { - val w = new StringWriter - x.printStackTrace(new PrintWriter(w)) - w.toString - } - - def warnAndLog(str: String) = { - warning(toStringTrunc(str, 800)) - logWriter append str - } - - def warnAndLogException(msg: String, ex: Throwable) = - warnAndLog(msg + throwableToString(ex)) - - def deleteLog(force: Boolean = false) = - if (universe.isNoCleanup && !force) debug("Not cleaning up " + logFile) - else logFile.deleteIfExists() - - def onException(x: Throwable) { logException(x) } - def logException(x: Throwable) = { - val msg = throwableToString(x) - if (!isTerse) - normal(msg) - - logWriter append msg - false - } - } - - /** A writer which doesn't create the file until a write comes in. - */ - class LazilyCreatedWriter(log: File) extends Writer { - @volatile private var isCreated = false - private lazy val underlying = { - isCreated = true - log.bufferedWriter() - } - - def flush() = if (isCreated) underlying.flush() - def close() = if (isCreated) underlying.close() - def write(chars: Array[Char], off: Int, len: Int) = { - underlying.write(chars, off, len) - underlying.flush() - } - } - - class LogWriter(log: File) extends PrintWriter(new LazilyCreatedWriter(log), true) { - override def print(s: String) = { - super.print(s) - flush() - } - } -}
\ No newline at end of file diff --git a/src/partest/scala/tools/partest/nest/AntRunner.scala b/src/partest/scala/tools/partest/nest/AntRunner.scala new file mode 100644 index 0000000000..cb819720fc --- /dev/null +++ b/src/partest/scala/tools/partest/nest/AntRunner.scala @@ -0,0 +1,30 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io.File +import scala.tools.nsc.io.{ Directory } + +class AntRunner extends DirectRunner { + + val fileManager = new FileManager { + var JAVACMD: String = "java" + var JAVAC_CMD: String = "javac" + var CLASSPATH: String = _ + var LATEST_LIB: String = _ + val testRootPath: String = "test" + val testRootDir: Directory = Directory(testRootPath) + } + + def reflectiveRunTestsForFiles(kindFiles: Array[File], kind: String) = + runTestsForFiles(kindFiles.toList, kind) +} diff --git a/src/partest/scala/tools/partest/nest/CompileManager.scala b/src/partest/scala/tools/partest/nest/CompileManager.scala new file mode 100644 index 0000000000..22568ad2d0 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/CompileManager.scala @@ -0,0 +1,197 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import scala.tools.nsc.{ Global, Settings, CompilerCommand, FatalError, io } +import scala.tools.nsc.reporters.{ Reporter, ConsoleReporter } +import scala.tools.nsc.util.ClassPath +import scala.tools.util.PathResolver +import io.Path + +import java.io.{ File, BufferedReader, PrintWriter, FileReader, Writer, FileWriter, StringWriter } +import File.pathSeparator + +class ExtConsoleReporter(override val settings: Settings, reader: BufferedReader, var writer: PrintWriter) +extends ConsoleReporter(settings, reader, writer) { + def this(settings: Settings) = this(settings, Console.in, new PrintWriter(new FileWriter("/dev/null"))) +} + +abstract class SimpleCompiler { + def compile(out: Option[File], files: List[File], kind: String, log: File): Boolean +} + +class TestSettings(fileMan: FileManager) extends Settings(_ => ()) { } + +class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { + def newGlobal(settings: Settings, reporter: Reporter): Global = + new Global(settings, reporter) + + def newGlobal(settings: Settings, logWriter: FileWriter): Global = { + val rep = newReporter(settings, logWriter) + rep.shortname = true + newGlobal(settings, rep) + } + + def newSettings(out: Option[String]) = { + val settings = new TestSettings(fileManager) + settings.usejavacp.value = true + settings.deprecation.value = true + settings.nowarnings.value = false + settings.encoding.value = "ISO-8859-1" // XXX why? + + val classpathElements = settings.classpath.value :: fileManager.LATEST_LIB :: out.toList + settings.classpath.value = ClassPath.join(classpathElements: _*) + out foreach (settings.outdir.value = _) + + settings + } + + def newReporter(sett: Settings, writer: Writer = new StringWriter) = + new ExtConsoleReporter(sett, Console.in, new PrintWriter(writer)) + + private def updatePluginPath(options: String): String = { + val dir = fileManager.testRootDir + def absolutize(path: String) = Path(path) match { + case x if x.isAbsolute => x.path + case x => (fileManager.testRootDir / x).toAbsolute.path + } + + val (opt1, opt2) = (options split "\\s").toList partition (_ startsWith "-Xplugin:") + val plugins = opt1 map (_ stripPrefix "-Xplugin:") flatMap (_ split pathSeparator) map absolutize + val pluginOption = if (opt1.isEmpty) Nil else List("-Xplugin:" + (plugins mkString pathSeparator)) + + (opt2 ::: pluginOption) mkString " " + } + + def compile(out: Option[File], files: List[File], kind: String, log: File): Boolean = { + val testSettings = newSettings(out map (_.getAbsolutePath)) + val logWriter = new FileWriter(log) + + // check whether there is a ".flags" file + val flagsFileName = "%s.flags" format (basename(log.getName) dropRight 4) // 4 is "-run" or similar + val argString = (io.File(log).parent / flagsFileName) ifFile (x => updatePluginPath(x.slurp())) getOrElse "" + val allOpts = fileManager.SCALAC_OPTS+" "+argString + val args = (allOpts split "\\s").toList + + NestUI.verbose("scalac options: "+allOpts) + + val command = new CompilerCommand(args, testSettings) + val global = newGlobal(command.settings, logWriter) + val testRep: ExtConsoleReporter = global.reporter.asInstanceOf[ExtConsoleReporter] + + val testFileFn: (File, FileManager) => TestFile = kind match { + case "pos" => PosTestFile.apply + case "neg" => NegTestFile.apply + case "run" => RunTestFile.apply + case "jvm" => JvmTestFile.apply + case "shootout" => ShootoutTestFile.apply + case "scalap" => ScalapTestFile.apply + case "scalacheck" => ScalaCheckTestFile.apply + } + val test: TestFile = testFileFn(files.head, fileManager) + test.defineSettings(command.settings, out.isEmpty) + val toCompile = files map (_.getPath) + + try { + NestUI.verbose("compiling "+toCompile) + try new global.Run compile toCompile + catch { + case FatalError(msg) => + testRep.error(null, "fatal error: " + msg) + } + + testRep.printSummary + testRep.writer.flush + testRep.writer.close + } + catch { + case e => + e.printStackTrace() + return false + } + finally logWriter.close() + + !testRep.hasErrors + } +} + +// 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 /*ReflectiveCompiler*/ 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) + } +} diff --git a/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala b/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala new file mode 100644 index 0000000000..58d16a3f45 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala @@ -0,0 +1,190 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io.{ File, FilenameFilter, IOException, StringWriter } +import java.net.URI +import scala.util.Properties.{ propOrElse, scalaCmd, scalacCmd } +import scala.tools.util.PathResolver +import scala.tools.nsc.{ Settings } +import scala.tools.nsc.{ io, util } +import util.{ ClassPath } +import io.{ Path, Directory } +import File.pathSeparator +import ClassPath.{ join } +import PathResolver.{ Environment, Defaults } +import RunnerUtils._ + + +class ConsoleFileManager extends FileManager { + var testBuild: Option[String] = PartestDefaults.testBuild + def testBuildFile = testBuild map (testParent / _) + + var testClasses: Option[String] = None + + def this(buildPath: String, rawClasses: Boolean) = { + this() + if (rawClasses) + testClasses = Some(buildPath) + else + testBuild = Some(buildPath) + // re-run because initialization of default + // constructor must be updated + findLatest() + } + + def this(buildPath: String) = { + this(buildPath, false) + } + + def this(buildPath: String, rawClasses: Boolean, moreOpts: String) = { + this(buildPath, rawClasses) + SCALAC_OPTS = SCALAC_OPTS+" "+moreOpts + } + + lazy val srcDir = PathSettings.srcDir + lazy val testRootDir = PathSettings.testRoot + lazy val testRootPath = testRootDir.toAbsolute.path + def testParent = testRootDir.parent + + var CLASSPATH = PartestDefaults.classPath + var JAVACMD = PartestDefaults.javaCmd + var JAVAC_CMD = PartestDefaults.javacCmd + + + NestUI.verbose("CLASSPATH: "+CLASSPATH) + + if (!srcDir.isDirectory) { + NestUI.failure("Source directory \"" + srcDir.path + "\" not found") + exit(1) + } + + CLASSPATH = { + val libs = (srcDir / Directory("lib")).files filter (_ hasExtension "jar") map (_.normalize.path) + + // add all jars in libs + (CLASSPATH :: libs.toList) mkString pathSeparator + } + + def findLatest() { + NestUI.verbose("test parent: "+testParent) + + def prefixFileWith(parent: File, relPath: String) = (io.File(parent) / relPath).normalize + def prefixFile(relPath: String) = (testParent / relPath).normalize + + if (!testClasses.isEmpty) { + testClassesDir = Path(testClasses.get).normalize.toDirectory + NestUI.verbose("Running with classes in "+testClassesDir) + + latestFile = testClassesDir.parent / "bin" + latestLibFile = testClassesDir / "library" + latestCompFile = testClassesDir / "compiler" + latestPartestFile = testClassesDir / "partest" + latestFjbgFile = testParent / "lib" / "fjbg.jar" + } + else if (testBuild.isDefined) { + val dir = Path(testBuild.get) + NestUI.verbose("Running on "+dir) + latestFile = dir / "bin" + latestLibFile = dir / "lib/scala-library.jar" + latestCompFile = dir / "lib/scala-compiler.jar" + latestPartestFile = dir / "lib/scala-partest.jar" + } + else { + def setupQuick() { + NestUI.verbose("Running build/quick") + latestFile = prefixFile("build/quick/bin") + latestLibFile = prefixFile("build/quick/classes/library") + latestCompFile = prefixFile("build/quick/classes/compiler") + latestPartestFile = prefixFile("build/quick/classes/partest") + } + + def setupInst() { + NestUI.verbose("Running dist (installed)") + val p = testParent.getParentFile + latestFile = prefixFileWith(p, "bin") + latestLibFile = prefixFileWith(p, "lib/scala-library.jar") + latestCompFile = prefixFileWith(p, "lib/scala-compiler.jar") + latestPartestFile = prefixFileWith(p, "lib/scala-partest.jar") + } + + def setupDist() { + NestUI.verbose("Running dists/latest") + latestFile = prefixFile("dists/latest/bin") + latestLibFile = prefixFile("dists/latest/lib/scala-library.jar") + latestCompFile = prefixFile("dists/latest/lib/scala-compiler.jar") + latestPartestFile = prefixFile("dists/latest/lib/scala-partest.jar") + } + + def setupPack() { + NestUI.verbose("Running build/pack") + latestFile = prefixFile("build/pack/bin") + latestLibFile = prefixFile("build/pack/lib/scala-library.jar") + latestCompFile = prefixFile("build/pack/lib/scala-compiler.jar") + latestPartestFile = prefixFile("build/pack/lib/scala-partest.jar") + } + + val dists = testParent / "dists" + val build = testParent / "build" + // in case of an installed dist, testRootDir is one level deeper + val bin = testParent.parent / "bin" + + def mostRecentOf(base: String, names: String*) = + names map (x => prefixFile(base + "/" + x).lastModified) reduceLeft (_ max _) + + // detect most recent build + val quickTime = mostRecentOf("build/quick/classes", "compiler/compiler.properties", "library/library.properties") + val packTime = mostRecentOf("build/pack/lib", "scala-compiler.jar", "scala-library.jar") + val distTime = mostRecentOf("dists/latest/lib", "scala-compiler.jar", "scala-library.jar") + val instTime = mostRecentOf("lib", "scala-compiler.jar", "scala-library.jar") + + val pairs = Map( + (quickTime, () => setupQuick()), + (packTime, () => setupPack()), + (distTime, () => setupDist()), + (instTime, () => setupInst()) + ) + + // run setup based on most recent time + pairs(pairs.keys max)() + + latestFjbgFile = prefixFile("lib/fjbg.jar") + } + + LATEST_LIB = latestLibFile.getAbsolutePath + } + + var LATEST_LIB: String = "" + + var latestFile: File = _ + var latestLibFile: File = _ + var latestCompFile: File = _ + var latestPartestFile: File = _ + var latestFjbgFile: File = _ + var testClassesDir: Directory = _ + // initialize above fields + findLatest() + + var testFiles: List[io.Path] = Nil + + def getFiles(kind: String, cond: Path => Boolean): List[File] = { + def ignoreDir(p: Path) = List("svn", "obj") exists (p hasExtension _) + + val dir = Directory(srcDir / kind) + + if (dir.isDirectory) NestUI.verbose("look in %s for tests" format dir) + else NestUI.failure("Directory '%s' not found" format dir) + + val files = + if (testFiles.nonEmpty) testFiles filter (_.parent isSame dir) + else dir.list filterNot ignoreDir filter cond toList + + ( if (failed) files filter (x => logFileExists(x, kind)) else files ) map (_.jfile) + } +} diff --git a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala new file mode 100644 index 0000000000..eae79f23af --- /dev/null +++ b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala @@ -0,0 +1,209 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io.{File, PrintStream, FileOutputStream, BufferedReader, + InputStreamReader, StringWriter, PrintWriter} +import utils.Properties._ +import RunnerUtils._ +import scala.tools.nsc.Properties.{ versionMsg, setProp } +import scala.tools.nsc.util.CommandLineParser +import scala.tools.nsc.io +import scala.tools.nsc.interpreter.returning +import io.{ Path, Process } + +class ConsoleRunner extends DirectRunner { + import PathSettings.{ srcDir, testRoot } + + case class TestSet(kind: String, filter: Path => Boolean, msg: String) + + val testSets = { + val pathFilter: Path => Boolean = _ hasExtension "scala" + + List( + TestSet("pos", pathFilter, "Testing compiler (on files whose compilation should succeed)"), + TestSet("neg", pathFilter, "Testing compiler (on files whose compilation should fail)"), + TestSet("run", pathFilter, "Testing JVM backend"), + TestSet("jvm", pathFilter, "Testing JVM backend"), + TestSet("res", x => x.isFile && (x hasExtension "res"), "Testing resident compiler"), + TestSet("buildmanager", _.isDirectory, "Testing Build Manager"), + TestSet("shootout", pathFilter, "Testing shootout tests"), + TestSet("script", pathFilter, "Testing script tests"), + TestSet("scalacheck", pathFilter, "Testing ScalaCheck tests"), + TestSet("scalap", _.isDirectory, "Run scalap decompiler tests") + ) + } + + var fileManager: ConsoleFileManager = _ + + private var testFiles: List[File] = List() + private val errors = PartestDefaults.errorCount + private val testSetKinds = testSets map (_.kind) + private val testSetArgs = testSets map ("--" + _.kind) + private val testSetArgMap = testSetArgs zip testSets toMap + + def denotesTestSet(arg: String) = testSetArgs contains arg + def denotesTestFile(arg: String) = (arg endsWith ".scala") || (arg endsWith ".res") + def denotesTestDir(arg: String) = Path(arg).isDirectory + def denotesTestPath(arg: String) = denotesTestDir(arg) || denotesTestFile(arg) + + private def printVersion { NestUI outline (versionMsg + "\n") } + + private val unaryArgs = List( + "--pack", "--all", "--verbose", "--show-diff", "--show-log", + "--failed", "--version", "--ansi", "--debug" + ) ::: testSetArgs + + private val binaryArgs = List( + "--grep", "--srcpath", "--buildpath", "--classpath" + ) + + def main(argstr: String) { + val parsed = CommandLineParser(argstr) withUnaryArgs unaryArgs withBinaryArgs binaryArgs + val args = parsed.residualArgs + + /** Early return on no args, version, or invalid args */ + if (argstr == "") return NestUI.usage() + if (parsed isSet "--version") return printVersion + if (args exists (x => !denotesTestPath(x))) { + val invalid = (args filterNot denotesTestPath).head + NestUI.failure("Invalid argument '%s'\n" format invalid) + return NestUI.usage() + } + + parsed get "--srcpath" foreach (x => setProp("partest.srcdir", x)) + + fileManager = + if (parsed isSet "--buildpath") new ConsoleFileManager(parsed("--buildpath")) + else if (parsed isSet "--classpath") new ConsoleFileManager(parsed("--classpath"), true) + else if (parsed isSet "--pack") new ConsoleFileManager("build/pack") + else new ConsoleFileManager // auto detection, see ConsoleFileManager.findLatest + + def argNarrowsTests(x: String) = denotesTestSet(x) || denotesTestFile(x) || denotesTestDir(x) + + NestUI._verbose = parsed isSet "--verbose" + fileManager.showDiff = parsed isSet "--show-diff" + fileManager.showLog = parsed isSet "--show-log" + fileManager.failed = parsed isSet "--failed" + + if (parsed isSet "--ansi") NestUI initialize NestUI.MANY + if (parsed isSet "--timeout") fileManager.timeout = parsed("--timeout") + if (parsed isSet "--debug") setProp("partest.debug", "true") + + def addTestFile(file: File) = { + if (!file.exists) + NestUI.failure("Test file '%s' not found, skipping.\n" format file) + else { + NestUI.verbose("adding test file " + file) + testFiles +:= file + } + } + + // If --grep is given we suck in every file it matches. + parsed get "--grep" foreach { expr => + val allFiles = srcDir.deepList() filter (_ hasExtension "scala") map (_.toFile) toList + val files = allFiles filter (_.slurp() contains expr) + + if (files.isEmpty) NestUI.failure("--grep string '%s' matched no files." format expr) + else NestUI.verbose("--grep string '%s' matched %d file(s)".format(expr, files.size)) + + files foreach (x => addTestFile(x.jfile)) + } + args foreach (x => addTestFile(new File(x))) + + // If no file arguments were given, we assume --all + val enabledTestSets: List[TestSet] = { + val enabledArgs = testSetArgs filter parsed.isSet + + if (args.isEmpty && !(parsed isSet "--grep") && (enabledArgs.isEmpty || (parsed isSet "--all"))) testSets + else enabledArgs map testSetArgMap + } + + val dir = + if (fileManager.testClasses.isDefined) fileManager.testClassesDir + else fileManager.testBuildFile getOrElse { + fileManager.latestCompFile.getParentFile.getParentFile.getCanonicalFile + } + + val vmBin = javaHome + File.separator + "bin" + val vmName = "%s (build %s, %s)".format(javaVmName, javaVmVersion, javaVmInfo) + val vmOpts = fileManager.JAVA_OPTS + + NestUI.verbose("enabled test sets: " + (enabledTestSets map (_.kind) mkString " ")) + + List( + "Scala compiler classes in: " + dir, + "Scala version is: " + versionMsg, + "Scalac options are: " + fileManager.SCALAC_OPTS, + "Java binaries in: " + vmBin, + "Java runtime is: " + vmName, + "Java options are: " + vmOpts, + "Source directory is: " + srcDir, + "" + ) foreach (x => NestUI outline (x + "\n")) + + val start = System.currentTimeMillis + val (successes, failures) = testCheckAll(enabledTestSets) + val end = System.currentTimeMillis + + val total = successes + failures + + val elapsedSecs = (end - start)/1000 + val elapsedMins = elapsedSecs/60 + val elapsedHrs = elapsedMins/60 + val dispMins = elapsedMins - elapsedHrs * 60 + val dispSecs = elapsedSecs - elapsedMins * 60 + + val dispElapsed = { + def form(num: Long) = if (num < 10) "0"+num else ""+num + form(elapsedHrs)+":"+form(dispMins)+":"+form(dispSecs) + } + + println + if (failures == 0) + NestUI.success("All of "+total+" tests were successful (elapsed time: "+dispElapsed+")\n") + else + NestUI.failure(failures+" of "+total+" tests failed (elapsed time: "+dispElapsed+")\n") + + System exit ( if (failures == errors) 0 else 1 ) + } + + def runTests(testSet: TestSet): (Int, Int) = { + val TestSet(kind, filter, msg) = testSet + + fileManager.getFiles(kind, filter) match { + case Nil => NestUI.verbose("test dir empty\n") ; (0, 0) + case files => + NestUI.verbose("test files: "+files) + NestUI.outline("\n"+msg+"\n") + resultsToStatistics(runTestsForFiles(files, kind)) + } + } + + /** + * @return (success count, failure count) + */ + def testCheckAll(enabledSets: List[TestSet]): (Int, Int) = { + def kindOf(f: File) = (srcDir relativize Path(f).normalize).segments.head + + val (valid, invalid) = testFiles partition (x => testSetKinds contains kindOf(x)) + invalid foreach (x => NestUI.failure("Invalid test file '%s', skipping.\n" format x)) + + val runTestsFileLists = + for ((kind, files) <- valid groupBy kindOf toList) yield { + NestUI.outline("\nTesting individual files\n") + resultsToStatistics(runTestsForFiles(files, kind)) + } + + NestUI.verbose("Run sets: "+enabledSets) + val results = runTestsFileLists ::: (enabledSets map runTests) + + (results map (_._1) sum, results map (_._2) sum) + } +} diff --git a/src/partest/scala/tools/partest/io/Diff.java b/src/partest/scala/tools/partest/nest/Diff.java index c7a3d42f30..abd09d0293 100644 --- a/src/partest/scala/tools/partest/io/Diff.java +++ b/src/partest/scala/tools/partest/nest/Diff.java @@ -1,6 +1,6 @@ // $Id$ -package scala.tools.partest.io; +package scala.tools.partest.nest; import java.util.Hashtable; diff --git a/src/partest/scala/tools/partest/io/DiffPrint.java b/src/partest/scala/tools/partest/nest/DiffPrint.java index 2b2ad93ec7..494bc06e4a 100644 --- a/src/partest/scala/tools/partest/io/DiffPrint.java +++ b/src/partest/scala/tools/partest/nest/DiffPrint.java @@ -1,6 +1,6 @@ // $Id$ -package scala.tools.partest.io; +package scala.tools.partest.nest; import java.io.*; import java.util.Vector; diff --git a/src/partest/scala/tools/partest/nest/DirectRunner.scala b/src/partest/scala/tools/partest/nest/DirectRunner.scala new file mode 100644 index 0000000000..f774320f4e --- /dev/null +++ b/src/partest/scala/tools/partest/nest/DirectRunner.scala @@ -0,0 +1,78 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io.{File, PrintStream, FileOutputStream, BufferedReader, + InputStreamReader, StringWriter, PrintWriter} +import java.util.StringTokenizer +import scala.util.Properties.{ setProp } +import scala.tools.nsc.io.Directory + +import scala.actors.Actor._ +import scala.actors.TIMEOUT + +trait DirectRunner { + + def fileManager: FileManager + + import PartestDefaults.numActors + + if (isPartestDebug) + scala.actors.Debug.level = 3 + + if (PartestDefaults.poolSize.isEmpty) { + scala.actors.Debug.info("actors.corePoolSize not defined") + setProp("actors.corePoolSize", "16") + } + + def runTestsForFiles(kindFiles: List[File], kind: String): scala.collection.immutable.Map[String, Int] = { + val len = kindFiles.length + val (testsEach, lastFrag) = (len/numActors, len%numActors) + val last = numActors-1 + val workers = for (i <- List.range(0, numActors)) yield { + val toTest = kindFiles.slice(i*testsEach, (i+1)*testsEach) + val worker = new Worker(fileManager) + worker.start() + if (i == last) + worker ! RunTests(kind, (kindFiles splitAt (last*testsEach))._2) + else + worker ! RunTests(kind, toTest) + worker + } + + var logsToDelete: List[File] = List() + var outdirsToDelete: List[File] = List() + var results = new scala.collection.immutable.HashMap[String, Int] + workers foreach { w => + receiveWithin(3600 * 1000) { + case Results(res, logs, outdirs) => + logsToDelete :::= logs filter (_.toDelete) + outdirsToDelete :::= outdirs + results ++= res + case TIMEOUT => + // add at least one failure + NestUI.verbose("worker timed out; adding failed test") + results += ("worker timed out; adding failed test" -> 2) + } + } + + if (isPartestDebug) + fileManager.showTestTimings() + + if (!isPartestDebug) { + for (x <- logsToDelete ::: outdirsToDelete) { + NestUI.verbose("deleting "+x) + Directory(x).deleteRecursively() + } + } + + results + } + +} diff --git a/src/partest/scala/tools/partest/nest/FileManager.scala b/src/partest/scala/tools/partest/nest/FileManager.scala new file mode 100644 index 0000000000..bdbb34b3c4 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/FileManager.scala @@ -0,0 +1,110 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io.{File, FilenameFilter, IOException, StringWriter, + FileInputStream, FileOutputStream, BufferedReader, + FileReader, PrintWriter, FileWriter} +import java.net.URI +import scala.tools.nsc.io.{ Path, Directory } +import scala.collection.mutable.HashMap + +trait FileManager { + /** + * Compares two files using a Java implementation of the GNU diff + * available at http://www.bmsi.com/java/#diff. + * + * @param f1 the first file to be compared + * @param f2 the second file to be compared + * @return the text difference between the compared files + */ + def compareFiles(f1: File, f2: File): String = { + val diffWriter = new StringWriter + val args = Array(f1.getCanonicalPath(), f2.getCanonicalPath()) + + DiffPrint.doDiff(args, diffWriter) + val res = diffWriter.toString + if (res startsWith "No") "" else res + } + + def testRootDir: Directory + def testRootPath: String + + var JAVACMD: String + var JAVAC_CMD: String + + var CLASSPATH: String + var LATEST_LIB: String + + var showDiff = false + var showLog = false + var failed = false + + var SCALAC_OPTS = PartestDefaults.scalacOpts + var JAVA_OPTS = PartestDefaults.javaOpts + var timeout = PartestDefaults.timeout + + /** Only when --debug is given. */ + lazy val testTimings = new HashMap[String, Long] + def recordTestTiming(name: String, milliseconds: Long) = + synchronized { testTimings(name) = milliseconds } + def showTestTimings() { + testTimings.toList sortBy (-_._2) foreach { case (k, v) => println("%s: %s".format(k, v)) } + } + + def getLogFile(dir: File, fileBase: String, kind: String): LogFile = + new LogFile(dir, fileBase + "-" + kind + ".log") + + def getLogFile(file: File, kind: String): LogFile = { + val dir = file.getParentFile + val fileBase = basename(file.getName) + getLogFile(dir, fileBase, kind) + } + + def logFileExists(file: File, kind: String) = + getLogFile(file, kind).canRead + + def overwriteFileWith(dest: File, file: File) = + dest.isFile && copyFile(file, dest) + + + def copyFile(from: File, dest: File): Boolean = { + def copyFile0(from: File, to: File): Boolean = + try { + val appender = StreamAppender(from, to) + appender.run() + appender.closeAll() + true + } catch { + case _: IOException => false + } + + if (from.isDirectory) { + assert(dest.isDirectory, "cannot copy directory to file") + val subDir:Directory = Path(dest) / Directory(from.getName) + subDir.createDirectory() + from.listFiles.toList.forall(copyFile(_, subDir)) + } else + copyFile0(from, if (dest.isDirectory) new File(dest, from.getName) else dest) + } + + def mapFile(file: File, suffix: String, dir: File, replace: String => String) { + val tmpFile = File.createTempFile("tmp", suffix, dir) // prefix required by API + + val appender = StreamAppender(file, tmpFile) + appender.runAndMap(replace) + appender.closeAll() + + val appender2 = StreamAppender(tmpFile, file) + appender2.run() + appender2.closeAll() + + tmpFile.delete() + } +} diff --git a/src/partest/scala/tools/partest/nest/NestRunner.scala b/src/partest/scala/tools/partest/nest/NestRunner.scala new file mode 100644 index 0000000000..158521875e --- /dev/null +++ b/src/partest/scala/tools/partest/nest/NestRunner.scala @@ -0,0 +1,16 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +object NestRunner { + def main(args: Array[String]) { + val argstr = args.mkString(" ") + (new ReflectiveRunner).main(argstr) + } +} diff --git a/src/partest/scala/tools/partest/nest/NestUI.scala b/src/partest/scala/tools/partest/nest/NestUI.scala new file mode 100644 index 0000000000..efff4e8375 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/NestUI.scala @@ -0,0 +1,118 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io.PrintWriter + +object NestUI { + + val NONE = 0 + val SOME = 1 + val MANY = 2 + + private var _outline = "" + private var _success = "" + private var _failure = "" + private var _warning = "" + private var _default = "" + + def initialize(number: Int) = number match { + case MANY => + _outline = Console.BOLD + Console.BLACK + _success = Console.BOLD + Console.GREEN + _failure = Console.BOLD + Console.RED + _warning = Console.BOLD + Console.YELLOW + _default = Console.RESET + case SOME => + _outline = Console.BOLD + Console.BLACK + _success = Console.RESET + _failure = Console.BOLD + Console.BLACK + _warning = Console.BOLD + Console.BLACK + _default = Console.RESET + case _ => + } + + def outline(msg: String) = print(_outline + msg + _default) + def outline(msg: String, wr: PrintWriter) = synchronized { + wr.print(_outline + msg + _default) + } + + def success(msg: String) = print(_success + msg + _default) + def success(msg: String, wr: PrintWriter) = synchronized { + wr.print(_success + msg + _default) + } + + def failure(msg: String) = print(_failure + msg + _default) + def failure(msg: String, wr: PrintWriter) = synchronized { + wr.print(_failure + msg + _default) + } + + def warning(msg: String) = print(_warning + msg + _default) + def warning(msg: String, wr: PrintWriter) = synchronized { + wr.print(_warning + msg + _default) + } + + def normal(msg: String) = print(_default + msg) + def normal(msg: String, wr: PrintWriter) = synchronized { + wr.print(_default + msg) + } + + def usage() { + println("Usage: NestRunner [<options>] [<testfile> ..] [<resfile>]") + println(" <testfile>: list of files ending in '.scala'") + println(" <resfile>: a file not ending in '.scala'") + 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(" --buildmanager run Build Manager tests") + println(" --scalacheck run ScalaCheck tests") + println(" --script run script runner tests") + println(" --shootout run shootout tests") + println(" --grep <expr> run all tests whose source file contains <expr>") + println + println(" Other options:") + println(" --pack pick compiler/library in build/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(" ex.: --buildpath build/pack") + println(" --classpath set (absolute) path to build classes") + println(" --srcpath set (relative) path to test source files") + println(" ex.: --srcpath pending") + println(" --debug enable debugging output") + println + println(utils.Properties.versionString) + println("maintained by Philipp Haller (EPFL)") + exit(1) + } + + var _verbose = false + var _debug = false + + def verbose(msg: String) { + if (_verbose) { + outline("debug: ") + println(msg) + } + } + def debug(msg: String) { + if (isPartestDebug) { + outline("debug: ") + println(msg) + } + } +} diff --git a/src/partest/scala/tools/partest/nest/PathSettings.scala b/src/partest/scala/tools/partest/nest/PathSettings.scala new file mode 100644 index 0000000000..41bba5782e --- /dev/null +++ b/src/partest/scala/tools/partest/nest/PathSettings.scala @@ -0,0 +1,41 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + */ + +package scala.tools.partest +package nest + +import scala.tools.nsc.Properties.{ setProp, propOrEmpty, propOrNone, propOrElse } +import scala.tools.nsc.util.ClassPath +import scala.tools.nsc.io +import io.{ Path, File, Directory } +import RunnerUtils._ +import java.net.URLClassLoader + +object PathSettings { + import PartestDefaults.{ testRootDir, srcDirName } + + private def cwd = Directory.Current getOrElse error("user.dir property not set") + private def isPartestDir(d: Directory) = (d.name == "test") && (d / srcDirName isDirectory) + + // Directory <root>/test + lazy val testRoot: Directory = testRootDir getOrElse { + val candidates: List[Directory] = (cwd :: cwd.parents) flatMap (d => List(d, Directory(d / "test"))) + + candidates find isPartestDir getOrElse error("Directory 'test' not found.") + } + + // Directory <root>/test/files + lazy val srcDir = Directory(testRoot / srcDirName normalize) + + // Directory <root>/test/files/lib + lazy val srcLibDir = Directory(srcDir / "lib") + + lazy val scalaCheck = srcLibDir.files find (_.name startsWith "scalacheck") getOrElse { + error("No scalacheck jar found in '%s'" format srcLibDir) + } +} + +class PathSettings() { + // def classpathAsURLs: List[URL] +} diff --git a/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala new file mode 100644 index 0000000000..b3f199a3d6 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala @@ -0,0 +1,88 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import scala.tools.nsc.Properties.{ setProp, propOrEmpty } +import scala.tools.nsc.util.ClassPath +import scala.tools.nsc.io +import io.Path +import RunnerUtils._ +import java.net.URLClassLoader + +/* This class is used to load an instance of DirectRunner using + * a custom class loader. + * The purpose is to "auto-detect" a good classpath for the + * rest of the classes (Worker, CompileManager etc.), so that + * the main NestRunner can be started merely by putting its + * class on the classpath (ideally). + */ +class ReflectiveRunner { + // TODO: we might also use fileManager.CLASSPATH + // to use the same classes as used by `scala` that + // was used to start the runner. + val sepRunnerClassName = "scala.tools.partest.nest.ConsoleRunner" + + def main(args: String) { + val argList = (args.split("\\s")).toList + + if (isPartestDebug) + showAllJVMInfo + + // find out which build to test + val buildPath = searchPath("--buildpath", argList) + val classPath = searchPath("--classpath", argList) + val fileManager = + if (!buildPath.isEmpty) + new ConsoleFileManager(buildPath.get) + else if (!classPath.isEmpty) + new ConsoleFileManager(classPath.get, true) + else if (argList contains "--pack") + new ConsoleFileManager("build/pack") + else // auto detection + new ConsoleFileManager + + import fileManager. + { latestCompFile, latestLibFile, latestPartestFile, latestFjbgFile } + val files = + Array(latestCompFile, latestLibFile, latestPartestFile, latestFjbgFile) map (x => io.File(x)) + + val sepUrls = files map (_.toURL) + val sepLoader = new URLClassLoader(sepUrls, null) + + if (isPartestDebug) + println("Loading classes from:\n" + sepUrls.mkString("\n")) + + val paths = classPath match { + case Some(cp) => Nil + case _ => files.toList map (_.path) + } + val newClasspath = ClassPath.join(paths: _*) + + setProp("java.class.path", newClasspath) + setProp("scala.home", "") + + if (isPartestDebug) + for (prop <- List("java.class.path", "sun.boot.class.path", "java.ext.dirs")) + println(prop + ": " + propOrEmpty(prop)) + + try { + val sepRunnerClass = sepLoader loadClass sepRunnerClassName + val sepRunner = sepRunnerClass.newInstance() + val sepMainMethod = sepRunnerClass.getMethod("main", Array(classOf[String]): _*) + val cargs: Array[AnyRef] = Array(args) + sepMainMethod.invoke(sepRunner, cargs: _*) + } + catch { + case cnfe: ClassNotFoundException => + cnfe.printStackTrace() + NestUI.failure(sepRunnerClassName +" could not be loaded from:\n") + sepUrls foreach (x => NestUI.failure(x + "\n")) + } + } +} diff --git a/src/partest/scala/tools/partest/nest/RunnerUtils.scala b/src/partest/scala/tools/partest/nest/RunnerUtils.scala new file mode 100644 index 0000000000..24445bb545 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/RunnerUtils.scala @@ -0,0 +1,29 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +object RunnerUtils { + def splitArgs(str: String) = str split "\\s" filterNot (_ == "") toList + + def searchPath(option: String, as: List[String]): Option[String] = as match { + case `option` :: r :: _ => Some(r) + case _ :: rest => searchPath(option, rest) + case Nil => None + } + + def searchAndRemovePath(option: String, as: List[String]) = (as indexOf option) match { + case -1 => (None, as) + case idx => (Some(as(idx + 1)), (as take idx) ::: (as drop (idx + 2))) + } + + def searchAndRemoveOption(option: String, as: List[String]) = (as indexOf option) match { + case -1 => (false, as) + case idx => (true, (as take idx) ::: (as drop (idx + 1))) + } +} diff --git a/src/partest/scala/tools/partest/nest/StreamAppender.scala b/src/partest/scala/tools/partest/nest/StreamAppender.scala new file mode 100644 index 0000000000..8cebcf1685 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/StreamAppender.scala @@ -0,0 +1,94 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io._ + +object StreamAppender { + def wrapIn(in: InputStream): BufferedReader = new BufferedReader(new InputStreamReader(in)) + def wrapIn(reader: Reader): BufferedReader = new BufferedReader(reader) + def wrapIn(str: String): BufferedReader = new BufferedReader(new StringReader(str)) + + def wrapOut(out: OutputStream): PrintWriter = new PrintWriter(new OutputStreamWriter(out), true) + def wrapOut(writer: Writer): PrintWriter = new PrintWriter(writer, true) + def wrapOut(): PrintWriter = wrapOut(new StringWriter) + + def apply(reader: BufferedReader, writer: Writer): StreamAppender = + new StreamAppender(reader, wrapOut(writer)) + + def apply(reader: Reader, writer: Writer): StreamAppender = + apply(wrapIn(reader), writer) + + def apply(in: InputStream, writer: Writer): StreamAppender = + apply(wrapIn(in), writer) + + def apply(str: String, writer: Writer): StreamAppender = + apply(wrapIn(str), writer) + + def apply(in: File, out: File): StreamAppender = + apply(new FileReader(in), new FileWriter(out)) + + def appendToString(in1: InputStream, in2: InputStream): String = { + val swriter1 = new StringWriter + val swriter2 = new StringWriter + val app1 = StreamAppender(wrapIn(in1), swriter1) + val app2 = StreamAppender(wrapIn(in2), swriter2) + + val async = new Thread(app2) + async.start() + app1.run() + async.join() + swriter1.toString + swriter2.toString + } +/* + private def inParallel(t1: Runnable, t2: Runnable, t3: Runnable) { + val thr1 = new Thread(t1) + val thr2 = new Thread(t2) + thr1.start() + thr2.start() + t3.run() + thr1.join() + thr2.join() + } +*/ + private def inParallel(t1: Runnable, t2: Runnable) { + val thr = new Thread(t2) + thr.start() + t1.run() + thr.join() + } + + def concat(in: InputStream, err: InputStream, out: OutputStream) = new Runnable { + override def run() { + val outWriter = wrapOut(out) + val inApp = StreamAppender(in, outWriter) + + val errStringWriter = new StringWriter + val errApp = StreamAppender(wrapIn(err), errStringWriter) + + inParallel(inApp, errApp) + + // append error string to out + StreamAppender(errStringWriter.toString, outWriter).run() + } + } +} + +class StreamAppender(reader: BufferedReader, writer: PrintWriter) extends Runnable { + override def run() = runAndMap(identity) + private def lines() = Iterator continually reader.readLine() takeWhile (_ != null) + def closeAll() = { + reader.close() + writer.close() + } + + def runAndMap(f: String => String) = + try lines() map f foreach (writer println _) + catch { case e: IOException => e.printStackTrace() } +} diff --git a/src/partest/scala/tools/partest/nest/TestFile.scala b/src/partest/scala/tools/partest/nest/TestFile.scala new file mode 100644 index 0000000000..741556fdd5 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/TestFile.scala @@ -0,0 +1,49 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io.{ File => JFile } +import scala.tools.nsc.Settings +import scala.tools.nsc.io._ + +abstract class TestFile(kind: String) { + def file: JFile + def fileManager: FileManager + + val dir = file.toAbsolute.parent + val fileBase = file.stripExtension + lazy val objectDir = dir / "%s-%s.obj".format(fileBase, kind) createDirectory true + val flags: Option[String] = dir / "%s.flags".format(fileBase) ifFile { _.slurp().trim } + + def setOutDirTo = objectDir + + def defineSettings(settings: Settings, setOutDir: Boolean) = { + settings.classpath append dir.path + if (setOutDir) + settings.outdir.value = setOutDirTo.path + + flags foreach (settings processArgumentString _) + settings.classpath append fileManager.CLASSPATH + } + + override def toString(): String = "%s %s".format(kind, file) +} + +case class PosTestFile(file: JFile, fileManager: FileManager) extends TestFile("pos") +case class NegTestFile(file: JFile, fileManager: FileManager) extends TestFile("neg") +case class RunTestFile(file: JFile, fileManager: FileManager) extends TestFile("run") +case class BuildManagerTestFile(file: JFile, fileManager: FileManager) extends TestFile("bm") +case class ScalaCheckTestFile(file: JFile, fileManager: FileManager) extends TestFile("scalacheck") +case class JvmTestFile(file: JFile, fileManager: FileManager) extends TestFile("jvm") +case class ShootoutTestFile(file: JFile, fileManager: FileManager) extends TestFile("shootout") { + override def setOutDirTo = file.parent +} +case class ScalapTestFile(file: JFile, fileManager: FileManager) extends TestFile("scalap") { + override def setOutDirTo = file.parent +} diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/Worker.scala new file mode 100644 index 0000000000..2f81dfd0f7 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/Worker.scala @@ -0,0 +1,1071 @@ +/* NEST (New Scala Test) + * Copyright 2007-2010 LAMP/EPFL + * @author Philipp Haller + */ + +// $Id$ + +package scala.tools.partest +package nest + +import java.io._ +import java.net.{ URLClassLoader, URL } +import java.util.{ Timer, TimerTask } + +import scala.util.Properties.{ isWin } +import scala.tools.nsc.{ ObjectRunner, Settings, CompilerCommand, Global } +import scala.tools.nsc.io.{ AbstractFile, PlainFile, Path, Directory, File => SFile } +import scala.tools.nsc.reporters.ConsoleReporter +import scala.tools.nsc.util.{ ClassPath, FakePos } +import ClassPath.{ join, split } + +import scala.actors.{ Actor, Exit, TIMEOUT } +import scala.actors.Actor._ +import scala.tools.scalap.scalax.rules.scalasig.{ByteCode, ClassFileParser, ScalaSigAttributeParsers} + +import scala.collection.immutable.{ HashMap, Map => ImmMap } +import scala.collection.Map + +import scala.tools.nsc.interactive.{BuildManager, RefinedBuildManager} + +case class RunTests(kind: String, files: List[File]) +case class Results(results: ImmMap[String, Int], logs: List[LogFile], outdirs: List[File]) + +case class LogContext(file: LogFile, writers: Option[(StringWriter, PrintWriter)]) + +abstract class TestResult { + def file: File +} +case class Result(override val file: File, context: LogContext) extends TestResult +case class Timeout(override val file: File) extends TestResult + +class LogFile(parent: File, child: String) extends File(parent, child) { + var toDelete = false +} + +class Worker(val fileManager: FileManager) extends Actor { + import fileManager._ + + var reporter: ConsoleReporter = _ + val timer = new Timer + + def error(msg: String): Unit = reporter.error( + FakePos("scalac"), + msg + "\n scalac -help gives more information" + ) + + def act() { + react { + case RunTests(kind, files) => + // NestUI.verbose("received "+files.length+" to test") + val master = sender + runTests(kind, files) { results => + master ! Results(results, createdLogFiles, createdOutputDirs) + } + } + } + + def printInfoStart(file: File, printer: PrintWriter) { + NestUI.outline("testing: ", printer) + val filesdir = file.getAbsoluteFile.getParentFile.getParentFile + val testdir = filesdir.getParentFile + val totalWidth = 56 + val name = { + // 1. try with [...]/files/run/test.scala + val testPathLen = testdir.getAbsolutePath.length + val name = file.getAbsolutePath.substring(testPathLen) + if (name.length <= totalWidth) + name + // 2. try with [...]/run/test.scala + else { + val filesPathLen = filesdir.getAbsolutePath.length + file.getAbsolutePath.substring(filesPathLen) + } + } + NestUI.normal("[...]%s%s".format(name, " " * (totalWidth - name.length)), printer) + } + + def printInfoEnd(success: Boolean, printer: PrintWriter) { + NestUI.normal("[", printer) + if (success) NestUI.success(" OK ", printer) + else NestUI.failure("FAILED", printer) + NestUI.normal("]\n", printer) + } + + def printInfoTimeout(printer: PrintWriter) { + NestUI.normal("[", printer) + NestUI.failure("TIMOUT", printer) + NestUI.normal("]\n", printer) + } + + var log = "" + var createdLogFiles: List[LogFile] = Nil + var createdOutputDirs: List[File] = Nil + + def createLogFile(file: File, kind: String): LogFile = { + val logFile = fileManager.getLogFile(file, kind) + createdLogFiles ::= logFile + logFile + } + + def createOutputDir(dir: File, fileBase: String, kind: String): File = { + val outDir = Path(dir) / Directory("%s-%s.obj".format(fileBase, kind)) + outDir.createDirectory() + createdOutputDirs ::= outDir.jfile + outDir.jfile + } + + /* Note: not yet used/tested. */ + // def execTestObjectRunner(file: File, outDir: File, logFile: File) { + // val consFM = new ConsoleFileManager + // + // val classpath: List[URL] = { + // import consFM.{ latestCompFile, latestLibFile, latestPartestFile } + // val units = ( + // List(outDir, latestCompFile, latestLibFile, latestPartestFile) ::: + // ((CLASSPATH split File.pathSeparatorChar).toList map (x => new File(x))) + // ) + // units map (_.toURI.toURL) + // } + // + // NestUI.verbose("ObjectRunner classpath: "+classpath) + // + // try { + // // configure input/output files + // val logOut = new FileOutputStream(logFile) + // val logWriter = new PrintStream(logOut) + // + // // grab global lock + // fileManager.synchronized { + // withOutputRedirected(logWriter) { + // System.setProperty("java.library.path", logFile.getParentFile.getCanonicalFile.getAbsolutePath) + // System.setProperty("partest.output", outDir.getCanonicalFile.getAbsolutePath) + // System.setProperty("partest.lib", LATEST_LIB) + // System.setProperty("partest.cwd", outDir.getParent) + // ObjectRunner.run(classpath, "Test", List("jvm")) + // } + // } + // + // /*val out = new FileOutputStream(logFile, true) + // Console.withOut(new PrintStream(out)) { + // ObjectRunner.run(classpath, "Test", List("jvm")) + // } + // out.flush + // out.close*/ + // } catch { + // case e: Exception => + // NestUI.verbose(e+" ("+file.getPath+")") + // e.printStackTrace() + // } + // } + + def javac(outDir: File, files: List[File], output: File): Boolean = { + // compile using command-line javac compiler + val javacCmd = if ((fileManager.JAVAC_CMD.indexOf("${env.JAVA_HOME}") != -1) || + fileManager.JAVAC_CMD.equals("/bin/javac") || + fileManager.JAVAC_CMD.equals("\\bin\\javac")) + "javac" + else + fileManager.JAVAC_CMD + + val cmd = javacCmd+ + " -d "+outDir.getAbsolutePath+ + " -classpath "+ join(outDir.toString, CLASSPATH) + + " "+files.mkString(" ") + + val (success, msg) = try { + val exitCode = runCommand(cmd, output) + NestUI.verbose("javac returned exit code: "+exitCode) + if (exitCode != 0) + (false, "Running \"javac\" failed with exit code: "+exitCode+"\n"+cmd+"\n") + else + (true, "") + } catch { + case e: Exception => + val swriter = new StringWriter + e.printStackTrace(new PrintWriter(swriter)) + (false, "Running \"javac\" failed:\n"+cmd+"\n"+swriter.toString+"\n") + } + if (!success) { + val writer = new PrintWriter(new FileWriter(output, true), true) + writer.print(msg) + writer.close() + } + success + } + + /** Runs <code>command</code> redirecting standard out and + * error out to <code>output</code> file. + */ + def runCommand(command: String, output: File): Int = { + NestUI.verbose("running command:\n"+command) + val proc = Runtime.getRuntime.exec(command) + val in = proc.getInputStream + val err = proc.getErrorStream + val writer = new PrintWriter(new FileWriter(output), true) + val inApp = StreamAppender(in, writer) + val errApp = StreamAppender(err, writer) + val async = new Thread(errApp) + async.start() + inApp.run() + async.join() + writer.close() + + try proc.exitValue() + catch { case _: IllegalThreadStateException => 0 } + } + + def execTest(outDir: File, logFile: File, fileBase: String) { + // check whether there is a ".javaopts" file + val argsFile = new File(logFile.getParentFile, fileBase+".javaopts") + val argString = if (argsFile.exists) { + NestUI.verbose("Found javaopts file: "+argsFile) + val fileReader = new FileReader(argsFile) + val reader = new BufferedReader(fileReader) + val options = reader.readLine() + reader.close() + NestUI.verbose("Found javaopts file '%s', using options: '%s'".format(argsFile, options)) + options + } else "" + + def quote(path: String) = "\""+path+"\"" + + // Note! As this currently functions, JAVA_OPTS must precede argString + // because when an option is repeated to java only the last one wins. + // That means until now all the .javaopts files were being ignored because + // they all attempt to change options which are also defined in + // partest.java_opts, leading to debug output like: + // + // debug: Found javaopts file 'files/shootout/message.scala-2.javaopts', using options: '-Xss32k' + // debug: java -Xss32k -Xss2m -Xms256M -Xmx1024M -classpath [...] + val propertyOptions = List( + "-Djava.library.path="+logFile.getParentFile.getAbsolutePath, + "-Dpartest.output="+outDir.getAbsolutePath, + "-Dpartest.lib="+LATEST_LIB, + "-Dpartest.cwd="+outDir.getParent, + "-Djavacmd="+JAVACMD, + "-Duser.language=en -Duser.country=US" + ) ::: ( + if (isPartestDebug) List("-Dpartest.debug=true") else Nil + ) + + val cmd = ( + List( + JAVACMD, + JAVA_OPTS, + argString, + "-classpath " + join(outDir.toString, CLASSPATH) + ) ::: propertyOptions ::: List( + "scala.tools.nsc.MainGenericRunner", + "-usejavacp", + "Test", + "jvm" + ) + ) mkString " " + + runCommand(cmd, logFile) + + if (fileManager.showLog) { + // produce log as string in `log` + val reader = new BufferedReader(new FileReader(logFile)) + val swriter = new StringWriter + val pwriter = new PrintWriter(swriter, true) + val appender = new StreamAppender(reader, pwriter) + appender.run() + log = swriter.toString + } + } + + def getCheckFile(dir: File, fileBase: String, kind: String) = { + def chkFile(s: String) = Directory(dir) / "%s%s.check".format(fileBase, s) + val checkFile = if (chkFile("").isFile) chkFile("") else chkFile("-" + kind) + + if (checkFile.canRead) Some(checkFile) else None + } + + def existsCheckFile(dir: File, fileBase: String, kind: String) = + getCheckFile(dir, fileBase, kind).isDefined + + def compareOutput(dir: File, fileBase: String, kind: String, logFile: File): String = + // if check file exists, compare with log file + getCheckFile(dir, fileBase, kind) match { + case Some(f) => fileManager.compareFiles(logFile, f.jfile) + case _ => file2String(logFile) + } + + def file2String(logFile: File) = SFile(logFile).slurp() + def isJava(f: File) = SFile(f) hasExtension "java" + def isScala(f: File) = SFile(f) hasExtension "scala" + def isJavaOrScala(f: File) = isJava(f) || isScala(f) + + /** Runs a list of tests. + * + * @param kind The test kind (pos, neg, run, etc.) + * @param files The list of test files + */ + def runTests(kind: String, files: List[File])(topcont: ImmMap[String, Int] => Unit) { + val compileMgr = new CompileManager(fileManager) + var errors = 0 + var succeeded = true + var diff = "" + var log = "" + + def fail(what: Any) { + NestUI.verbose("scalac: compilation of "+what+" failed\n") + succeeded = false + } + def diffCheck(latestDiff: String) = { + diff = latestDiff + if (latestDiff != "") { + NestUI.verbose("output differs from log file\n") + succeeded = false + } + } + + /** 1. Creates log file and output directory. + * 2. Runs <code>script</code> function, providing log file and + * output directory as arguments. + */ + def runInContext(file: File, kind: String, script: (File, File) => Unit): LogContext = { + // when option "--failed" is provided + // execute test only if log file is present + // (which means it failed before) + val logFile = createLogFile(file, kind) + if (!fileManager.failed || logFile.canRead) { + val swr = new StringWriter + val wr = new PrintWriter(swr) + succeeded = true + diff = "" + log = "" + printInfoStart(file, wr) + + val fileBase: String = basename(file.getName) + 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 { + if (isPartestDebug) { + val t1 = System.currentTimeMillis + script(logFile, outDir) + val t2 = System.currentTimeMillis + fileManager.recordTestTiming(file.getPath, t2 - t1) + } + else { + script(logFile, outDir) + } + } catch { + case e: Exception => + val writer = new PrintWriter(new FileWriter(logFile), true) + e.printStackTrace(writer) + writer.close() + succeeded = false + } + + LogContext(logFile, Some((swr, wr))) + } else + LogContext(logFile, None) + } + + def compileFilesIn(dir: File, kind: String, logFile: File, outDir: 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 -- groups.flatten + + def compileGroup(g: List[File]) { + val (scalaFiles, javaFiles) = g partition isScala + + if (scalaFiles.nonEmpty) { + if (!compileMgr.shouldCompile(outDir, javaFiles ::: scalaFiles, kind, logFile)) + fail(g) + } + + if (succeeded && javaFiles.nonEmpty) { + succeeded = javac(outDir, javaFiles, logFile) + if (succeeded && scalaFiles.nonEmpty && !compileMgr.shouldCompile(outDir, scalaFiles, kind, logFile)) + fail(scalaFiles) + } + } + + if (noGroupSuffix.nonEmpty) + compileGroup(noGroupSuffix) + + groups foreach (grp => if (succeeded) compileGroup(grp)) + } + + def failCompileFilesIn(dir: File, kind: String, logFile: File, outDir: File) { + val testFiles = dir.listFiles.toList + val sourceFiles = testFiles filter isJavaOrScala + + if (sourceFiles.nonEmpty) { + if (!compileMgr.shouldFailCompile(outDir, sourceFiles, kind, logFile)) + fail(testFiles filter isScala) + } + } + + def runTestCommon(file: File, kind: String, expectFailure: Boolean)(onSuccess: (File, File) => Unit): LogContext = + runInContext(file, kind, (logFile: File, outDir: File) => { + + if (file.isDirectory) { + val f = if (expectFailure) failCompileFilesIn _ else compileFilesIn _ + f(file, kind, logFile, outDir) + } + else { + val f: (List[File], String, File) => Boolean = + if (expectFailure) compileMgr.shouldFailCompile _ + else compileMgr.shouldCompile _ + + if (!f(List(file), kind, logFile)) + fail(file) + } + + if (succeeded) // run test + onSuccess(logFile, outDir) + }) + + def runJvmTest(file: File, kind: String): LogContext = + runTestCommon(file, kind, expectFailure = false)((logFile, outDir) => { + val fileBase = basename(file.getName) + val dir = file.getParentFile + + //TODO: detect whether we have to use Runtime.exec + // val useRuntime = true + // + // if (useRuntime) + // execTest(outDir, logFile, fileBase) + // else + // execTestObjectRunner(file, outDir, logFile) + // // NestUI.verbose(this+" finished running "+fileBase) + execTest(outDir, logFile, fileBase) + + diffCheck(compareOutput(dir, fileBase, kind, logFile)) + }) + + def processSingleFile(file: File): LogContext = kind match { + case "scalacheck" => + runTestCommon(file, kind, expectFailure = false)((logFile, outDir) => { + val consFM = new ConsoleFileManager + import consFM.{ latestCompFile, latestLibFile, latestPartestFile } + + NestUI.verbose("compilation of "+file+" succeeded\n") + + val scalacheckURL = PathSettings.scalaCheck.toURL + val outURL = outDir.getCanonicalFile.toURI.toURL + val classpath: List[URL] = + List(outURL, scalacheckURL, latestCompFile.toURI.toURL, latestLibFile.toURI.toURL, latestPartestFile.toURI.toURL).distinct + + NestUI.debug("scalacheck urls") + classpath foreach (x => NestUI.debug(x.toString)) + + val logWriter = new PrintStream(new FileOutputStream(logFile)) + + withOutputRedirected(logWriter) { + ObjectRunner.run(classpath, "Test", Nil) + } + + NestUI.verbose(SFile(logFile).slurp()) + // obviously this must be improved upon + succeeded = SFile(logFile).lines() forall (_ contains " OK") + }) + + case "pos" => + runTestCommon(file, kind, expectFailure = false)((_, _) => ()) + + case "neg" => + runTestCommon(file, kind, expectFailure = true)((logFile, outDir) => { + // compare log file to check file + val fileBase = basename(file.getName) + val dir = file.getParentFile + + diffCheck( + // diff is contents of logFile + if (!existsCheckFile(dir, fileBase, kind)) file2String(logFile) + else compareOutput(dir, fileBase, kind, logFile) + ) + }) + + case "run" | "jvm" => + runJvmTest(file, kind) + + case "buildmanager" => + val logFile = createLogFile(file, kind) + if (!fileManager.failed || 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.isFile || !testFile.isFile) { + // if changes exists then it has to be a dir + if (!testFile.isFile) NestUI.verbose("invalid build manager test file") + if (changesDir.isFile) NestUI.verbose("invalid build manager changes directory") + 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(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 + settings.Ybuildmanagerdebug.value = true + + // 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) + 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.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, _.replace(sourcepath, "")) + + diffCheck(compareOutput(file, fileBase, kind, logFile)) + } + 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 + // (which means it failed before) + + //val (logFileOut, logFileErr) = createLogFiles(file, kind) + val logFile = createLogFile(file, kind) + if (!fileManager.failed || logFile.canRead) { + val swr = new StringWriter + val wr = new PrintWriter(swr) + succeeded = true; diff = ""; log = "" + printInfoStart(file, wr) + + val fileBase: String = basename(file.getName) + NestUI.verbose(this+" running test "+fileBase) + val dir = file.getParentFile + val outDir = createOutputDir(dir, fileBase, kind) + if (!outDir.exists) outDir.mkdir() + val resFile = new File(dir, fileBase + ".res") + NestUI.verbose("outDir: "+outDir) + NestUI.verbose("logFile: "+logFile) + //NestUI.verbose("logFileErr: "+logFileErr) + NestUI.verbose("resFile: "+resFile) + + // run compiler in resident mode + // $SCALAC -d "$os_dstbase".obj -Xresident -sourcepath . "$@" + + try { + + val sourcedir = logFile.getParentFile.getCanonicalFile + val sourcepath = sourcedir.getAbsolutePath+File.separator + NestUI.verbose("sourcepath: "+sourcepath) + + val argString = + "-d "+outDir.getCanonicalFile.getAbsolutePath+ + " -Xresident"+ + " -sourcepath "+sourcepath + val argList = argString split ' ' toList + + // configure input/output files + val logOut = new FileOutputStream(logFile) + val logWriter = new PrintStream(logOut) + val resReader = new BufferedReader(new FileReader(resFile)) + val logConsoleWriter = new PrintWriter(new OutputStreamWriter(logOut)) + + // create compiler + val settings = new Settings(error) + settings.sourcepath.value = sourcepath + settings.classpath.value = fileManager.CLASSPATH + reporter = new ConsoleReporter(settings, scala.Console.in, logConsoleWriter) + val command = new CompilerCommand(argList, settings) + object compiler extends Global(command.settings, reporter) + + // simulate resident compiler loop + val prompt = "\nnsc> " + + val resCompile = (line: String) => { + NestUI.verbose("compiling "+line) + val cmdArgs = (line split ' ').toList map (fs => new File(dir, fs).getAbsolutePath) + NestUI.verbose("cmdArgs: "+cmdArgs) + val sett = new Settings(error) + sett.sourcepath.value = sourcepath + val command = new CompilerCommand(cmdArgs, sett) + (new compiler.Run) compile command.files + } + + def loop(action: (String) => Unit) { + logWriter.print(prompt) + val line = resReader.readLine() + if ((line ne null) && line.length() > 0) { +/* + val parent = self + self.trapExit = true + val child = link { + action(line) + } + + receiveWithin(fileManager.timeout.toLong) { + case TIMEOUT => + NestUI.verbose("action timed out") + false + case Exit(from, reason) if from == child => reason match { + case 'normal => // do nothing + case t: Throwable => + NestUI.verbose("while invoking compiler:") + NestUI.verbose("caught "+t) + t.printStackTrace + if (t.getCause != null) + t.getCause.printStackTrace + false + } + } +*/ + action(line) + loop(action) + } + } + + withOutputRedirected(logWriter) { + loop(resCompile) + resReader.close() + } + + def replaceSlashes(s: String): String = { + val path = dir.getAbsolutePath+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('\\', '/') + } + + fileManager.mapFile(logFile, "tmp", dir, replaceSlashes) + diffCheck(compareOutput(dir, fileBase, kind, logFile)) + + } catch { + case e: Exception => + e.printStackTrace() + succeeded = false + } + + LogContext(logFile, Some((swr, wr))) + } else + LogContext(logFile, None) + } + + case "shootout" => { + // when option "--failed" is provided + // execute test only if log file is present + // (which means it failed before) + val logFile = createLogFile(file, kind) + if (!fileManager.failed || logFile.canRead) { + val swr = new StringWriter + val wr = new PrintWriter(swr) + succeeded = true; diff = ""; log = "" + printInfoStart(file, wr) + + val fileBase: String = basename(file.getName) + NestUI.verbose(this+" running test "+fileBase) + val dir = file.getParentFile + val outDir = createOutputDir(dir, fileBase, kind) + if (!outDir.exists) outDir.mkdir() + + // 2. define file {outDir}/test.scala that contains code to compile/run + val testFile = new File(outDir, "test.scala") + NestUI.verbose("outDir: "+outDir) + NestUI.verbose("logFile: "+logFile) + NestUI.verbose("testFile: "+testFile) + + // 3. cat {test}.scala.runner {test}.scala > testFile + val runnerFile = new File(dir, fileBase+".scala.runner") + val bodyFile = new File(dir, fileBase+".scala") + val appender = StreamAppender.concat(new FileInputStream(runnerFile), + new FileInputStream(bodyFile), + new FileOutputStream(testFile)) + appender.run() + + try { // *catch-all* + // 4. compile testFile + if (!compileMgr.shouldCompile(List(testFile), kind, logFile)) { + NestUI.verbose("compilation of "+file+" failed\n") + succeeded = false + } else { + NestUI.verbose("compilation of "+testFile+"succeeded") + // -------- run test -------- + + //TODO: detect whether we have to use Runtime.exec + // val useRuntime = true + // + // if (useRuntime) + // execTest(outDir, logFile, fileBase) + // else + // execTestObjectRunner(file, outDir, logFile) + + execTest(outDir, logFile, fileBase) + + NestUI.verbose(this+" finished running "+fileBase) + } // successful compile + } catch { // *catch-all* + case e: Exception => + NestUI.verbose("caught "+e) + succeeded = false + } + + diffCheck(compareOutput(dir, fileBase, kind, logFile)) + + LogContext(logFile, Some((swr, wr))) + } else + LogContext(logFile, None) + } + + case "scalap" => { + + runInContext(file, kind, (logFile: File, outDir: File) => { + val sourceDir = file.getParentFile + val sourceDirName = sourceDir.getName + + // 1. Find file with result text + val results = sourceDir.listFiles(new FilenameFilter { + def accept(dir: File, name: String) = name == "result.test" + }) + + if (results.length != 1) { + NestUI.verbose("Result file not found in directory " + sourceDirName + " \n") + } else { + val resFile = results(0) + // 2. Compile source file + if (!compileMgr.shouldCompile(outDir, List(file), kind, logFile)) { + NestUI.verbose("compilerMgr failed to compile %s to %s".format(file, outDir)) + succeeded = false + } else { + + // 3. Decompile file and compare results + val isPackageObject = sourceDir.getName.startsWith("package") + val className = sourceDirName.capitalize + (if (!isPackageObject) "" else ".package") + val url = outDir.toURI.toURL + val loader = new URLClassLoader(Array(url), getClass.getClassLoader) + val clazz = loader.loadClass(className) + + val byteCode = ByteCode.forClass(clazz) + val result = scala.tools.scalap.Main.decompileScala(byteCode.bytes, isPackageObject) + + try { + val fstream = new FileWriter(logFile); + val out = new BufferedWriter(fstream); + out.write(result) + out.close(); + } catch { + case e: IOException => NestUI.verbose(e.getMessage()); succeeded = false + } + + diffCheck(fileManager.compareFiles(logFile, resFile)) + } + } + }) + } + + case "script" => { + // when option "--failed" is provided + // execute test only if log file is present + // (which means it failed before) + val logFile = createLogFile(file, kind) + if (!fileManager.failed || logFile.canRead) { + val swr = new StringWriter + val wr = new PrintWriter(swr) + succeeded = true; diff = ""; log = "" + printInfoStart(file, wr) + + val fileBase: String = basename(file.getName) + NestUI.verbose(this+" running test "+fileBase) + + // check whether there is an args file + val argsFile = new File(file.getParentFile, fileBase+".args") + NestUI.verbose("argsFile: "+argsFile) + val argString = if (argsFile.exists) { + val swriter = new StringWriter + val app = StreamAppender(new BufferedReader(new FileReader(argsFile)), + swriter) + app.run() + " "+swriter.toString + } else "" + + try { + val cmdString = + if (isWin) { + val batchFile = new File(file.getParentFile, fileBase+".bat") + NestUI.verbose("batchFile: "+batchFile) + batchFile.getAbsolutePath + } + else file.getAbsolutePath + val proc = Runtime.getRuntime.exec(cmdString+argString) + 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() + + diffCheck(compareOutput(file.getParentFile, fileBase, kind, logFile)) + } catch { // *catch-all* + case e: Exception => + NestUI.verbose("caught "+e) + succeeded = false + } + + LogContext(logFile, Some((swr, wr))) + } else + LogContext(logFile, None) + } + } + + def reportAll(results: ImmMap[String, Int], cont: ImmMap[String, Int] => Unit) { + // NestUI.verbose("finished testing "+kind+" with "+errors+" errors") + // NestUI.verbose("created "+compileMgr.numSeparateCompilers+" separate compilers") + timer.cancel() + cont(results) + } + + def reportResult(state: Int, logFile: Option[LogFile], writers: Option[(StringWriter, PrintWriter)]) { + val good = (state == 0) + if (!good) { + errors += 1 + NestUI.verbose("incremented errors: "+errors) + } + + try { + // delete log file only if test was successful + if (good && !logFile.isEmpty && !isPartestDebug) + logFile.get.toDelete = true + + writers match { + case Some((swr, wr)) => + if (state == 2) + printInfoTimeout(wr) + else + printInfoEnd(good, wr) + wr.flush() + swr.flush() + NestUI.normal(swr.toString) + if (state == 1 && fileManager.showDiff && diff != "") + NestUI.normal(diff) + if (state == 1 && fileManager.showLog) + showLog(logFile.get) + case None => + } + } catch { + case npe: NullPointerException => + } + } + + val numFiles = files.size + if (numFiles == 0) + reportAll(ImmMap(), topcont) + + // maps canonical file names to the test result (0: OK, 1: FAILED, 2: TIMOUT) + var status = new HashMap[String, Int] + + var fileCnt = 1 + Actor.loopWhile(fileCnt <= numFiles) { + val parent = self + + actor { + val testFile = files(fileCnt-1) + + val ontimeout = new TimerTask { + def run() = parent ! Timeout(testFile) + } + timer.schedule(ontimeout, fileManager.timeout.toLong) + + val context = try { + processSingleFile(testFile) + } catch { + case t: Throwable => + NestUI.verbose("while invoking compiler ("+files+"):") + NestUI.verbose("caught "+t) + t.printStackTrace + if (t.getCause != null) + t.getCause.printStackTrace + LogContext(null, None) + } + parent ! Result(testFile, context) + } + + react { + case res: TestResult => + val path = res.file.getCanonicalPath + status.get(path) match { + case Some(stat) => // ignore message + case None => + res match { + case Timeout(_) => + status = status + (path -> 2) + val swr = new StringWriter + val wr = new PrintWriter(swr) + printInfoStart(res.file, wr) + succeeded = false + reportResult(2, None, Some((swr, wr))) + case Result(_, logs) => + status = status + (path -> (if (succeeded) 0 else 1)) + reportResult( + if (succeeded) 0 else 1, + if (logs != null) Some(logs.file) else None, + if (logs != null) logs.writers else None) + } + if (fileCnt == numFiles) + reportAll(status, topcont) + fileCnt += 1 + } + } + } + } + + 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] = + fs flatMap (s => Option(AbstractFile getFile (pre + s))) toSet + + private def copyTestFiles(testDir: File, destDir: File) { + val invalidExts = List("changes", "svn", "obj") + testDir.listFiles.toList filter ( + f => (isJavaOrScala(f) && f.isFile) || + (f.isDirectory && !(invalidExts.contains(SFile(f).extension)))) foreach + { f => fileManager.copyFile(f, destDir) } + } + + def showLog(logFile: File) { + try { + val logReader = new BufferedReader(new FileReader(logFile)) + val strWriter = new StringWriter + val logWriter = new PrintWriter(strWriter, true) + val logAppender = new StreamAppender(logReader, logWriter) + logAppender.run() + logReader.close() + val log = strWriter.toString + NestUI.normal(log) + } catch { + case fnfe: java.io.FileNotFoundException => + NestUI.failure("Couldn't open log file \""+logFile+"\".") + } + } +} diff --git a/src/partest/scala/tools/partest/package.scala b/src/partest/scala/tools/partest/package.scala index f6d216e379..e9eda6fb75 100644 --- a/src/partest/scala/tools/partest/package.scala +++ b/src/partest/scala/tools/partest/package.scala @@ -4,42 +4,37 @@ package scala.tools -import nsc.io.{ File, Path, Process, Directory } -import java.nio.charset.CharacterCodingException +import java.io.{ File => JFile } +import nsc.io.{ Path, Process, Directory } +import util.{ PathResolver } +import nsc.Properties.{ propOrElse, propOrNone, propOrEmpty } package object partest { - /** The CharacterCodingExceptions are thrown at least on windows trying - * to read a file like script/utf-8.scala - */ - private[partest] def safeSlurp(f: File) = - try if (f.exists) f.slurp() else "" - catch { case _: CharacterCodingException => "" } - - private[partest] def safeLines(f: File) = safeSlurp(f) split """\r\n|\r|\n""" toList - private[partest] def safeArgs(f: File) = toArgs(safeSlurp(f)) - private[partest] def isJava(f: Path) = f.isFile && (f hasExtension "java") - private[partest] def isScala(f: Path) = f.isFile && (f hasExtension "scala") - private[partest] def isJavaOrScala(f: Path) = isJava(f) || isScala(f) - - private[partest] def toArgs(line: String) = cmd toArgs line - private[partest] def fromArgs(args: List[String]) = cmd fromArgs args - - /** Strings, argument lists, etc. */ - - private[partest] def fromAnyArgs(args: List[Any]) = args mkString " " // separate to avoid accidents - private[partest] def toStringTrunc(x: Any, max: Int = 240) = { - val s = x.toString - if (s.length < max) s - else (s take max) + " [...]" + import nest.NestUI + + implicit private[partest] def temporaryPath2File(x: Path): JFile = x.jfile + implicit private[partest] def temporaryFile2Path(x: JFile): Path = Path(x) + + def basename(name: String): String = Path(name).stripExtension + def resultsToStatistics(results: Iterable[(_, Int)]): (Int, Int) = { + val (files, failures) = results map (_._2 == 0) partition (_ == true) + (files.size, failures.size) + } + + def vmArgString = { + val str = Process.javaVmArguments mkString " " + "Java VM started with arguments: '%s'" format str + } + + def allPropertiesString = { + import collection.JavaConversions._ + System.getProperties.toList.sorted map { case (k, v) => "%s -> %s\n".format(k, v) } mkString } - private[partest] def setProp(k: String, v: String) = scala.util.Properties.setProp(k, v) - /** Pretty self explanatory. */ - def printAndExit(msg: String): Unit = { - println(msg) - exit(1) + def showAllJVMInfo { + NestUI.verbose(vmArgString) + NestUI.verbose(allPropertiesString) } - /** Apply a function and return the passed value */ - def returning[T](x: T)(f: T => Unit): T = { f(x) ; x } + def isPartestDebug = propOrEmpty("partest.debug") == "true" }
\ No newline at end of file diff --git a/src/partest/scala/tools/partest/util/package.scala b/src/partest/scala/tools/partest/util/package.scala deleted file mode 100644 index bc5470ba5d..0000000000 --- a/src/partest/scala/tools/partest/util/package.scala +++ /dev/null @@ -1,61 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2010 LAMP/EPFL - */ - -package scala.tools -package partest - -import java.util.{ Timer, TimerTask } -import java.io.StringWriter -import nsc.io._ - -/** Misc code still looking for a good home. - */ -package object util { - - def allPropertiesString() = javaHashtableToString(System.getProperties) - - private def javaHashtableToString(table: java.util.Hashtable[_,_]) = { - import collection.JavaConversions._ - (table.toList map { case (k, v) => "%s -> %s\n".format(k, v) }).sorted mkString - } - - def filesToSet(pre: String, fs: List[String]): Set[AbstractFile] = - fs flatMap (x => Option(AbstractFile getFile (Path(pre) / x).path)) toSet - - /** Copies one Path to another Path, trying to be sensible when one or the - * other is a Directory. Returns true if it believes it succeeded. - */ - def copyPath(from: Path, to: Path): Boolean = { - if (!to.parent.isDirectory) - to.parent.createDirectory(force = true) - - def copyDir = { - val sub = to / from.name createDirectory true - from.toDirectory.list forall (x => copyPath(x, sub)) - } - (from.isDirectory, to.isDirectory) match { - case (true, true) => copyDir - case (true, false) => false - case (false, true) => from.toFile copyTo (to / from.name) - case (false, false) => from.toFile copyTo to - } - } - - /** - * Compares two files using a Java implementation of the GNU diff - * available at http://www.bmsi.com/java/#diff. - * - * @param f1 the first file to be compared - * @param f2 the second file to be compared - * @return the text difference between the compared files - */ - def diffFiles(f1: File, f2: File): String = { - val diffWriter = new StringWriter - val args = Array(f1.toAbsolute.path, f2.toAbsolute.path) - - io.DiffPrint.doDiff(args, diffWriter) - val result = diffWriter.toString - if (result == "No differences") "" else result - } -} diff --git a/src/partest/scala/tools/partest/utils/PrintMgr.scala b/src/partest/scala/tools/partest/utils/PrintMgr.scala new file mode 100644 index 0000000000..10533130f1 --- /dev/null +++ b/src/partest/scala/tools/partest/utils/PrintMgr.scala @@ -0,0 +1,52 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.tools.partest +package utils + +/** + * @author Thomas Hofer + */ +object PrintMgr { + + val NONE = 0 + val SOME = 1 + val MANY = 2 + + var outline = "" + var success = "" + var failure = "" + var warning = "" + var default = "" + + def initialization(number: Int) = number match { + case MANY => + outline = Console.BOLD + Console.BLACK + success = Console.BOLD + Console.GREEN + failure = Console.BOLD + Console.RED + warning = Console.BOLD + Console.YELLOW + default = Console.RESET + case SOME => + outline = Console.BOLD + Console.BLACK + success = Console.RESET + failure = Console.BOLD + Console.BLACK + warning = Console.BOLD + Console.BLACK + default = Console.RESET + case _ => + } + + def printOutline(msg: String) = print(outline + msg + default) + + def printSuccess(msg: String) = print(success + msg + default) + + def printFailure(msg: String) = print(failure + msg + default) + + def printWarning(msg: String) = print(warning + msg + default) +} diff --git a/src/partest/scala/tools/partest/Properties.scala b/src/partest/scala/tools/partest/utils/Properties.scala index 4eeb0359ec..237ddea14e 100644 --- a/src/partest/scala/tools/partest/Properties.scala +++ b/src/partest/scala/tools/partest/utils/Properties.scala @@ -8,8 +8,8 @@ // $Id$ -package scala.tools -package partest +package scala.tools.partest +package utils /** Loads partest.properties from the jar. */ object Properties extends scala.util.PropertiesTrait { |