summaryrefslogtreecommitdiff
path: root/src/partest
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-04-05 06:25:16 +0000
committerPaul Phillips <paulp@improving.org>2010-04-05 06:25:16 +0000
commitedc621d245520a3b8a9ceabeb06b5c31ace98ae0 (patch)
tree0afd97374527579ca2d9329fa530e0bfac9430f7 /src/partest
parente8a121e9e1ade3f283f42fceb3c18f30a8468f57 (diff)
downloadscala-edc621d245520a3b8a9ceabeb06b5c31ace98ae0.tar.gz
scala-edc621d245520a3b8a9ceabeb06b5c31ace98ae0.tar.bz2
scala-edc621d245520a3b8a9ceabeb06b5c31ace98ae0.zip
The code part of the partest patch.
it they can be my guest (reviewbot: review by community!) More realistically: more than likely I have unwittingly altered or impaired some piece of functionality used by someone somewhere. Please alert me if this is the case and I will remedy it. I have to call it at this point as the best interests of 2.8 cannot be served by me nursing this patch along any further.
Diffstat (limited to 'src/partest')
-rw-r--r--src/partest/README76
-rw-r--r--src/partest/scala/tools/partest/Actions.scala168
-rw-r--r--src/partest/scala/tools/partest/BuildContributors.scala102
-rw-r--r--src/partest/scala/tools/partest/Categories.scala69
-rw-r--r--src/partest/scala/tools/partest/Compilable.scala103
-rw-r--r--src/partest/scala/tools/partest/Config.scala116
-rw-r--r--src/partest/scala/tools/partest/Dispatcher.scala198
-rw-r--r--src/partest/scala/tools/partest/Entities.scala78
-rw-r--r--src/partest/scala/tools/partest/Housekeeping.scala168
-rw-r--r--src/partest/scala/tools/partest/Partest.scala75
-rw-r--r--src/partest/scala/tools/partest/PartestDefaults.scala30
-rw-r--r--src/partest/scala/tools/partest/PartestSpec.scala110
-rw-r--r--src/partest/scala/tools/partest/PartestTask.scala288
-rw-r--r--src/partest/scala/tools/partest/Properties.scala (renamed from src/partest/scala/tools/partest/utils/Properties.scala)4
-rw-r--r--src/partest/scala/tools/partest/Results.scala114
-rw-r--r--src/partest/scala/tools/partest/Runner.scala39
-rw-r--r--src/partest/scala/tools/partest/Statistics.scala46
-rw-r--r--src/partest/scala/tools/partest/Universe.scala100
-rw-r--r--src/partest/scala/tools/partest/ant/JavaTask.scala52
-rw-r--r--src/partest/scala/tools/partest/ant/PartestTask.scala90
-rw-r--r--src/partest/scala/tools/partest/antlib.xml3
-rw-r--r--src/partest/scala/tools/partest/category/AllCategories.scala20
-rw-r--r--src/partest/scala/tools/partest/category/Analysis.scala65
-rw-r--r--src/partest/scala/tools/partest/category/Compiler.scala142
-rw-r--r--src/partest/scala/tools/partest/category/Runner.scala108
-rw-r--r--src/partest/scala/tools/partest/io/ANSIWriter.scala58
-rw-r--r--src/partest/scala/tools/partest/io/Diff.java (renamed from src/partest/scala/tools/partest/nest/Diff.java)2
-rw-r--r--src/partest/scala/tools/partest/io/DiffPrint.java (renamed from src/partest/scala/tools/partest/nest/DiffPrint.java)2
-rw-r--r--src/partest/scala/tools/partest/io/JUnitReport.scala38
-rw-r--r--src/partest/scala/tools/partest/io/Logging.scala128
-rw-r--r--src/partest/scala/tools/partest/nest/AntRunner.scala30
-rw-r--r--src/partest/scala/tools/partest/nest/CompileManager.scala197
-rw-r--r--src/partest/scala/tools/partest/nest/ConsoleFileManager.scala190
-rw-r--r--src/partest/scala/tools/partest/nest/ConsoleRunner.scala209
-rw-r--r--src/partest/scala/tools/partest/nest/DirectRunner.scala78
-rw-r--r--src/partest/scala/tools/partest/nest/FileManager.scala110
-rw-r--r--src/partest/scala/tools/partest/nest/NestRunner.scala16
-rw-r--r--src/partest/scala/tools/partest/nest/NestUI.scala118
-rw-r--r--src/partest/scala/tools/partest/nest/PathSettings.scala41
-rw-r--r--src/partest/scala/tools/partest/nest/ReflectiveRunner.scala88
-rw-r--r--src/partest/scala/tools/partest/nest/RunnerUtils.scala29
-rw-r--r--src/partest/scala/tools/partest/nest/StreamAppender.scala94
-rw-r--r--src/partest/scala/tools/partest/nest/TestFile.scala49
-rw-r--r--src/partest/scala/tools/partest/nest/Worker.scala1071
-rw-r--r--src/partest/scala/tools/partest/package.scala70
-rw-r--r--src/partest/scala/tools/partest/util/package.scala111
-rw-r--r--src/partest/scala/tools/partest/utils/PrintMgr.scala52
47 files changed, 2397 insertions, 2748 deletions
diff --git a/src/partest/README b/src/partest/README
index 81876fc810..c7673fe2f8 100644
--- a/src/partest/README
+++ b/src/partest/README
@@ -1,32 +1,50 @@
-How partest choses the compiler / library:
+If you're looking for something to read, I suggest running ../test/partest
+with no arguments, which at this moment prints this:
- * ''-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'')
+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
-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 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
-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
+ 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
diff --git a/src/partest/scala/tools/partest/Actions.scala b/src/partest/scala/tools/partest/Actions.scala
new file mode 100644
index 0000000000..3e745714cb
--- /dev/null
+++ b/src/partest/scala/tools/partest/Actions.scala
@@ -0,0 +1,168 @@
+/* __ *\
+** ________ ___ / / ___ 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(execEnv.mkString("ENV(", "\n", "\n)"))
+ execCwd foreach (x => trace("CWD(" + x + ")"))
+ }
+
+ trace(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(testTimeout) {
+ loggingResult {
+ proc = Process.exec(toArgs(cmd), execEnv, execCwd.orNull, true)
+ proc.slurp()
+ }
+ proc.waitFor() == 0
+ }
+ result getOrElse {
+ warning("Process never terminated: '%s'" format cmd)
+ if (proc != null)
+ proc.destroy()
+
+ false
+ }
+ }
+ }
+
+ trait ScriptableTest {
+ self: TestEntity =>
+
+ // def customTestStep(line: String): TestStep
+
+ /** Translates a line from a .cmds file into a teststep.
+ */
+ def customTestStep(line: String): TestStep = {
+ val (cmd, rest) = line span (x => !Character.isWhitespace(x))
+ val args = toArgs(rest)
+ 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 "diff" => if (args.size != 2) fail else _ => diffFiles(File(args(0)), File(args(1))) == ""
+ 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() = {
+ 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 normalizePaths(s: String) = {
+ /** This accomodates slash/backslash issues by noticing when a given
+ * line was altered, which means it held a path, and only then converting any
+ * backslashes to slashes. It's not foolproof but it's as close as we
+ * can get in one line.
+ */
+ val s2 = s.replaceAll("""(?m)\Q%s\E""" format (sourcesDir + File.separator), "")
+ if (s != s2) s2.replaceAll("""\\""", "/") else s2
+ }
+
+ /** The default cleanup normalizes paths relative to sourcesDir.
+ */
+ def diffCleanup(f: File) = safeLines(f) map normalizePaths mkString "\n"
+
+ /** 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(check: File, log: File) = {
+ def arg1 = tracePath(check)
+ def arg2 = tracePath(log)
+ def noCheck = !check.exists && returning(true)(_ => trace("diff %s %s [unchecked]".format(arg1, arg2)))
+
+ noCheck || {
+ def result = safeSlurp(check).trim == diffCleanup(log).trim
+ def msg = if (result) "passed" else "failed"
+
+ if (isDryRun) {
+ trace("diff %s %s".format(arg1, arg2))
+ true
+ }
+ else {
+ trace("diff %s %s [%s]".format(arg1, arg2, msg))
+ result
+ }
+ }
+ }
+
+ private def cleanedLog = returning(File makeTemp "partest-diff")(_ writeAll diffCleanup(logFile))
+ def diffOutput(): String = checkFile ifFile (f => diffFiles(f, cleanedLog)) getOrElse ""
+ }
+}
diff --git a/src/partest/scala/tools/partest/BuildContributors.scala b/src/partest/scala/tools/partest/BuildContributors.scala
new file mode 100644
index 0000000000..abd3bb318b
--- /dev/null
+++ b/src/partest/scala/tools/partest/BuildContributors.scala
@@ -0,0 +1,102 @@
+/* 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)
+ 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
new file mode 100644
index 0000000000..1d5a21153f
--- /dev/null
+++ b/src/partest/scala/tools/partest/Categories.scala
@@ -0,0 +1,69 @@
+/* __ *\
+** ________ ___ / / ___ 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 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
new file mode 100644
index 0000000000..a1d987ad6d
--- /dev/null
+++ b/src/partest/scala/tools/partest/Compilable.scala
@@ -0,0 +1,103 @@
+/* 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 = {
+ // javac -d outdir -classpath <basepath> <files>
+ val cmd = "%s -d %s %s %s".format(javacCmd, outDir, javacpArg, fromArgs(args))
+ 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)
+ val foundFiles = execCwd match {
+ case Some(cwd) => files map (x => File(cwd / x))
+ case _ => files map (x => File(x))
+ }
+ def nonFileArgs = if (isVerbose) global.settings.recreateArgs else assembleScalacArgs(Nil)
+ def traceArgs = fromArgs(nonFileArgs ++ (foundFiles map tracePath))
+ def traceMsg =
+ if (isVerbose) "%s %s".format(build.scalaBin / "scalac", traceArgs)
+ else "scalac " + traceArgs
+
+ trace(traceMsg)
+ isDryRun || global.partestCompile(foundFiles map (_.path), 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(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) }
+
+ 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)
+ }
+ }
+} \ No newline at end of file
diff --git a/src/partest/scala/tools/partest/Config.scala b/src/partest/scala/tools/partest/Config.scala
new file mode 100644
index 0000000000..6be08ae8d2
--- /dev/null
+++ b/src/partest/scala/tools/partest/Config.scala
@@ -0,0 +1,116 @@
+/* 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, seems overly generous
+ def testWarningSecs = testWarning.toInt // test warning - 90s by default
+ def testTimeout = testWarningSecs * 10 // test timeout
+ 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"
+
+ /** 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 " "),
+ "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
new file mode 100644
index 0000000000..e453e27293
--- /dev/null
+++ b/src/partest/scala/tools/partest/Dispatcher.scala
@@ -0,0 +1,198 @@
+/* NEST (New Scala Test)
+ * Copyright 2007-2010 LAMP/EPFL
+ * @author Philipp Haller
+ */
+
+package scala.tools
+package partest
+
+import util._
+import scala.tools.nsc.io._
+import scala.actors.{ Actor, Exit, TIMEOUT }
+import scala.actors.Actor._
+import scala.collection.immutable
+import scala.util.control.ControlThrowable
+
+/** 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)
+ 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, Int] = {
+ // 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)
+ immutable.Map[TestEntity, Int]()
+ // mark all the worker's tests as having timed out - should be hard to miss
+ groups(w.workerNum) map (_ -> 2) 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)
+
+ CombinedTestResults(passed, failed, milliSeconds)
+ }
+
+ /** 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, Int] => Unit) {
+ var results = new immutable.HashMap[TestEntity, Int] // 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 -> state)
+
+ // 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
+ val test = testIterator.next
+
+ // Sets three alarms: two slowness warnings and a timeout.
+ def setAlarms() =
+ new Alarmer(
+ testWarningSecs -> (() => warning(
+ """|I've been waiting %d seconds for this to complete:
+ | "%s"
+ |Either it's stuck or it is unreasonably slow and should
+ |be divided into smaller tests.
+ |""".stripMargin.format(testWarning, test))),
+
+ (testWarningSecs * 2) -> (() => warning(
+ """|Now I've been waiting %d seconds for this to complete:
+ | "%s"
+ |If partest seems hung, let's blame that one.
+ |""".stripMargin.format(testWarning * 2, test))),
+
+ testTimeout -> (() => parent ! new Timeout(test))
+ )
+
+ actor {
+ /** Debugging alarm issues */
+ if (isNoAlarms) {
+ parent ! TestResult(test, test.isSuccess)
+ }
+ else {
+ // Set alarms, kick it off. Calling isSuccess forces the lazy val
+ // "process" inside the test, running it.
+ val alarmer = setAlarms()
+ def respondWith(res: TestResult) = {
+ // Cancel the alarms and alert the media.
+ alarmer.cancelAll()
+ parent ! res
+ }
+
+ try respondWith(TestResult(test, test.isSuccess))
+ catch {
+ case x: ControlThrowable =>
+ test.warnAndLogException("Worker caught " + x + ", rethrowing: ", x)
+ throw x
+ case x =>
+ test.warnAndLogException("Worker caught " + x + ", failing: ", x)
+ respondWith(TestResult(test, false))
+ }
+ }
+ }
+
+ 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
new file mode 100644
index 0000000000..aea794d793
--- /dev/null
+++ b/src/partest/scala/tools/partest/Entities.scala
@@ -0,0 +1,78 @@
+/* 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 EntityLogging
+ with CompilableTest
+ with ScriptableTest
+ with DiffableTest {
+ def location: Path
+ def category: TestCategory
+
+ lazy val label = location.stripExtension
+ lazy val testClasspath = createClasspathString()
+
+ /** 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)
+ def argumentsToDiff = ((checkFile, logFile))
+
+ /** 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(argumentsToDiff._1, argumentsToDiff._2)
+
+ /** The memoized result of the test run.
+ */
+ private lazy val process = {
+ def preCheck = precondition || returning(false)(_ => trace("precondition failed"))
+ def allSteps = testSequence.actions forall (f => f(this))
+ val outcome = runWrappers(preCheck && allSteps)
+
+ // if outcome is empty, the JVM is trying to shut down, so we clam up
+ // to avoid echoing lots of spurious failure messages.
+ if (outcome.isEmpty && !isShuttingDown) setShuttingDown()
+ else 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
new file mode 100644
index 0000000000..fdc04de7e3
--- /dev/null
+++ b/src/partest/scala/tools/partest/Housekeeping.scala
@@ -0,0 +1,168 @@
+/* NEST (New Scala Test)
+ * Copyright 2007-2010 LAMP/EPFL
+ */
+
+package scala.tools
+package partest
+
+import util._
+import nsc.io._
+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. */
+ private var _shuttingDown = false
+ protected def setShuttingDown() = {
+ warning("Received shutdown signal, partest is cleaning up...\n")
+ _shuttingDown = true
+ false
+ }
+ def isShuttingDown = _shuttingDown
+
+ /** 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() =
+ if (isNoCleanup && outDir.isDirectory) debug("Not deleting " + outDir)
+ else outDir.deleteRecursively()
+
+ def cleanup() {
+ // otherwise, delete obj dir and logs on success
+ deleteOutDir()
+ if (isSuccess)
+ deleteLog()
+ }
+
+ protected def runWrappers[T](body: => T): Option[T] = {
+ prepareForTestRun()
+
+ withShutdownHook({ debug("Shutdown hook deleting " + outDir) ; deleteOutDir }) {
+ loggingOutAndErr {
+ possiblyTimed {
+ body
+ }
+ }
+ }
+ }
+
+ 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
new file mode 100644
index 0000000000..b3580d0f1c
--- /dev/null
+++ b/src/partest/scala/tools/partest/Partest.scala
@@ -0,0 +1,75 @@
+/* NEST (New Scala Test)
+ * Copyright 2007-2010 LAMP/EPFL
+ */
+
+package scala.tools
+package partest
+
+import nsc.io._
+import nsc.util.CommandLine
+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 = new CommandLine(args, PartestSpecReference.unary, PartestSpecReference.binary) {
+ override def onlyKnownOptions = true
+ override def errorFn(msg: String) = printAndExit("Error: " + msg)
+ }
+} with Universe with PartestSpec with AllCategories {
+
+ debug("Partest object created with args: " + (args mkString " "))
+
+ // 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
+
+ // Coarse validation of partest directory: holds a file called partest.
+ (partestDir / "partest").isFile || error("'%s' is not a valid partest directory." format partestDir)
+
+ def runSets = toArgs(parsed.getOrElse("--runsets", ""))
+ 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() = runSelection(selectedCategories, filter)
+}
+
+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
deleted file mode 100644
index 139c54dedd..0000000000
--- a/src/partest/scala/tools/partest/PartestDefaults.scala
+++ /dev/null
@@ -1,30 +0,0 @@
-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
new file mode 100644
index 0000000000..55d2fdcca5
--- /dev/null
+++ b/src/partest/scala/tools/partest/PartestSpec.scala
@@ -0,0 +1,110 @@
+/* NEST (New Scala Test)
+ * Copyright 2007-2010 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools
+package partest
+
+import Properties._
+import nsc.io._
+import nsc.util.{ CommandLine, CommandLineSpec }
+
+/** 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 CommandLineSpec {
+ override def isPassthroughProperty(key: String) = key == "partest.options"
+ override def isSysPropOption(key: String) = {
+ val segments = (key split '.').toList
+ if (segments.size == 2 && segments.head == "partest") Some(segments.last)
+ else None
+ }
+
+ private var _testKinds: List[String] = Nil
+ private def kind(s: String) = returning(s)(_testKinds +:= _)
+
+ def testKinds = _testKinds
+ def versionMsg = Properties.versionMsg
+
+ help("""
+ |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""")
+
+ heading ("Test categories:")
+ 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 (default: test)" |> "test"
+ val buildDir = "builddir" / "path from ~ to test build (default: build/pack)" |> "build/pack"
+ val srcDir = "srcdir" / "path from --rootdir to sources (default: files)" |> "files"
+ val javaOpts = "javaopts" / "flags to java on all runs (overrides JAVA_OPTS)" |> envOrElse("JAVA_OPTS", "")
+ val scalacOpts = "scalacopts" / "flags to scalac on all tests (overrides SCALAC_OPTS)" |> envOrElse("SCALAC_OPTS", "")
+
+ ("pack" / "alias for --builddir build/pack") ?> setProp("partest.build", "build/pack")
+ ("quick" / "alias for --builddir build/quick") ?> setProp("partest.build", "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" / "Timeout in seconds" >> ;
+ 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 isVersion = "version" / "print version" ?
+
+ // no help for anything below this line - secret options
+ // mostly intended for property configuration.
+ val runsets = "runsets" |> ""
+ val isNoAlarms = ("noalarms" ?)
+ val isInsideAnt = ("is-in-ant" ?)
+ val testWarning = "test-warning" |> "90"
+}
+
+object PartestSpecReference extends PartestSpec {
+ import CommandLineSpec._
+
+ def parsed: CommandLine = null
+ override def isReferenceSpec = true
+
+ def unary = unaryOptions
+ def binary = binaryOptions
+ def allArgs = unary ++ binary
+
+ def isunaryOption(s: String) = unary contains toOpt(s)
+ def isbinaryOption(s: String) = binary contains toOpt(s)
+
+ def main(args: Array[String]): Unit = println(bashCompletion("partest"))
+
+ /** Append bash completion for partest to the given file.
+ */
+ def appendCompletionTo(f: File) = f appendAll bashCompletion("partest")
+}
+
diff --git a/src/partest/scala/tools/partest/PartestTask.scala b/src/partest/scala/tools/partest/PartestTask.scala
deleted file mode 100644
index 96802370fc..0000000000
--- a/src/partest/scala/tools/partest/PartestTask.scala
+++ /dev/null
@@ -1,288 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ 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
-import io.{ Directory }
-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[io.Path] = {
- 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[io.Path] = 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/utils/Properties.scala b/src/partest/scala/tools/partest/Properties.scala
index 237ddea14e..4eeb0359ec 100644
--- a/src/partest/scala/tools/partest/utils/Properties.scala
+++ b/src/partest/scala/tools/partest/Properties.scala
@@ -8,8 +8,8 @@
// $Id$
-package scala.tools.partest
-package utils
+package scala.tools
+package partest
/** Loads partest.properties from the jar. */
object Properties extends scala.util.PropertiesTrait {
diff --git a/src/partest/scala/tools/partest/Results.scala b/src/partest/scala/tools/partest/Results.scala
new file mode 100644
index 0000000000..a830652d5f
--- /dev/null
+++ b/src/partest/scala/tools/partest/Results.scala
@@ -0,0 +1,114 @@
+/* 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, Int])
+
+ /** 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, if (passed) "passed" else "failed")
+ }
+
+ 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))
+ }
+ }
+ 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
+ ) {
+ // 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
+ )
+
+ def elapsedString = "%02d:%02d:%02d".format(elapsedHrs, dispMins, dispSecs)
+ 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)
+ 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
new file mode 100644
index 0000000000..7f67c93478
--- /dev/null
+++ b/src/partest/scala/tools/partest/Runner.scala
@@ -0,0 +1,39 @@
+/* NEST (New Scala Test)
+ * Copyright 2007-2010 LAMP/EPFL
+ * @author Philipp Haller
+ */
+
+package scala.tools
+package partest
+
+import nsc.io._
+
+object Runner {
+ def main(mainArgs: Array[String]) {
+ val propArgs = PartestSpecReference.sysPropsAsOptions()
+ val args = (propArgs ++ mainArgs).toList
+ val runner = Partest(args: _*)
+ import runner._
+
+ if (isVersion) return println(versionMsg)
+ 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
new file mode 100644
index 0000000000..2ea3c6e8f0
--- /dev/null
+++ b/src/partest/scala/tools/partest/Statistics.scala
@@ -0,0 +1,46 @@
+/* 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
new file mode 100644
index 0000000000..568a155281
--- /dev/null
+++ b/src/partest/scala/tools/partest/Universe.scala
@@ -0,0 +1,100 @@
+/* __ *\
+** ________ ___ / / ___ 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 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
+
+ /** Any preconditions before running the test. Test fails
+ * immediately if this returns false.
+ */
+ def precondition: Boolean = true
+
+ /** 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
new file mode 100644
index 0000000000..37e08fee1b
--- /dev/null
+++ b/src/partest/scala/tools/partest/ant/JavaTask.scala
@@ -0,0 +1,52 @@
+/* __ *\
+** ________ ___ / / ___ 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.{ EnumeratedAttribute, Commandline, Environment, PropertySet }
+
+import scala.tools.nsc.io._
+import scala.tools.nsc.util.{ ClassPath, CommandLineSpec }
+import CommandLineSpec._
+
+class JavaTask extends Java {
+ override def getTaskName() = "partest"
+ private val scalaRunnerClass = "scala.tools.nsc.MainGenericRunner"
+
+ protected def rootDir = prop("partest.rootdir") getOrElse (baseDir / "test").path
+ protected def partestJVMArgs = prop("partest.jvm.args") getOrElse "-Xms64M -Xmx768M -Xss768K -XX:MaxPermSize=96M"
+ protected def runnerArgs = List("-usejavacp", "scala.tools.partest.Runner", "--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)
+ getProject.setSystemProperties()
+ setClassname(scalaRunnerClass)
+ // setDir(Path(rootDir).jfile)
+ // addSyspropertyset(partestPropSet)
+ addSysproperty(newKeyValue("partest.is-in-ant", "true"))
+ jvmline(partestJVMArgs)
+ runnerArgs foreach addArg
+ }
+
+ override def execute() {
+ setDefaults()
+ super.execute()
+ }
+}
diff --git a/src/partest/scala/tools/partest/ant/PartestTask.scala b/src/partest/scala/tools/partest/ant/PartestTask.scala
new file mode 100644
index 0000000000..65848fabb0
--- /dev/null
+++ b/src/partest/scala/tools/partest/ant/PartestTask.scala
@@ -0,0 +1,90 @@
+/* __ *\
+** ________ ___ / / ___ Scala Parallel Testing **
+** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+/**** Note -- this isn't used anymore, but I left it in for the moment. ****/
+
+package scala.tools
+package partest
+package ant
+
+import java.io.{ File => JFile }
+
+import org.apache.tools.ant.Task
+import org.apache.tools.ant.types.{ Reference, FileSet}
+
+import scala.reflect.BeanProperty
+import scala.tools.ant.sabbus.CompilationPathProperty
+import scala.tools.nsc.io
+import scala.tools.nsc.util.CommandLineSpec._
+
+class PartestTask extends Task with CompilationPathProperty {
+ /** Used only in ant task */
+ @BeanProperty protected var errorOnFailed: Boolean = _
+ @BeanProperty protected var jUnitReportDir: JFile = _
+
+ /** Propagated to partest run via system properties */
+ @BeanProperty protected var debug: Boolean = _
+ @BeanProperty protected var javaOpts: String = _
+ @BeanProperty protected var partestOpts: String = _
+ @BeanProperty protected var runSets: String = _
+ @BeanProperty protected var scalacOpts: String = _
+ @BeanProperty protected var showDiff: Boolean = _
+ @BeanProperty protected var showLog: Boolean = _
+ @BeanProperty protected var srcDir: String = _
+ @BeanProperty protected var timeout: Int = _
+
+ /** Translating ant information into command line arguments. */
+ private def notEmpty(s: String) = s != null && s.length > 0
+ private def quoted(s: String) = if (s exists (_.isWhitespace)) "\"" + s.trim + "\"" else s
+ private def optionCollection = List[(Boolean, () => List[String])](
+ debug -> (() => List("--debug")),
+ showLog -> (() => List("--show-log")),
+ showDiff -> (() => List("--show-diff")),
+ (timeout > 0) -> (() => List("--timeout", timeout.toString)),
+ notEmpty(javaOpts) -> (() => List("--javaopts", javaOpts)),
+ notEmpty(scalacOpts) -> (() => List("--scalacopts", scalacOpts)),
+ notEmpty(srcDir) -> (() => List("--srcdir", srcDir)),
+ notEmpty(partestOpts) -> (() => toArgs(partestOpts))
+ )
+
+ private def antPropOrNone(name: String) = Option(getProject getProperty name)
+ private def antPropsToCommandLine() = {
+ setProp("partest.isInAnt", "true")
+ val partestDir = antPropOrNone("partest.dir") getOrElse error("Mandatory attribute 'partest.dir' is not set.")
+
+ val root = List("--rootdir", io.Path(partestDir).path)
+ val opts = optionCollection collect { case (true, f) => f() } flatten
+ val sets = Option(runSets).toList flatMap toArgs map toOpt
+
+ root ++ opts ++ sets
+ }
+ private def antRunTests() = {
+ val args = antPropsToCommandLine()
+ val runner = Partest(args: _*)
+ import runner._
+
+ normal("Ant options translate to command line: partest " + fromArgs(args))
+ printConfigBanner()
+
+ val result = launchTestSuite()
+ val msg = result.toString
+
+ if (result.hasFailures && errorOnFailed) error(msg)
+ else log(msg)
+ }
+
+ override def execute() {
+ try antRunTests()
+ catch {
+ case x =>
+ System.err.println("Uncaught exception %s in partest ant ask: aborting." format x)
+ x.printStackTrace()
+ throw x
+ }
+ }
+}
diff --git a/src/partest/scala/tools/partest/antlib.xml b/src/partest/scala/tools/partest/antlib.xml
index b3b98e853f..af36f11368 100644
--- a/src/partest/scala/tools/partest/antlib.xml
+++ b/src/partest/scala/tools/partest/antlib.xml
@@ -1,4 +1,3 @@
<antlib>
- <taskdef name="partest"
- classname="scala.tools.partest.PartestTask"/>
+ <taskdef name="partest" classname="scala.tools.partest.ant.JavaTask"/>
</antlib>
diff --git a/src/partest/scala/tools/partest/category/AllCategories.scala b/src/partest/scala/tools/partest/category/AllCategories.scala
new file mode 100644
index 0000000000..ce6573123a
--- /dev/null
+++ b/src/partest/scala/tools/partest/category/AllCategories.scala
@@ -0,0 +1,20 @@
+/* __ *\
+** ________ ___ / / ___ 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(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
new file mode 100644
index 0000000000..f2b43ebf6d
--- /dev/null
+++ b/src/partest/scala/tools/partest/category/Analysis.scala
@@ -0,0 +1,65 @@
+/* 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(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")
+ override def precondition = checkFile.isFile && super.precondition
+
+ 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
new file mode 100644
index 0000000000..58fd8230e2
--- /dev/null
+++ b/src/partest/scala/tools/partest/category/Compiler.scala
@@ -0,0 +1,142 @@
+/* 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(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 precondition = checkFile.isFile && super.precondition
+ 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(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 precondition = checkFile.isFile && super.precondition
+
+ 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(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
new file mode 100644
index 0000000000..a7713d7dbe
--- /dev/null
+++ b/src/partest/scala/tools/partest/category/Runner.scala
@@ -0,0 +1,108 @@
+/* __ *\
+** ________ ___ / / ___ 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("scalacheck result for %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
new file mode 100644
index 0000000000..0ddcd97a5f
--- /dev/null
+++ b/src/partest/scala/tools/partest/io/ANSIWriter.scala
@@ -0,0 +1,58 @@
+/* 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/nest/Diff.java b/src/partest/scala/tools/partest/io/Diff.java
index abd09d0293..c7a3d42f30 100644
--- a/src/partest/scala/tools/partest/nest/Diff.java
+++ b/src/partest/scala/tools/partest/io/Diff.java
@@ -1,6 +1,6 @@
// $Id$
-package scala.tools.partest.nest;
+package scala.tools.partest.io;
import java.util.Hashtable;
diff --git a/src/partest/scala/tools/partest/nest/DiffPrint.java b/src/partest/scala/tools/partest/io/DiffPrint.java
index 494bc06e4a..2b2ad93ec7 100644
--- a/src/partest/scala/tools/partest/nest/DiffPrint.java
+++ b/src/partest/scala/tools/partest/io/DiffPrint.java
@@ -1,6 +1,6 @@
// $Id$
-package scala.tools.partest.nest;
+package scala.tools.partest.io;
import java.io.*;
import java.util.Vector;
diff --git a/src/partest/scala/tools/partest/io/JUnitReport.scala b/src/partest/scala/tools/partest/io/JUnitReport.scala
new file mode 100644
index 0000000000..63ae200020
--- /dev/null
+++ b/src/partest/scala/tools/partest/io/JUnitReport.scala
@@ -0,0 +1,38 @@
+/* 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
new file mode 100644
index 0000000000..2d67ac5ae4
--- /dev/null
+++ b/src/partest/scala/tools/partest/io/Logging.scala
@@ -0,0 +1,128 @@
+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()
+ }
+
+ /** XXX needs attention.
+ */
+ def failureMessage() = 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) = if (isVerbose) path.path else path.name
+
+ /** 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: Throwable => logException(x)
+ }
+
+ def throwableToString(x: Throwable): String = {
+ val w = new StringWriter
+ x.printStackTrace(new PrintWriter(w))
+ w.toString
+ }
+
+ def warnAndLogException(msg: String, ex: Throwable) = {
+ val str = msg + throwableToString(ex)
+ warning(toStringTrunc(str, 800))
+ logWriter append str
+ }
+
+ 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
deleted file mode 100644
index cb819720fc..0000000000
--- a/src/partest/scala/tools/partest/nest/AntRunner.scala
+++ /dev/null
@@ -1,30 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ 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
deleted file mode 100644
index 22568ad2d0..0000000000
--- a/src/partest/scala/tools/partest/nest/CompileManager.scala
+++ /dev/null
@@ -1,197 +0,0 @@
-/* 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
deleted file mode 100644
index 58d16a3f45..0000000000
--- a/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala
+++ /dev/null
@@ -1,190 +0,0 @@
-/* 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
deleted file mode 100644
index eae79f23af..0000000000
--- a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala
+++ /dev/null
@@ -1,209 +0,0 @@
-/* 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/nest/DirectRunner.scala b/src/partest/scala/tools/partest/nest/DirectRunner.scala
deleted file mode 100644
index f774320f4e..0000000000
--- a/src/partest/scala/tools/partest/nest/DirectRunner.scala
+++ /dev/null
@@ -1,78 +0,0 @@
-/* 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
deleted file mode 100644
index bdbb34b3c4..0000000000
--- a/src/partest/scala/tools/partest/nest/FileManager.scala
+++ /dev/null
@@ -1,110 +0,0 @@
-/* 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
deleted file mode 100644
index 158521875e..0000000000
--- a/src/partest/scala/tools/partest/nest/NestRunner.scala
+++ /dev/null
@@ -1,16 +0,0 @@
-/* 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
deleted file mode 100644
index efff4e8375..0000000000
--- a/src/partest/scala/tools/partest/nest/NestUI.scala
+++ /dev/null
@@ -1,118 +0,0 @@
-/* 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
deleted file mode 100644
index 41bba5782e..0000000000
--- a/src/partest/scala/tools/partest/nest/PathSettings.scala
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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
deleted file mode 100644
index b3f199a3d6..0000000000
--- a/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala
+++ /dev/null
@@ -1,88 +0,0 @@
-/* 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
deleted file mode 100644
index 24445bb545..0000000000
--- a/src/partest/scala/tools/partest/nest/RunnerUtils.scala
+++ /dev/null
@@ -1,29 +0,0 @@
-/* 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
deleted file mode 100644
index 8cebcf1685..0000000000
--- a/src/partest/scala/tools/partest/nest/StreamAppender.scala
+++ /dev/null
@@ -1,94 +0,0 @@
-/* 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
deleted file mode 100644
index 741556fdd5..0000000000
--- a/src/partest/scala/tools/partest/nest/TestFile.scala
+++ /dev/null
@@ -1,49 +0,0 @@
-/* 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
deleted file mode 100644
index 2f81dfd0f7..0000000000
--- a/src/partest/scala/tools/partest/nest/Worker.scala
+++ /dev/null
@@ -1,1071 +0,0 @@
-/* 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 e9eda6fb75..abab74de6e 100644
--- a/src/partest/scala/tools/partest/package.scala
+++ b/src/partest/scala/tools/partest/package.scala
@@ -4,37 +4,61 @@
package scala.tools
-import java.io.{ File => JFile }
-import nsc.io.{ Path, Process, Directory }
-import util.{ PathResolver }
-import nsc.Properties.{ propOrElse, propOrNone, propOrEmpty }
+import scala.util.control.Exception.catching
+import nsc.io.{ File, Path, Process, Directory }
+import nsc.util.CommandLineSpec
+import Process.runtime
+import java.nio.charset.CharacterCodingException
package object partest {
- import nest.NestUI
+ /** 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 => "" }
- implicit private[partest] def temporaryPath2File(x: Path): JFile = x.jfile
- implicit private[partest] def temporaryFile2Path(x: JFile): Path = Path(x)
+ 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)
- 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)
- }
+ private[partest] def toArgs(line: String) = CommandLineSpec toArgs line
+ private[partest] def fromArgs(args: List[String]) = CommandLineSpec fromArgs args
- def vmArgString = {
- val str = Process.javaVmArguments mkString " "
- "Java VM started with arguments: '%s'" format str
- }
+ /** Strings, argument lists, etc. */
- def allPropertiesString = {
- import collection.JavaConversions._
- System.getProperties.toList.sorted map { case (k, v) => "%s -> %s\n".format(k, v) } mkString
+ 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) + " [...]"
}
+ private[partest] def setProp(k: String, v: String) = scala.util.Properties.setProp(k, v)
- def showAllJVMInfo {
- NestUI.verbose(vmArgString)
- NestUI.verbose(allPropertiesString)
+ /** Pretty self explanatory. */
+ def printAndExit(msg: String): Unit = {
+ println(msg)
+ exit(1)
}
- def isPartestDebug = propOrEmpty("partest.debug") == "true"
+ /** 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() = hook }
+ runtime addShutdownHook t
+
+ try body
+ finally runtime removeShutdownHook t
+ }
+
+ /** Apply a function and return the passed value */
+ def returning[T](x: T)(f: T => Unit): T = { f(x) ; x }
} \ 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
new file mode 100644
index 0000000000..d20b0659ec
--- /dev/null
+++ b/src/partest/scala/tools/partest/util/package.scala
@@ -0,0 +1,111 @@
+/* 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 interruptMeIn[T](seconds: Int)(body: => T): Option[T] = {
+ val thisThread = currentThread
+ val alarm = new SimpleAlarm(seconds * 1000) set thisThread.interrupt()
+
+ try { Some(body) }
+ catch { case _: InterruptedException => None }
+ finally { alarm.cancel() ; Thread.interrupted() }
+ }
+
+ 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
+ }
+}
+
+package util {
+ /** Set any number of alarms up with tuples of the form:
+ * seconds to alarm -> Function0[Unit] to execute
+ */
+ class Alarmer(alarmTimes: (Int, () => Unit)*) {
+ import java.util.concurrent._
+
+ val exec = Executors.newSingleThreadScheduledExecutor()
+ private def sched(secs: Int, f: () => Unit) =
+ exec.schedule(new Runnable { def run() = f() }, secs, TimeUnit.SECONDS)
+
+ alarmTimes foreach (sched _).tupled
+ 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()
+ }
+
+ // 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()
+ // }
+} \ No newline at end of file
diff --git a/src/partest/scala/tools/partest/utils/PrintMgr.scala b/src/partest/scala/tools/partest/utils/PrintMgr.scala
deleted file mode 100644
index 10533130f1..0000000000
--- a/src/partest/scala/tools/partest/utils/PrintMgr.scala
+++ /dev/null
@@ -1,52 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ 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)
-}