summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x[-rw-r--r--]build.xml85
-rw-r--r--src/partest/scala/tools/partest/DirectTest.scala5
-rw-r--r--src/partest/scala/tools/partest/IcodeTest.scala4
-rw-r--r--src/partest/scala/tools/partest/PartestDefaults.scala9
-rw-r--r--src/partest/scala/tools/partest/PartestTask.scala341
-rw-r--r--src/partest/scala/tools/partest/TestKinds.scala67
-rw-r--r--src/partest/scala/tools/partest/TestState.scala54
-rw-r--r--src/partest/scala/tools/partest/nest/AntRunner.scala25
-rw-r--r--src/partest/scala/tools/partest/nest/CompileManager.scala182
-rw-r--r--src/partest/scala/tools/partest/nest/ConsoleFileManager.scala33
-rw-r--r--src/partest/scala/tools/partest/nest/ConsoleRunner.scala321
-rw-r--r--src/partest/scala/tools/partest/nest/DirectCompiler.scala105
-rw-r--r--src/partest/scala/tools/partest/nest/DirectRunner.scala52
-rw-r--r--src/partest/scala/tools/partest/nest/FileManager.scala47
-rw-r--r--src/partest/scala/tools/partest/nest/NestUI.scala111
-rw-r--r--src/partest/scala/tools/partest/nest/Runner.scala584
-rw-r--r--src/partest/scala/tools/partest/nest/RunnerManager.scala689
-rw-r--r--src/partest/scala/tools/partest/nest/SBTRunner.scala29
-rw-r--r--src/partest/scala/tools/partest/nest/StreamCapture.scala53
-rw-r--r--src/partest/scala/tools/partest/nest/TestFile.scala80
-rw-r--r--src/partest/scala/tools/partest/package.scala151
-rwxr-xr-xtest/build-partest.xml24
-rw-r--r--test/files/neg/choices.check2
-rw-r--r--test/files/neg/choices.flags2
-rw-r--r--test/files/run/reify_this.scala6
-rw-r--r--test/files/scalap/abstractClass.check (renamed from test/files/scalap/abstractClass/result.test)0
-rw-r--r--test/files/scalap/abstractClass.scala (renamed from test/files/scalap/abstractClass/A.scala)0
-rw-r--r--test/files/scalap/abstractMethod.check (renamed from test/files/scalap/abstractMethod/result.test)0
-rw-r--r--test/files/scalap/abstractMethod.scala (renamed from test/files/scalap/abstractMethod/A.scala)0
-rw-r--r--test/files/scalap/caseClass.check (renamed from test/files/scalap/caseClass/result.test)0
-rw-r--r--test/files/scalap/caseClass.scala (renamed from test/files/scalap/caseClass/A.scala)0
-rw-r--r--test/files/scalap/caseObject.check (renamed from test/files/scalap/caseObject/result.test)0
-rw-r--r--test/files/scalap/caseObject.scala (renamed from test/files/scalap/caseObject/A.scala)0
-rw-r--r--test/files/scalap/cbnParam.check (renamed from test/files/scalap/cbnParam/result.test)0
-rw-r--r--test/files/scalap/cbnParam.scala (renamed from test/files/scalap/cbnParam/A.scala)0
-rw-r--r--test/files/scalap/classPrivate.check (renamed from test/files/scalap/classPrivate/result.test)0
-rw-r--r--test/files/scalap/classPrivate.scala (renamed from test/files/scalap/classPrivate/A.scala)0
-rw-r--r--test/files/scalap/classWithExistential.check (renamed from test/files/scalap/classWithExistential/result.test)0
-rw-r--r--test/files/scalap/classWithExistential.scala (renamed from test/files/scalap/classWithExistential/A.scala)0
-rw-r--r--test/files/scalap/classWithSelfAnnotation.check (renamed from test/files/scalap/classWithSelfAnnotation/result.test)0
-rw-r--r--test/files/scalap/classWithSelfAnnotation.scala (renamed from test/files/scalap/classWithSelfAnnotation/A.scala)0
-rw-r--r--test/files/scalap/covariantParam.check (renamed from test/files/scalap/covariantParam/result.test)0
-rw-r--r--test/files/scalap/covariantParam.scala (renamed from test/files/scalap/covariantParam/A.scala)0
-rw-r--r--test/files/scalap/defaultParameter.check (renamed from test/files/scalap/defaultParameter/result.test)0
-rw-r--r--test/files/scalap/defaultParameter.scala (renamed from test/files/scalap/defaultParameter/A.scala)0
-rw-r--r--test/files/scalap/implicitParam.check (renamed from test/files/scalap/implicitParam/result.test)0
-rw-r--r--test/files/scalap/implicitParam.scala (renamed from test/files/scalap/implicitParam/A.scala)0
-rw-r--r--test/files/scalap/packageObject.check (renamed from test/files/scalap/packageObject/result.test)0
-rw-r--r--test/files/scalap/packageObject.scala (renamed from test/files/scalap/packageObject/A.scala)0
-rw-r--r--test/files/scalap/paramClauses.check (renamed from test/files/scalap/paramClauses/result.test)0
-rw-r--r--test/files/scalap/paramClauses.scala (renamed from test/files/scalap/paramClauses/A.scala)0
-rw-r--r--test/files/scalap/paramNames.check (renamed from test/files/scalap/paramNames/result.test)0
-rw-r--r--test/files/scalap/paramNames.scala (renamed from test/files/scalap/paramNames/A.scala)0
-rw-r--r--test/files/scalap/sequenceParam.check (renamed from test/files/scalap/sequenceParam/result.test)0
-rw-r--r--test/files/scalap/sequenceParam.scala (renamed from test/files/scalap/sequenceParam/A.scala)0
-rw-r--r--test/files/scalap/simpleClass.check (renamed from test/files/scalap/simpleClass/result.test)0
-rw-r--r--test/files/scalap/simpleClass.scala (renamed from test/files/scalap/simpleClass/A.scala)0
-rw-r--r--test/files/scalap/traitObject.check (renamed from test/files/scalap/traitObject/result.test)0
-rw-r--r--test/files/scalap/traitObject.scala (renamed from test/files/scalap/traitObject/A.scala)0
-rw-r--r--test/files/scalap/typeAnnotations.check (renamed from test/files/scalap/typeAnnotations/result.test)0
-rw-r--r--test/files/scalap/typeAnnotations.scala (renamed from test/files/scalap/typeAnnotations/A.scala)0
-rw-r--r--test/files/scalap/valAndVar.check (renamed from test/files/scalap/valAndVar/result.test)0
-rw-r--r--test/files/scalap/valAndVar.scala (renamed from test/files/scalap/valAndVar/A.scala)0
-rw-r--r--test/files/scalap/wildcardType.check (renamed from test/files/scalap/wildcardType/result.test)0
-rw-r--r--test/files/scalap/wildcardType.scala (renamed from test/files/scalap/wildcardType/A.scala)0
-rwxr-xr-xtest/partest16
66 files changed, 1480 insertions, 1597 deletions
diff --git a/build.xml b/build.xml
index fa2e9e1d9b..db30b5d5ca 100644..100755
--- a/build.xml
+++ b/build.xml
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="sabbus" default="build" xmlns:artifact="urn:maven-artifact-ant">
+ <include file="test/build-partest.xml" as="partest"/>
+
<description>
SuperSabbus for Scala core, builds the scala library and compiler. It can also package it as a simple distribution, tests it for stable bootstrapping and against the Scala test suite.
</description>
@@ -135,7 +137,7 @@ TODO:
<property name="dists.dir" value="${basedir}/dists"/>
<property name="copyright.string" value="Copyright 2002-2013, LAMP/EPFL"/>
- <property name="partest.version.number" value="0.9.2"/>
+ <property name="partest.version.number" value="0.9.3"/>
<!-- These are NOT the flags used to run SuperSabbus, but the ones written
into the script runners created with scala.tools.ant.ScalaTool -->
@@ -530,7 +532,7 @@ TODO:
<path id="quick.partest.build.path">
<path refid="quick.scalap.build.path"/>
<path refid="partest.extras.classpath"/>
- <pathelement location="${build-quick.dir}/classes/repl"/>
+ <pathelement location="${build-quick.dir}/classes/repl"/>
<pathelement location="${scalacheck.jar}"/>
</path>
@@ -650,11 +652,15 @@ TODO:
<path refid="partest.extras.classpath"/>
</path>
+ <!-- obsolete? -->
+ <!-- TODO - segregate swing tests (there can't be many) -->
+ <!--
<path id="partest.build.path">
<path refid="pack.compiler.path"/>
<fileset dir="${partest.dir}/files/lib" includes="*.jar" />
- <pathelement location="${pack.dir}/lib/scala-swing.jar"/> <!-- TODO - segregate swing tests (there can't be many) -->
+ <pathelement location="${pack.dir}/lib/scala-swing.jar"/>
</path>
+ -->
<path id="test.osgi.compiler.build.path">
<pathelement location="${test.osgi.classes}"/>
@@ -762,8 +768,8 @@ TODO:
<touch file="${build-libs.dir}/@{project}.complete" verbose="no"/>
</then></if>
</sequential>
- </macrodef>
-
+ </macrodef>
+
<target name="asm.done" depends="init"> <simple-javac project="asm" jar="no"/> </target>
<target name="forkjoin.done" depends="init"> <simple-javac project="forkjoin" args="-XDignore.symbol.file"/></target>
@@ -1418,72 +1424,32 @@ TODO:
<stopwatch name="quick.sbt-interface.timer" action="total"/>
</target>
- <property name="partest.srcdir" value="files" /> <!-- TODO: make targets for `pending` and other subdirs -->
+ <!-- See test/build-partest.xml for the macro(s) being used here. -->
- <target name="test.run" depends="pack.done">
- <partest showlog="yes" erroronfailed="yes" javacmd="${java.home}/bin/java"
- timeout="1200000"
- srcdir="${partest.srcdir}"
- scalacopts="${scalac.args.optimise}">
-
- <compilationpath refid="partest.build.path"/>
- <runtests dir="${partest.dir}/${partest.srcdir}/run" includes="*.scala"/>
- <jvmtests dir="${partest.dir}/${partest.srcdir}/jvm" includes="*.scala"/>
- </partest>
+ <target name="test.suite" depends="pack.done">
+ <testSuite/>
</target>
- <target name="test.suite" depends="pack.done">
- <partest showlog="yes" erroronfailed="yes" javacmd="${java.home}/bin/java"
- timeout="2400000"
- srcdir="${partest.srcdir}"
- scalacopts="${scalac.args.optimise}">
- <compilationpath refid="partest.build.path"/>
- <postests dir="${partest.dir}/${partest.srcdir}/pos" includes="*.scala"/>
- <negtests dir="${partest.dir}/${partest.srcdir}/neg" includes="*.scala"/>
- <runtests dir="${partest.dir}/${partest.srcdir}/run" includes="*.scala"/>
- <jvmtests dir="${partest.dir}/${partest.srcdir}/jvm" includes="*.scala"/>
- <residenttests dir="${partest.dir}/${partest.srcdir}/res" includes="*.res"/>
- <scalaptests dir="${partest.dir}/${partest.srcdir}/scalap" includes="**/*.scala"/>
- <scalachecktests dir="${partest.dir}/${partest.srcdir}/scalacheck">
- <include name="*.scala"/>
- </scalachecktests>
- <specializedtests dir="${partest.dir}/${partest.srcdir}/specialized">
- <include name="*.scala"/>
- </specializedtests>
- <instrumentedtests dir="${partest.dir}/${partest.srcdir}/instrumented">
- <include name="*.scala"/>
- </instrumentedtests>
- </partest>
+ <target name="test.suite.color" depends="pack.done">
+ <testSuite colors="8"/>
+ </target>
+
+ <target name="test.run" depends="pack.done">
+ <testSuite kinds="run jvm"/>
</target>
<target name="test.continuations.suite" depends="pack.done">
- <partest showlog="yes" erroronfailed="yes" javacmd="${java.home}/bin/java"
- timeout="2400000"
- srcdir="${partest.srcdir}"
- scalacopts="${scalac.args.optimise} -Xplugin-require:continuations -P:continuations:enable">
- <compilerarg value="-Xpluginsdir"/>
- <compilerarg file="${build-quick.dir}/misc/scala-devel/plugins"/>
- <compilationpath refid="partest.build.path"/>
- <negtests dir="${partest.dir}/${partest.srcdir}/continuations-neg" includes="*.scala"/>
- <runtests dir="${partest.dir}/${partest.srcdir}/continuations-run" includes="*.scala"/>
- </partest>
+ <testSuite kinds="continuations-neg continuations-run"
+ scalacOpts="${scalac.args.optimise} -Xpluginsdir ${build-quick.dir}/misc/scala-devel/plugins -Xplugin-require:continuations -P:continuations:enable"
+ />
</target>
<target name="test.scaladoc" depends="pack.done">
- <partest erroronfailed="yes" scalacopts="${scalac.args.optimise}" showlog="yes">
- <compilationpath refid="partest.build.path"/>
- <runtests dir="${partest.dir}/scaladoc/run" includes="*.scala" />
- <scalachecktests dir="${partest.dir}/scaladoc/scalacheck" includes="*.scala" />
- </partest>
+ <testSuite kinds="run scalacheck" srcdir="scaladoc"/>
</target>
<target name="test.interactive" depends="pack.done">
- <partest erroronfailed="yes" scalacopts="${scalac.args.optimise}" showlog="yes">
- <compilationpath refid="partest.build.path"/>
- <presentationtests dir="${partest.dir}/${partest.srcdir}/presentation">
- <include name="*/*.scala"/>
- </presentationtests>
- </partest>
+ <testSuite kinds="presentation"/>
</target>
<!-- for use in PR validation, where stability is rarely broken, so we're going to use starr for locker,
@@ -1491,7 +1457,6 @@ TODO:
<target name="test.core" depends="test.osgi, test.sbt, test.bc, test.interactive, test.continuations.suite, test.scaladoc, test.suite"/>
<target name="test.done" depends="test.core, test.stability"/>
-
<!-- ===========================================================================
BINARY COMPATIBILITY TESTING
============================================================================ -->
diff --git a/src/partest/scala/tools/partest/DirectTest.scala b/src/partest/scala/tools/partest/DirectTest.scala
index 3f61062073..46e9621b31 100644
--- a/src/partest/scala/tools/partest/DirectTest.scala
+++ b/src/partest/scala/tools/partest/DirectTest.scala
@@ -6,7 +6,6 @@
package scala.tools.partest
import scala.tools.nsc._
-import io.Directory
import util.{BatchSourceFile, CommandLineParser}
import reporters.{Reporter, ConsoleReporter}
@@ -21,8 +20,8 @@ abstract class DirectTest extends App {
def show(): Unit
// the test file or dir, and output directory
- def testPath = io.File(sys.props("partest.test-path"))
- def testOutput = io.Directory(sys.props("partest.output"))
+ def testPath = SFile(sys.props("partest.test-path"))
+ def testOutput = Directory(sys.props("partest.output"))
// override to add additional settings with strings
def extraSettings: String = ""
diff --git a/src/partest/scala/tools/partest/IcodeTest.scala b/src/partest/scala/tools/partest/IcodeTest.scala
index f5333cc5f9..b12ec0de61 100644
--- a/src/partest/scala/tools/partest/IcodeTest.scala
+++ b/src/partest/scala/tools/partest/IcodeTest.scala
@@ -5,9 +5,7 @@
package scala.tools.partest
-import scala.tools.nsc._
-import nest.FileUtil._
-import io.Directory
+import scala.tools.partest.nest.FileUtil.compareContents
/** A trait for testing icode. All you need is this in a
* partest source file:
diff --git a/src/partest/scala/tools/partest/PartestDefaults.scala b/src/partest/scala/tools/partest/PartestDefaults.scala
index 5d98a8be81..16f1a6933f 100644
--- a/src/partest/scala/tools/partest/PartestDefaults.scala
+++ b/src/partest/scala/tools/partest/PartestDefaults.scala
@@ -1,13 +1,10 @@
package scala.tools
package partest
-import nsc.io.{ File, Path, Directory }
-import scala.tools.util.PathResolver
-import nsc.Properties.{ propOrElse, propOrNone, propOrEmpty }
-import java.lang.Runtime.getRuntime
+import scala.tools.nsc.Properties.{ propOrElse, propOrNone, propOrEmpty }
+import java.lang.Runtime.{ getRuntime => runtime }
object PartestDefaults {
- import nsc.Properties._
def testRootName = propOrNone("partest.root")
def srcDirName = propOrElse("partest.srcdir", "files")
@@ -23,7 +20,7 @@ object PartestDefaults {
def testBuild = propOrNone("partest.build")
def errorCount = propOrElse("partest.errors", "0").toInt
- def numThreads = propOrNone("partest.threads") map (_.toInt) getOrElse getRuntime.availableProcessors
+ def numThreads = propOrNone("partest.threads") map (_.toInt) getOrElse runtime.availableProcessors
def timeout = "1200000"
}
diff --git a/src/partest/scala/tools/partest/PartestTask.scala b/src/partest/scala/tools/partest/PartestTask.scala
index 13207b16fd..b5b09a753a 100644
--- a/src/partest/scala/tools/partest/PartestTask.scala
+++ b/src/partest/scala/tools/partest/PartestTask.scala
@@ -10,14 +10,10 @@ package scala.tools
package partest
import scala.util.Properties.setProp
-import scala.tools.nsc.io.{ Directory, Path => SPath }
-import nsc.util.ClassPath
-import util.PathResolver
import scala.tools.ant.sabbus.CompilationPathProperty
-import java.io.File
import java.lang.reflect.Method
import org.apache.tools.ant.Task
-import org.apache.tools.ant.types.{Path, Reference, FileSet}
+import org.apache.tools.ant.types.{ Reference, FileSet}
import org.apache.tools.ant.types.Commandline.Argument
/** An Ant task to execute the Scala test suite (NSC).
@@ -26,92 +22,42 @@ import org.apache.tools.ant.types.Commandline.Argument
* - `srcdir`,
* - `classpath`,
* - `classpathref`,
- * - `showlog`,
- * - `showdiff`,
* - `erroronfailed`,
* - `javacmd`,
* - `javaccmd`,
* - `scalacopts`,
- * - `timeout`,
* - `debug`,
* - `junitreportdir`.
*
* It also takes the following parameters as nested elements:
* - `compilationpath`.
- * - `postests`,
- * - `negtests`,
- * - `runtests`,
- * - `jvmtests`,
- * - `residenttests`,
- * - `shootouttests`,
- * - `scalaptests`,
- * - `scalachecktests`,
- * - `specializedtests`,
- * - `instrumentedtests`,
- * - `presentationtests`,
- * - `scripttests`.
*
* @author Philippe Haller
*/
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 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 addConfiguredSpecializedTests(input: FileSet) {
- specializedFiles = Some(input)
- }
-
- def addConfiguredInstrumentedTests(input: FileSet) {
- instrumentedFiles = Some(input)
- }
-
- def addConfiguredPresentationTests(input: FileSet) {
- presentationFiles = Some(input)
- }
-
- def addConfiguredAntTests(input: FileSet) {
- antFiles = Some(input)
- }
-
+ type Path = org.apache.tools.ant.types.Path
+
+ private var kinds: List[String] = Nil
+ private var classpath: Option[Path] = None
+ private var debug = false
+ private var errorOnFailed: Boolean = true
+ private var jUnitReportDir: Option[File] = None
+ private var javaccmd: Option[File] = None
+ private var javacmd: Option[File] = Option(sys.props("java.home")) map (x => new File(x, "bin/java"))
+ private var scalacArgs: Option[Seq[Argument]] = None
+ private var srcDir: Option[String] = None
+ private var colors: Int = 0
def setSrcDir(input: String) {
srcDir = Some(input)
}
+ def setColors(input: String) {
+ try colors = input.toInt catch { case _: NumberFormatException => () }
+ if (colors > 0)
+ sys.props("partest.colors") = colors.toString
+ }
+
def setClasspath(input: Path) {
if (classpath.isEmpty)
classpath = Some(input)
@@ -127,15 +73,6 @@ class PartestTask extends Task with CompilationPathProperty {
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
}
@@ -144,6 +81,10 @@ class PartestTask extends Task with CompilationPathProperty {
javacmd = Some(input)
}
+ def setKinds(input: String) {
+ kinds = words(input)
+ }
+
def setJavacCmd(input: File) {
javaccmd = Some(input)
}
@@ -159,10 +100,6 @@ class PartestTask extends Task with CompilationPathProperty {
a
}
- def setTimeout(delay: String) {
- timeout = Some(delay)
- }
-
def setDebug(input: Boolean) {
debug = input
}
@@ -171,172 +108,35 @@ class PartestTask extends Task with CompilationPathProperty {
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 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 scalacheckFiles: Option[FileSet] = None
- private var scriptFiles: Option[FileSet] = None
- private var shootoutFiles: Option[FileSet] = None
- private var scalapFiles: Option[FileSet] = None
- private var specializedFiles: Option[FileSet] = None
- private var instrumentedFiles: Option[FileSet] = None
- private var presentationFiles: Option[FileSet] = None
- private var antFiles: Option[FileSet] = None
- private var errorOnFailed: Boolean = false
- private var scalacArgs: Option[Seq[Argument]] = None
- private var timeout: Option[String] = None
- private var jUnitReportDir: Option[File] = None
- private var debug = false
-
- def fileSetToDir(fs: FileSet) = Directory(fs getDir getProject)
- def fileSetToArray(fs: FileSet): Array[SPath] = {
- val root = fileSetToDir(fs)
- (fs getDirectoryScanner getProject).getIncludedFiles map (root / _)
- }
-
- private def getFiles(fileSet: Option[FileSet]): Array[File] = fileSet match {
- case None => Array()
- case Some(fs) => fileSetToArray(fs) filterNot (_ hasExtension "log") map (_.jfile)
- }
-
- private def getFilesAndDirs(fileSet: Option[FileSet]): Array[File] = fileSet match {
- case None => Array()
- case Some(fs) =>
- def shouldExclude(name: String) = (name endsWith ".obj") || (name startsWith ".")
- // println("----> " + fileSet)
-
- val fileTests = getFiles(Some(fs)) filterNot (x => shouldExclude(x.getName))
- val dirResult = getDirs(Some(fs)) filterNot (x => shouldExclude(x.getName))
- // println("dirs: " + dirResult.toList)
- // println("files: " + fileTests.toList)
-
- dirResult ++ fileTests
- }
-
- private def getDirs(fileSet: Option[FileSet]): Array[File] = fileSet match {
- case None => Array()
- case Some(fs) =>
- def shouldExclude(name: String) = (name endsWith ".obj") || (name startsWith ".")
-
- val dirTests: Iterator[SPath] = fileSetToDir(fs).dirs filterNot (x => shouldExclude(x.name))
- val dirResult = dirTests.toList.toArray map (_.jfile)
-
- dirResult
- }
-
-
- private def getPosFiles = getFilesAndDirs(posFiles)
- private def getNegFiles = getFilesAndDirs(negFiles)
- private def getRunFiles = getFilesAndDirs(runFiles)
- private def getJvmFiles = getFilesAndDirs(jvmFiles)
- private def getResidentFiles = getFiles(residentFiles)
- private def getScalacheckFiles = getFilesAndDirs(scalacheckFiles)
- private def getScriptFiles = getFiles(scriptFiles)
- private def getShootoutFiles = getFiles(shootoutFiles)
- private def getScalapFiles = getFiles(scalapFiles)
- private def getSpecializedFiles = getFiles(specializedFiles)
- private def getInstrumentedFiles = getFilesAndDirs(instrumentedFiles)
- private def getPresentationFiles = getDirs(presentationFiles)
- private def getAntFiles = getFiles(antFiles)
-
override def execute() {
- val opts = getProject().getProperties() get "env.PARTEST_OPTS"
- if (opts != null && opts.toString != "")
- opts.toString.split(" ") foreach { propDef =>
- log("setting system property " + propDef)
- val kv = propDef split "="
- val key = kv(0) substring 2
- val value = kv(1)
- setProp(key, value)
- }
-
- if (isPartestDebug || debug) {
- setProp("partest.debug", "true")
- nest.NestUI._verbose = true
+ if (debug || sys.props.contains("partest.debug")) {
+ nest.NestUI.setDebug()
}
srcDir foreach (x => setProp("partest.srcdir", x))
val classpath = this.compilationPath getOrElse sys.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 sys.error("Provided classpath does not contain a Scala library.")
-
- val scalaReflect = {
- (classpath.list map { fs => new File(fs) }) find { f =>
- f.getName match {
- case "scala-reflect.jar" => true
- case "reflect" if (f.getParentFile.getName == "classes") => true
- case _ => false
- }
- }
- } getOrElse sys.error("Provided classpath does not contain a Scala reflection library.")
-
- val scalaCompiler = {
- (classpath.list map { fs => new File(fs) }) find { f =>
- f.getName match {
- case "scala-compiler.jar" => true
- case "compiler" if (f.getParentFile.getName == "classes") => true
- case _ => false
- }
- }
- } getOrElse sys.error("Provided classpath does not contain a Scala compiler.")
-
- val scalaPartest = {
- (classpath.list map { fs => new File(fs) }) find { f =>
- f.getName match {
- case "scala-partest.jar" => true
- case "partest" if (f.getParentFile.getName == "classes") => true
- case _ => false
- }
- }
- } getOrElse sys.error("Provided classpath does not contain a Scala partest.")
-
- val scalaActors = {
- (classpath.list map { fs => new File(fs) }) find { f =>
- f.getName match {
- case "scala-actors.jar" => true
- case "actors" if (f.getParentFile.getName == "classes") => true
- case _ => false
- }
- }
- } getOrElse sys.error("Provided classpath does not contain a Scala actors.")
+ val cpfiles = classpath.list map { fs => new File(fs) } toList
+ def findCp(name: String) = cpfiles find (f =>
+ (f.getName == s"scala-$name.jar")
+ || (f.absolutePathSegments endsWith Seq("classes", name))
+ ) getOrElse sys.error(s"Provided classpath does not contain a Scala $name element.")
+
+ val scalaLibrary = findCp("library")
+ val scalaReflect = findCp("reflect")
+ val scalaCompiler = findCp("compiler")
+ val scalaPartest = findCp("partest")
+ val scalaActors = findCp("actors")
def scalacArgsFlat: Option[Seq[String]] = scalacArgs map (_ flatMap { a =>
val parts = a.getParts
- if(parts eq null) Seq[String]() else parts.toSeq
+ if (parts eq null) Nil else parts.toSeq
})
val antRunner = new scala.tools.partest.nest.AntRunner
val antFileManager = antRunner.fileManager
- // this is a workaround for https://issues.scala-lang.org/browse/SI-5433
- // when that bug is fixed, this paragraph of code can be safely removed
- // we hack into the classloader that will become parent classloader for scalac
- // this way we ensure that reflective macro lookup will pick correct Code.lift
- val loader = getClass.getClassLoader.asInstanceOf[org.apache.tools.ant.AntClassLoader]
- val path = new org.apache.tools.ant.types.Path(getProject())
- val newClassPath = ClassPath.join(nest.PathSettings.srcCodeLib.toString, loader.getClasspath)
- path.setPath(newClassPath)
- loader.setClassPath(path)
-
- antFileManager.showDiff = showDiff
- antFileManager.showLog = showLog
+ // antFileManager.failed = runFailed
antFileManager.CLASSPATH = ClassPath.join(classpath.list: _*)
antFileManager.LATEST_LIB = scalaLibrary.getAbsolutePath
antFileManager.LATEST_REFLECT = scalaReflect.getAbsolutePath
@@ -347,53 +147,35 @@ class PartestTask extends Task with CompilationPathProperty {
javacmd foreach (x => antFileManager.JAVACMD = x.getAbsolutePath)
javaccmd foreach (x => antFileManager.JAVAC_CMD = x.getAbsolutePath)
scalacArgsFlat 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"),
- (getScalacheckFiles, "scalacheck", "Running scalacheck tests"),
- (getScriptFiles, "script", "Running script files"),
- (getShootoutFiles, "shootout", "Running shootout tests"),
- (getScalapFiles, "scalap", "Running scalap tests"),
- (getSpecializedFiles, "specialized", "Running specialized files"),
- (getInstrumentedFiles, "instrumented", "Running instrumented files"),
- (getPresentationFiles, "presentation", "Running presentation compiler test files"),
- (getAntFiles, "ant", "Running ant task tests")
- )
-
- def runSet(set: TFSet): (Int, Int, Iterable[String]) = {
- val (files, name, msg) = set
+
+ def runSet(kind: String, files: Array[File]): (Int, Int, List[String]) = {
if (files.isEmpty) (0, 0, List())
else {
- log(msg)
- val results: Iterable[(String, TestState)] = antRunner.reflectiveRunTestsForFiles(files, name)
- val (succs, fails) = resultsToStatistics(results)
+ log(s"Running ${files.length} tests in '$kind' at $now")
+ // log(s"Tests: ${files.toList}")
+ val results = antRunner.reflectiveRunTestsForFiles(files, kind)
+ val (passed, failed) = results partition (_.isOk)
+ val numPassed = passed.size
+ val numFailed = failed.size
+ def failedMessages = failed map (_.longStatus)
- val failed: Iterable[String] = results collect {
- case (path, TestState.Fail) => path + " [FAILED]"
- case (path, TestState.Timeout) => path + " [TIMOUT]"
- }
+ log(s"Completed '$kind' at $now")
// 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)
+ val report = testReport(kind, results, numPassed, numFailed)
+ scala.xml.XML.save(d.getAbsolutePath+"/"+kind+".xml", report)
}
- (succs, fails, failed)
+ (numPassed, numFailed, failedMessages)
}
}
- val _results = testFileSets map runSet
- val allSuccesses = _results map (_._1) sum
- val allFailures = _results map (_._2) sum
+ val _results = kinds map (k => runSet(k, TestKinds testsFor k map (_.jfile) toArray))
+ val allSuccesses = _results map (_._1) sum
+ val allFailures = _results map (_._2) sum
val allFailedPaths = _results flatMap (_._3)
def f = if (errorOnFailed && allFailures > 0) (sys error _) else log(_: String)
@@ -408,20 +190,17 @@ class PartestTask extends Task with CompilationPathProperty {
f(msg)
}
- private def oneResult(res: (String, TestState)) =
- <testcase name={res._1}>{
- res._2 match {
- case TestState.Ok => scala.xml.NodeSeq.Empty
- case TestState.Fail => <failure message="Test failed"/>
- case TestState.Timeout => <failure message="Test timed out"/>
- }
+ private def oneResult(res: TestState) =
+ <testcase name={res.testIdent}>{
+ if (res.isOk) scala.xml.NodeSeq.Empty
+ else <failure message="Test failed"/>
}</testcase>
- private def testReport(kind: String, results: Iterable[(String, TestState)], succs: Int, fails: Int) =
+ private def testReport(kind: String, results: Iterable[TestState], succs: Int, fails: Int) =
<testsuite name={kind} tests={(succs + fails).toString} failures={fails.toString}>
<properties/>
{
- results.map(oneResult(_))
+ results map oneResult
}
</testsuite>
}
diff --git a/src/partest/scala/tools/partest/TestKinds.scala b/src/partest/scala/tools/partest/TestKinds.scala
new file mode 100644
index 0000000000..ec682690ca
--- /dev/null
+++ b/src/partest/scala/tools/partest/TestKinds.scala
@@ -0,0 +1,67 @@
+package scala.tools
+package partest
+
+import nest.PathSettings.srcDir
+
+object TestKinds {
+ val standardKinds = "pos neg run jvm res buildmanager scalacheck scalap specialized instrumented presentation ant" split "\\s+" toList
+ val standardArgs = standardKinds map ("--" + _)
+
+ def denotesTestFile(p: Path) = p.isFile && p.hasExtension("scala", "res", "xml")
+ def denotesTestDir(p: Path) = kindOf(p) match {
+ case "res" => false
+ case _ => p.isDirectory && p.extension == ""
+ }
+ def denotesTestPath(p: Path) = denotesTestDir(p) || denotesTestFile(p)
+
+ // TODO
+ def isTestForPartest(p: Path) = (
+ (p.name == "intentional-failure.scala")
+ || (p.path contains "test-for-partest")
+ )
+
+ def kindOf(p: Path) = {
+ p.toAbsolute.segments takeRight 2 head
+
+ // (srcDir relativize p.toCanonical).segments match {
+ // case (".." :: "scaladoc" :: xs) => xs.head
+ // case xs => xs.head
+ // }
+ }
+ def logOf(p: Path) = {
+ p.parent / s"${p.stripExtension}-${kindOf(p)}.log"
+ // p.parent / s"${p.stripExtension}.log"
+ }
+
+ // true if a test path matches the --grep expression.
+ private def pathMatchesExpr(path: Path, expr: String) = {
+ // Matches the expression if any source file contains the expr,
+ // or if the checkfile contains it, or if the filename contains
+ // it (the last is case-insensitive.)
+ def matches(p: Path) = (
+ (p.path.toLowerCase contains expr.toLowerCase)
+ || (p.fileContents contains expr)
+ )
+ def candidates = {
+ (path changeExtension "check") +: {
+ if (path.isFile) List(path)
+ else path.toDirectory.deepList() filter (_.isJavaOrScala) toList
+ }
+ }
+
+ (candidates exists matches)
+ }
+
+ def groupedTests(paths: List[Path]): List[(String, List[Path])] =
+ (paths.distinct groupBy kindOf).toList sortBy (standardKinds indexOf _._1)
+
+ /** Includes tests for testing partest. */
+ private def allTestsForKind(kind: String): List[Path] =
+ (srcDir / kind toDirectory).list.toList filter denotesTestPath
+
+ def testsForPartest: List[Path] = standardKinds flatMap allTestsForKind filter isTestForPartest
+ def testsFor(kind: String): List[Path] = allTestsForKind(kind) filterNot isTestForPartest
+ def grepFor(expr: String): List[Path] = standardTests filter (t => pathMatchesExpr(t, expr))
+ def standardTests: List[Path] = standardKinds flatMap testsFor
+ def failedTests: List[Path] = standardTests filter (p => logOf(p).isFile)
+}
diff --git a/src/partest/scala/tools/partest/TestState.scala b/src/partest/scala/tools/partest/TestState.scala
new file mode 100644
index 0000000000..ce8e72f616
--- /dev/null
+++ b/src/partest/scala/tools/partest/TestState.scala
@@ -0,0 +1,54 @@
+package scala.tools.partest
+
+import scala.tools.nsc.FatalError
+import scala.tools.nsc.util.stackTraceString
+
+sealed abstract class TestState {
+ def testFile: File
+ def what: String
+ def reason: String
+ def transcript: List[String]
+
+ def isOk = false
+ def isSkipped = false
+ def testIdent = testFile.testIdent
+ def transcriptString = transcript.mkString("\n")
+
+ def identAndReason = testIdent + reasonString
+ def status = s"$what - $identAndReason"
+ def longStatus = status + transcriptString
+ def reasonString = if (reason == "") "" else s" [$reason]"
+
+ override def toString = status
+}
+
+object TestState {
+ case class Uninitialized(testFile: File) extends TestState {
+ def what = "uninitialized"
+ def reason = what
+ def transcript = Nil
+ }
+ case class Pass(testFile: File) extends TestState {
+ final override def isOk = true
+ def what = "pass"
+ def transcript: List[String] = Nil
+ def reason = ""
+ }
+ case class Skip(testFile: File, reason: String) extends TestState {
+ override def isOk = true
+ final override def isSkipped = true
+ def transcript: List[String] = Nil
+ def what = "skip"
+ }
+ case class Fail(testFile: File, reason: String, transcript: List[String]) extends TestState {
+ def what = "fail"
+ }
+ case class Crash(testFile: File, caught: Throwable, transcript: List[String]) extends TestState {
+ def what = "crash"
+ def reason = s"caught $caught_s - ${caught.getMessage}"
+
+ private def caught_s = (caught.getClass.getName split '.').last
+ private def stack_s = stackTraceString(caught)
+ override def transcriptString = nljoin(super.transcriptString, caught_s)
+ }
+}
diff --git a/src/partest/scala/tools/partest/nest/AntRunner.scala b/src/partest/scala/tools/partest/nest/AntRunner.scala
index 93045b8c1d..1d3b79171b 100644
--- a/src/partest/scala/tools/partest/nest/AntRunner.scala
+++ b/src/partest/scala/tools/partest/nest/AntRunner.scala
@@ -10,24 +10,21 @@
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 = _
- var LATEST_REFLECT: String = _
- var LATEST_COMP: String = _
- var LATEST_PARTEST: String = _
- var LATEST_ACTORS: String = _
- val testRootPath: String = "test"
- val testRootDir: Directory = Directory(testRootPath)
+ var JAVACMD: String = "java"
+ var JAVAC_CMD: String = "javac"
+ var CLASSPATH: String = _
+ var LATEST_LIB: String = _
+ var LATEST_REFLECT: String = _
+ var LATEST_COMP: String = _
+ var LATEST_PARTEST: String = _
+ var LATEST_ACTORS: String = _
+ val testRootPath: String = "test"
+ val testRootDir: Directory = Directory(testRootPath)
}
- def reflectiveRunTestsForFiles(kindFiles: Array[File], kind: String) =
+ def reflectiveRunTestsForFiles(kindFiles: Array[File], kind: String): List[TestState] =
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 a8694cc0d6..0000000000
--- a/src/partest/scala/tools/partest/nest/CompileManager.scala
+++ /dev/null
@@ -1,182 +0,0 @@
-/* NEST (New Scala Test)
- * Copyright 2007-2013 LAMP/EPFL
- * @author Philipp Haller
- */
-
-// $Id$
-
-package scala.tools.partest
-package nest
-
-import scala.tools.nsc.{ Global, Settings, CompilerCommand, FatalError, io }
-import scala.reflect.io.{ Directory, File => SFile, FileOperationException }
-import scala.tools.nsc.reporters.{ Reporter, ConsoleReporter }
-import scala.tools.nsc.util.{ ClassPath, FakePos }
-import scala.tools.nsc.Properties.{ setProp, propOrEmpty }
-import scala.tools.util.PathResolver
-import io.Path
-import java.io.{ File, BufferedReader, PrintWriter, FileReader, Writer, FileWriter, StringWriter }
-import File.pathSeparator
-
-sealed abstract class CompilationOutcome {
- def merge(other: CompilationOutcome): CompilationOutcome
- def isPositive = this eq CompileSuccess
- def isNegative = this eq CompileFailed
-}
-case object CompileSuccess extends CompilationOutcome {
- def merge(other: CompilationOutcome) = other
-}
-case object CompileFailed extends CompilationOutcome {
- def merge(other: CompilationOutcome) = if (other eq CompileSuccess) this else other
-}
-case object CompilerCrashed extends CompilationOutcome {
- def merge(other: CompilationOutcome) = this
-}
-
-class ExtConsoleReporter(settings: Settings, val writer: PrintWriter) extends ConsoleReporter(settings, Console.in, writer) {
- shortname = true
-}
-
-class TestSettings(cp: String, error: String => Unit) extends Settings(error) {
- def this(cp: String) = this(cp, _ => ())
-
- nowarnings.value = false
- encoding.value = "UTF-8"
- classpath.value = cp
-}
-
-abstract class SimpleCompiler {
- def compile(out: Option[File], files: List[File], kind: String, log: File): CompilationOutcome
-}
-
-class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler {
- def newGlobal(settings: Settings, reporter: Reporter): Global =
- Global(settings, reporter)
-
- def newGlobal(settings: Settings, logWriter: FileWriter): Global =
- newGlobal(settings, new ExtConsoleReporter(settings, new PrintWriter(logWriter)))
-
- def newSettings(): TestSettings = new TestSettings(fileManager.LATEST_LIB)
- def newSettings(outdir: String): TestSettings = {
- val cp = ClassPath.join(fileManager.LATEST_LIB, outdir)
- val s = new TestSettings(cp)
- s.outdir.value = outdir
-
- s
- }
-
- implicit class Copier(f: SFile) {
- // But what if f is bigger than CHUNK?!
- def copyTo(dest: Path) {
- dest.toFile writeAll f.slurp
- }
- }
-
- // plugin path can be relative to test root, or cwd is out
- private def updatePluginPath(options: String, out: Option[File], srcdir: Directory): String = {
- val dir = fileManager.testRootDir
- def pathOrCwd(p: String) =
- if (p == "." && out.isDefined) {
- val plugxml = "scalac-plugin.xml"
- val pout = Path(out.get)
- val pd = (srcdir / plugxml).toFile
- if (pd.exists) pd copyTo (pout / plugxml)
- pout
- } else Path(p)
- def absolutize(path: String) = pathOrCwd(path) match {
- case x if x.isAbsolute => x.path
- case x => (dir / 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): CompilationOutcome = {
- val testSettings = out match {
- case Some(f) => newSettings(f.getAbsolutePath)
- case _ => newSettings()
- }
- val logWriter = new FileWriter(log)
-
- // this api has no notion of srcdir, so fake it
- val fstFile = SFile(files(0))
- val srcdir = fstFile.parent
-
- // check whether there is a ".flags" file
- def convertFlags(f: SFile) = updatePluginPath(f.slurp(), out, srcdir)
- val logFile = basename(log.getName)
- val flagsFileName = "%s.flags" format (logFile.substring(0, logFile.lastIndexOf("-")))
- val argString = (SFile(log).parent / flagsFileName) ifFile (convertFlags) getOrElse ""
-
- // slurp local flags (e.g., "A_1.flags")
- def isInGroup(num: Int) = fstFile.stripExtension endsWith ("_" + num)
- val inGroup = (1 to 9) flatMap (group => if (isInGroup(group)) List(group) else List())
- val localFlagsList = if (inGroup.nonEmpty) {
- val localArgString = (srcdir / (fstFile.stripExtension + ".flags")) ifFile (convertFlags) getOrElse ""
- localArgString.split(' ').toList.filter(_.length > 0)
- } else List()
-
- val allOpts = fileManager.SCALAC_OPTS.toList ::: argString.split(' ').toList.filter(_.length > 0) ::: localFlagsList
- val args = allOpts.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
- case "specialized" => SpecializedTestFile.apply
- case "instrumented" => InstrumentedTestFile.apply
- case "presentation" => PresentationTestFile.apply
- case "ant" => AntTestFile.apply
- }
- val test: TestFile = testFileFn(files.head, fileManager)
- if (!test.defineSettings(command.settings, out.isEmpty)) {
- testRep.error(FakePos("partest"), test.flags match {
- case Some(flags) => "bad flags: " + flags
- case _ => "bad settings: " + command.settings
- })
- }
-
- val toCompile = files map (_.getPath)
-
- try {
- NestUI.verbose("compiling "+toCompile)
- NestUI.verbose("with classpath: "+global.classPath.toString)
- NestUI.verbose("and java classpath: "+ propOrEmpty("java.class.path"))
- try {
- if (command.shouldStopWithInfo) logWriter append (command getInfoMessage global)
- else new global.Run compile toCompile
- } catch {
- case FatalError(msg) =>
- testRep.error(null, "fatal error: " + msg)
- return CompilerCrashed
- }
-
- testRep.printSummary()
- testRep.writer.close()
- }
- finally logWriter.close()
-
- if (testRep.hasErrors || command.shouldStopWithInfo) CompileFailed
- else CompileSuccess
- }
-}
-
-class CompileManager(val fileManager: FileManager) {
- private def newCompiler = new DirectCompiler(fileManager)
- def attemptCompile(outdir: Option[File], sources: List[File], kind: String, log: File): CompilationOutcome =
- newCompiler.compile(outdir, sources, kind, log)
-}
diff --git a/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala b/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala
index 0ec3f60bf5..b436675d3a 100644
--- a/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala
+++ b/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala
@@ -3,20 +3,15 @@
* @author Philipp Haller
*/
-// $Id$
+
package scala.tools.partest
package nest
-import java.io.{ File, FilenameFilter, IOException, StringWriter }
+import java.io.{ FilenameFilter, IOException }
import java.net.URI
import scala.util.Properties.{ propOrElse, scalaCmd, scalacCmd }
-import scala.tools.util.PathResolver
import scala.tools.nsc.{ io, util }
-import util.{ ClassPath }
-import io.{ Path, Directory }
-import File.pathSeparator
-import ClassPath.{ join }
import PathResolver.{ Environment, Defaults }
class ConsoleFileManager extends FileManager {
@@ -55,7 +50,7 @@ class ConsoleFileManager extends FileManager {
var JAVAC_CMD = PartestDefaults.javacCmd
- NestUI.verbose("CLASSPATH: "+CLASSPATH)
+ vlog("CLASSPATH: "+CLASSPATH)
if (!srcDir.isDirectory) {
NestUI.failure("Source directory \"" + srcDir.path + "\" not found")
@@ -70,14 +65,14 @@ class ConsoleFileManager extends FileManager {
}
def findLatest() {
- NestUI.verbose("test parent: "+testParent)
+ vlog("test parent: "+testParent)
- def prefixFileWith(parent: File, relPath: String) = (io.File(parent) / relPath).toCanonical
+ def prefixFileWith(parent: File, relPath: String) = (SFile(parent) / relPath).toCanonical
def prefixFile(relPath: String) = (testParent / relPath).toCanonical
if (!testClasses.isEmpty) {
testClassesDir = Path(testClasses.get).toCanonical.toDirectory
- NestUI.verbose("Running with classes in "+testClassesDir)
+ vlog("Running with classes in "+testClassesDir)
latestLibFile = testClassesDir / "library"
latestActorsFile = testClassesDir / "library" / "actors"
@@ -87,7 +82,7 @@ class ConsoleFileManager extends FileManager {
}
else if (testBuild.isDefined) {
val dir = Path(testBuild.get)
- NestUI.verbose("Running on "+dir)
+ vlog("Running on "+dir)
latestLibFile = dir / "lib/scala-library.jar"
latestActorsFile = dir / "lib/scala-actors.jar"
latestReflectFile = dir / "lib/scala-reflect.jar"
@@ -96,7 +91,7 @@ class ConsoleFileManager extends FileManager {
}
else {
def setupQuick() {
- NestUI.verbose("Running build/quick")
+ vlog("Running build/quick")
latestLibFile = prefixFile("build/quick/classes/library")
latestActorsFile = prefixFile("build/quick/classes/library/actors")
latestReflectFile = prefixFile("build/quick/classes/reflect")
@@ -105,7 +100,7 @@ class ConsoleFileManager extends FileManager {
}
def setupInst() {
- NestUI.verbose("Running dist (installed)")
+ vlog("Running dist (installed)")
val p = testParent.getParentFile
latestLibFile = prefixFileWith(p, "lib/scala-library.jar")
latestActorsFile = prefixFileWith(p, "lib/scala-actors.jar")
@@ -115,7 +110,7 @@ class ConsoleFileManager extends FileManager {
}
def setupDist() {
- NestUI.verbose("Running dists/latest")
+ vlog("Running dists/latest")
latestLibFile = prefixFile("dists/latest/lib/scala-library.jar")
latestActorsFile = prefixFile("dists/latest/lib/scala-actors.jar")
latestReflectFile = prefixFile("dists/latest/lib/scala-reflect.jar")
@@ -124,7 +119,7 @@ class ConsoleFileManager extends FileManager {
}
def setupPack() {
- NestUI.verbose("Running build/pack")
+ vlog("Running build/pack")
latestLibFile = prefixFile("build/pack/lib/scala-library.jar")
latestActorsFile = prefixFile("build/pack/lib/scala-actors.jar")
latestReflectFile = prefixFile("build/pack/lib/scala-reflect.jar")
@@ -170,11 +165,13 @@ class ConsoleFileManager extends FileManager {
var latestReflectFile: File = _
var latestCompFile: File = _
var latestPartestFile: File = _
- def latestScalapFile: File = (latestLibFile.parent / "scalap.jar").jfile
+ //def latestScalapFile: File = (latestLibFile.parent / "scalap.jar").jfile
+ //def latestScalapFile: File = new File(latestLibFile.getParentFile, "scalap.jar")
var testClassesDir: Directory = _
// initialize above fields
findLatest()
+ /*
def getFiles(kind: String, cond: Path => Boolean): List[File] = {
def ignoreDir(p: Path) = List("svn", "obj") exists (p hasExtension _)
@@ -187,4 +184,6 @@ class ConsoleFileManager extends FileManager {
( if (failed) files filter (x => logFileExists(x, kind)) else files ) map (_.jfile)
}
+ */
+ var latestFjbgFile: File = _
}
diff --git a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala
index fd4d52f603..ddd42f5601 100644
--- a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala
+++ b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala
@@ -3,83 +3,110 @@
* @author Philipp Haller
*/
-// $Id$
-
-package scala.tools.partest
+package scala.tools
+package partest
package nest
-import java.io.{File, PrintStream, FileOutputStream, BufferedReader,
- InputStreamReader, StringWriter, PrintWriter}
import utils.Properties._
import scala.tools.nsc.Properties.{ versionMsg, setProp }
import scala.tools.nsc.util.CommandLineParser
-import scala.tools.nsc.io
-import io.{ Path }
import scala.collection.{ mutable, immutable }
+import PathSettings.srcDir
+import TestKinds._
class ConsoleRunner extends DirectRunner {
- import PathSettings.{ srcDir, testRoot }
-
- case class TestSet(kind: String, filter: Path => Boolean, msg: String)
- private def stdFilter(p: Path) = p.isDirectory || (p hasExtension "scala")
- private def antFilter(p: Path) = p.isFile && (p endsWith "build.xml")
-
- val testSets = {
- List(
- TestSet("pos", stdFilter, "Testing compiler (on files whose compilation should succeed)"),
- TestSet("neg", stdFilter, "Testing compiler (on files whose compilation should fail)"),
- TestSet("run", stdFilter, "Testing interpreter and backend"),
- TestSet("jvm", stdFilter, "Testing JVM backend"),
- TestSet("res", x => x.isFile && (x hasExtension "res"), "Testing resident compiler"),
- TestSet("shootout", stdFilter, "Testing shootout tests"),
- TestSet("script", stdFilter, "Testing script tests"),
- TestSet("scalacheck", stdFilter, "Testing ScalaCheck tests"),
- TestSet("scalap", _.isDirectory, "Run scalap decompiler tests"),
- TestSet("specialized", stdFilter, "Testing specialized tests"),
- TestSet("instrumented", stdFilter, "Testing instrumented tests"),
- TestSet("presentation", _.isDirectory, "Testing presentation compiler tests."),
- TestSet("ant", antFilter, "Run Ant task tests.")
- )
- }
+ import NestUI._
+ import NestUI.color._
+
+ // So we can ctrl-C a test run and still hear all
+ // the buffered failure info.
+ scala.sys addShutdownHook issueSummaryReport()
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
+ private var totalTests = 0
+ private val passedTests = mutable.ListBuffer[TestState]()
+ private val failedTests = mutable.ListBuffer[TestState]()
+
+ def comment(s: String) = echo(magenta("# " + s))
+ def levyJudgment() = {
+ if (totalTests == 0) echoMixed("No tests to run.")
+ else if (elapsedMillis == 0) echoMixed("Test Run ABORTED")
+ else if (isSuccess) echoPassed("Test Run PASSED")
+ else echoFailed("Test Run FAILED")
+ }
+
+ def passFailString(passed: Int, failed: Int, skipped: Int): String = {
+ val total = passed + failed + skipped
+ val isSuccess = failed == 0
+ def p0 = s"$passed/$total"
+ def p = ( if (isSuccess) bold(green(p0)) else p0 ) + " passed"
+ def f = if (failed == 0) "" else bold(red("" + failed)) + " failed"
+ def s = if (skipped == 0) "" else bold(yellow("" + skipped)) + " skipped"
+
+ oempty(p, f, s) mkString ", "
+ }
+
+ private var summarizing = false
+ private var elapsedMillis = 0L
+ private var expectedFailures = 0
+ private def isSuccess = failedTests.size == expectedFailures
+
+ def issueSummaryReport() {
+ // Don't run twice
+ if (!summarizing) {
+ summarizing = true
+
+ val passed0 = passedTests.toList
+ val failed0 = failedTests.toList
+ val passed = passed0.size
+ val failed = failed0.size
+ val skipped = totalTests - (passed + failed)
+ val passFail = passFailString(passed, failed, skipped)
+ val elapsed = if (elapsedMillis > 0) " (elapsed time: " + elapsedString(elapsedMillis) + ")" else ""
+ val message = passFail + elapsed
+
+ if (failed0.nonEmpty) {
+ echo(bold(cyan("##### Transcripts from failed tests #####\n")))
+ failed0 foreach { state =>
+ comment("partest " + state.testFile)
+ echo(state.transcriptString + "\n")
+ }
+ }
- private def printVersion() { NestUI outline (versionMsg + "\n") }
+ echo(message)
+ levyJudgment()
+ }
+ }
private val unaryArgs = List(
- "--pack", "--all", "--verbose", "--show-diff", "--show-log",
+ "--pack", "--all",
+ "--terse", "--verbose", "--show-diff", "--show-log", "--self-test",
"--failed", "--update-check", "--version", "--ansi", "--debug", "--help"
- ) ::: testSetArgs
+ ) ::: standardArgs
private val binaryArgs = List(
"--grep", "--srcpath", "--buildpath", "--classpath"
)
- // true if a test path matches the --grep expression.
- private def pathMatchesExpr(path: Path, expr: String) = {
- def pred(p: Path) = file2String(p.toFile) contains expr
- def greppable(f: Path) = f.isFile && (f hasExtension ("scala", "java"))
- def any(d: Path) = d.toDirectory.deepList() exists (f => greppable(f) && pred(f))
-
- (path.isFile && pred(path)) ||
- (path.isDirectory && any(path)) ||
- (pred(path changeExtension "check"))
- }
-
def main(argstr: String) {
val parsed = CommandLineParser(argstr) withUnaryArgs unaryArgs withBinaryArgs binaryArgs
- val args = onlyValidTestPaths(parsed.residualArgs)
- /* Early return on no args, version, or invalid args */
- if (argstr == "") return NestUI.usage()
- if (parsed isSet "--version") return printVersion
- if (parsed isSet "--help") return NestUI.usage()
+ if (parsed isSet "--debug") NestUI.setDebug()
+ if (parsed isSet "--verbose") NestUI.setVerbose()
+ if (parsed isSet "--terse") NestUI.setTerse()
+
+ // Early return on no args, version, or invalid args
+ if (parsed isSet "--version") return echo(versionMsg)
+ if ((argstr == "") || (parsed isSet "--help")) return NestUI.usage()
+
+ val (individualTests, invalid) = parsed.residualArgs map (p => Path(p)) partition denotesTestPath
+ if (invalid.nonEmpty) {
+ if (isPartestVerbose)
+ invalid foreach (p => echoWarning(s"Discarding invalid test path " + p))
+ else if (!isPartestTerse)
+ echoWarning(s"Discarding ${invalid.size} invalid test paths")
+ }
parsed get "--srcpath" foreach (x => setProp("partest.srcdir", x))
@@ -89,144 +116,102 @@ class ConsoleRunner extends DirectRunner {
else if (parsed isSet "--pack") new ConsoleFileManager("build/pack")
else new ConsoleFileManager // auto detection, see ConsoleFileManager.findLatest
- NestUI._verbose = parsed isSet "--verbose"
- fileManager.showDiff = true
- // parsed isSet "--show-diff"
fileManager.updateCheck = parsed isSet "--update-check"
- 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")
+ val partestTests = (
+ if (parsed isSet "--self-test") TestKinds.testsForPartest
+ else Nil
+ )
- 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
- }
- }
+ val grepExpr = parsed get "--grep" getOrElse ""
// If --grep is given we suck in every file it matches.
-
- val grepOption = parsed get "--grep"
- val grepPaths = grepOption.toList flatMap { expr =>
- val subjectDirs = testSetKinds map (srcDir / _ toDirectory)
- val testPaths = subjectDirs flatMap (_.list filter stdFilter)
- val paths = testPaths filter (p => pathMatchesExpr(p, expr))
-
+ var grepMessage = ""
+ val greppedTests = if (grepExpr == "") Nil else {
+ val paths = grepFor(grepExpr)
if (paths.isEmpty)
- NestUI.failure("--grep string '%s' matched no tests." format expr)
+ echoWarning(s"grep string '$grepExpr' matched no tests.\n")
- paths map (_.jfile)
+ paths.sortBy(_.toString)
}
- val grepMessage = grepOption map (x => "Argument '%s' matched %d test(s)".format(x, grepPaths.size)) getOrElse ""
-
- grepPaths foreach addTestFile
- 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 isRerun = parsed isSet "--failed"
+ val rerunTests = if (isRerun) TestKinds.failedTests else Nil
+ def miscTests = partestTests ++ individualTests ++ greppedTests ++ rerunTests
+ val givenKinds = standardArgs filter parsed.isSet
+ val kinds = (
+ if (parsed isSet "--all") standardKinds
+ else if (givenKinds.nonEmpty) givenKinds map (_ stripPrefix "--")
+ else if (invalid.isEmpty && miscTests.isEmpty && !isRerun) standardKinds // If no kinds, --grep, or individual tests were given, assume --all
+ else Nil
+ )
+ val kindsTests = kinds flatMap testsFor
val dir =
if (fileManager.testClasses.isDefined) fileManager.testClassesDir
else fileManager.testBuildFile getOrElse {
fileManager.latestCompFile.getParentFile.getParentFile.getAbsoluteFile
}
- 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 verbose (x + "\n"))
-
- NestUI.verbose("available processors: " + Runtime.getRuntime().availableProcessors())
-
- // Dragged down here so it isn't buried under the banner.
- if (grepMessage != "")
- NestUI.normal(grepMessage + "\n")
-
- val ((successes, failures), elapsedMillis) = timed(testCheckAll(enabledTestSets))
- val total = successes + failures
-
- val elapsedSecs = elapsedMillis/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)
+ def testContributors = {
+ List(
+ if (partestTests.isEmpty) "" else "partest self-tests",
+ if (rerunTests.isEmpty) "" else "previously failed tests",
+ if (kindsTests.isEmpty) "" else s"${kinds.size} named test categories",
+ if (greppedTests.isEmpty) "" else s"${greppedTests.size} tests matching '$grepExpr'",
+ if (individualTests.isEmpty) "" else "specified tests"
+ ) filterNot (_ == "") mkString ", "
}
- 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")
+ def banner = {
+ val vmBin = javaHome + fileSeparator + "bin"
+ val vmName = "%s (build %s, %s)".format(javaVmName, javaVmVersion, javaVmInfo)
+ val vmOpts = fileManager.JAVA_OPTS
+
+ s"""|Scala compiler classes in: $dir
+ |Scala version is: $versionMsg
+ |Scalac options are: ${fileManager.SCALAC_OPTS mkString " "}
+ |Java binaries in: $vmBin
+ |Java runtime is: $vmName
+ |Java options are: $vmOpts
+ |Source directory is: $srcDir
+ |Available processors: ${Runtime.getRuntime().availableProcessors()}
+ |Java Classpath: ${sys.props("java.class.path")}
+ """.stripMargin
+ }
- System exit ( if (failures == errors) 0 else 1 )
- }
+ chatty(banner)
- def runTests(testSet: TestSet): (Int, Int) = {
- val TestSet(kind, filter, msg) = testSet
+ val allTests = (miscTests ++ (kinds flatMap testsFor)).distinct
+ val grouped = (allTests groupBy kindOf).toList sortBy (x => standardKinds indexOf x._1)
- 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))
+ totalTests = allTests.size
+ expectedFailures = propOrNone("partest.errors") match {
+ case Some(num) => num.toInt
+ case _ => 0
}
- }
-
- /**
- * @return (success count, failure count)
- */
- def testCheckAll(enabledSets: List[TestSet]): (Int, Int) = {
- def kindOf(f: File) = {
- (srcDir relativize Path(f).toCanonical).segments match {
- case (".." :: "scaladoc" :: xs) => xs.head
- case xs => xs.head
+ val expectedFailureMessage = if (expectedFailures == 0) "" else s" (expecting $expectedFailures to fail)"
+ echo(s"Selected $totalTests tests drawn from $testContributors$expectedFailureMessage\n")
+
+ val (_, millis) = timed {
+ for ((kind, paths) <- grouped) {
+ val num = paths.size
+ val ss = if (num == 1) "" else "s"
+ comment(s"starting $num test$ss in $kind")
+ val results = runTestsForFiles(paths map (_.jfile), kind)
+ val (passed, failed) = results partition (_.isOk)
+
+ passedTests ++= passed
+ failedTests ++= failed
+ if (failed.nonEmpty) {
+ comment(passFailString(passed.size, failed.size, 0) + " in " + kind)
+ }
+ echo("")
}
}
-
- val (valid, invalid) = testFiles partition (x => testSetKinds contains kindOf(x))
- invalid foreach (x => NestUI.failure(
- "Invalid test file '%s', skipping.\n".format(x) +
- "(Test kind '%s' not in known set '%s')".format(kindOf(x), testSetKinds))
- )
-
- val grouped = (valid groupBy kindOf).toList sortBy (x => testSetKinds indexOf x._1)
- val runTestsFileLists =
- for ((kind, files) <- grouped) yield {
- NestUI.outline("\nTesting individual files\n")
- resultsToStatistics(runTestsForFiles(files, kind))
- }
-
- if (enabledSets.nonEmpty)
- NestUI.verbose("Run sets: "+enabledSets)
-
- val results = runTestsFileLists ::: (enabledSets map runTests)
-
- (results map (_._1) sum, results map (_._2) sum)
+ this.elapsedMillis = millis
+ issueSummaryReport()
+ System exit ( if (isSuccess) 0 else 1 )
}
}
diff --git a/src/partest/scala/tools/partest/nest/DirectCompiler.scala b/src/partest/scala/tools/partest/nest/DirectCompiler.scala
new file mode 100644
index 0000000000..650b6c35c8
--- /dev/null
+++ b/src/partest/scala/tools/partest/nest/DirectCompiler.scala
@@ -0,0 +1,105 @@
+/* NEST (New Scala Test)
+ * Copyright 2007-2013 LAMP/EPFL
+ * @author Philipp Haller
+ */
+
+package scala.tools.partest
+package nest
+
+import scala.tools.nsc.{ Global, Settings, CompilerCommand, FatalError }
+import scala.tools.nsc.reporters.{ Reporter, ConsoleReporter }
+import scala.tools.nsc.util.{ FakePos, stackTraceString }
+import scala.tools.nsc.Properties.{ setProp, propOrEmpty }
+import scala.reflect.io.AbstractFile
+import scala.reflect.internal.util.Position
+import java.io.{ BufferedReader, PrintWriter, FileReader, Writer, FileWriter }
+
+class ExtConsoleReporter(settings: Settings, val writer: PrintWriter) extends ConsoleReporter(settings, Console.in, writer) {
+ shortname = true
+ // override def error(pos: Position, msg: String): Unit
+}
+
+class TestSettings(cp: String, error: String => Unit) extends Settings(error) {
+ def this(cp: String) = this(cp, _ => ())
+
+ nowarnings.value = false
+ encoding.value = "UTF-8"
+ classpath.value = cp
+}
+
+class PartestGlobal(settings: Settings, reporter: Reporter) extends Global(settings, reporter) {
+ // override def abort(msg: String): Nothing
+ // override def globalError(msg: String): Unit
+ // override def supplementErrorMessage(msg: String): String
+}
+class DirectCompiler(val fileManager: FileManager) {
+ def newGlobal(settings: Settings, reporter: Reporter): PartestGlobal =
+ new PartestGlobal(settings, reporter)
+
+ def newGlobal(settings: Settings, logWriter: FileWriter): Global =
+ newGlobal(settings, new ExtConsoleReporter(settings, new PrintWriter(logWriter)))
+
+ def newSettings(): TestSettings = new TestSettings(fileManager.LATEST_LIB)
+ def newSettings(outdir: String): TestSettings = {
+ val cp = ClassPath.join(fileManager.LATEST_LIB, outdir)
+ val s = new TestSettings(cp)
+ s.outdir.value = outdir
+ s
+ }
+
+ def compile(runner: Runner, opts0: List[String], sources: List[File]): TestState = {
+ import runner._
+
+ val testSettings = new TestSettings(ClassPath.join(fileManager.LATEST_LIB, outDir.getPath))
+ val logWriter = new FileWriter(logFile)
+ val srcDir = if (testFile.isDirectory) testFile else Path(testFile).parent.jfile
+ val opts = fileManager.updatePluginPath(opts0, AbstractFile getDirectory outDir, AbstractFile getDirectory srcDir)
+ val command = new CompilerCommand(opts, testSettings)
+ val global = newGlobal(testSettings, logWriter)
+ val reporter = global.reporter.asInstanceOf[ExtConsoleReporter]
+ def errorCount = reporter.ERROR.count
+
+ def defineSettings(s: Settings) = {
+ s.outputDirs setSingleOutput outDir.getPath
+ // adding codelib.jar to the classpath
+ // codelib provides the possibility to override standard reify
+ // this shields the massive amount of reification tests from changes in the API
+ prependToClasspaths(s, codelib)
+ s.classpath append fileManager.CLASSPATH // adding this why?
+
+ // add the instrumented library version to classpath
+ if (kind == "specialized")
+ prependToClasspaths(s, speclib)
+
+ // check that option processing succeeded
+ opts0.isEmpty || command.ok
+ }
+
+ if (!defineSettings(testSettings))
+ if (opts0.isEmpty)
+ reporter.error(null, s"bad settings: $testSettings")
+ else
+ reporter.error(null, opts0.mkString("bad options: ", space, ""))
+
+ def ids = sources.map(_.testIdent) mkString space
+ vlog(s"% scalac $ids")
+
+ def execCompile() =
+ if (command.shouldStopWithInfo) {
+ logWriter append (command getInfoMessage global)
+ runner genFail "compilation stopped with info"
+ } else {
+ new global.Run compile sources.map(_.getPath)
+ if (!reporter.hasErrors) runner.genPass()
+ else {
+ reporter.printSummary()
+ reporter.writer.close()
+ runner.genFail(s"compilation failed with $errorCount errors")
+ }
+ }
+
+ try { execCompile() }
+ catch { case t: Throwable => reporter.error(null, t.getMessage) ; runner.genCrash(t) }
+ finally { logWriter.close() }
+ }
+}
diff --git a/src/partest/scala/tools/partest/nest/DirectRunner.scala b/src/partest/scala/tools/partest/nest/DirectRunner.scala
index 7e4c3b842c..49dd39c344 100644
--- a/src/partest/scala/tools/partest/nest/DirectRunner.scala
+++ b/src/partest/scala/tools/partest/nest/DirectRunner.scala
@@ -3,14 +3,12 @@
* @author Philipp Haller
*/
-// $Id$
-
package scala.tools.partest
package nest
import java.io.File
import scala.util.Properties.setProp
-import scala.tools.nsc.util.ScalaClassLoader
+import scala.tools.nsc.util.{ ScalaClassLoader, Exceptional }
import scala.tools.nsc.io.Path
import scala.collection.{ mutable, immutable }
import java.util.concurrent._
@@ -22,41 +20,33 @@ trait DirectRunner {
import PartestDefaults.numThreads
- def denotesTestFile(arg: String) = Path(arg).hasExtension("scala", "res", "xml")
- def denotesTestDir(arg: String) = Path(arg).ifDirectory(_.files.nonEmpty) exists (x => x)
- def denotesTestPath(arg: String) = denotesTestDir(arg) || denotesTestFile(arg)
-
- /** No duplicate, no empty directories, don't mess with this unless
- * you like partest hangs.
- */
- def onlyValidTestPaths[T](args: List[T]): List[T] = {
- args.distinct filter (arg => denotesTestPath("" + arg) || {
- NestUI.warning("Discarding invalid test path '%s'\n" format arg)
- false
- })
- }
- def runTestsForFiles(_kindFiles: List[File], kind: String): immutable.Map[String, TestState] = {
- System.setProperty("line.separator", "\n")
+ Thread.setDefaultUncaughtExceptionHandler(
+ new Thread.UncaughtExceptionHandler {
+ def uncaughtException(thread: Thread, t: Throwable) {
+ val t1 = Exceptional unwrap t
+ System.err.println(s"Uncaught exception on thread $thread: $t1")
+ t1.printStackTrace()
+ }
+ }
+ )
+ def runTestsForFiles(kindFiles: List[File], kind: String): List[TestState] = {
- val allUrls = PathSettings.scalaCheck.toURL :: fileManager.latestUrls
- val scalaCheckParentClassLoader = ScalaClassLoader.fromURLs(allUrls)
- val kindFiles = onlyValidTestPaths(_kindFiles)
- val pool = Executors.newFixedThreadPool(numThreads)
- val manager = new RunnerManager(kind, fileManager, TestRunParams(scalaCheckParentClassLoader))
- val futures = kindFiles map (f => (f, pool submit callable(manager runTest f))) toMap
+ NestUI.resetTestNumber()
- pool.shutdown()
+ val allUrls = PathSettings.scalaCheck.toURL :: fileManager.latestUrls
+ val parentClassLoader = ScalaClassLoader fromURLs allUrls
+ val pool = Executors newFixedThreadPool numThreads
+ val manager = new RunnerManager(kind, fileManager, TestRunParams(parentClassLoader))
+ val futures = kindFiles map (f => pool submit callable(manager runTest f))
+ pool.shutdown()
try if (!pool.awaitTermination(4, TimeUnit.HOURS))
- NestUI.warning("Thread pool timeout elapsed before all tests were complete!")
+ NestUI warning "Thread pool timeout elapsed before all tests were complete!"
catch { case t: InterruptedException =>
- NestUI.warning("Thread pool was interrupted")
+ NestUI warning "Thread pool was interrupted"
t.printStackTrace()
}
- for ((file, future) <- futures) yield {
- val state = if (future.isCancelled) TestState.Timeout else future.get
- (file.getAbsolutePath, state)
- }
+ futures map (_.get)
}
}
diff --git a/src/partest/scala/tools/partest/nest/FileManager.scala b/src/partest/scala/tools/partest/nest/FileManager.scala
index 23546c6511..25371b7d54 100644
--- a/src/partest/scala/tools/partest/nest/FileManager.scala
+++ b/src/partest/scala/tools/partest/nest/FileManager.scala
@@ -12,7 +12,7 @@ import java.io.{File, FilenameFilter, IOException, StringWriter,
FileInputStream, FileOutputStream, BufferedReader,
FileReader, PrintWriter, FileWriter}
import java.net.URI
-import scala.tools.nsc.io.{ Path, Directory, File => SFile }
+import scala.reflect.io.AbstractFile
import scala.collection.mutable
trait FileUtil {
@@ -62,16 +62,19 @@ trait FileManager extends FileUtil {
var LATEST_ACTORS: String
protected def relativeToLibrary(what: String): String = {
- if (LATEST_LIB endsWith ".jar") {
- (SFile(LATEST_LIB).parent / s"scala-$what.jar").toAbsolute.path
- }
- else {
+ def jarname = if (what startsWith "scala") s"$what.jar" else s"scala-$what.jar"
+ if (LATEST_LIB endsWith ".jar")
+ (SFile(LATEST_LIB).parent / jarname).toAbsolute.path
+ else
(SFile(LATEST_LIB).parent.parent / "classes" / what).toAbsolute.path
- }
}
def latestScaladoc = relativeToLibrary("scaladoc")
def latestInteractive = relativeToLibrary("interactive")
- def latestPaths = List(LATEST_LIB, LATEST_REFLECT, LATEST_COMP, LATEST_PARTEST, LATEST_ACTORS, latestScaladoc, latestInteractive)
+ def latestScalapFile = relativeToLibrary("scalap")
+ def latestPaths = List(
+ LATEST_LIB, LATEST_REFLECT, LATEST_COMP, LATEST_PARTEST, LATEST_ACTORS,
+ latestScalapFile, latestScaladoc, latestInteractive
+ )
def latestFiles = latestPaths map (p => new java.io.File(p))
def latestUrls = latestFiles map (_.toURI.toURL)
@@ -128,4 +131,34 @@ trait FileManager extends FileUtil {
f.printlnAll(f.lines.toList map replace: _*)
}
+
+ /** Massage args to merge plugins and fix paths.
+ * Plugin path can be relative to test root, or cwd is out.
+ * While we're at it, mix in the baseline options, too.
+ * That's how ant passes in the plugins dir.
+ */
+ def updatePluginPath(args: List[String], out: AbstractFile, srcdir: AbstractFile): List[String] = {
+ val dir = testRootDir
+ // The given path, or the output dir if ".", or a temp dir if output is virtual (since plugin loading doesn't like virtual)
+ def pathOrCwd(p: String) =
+ if (p == ".") {
+ val plugxml = "scalac-plugin.xml"
+ val pout = if (out.isVirtual) Directory.makeTemp() else Path(out.path)
+ val srcpath = Path(srcdir.path)
+ val pd = (srcpath / plugxml).toFile
+ if (pd.exists) pd copyTo (pout / plugxml)
+ pout
+ } else Path(p)
+ def absolutize(path: String) = pathOrCwd(path) match {
+ case x if x.isAbsolute => x.path
+ case x => (dir / x).toAbsolute.path
+ }
+
+ val xprefix = "-Xplugin:"
+ val (xplugs, others) = args partition (_ startsWith xprefix)
+ val Xplugin = if (xplugs.isEmpty) Nil else List(xprefix +
+ (xplugs map (_ stripPrefix xprefix) flatMap (_ split pathSeparator) map absolutize mkString pathSeparator)
+ )
+ SCALAC_OPTS.toList ::: others ::: Xplugin
+ }
}
diff --git a/src/partest/scala/tools/partest/nest/NestUI.scala b/src/partest/scala/tools/partest/nest/NestUI.scala
index df90b22448..2e203bfd91 100644
--- a/src/partest/scala/tools/partest/nest/NestUI.scala
+++ b/src/partest/scala/tools/partest/nest/NestUI.scala
@@ -3,14 +3,37 @@
* @author Philipp Haller
*/
-// $Id$
-
package scala.tools.partest
package nest
import java.io.PrintWriter
+class Colors(enabled: => Boolean) {
+ import Console._
+
+ val bold = colored(BOLD)
+ val yellow = colored(YELLOW)
+ val green = colored(GREEN)
+ val blue = colored(BLUE)
+ val red = colored(RED)
+ val red_b = colored(RED_B)
+ val green_b = colored(GREEN_B)
+ val cyan = colored(CYAN)
+ val magenta = colored(MAGENTA)
+
+ private def colored(code: String): String => String =
+ s => if (enabled) code + s + RESET else s
+}
+
object NestUI {
+ private val testNum = new java.util.concurrent.atomic.AtomicInteger(1)
+ // @volatile private var testNumber = 1
+ private def testNumber = "%3d" format testNum.getAndIncrement()
+ def resetTestNumber() = testNum set 1
+
+ var colorEnabled = sys.props contains "partest.colors"
+ val color = new Colors(colorEnabled)
+ import color._
val NONE = 0
val SOME = 1
@@ -22,11 +45,57 @@ object NestUI {
private var _warning = ""
private var _default = ""
+ private var dotCount = 0
+ private val DotWidth = 72
+
+ def leftFlush() {
+ if (dotCount != 0) {
+ normal("\n")
+ dotCount = 0
+ }
+ }
+
+ def statusLine(state: TestState) = {
+ import state._
+ val word = bold(
+ if (isSkipped) yellow("--")
+ else if (isOk) green("ok")
+ else red("!!")
+ )
+ word + f" $testNumber%3s - $testIdent%-40s$reasonString"
+ }
+
+ def reportTest(state: TestState) = {
+ if (isTerse && state.isOk) {
+ if (dotCount >= DotWidth) {
+ outline("\n.")
+ dotCount = 1
+ }
+ else {
+ outline(".")
+ dotCount += 1
+ }
+ }
+ else echo(statusLine(state))
+ }
+
+ def echo(message: String): Unit = synchronized {
+ leftFlush()
+ print(message + "\n")
+ }
+ def chatty(msg: String) = if (isVerbose) echo(msg)
+
+ def echoSkipped(msg: String) = echo(yellow(msg))
+ def echoPassed(msg: String) = echo(bold(green(msg)))
+ def echoFailed(msg: String) = echo(bold(red(msg)))
+ def echoMixed(msg: String) = echo(bold(yellow(msg)))
+ def echoWarning(msg: String) = echo(bold(red(msg)))
+
def initialize(number: Int) = number match {
case MANY =>
_outline = Console.BOLD + Console.BLACK
_success = Console.BOLD + Console.GREEN
- _failure = Console.BOLD + Console.RED
+ _failure = Console.BOLD + Console.RED
_warning = Console.BOLD + Console.YELLOW
_default = Console.RESET
case SOME =>
@@ -61,10 +130,7 @@ object NestUI {
}
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("Usage: NestRunner [options] [test test ...]")
println
println(" Test categories:")
println(" --all run all tests")
@@ -74,16 +140,12 @@ object NestUI {
println(" --jvm run JVM backend tests")
println(" --res run resident compiler tests")
println(" --scalacheck run ScalaCheck tests")
- println(" --script run script runner tests")
- println(" --shootout run shootout tests")
println(" --instrumented run instrumented tests")
println(" --presentation run presentation compiler tests")
- println(" --grep <expr> run all tests whose source file contains <expr>")
println
println(" Other options:")
println(" --pack pick compiler/reflect/library in build/pack, and run all tests")
- println(" --show-log show log")
- println(" --show-diff show diff between log and check file")
+ println(" --grep <expr> run all tests whose source file contains <expr>")
println(" --failed run only those tests that failed during the last run")
println(" --update-check instead of failing tests with output change, update checkfile. (Use with care!)")
println(" --verbose show progress information")
@@ -100,11 +162,28 @@ object NestUI {
}
var _verbose = false
+ var _debug = false
+ var _terse = false
+ def isVerbose = _verbose
+ def isDebug = _debug
+ def isTerse = _terse
+
+ def setVerbose() {
+ _verbose = true
+ }
+ def setDebug() {
+ _debug = true
+ }
+ def setTerse() {
+ _terse = true
+ }
def verbose(msg: String) {
- if (_verbose) {
- outline("debug: ")
- println(msg)
- }
+ if (isVerbose)
+ System.err.println(msg)
+ }
+ def debug(msg: String) {
+ if (isDebug)
+ System.err.println(msg)
}
}
diff --git a/src/partest/scala/tools/partest/nest/Runner.scala b/src/partest/scala/tools/partest/nest/Runner.scala
new file mode 100644
index 0000000000..95b304a56d
--- /dev/null
+++ b/src/partest/scala/tools/partest/nest/Runner.scala
@@ -0,0 +1,584 @@
+/* NEST (New Scala Test)
+ * Copyright 2007-2013 LAMP/EPFL
+ * @author Paul Phillips
+ */
+package scala.tools.partest
+package nest
+
+import java.io.{ Console => _, _ }
+import java.net.URL
+import scala.tools.nsc.Properties.{ jdkHome, javaHome, propOrElse, propOrEmpty }
+import scala.util.Properties.{ envOrElse, isWin }
+import scala.tools.nsc.{ 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, ScalaClassLoader, stackTraceString }
+import ClassPath.{ join, split }
+import scala.tools.scalap.scalax.rules.scalasig.ByteCode
+import scala.collection.{ mutable, immutable }
+import scala.sys.process._
+import java.util.concurrent.{ Executors, TimeUnit, TimeoutException }
+import PartestDefaults.{ javaCmd, javacCmd }
+import scala.tools.scalap.Main.decompileScala
+
+trait PartestRunSettings {
+ def gitPath: Path
+ def reportPath: Path
+ def logPath: Path
+
+ def testPaths: List[Path]
+
+ def gitDiffOptions: List[String]
+ def extraScalacOptions: List[String]
+ def extraJavaOptions: List[String]
+}
+
+class TestTranscript {
+ import NestUI.color._
+ private val buf = mutable.ListBuffer[String]()
+ private def pass(s: String) = bold(green("% ")) + s
+ private def fail(s: String) = bold(red("% ")) + s
+
+ def add(action: String): this.type = { buf += action ; this }
+ def append(text: String) { val s = buf.last ; buf.trimEnd(1) ; buf += (s + text) }
+
+ // Colorize prompts according to pass/fail
+ def fail: List[String] = buf.toList match {
+ case Nil => Nil
+ case xs => (xs.init map pass) :+ fail(xs.last)
+ }
+}
+
+class Runner(val testFile: File, fileManager: FileManager) {
+ import fileManager._
+
+ // Override to true to have the outcome of this test displayed
+ // whether it passes or not; in general only failures are reported,
+ // except for a . per passing test to show progress.
+ def isEnumeratedTest = false
+
+ def testRunParams: TestRunParams = ???
+
+ private var _lastState: TestState = null
+ private var _transcript = new TestTranscript
+
+ def lastState = if (_lastState == null) TestState.Uninitialized(testFile) else _lastState
+ def setLastState(s: TestState) = _lastState = s
+ def transcript: List[String] = _transcript.fail ++ logFile.fileLines
+ def pushTranscript(msg: String) = _transcript add msg
+
+ val parentFile = testFile.getParentFile
+ val kind = parentFile.getName
+ val fileBase = basename(testFile.getName)
+ val logFile = new File(parentFile, s"$fileBase-$kind.log")
+ val outFile = logFile changeExtension "obj"
+ val checkFile = testFile changeExtension "check"
+ val flagsFile = testFile changeExtension "flags"
+ val testIdent = testFile.testIdent // e.g. pos/t1234
+
+ lazy val outDir = { outFile.mkdirs() ; outFile }
+
+ type RanOneTest = (Boolean, LogContext)
+
+ def showCrashInfo(t: Throwable) {
+ System.err.println("Crashed running test $testIdent: " + t)
+ if (!isPartestTerse)
+ System.err.println(stackTraceString(t))
+ }
+ protected def crashHandler: PartialFunction[Throwable, TestState] = {
+ case t: InterruptedException =>
+ genTimeout()
+ case t: Throwable =>
+ showCrashInfo(t)
+ logFile.appendAll(stackTraceString(t))
+ genCrash(t)
+ }
+
+ def genPass() = TestState.Pass(testFile)
+ def genFail(reason: String) = TestState.Fail(testFile, reason, _transcript.fail)
+ def genTimeout() = TestState.Fail(testFile, "timed out", _transcript.fail)
+ def genCrash(caught: Throwable) = TestState.Crash(testFile, caught, _transcript.fail)
+
+ def speclib = PathSettings.srcSpecLib.toString // specialization lib
+ def codelib = PathSettings.srcCodeLib.toString // reify lib
+
+ // Prepend to a classpath, but without incurring duplicate entries
+ def prependTo(classpath: String, path: String): String = {
+ val segments = ClassPath split classpath
+
+ if (segments startsWith path) classpath
+ else ClassPath.join(path :: segments distinct: _*)
+ }
+
+ def prependToJavaClasspath(path: String) {
+ val jcp = sys.props.getOrElse("java.class.path", "")
+ prependTo(jcp, path) match {
+ case `jcp` =>
+ case cp => sys.props("java.class.path") = cp
+ }
+ }
+ def prependToClasspaths(s: Settings, path: String) {
+ prependToJavaClasspath(path)
+ val scp = s.classpath.value
+ prependTo(scp, path) match {
+ case `scp` =>
+ case cp => s.classpath.value = cp
+ }
+ }
+
+ private def workerError(msg: String): Unit = System.err.println("Error: " + msg)
+
+ def javac(files: List[File]): TestState = {
+ // compile using command-line javac compiler
+ val args = Seq(
+ javacCmd,
+ "-d",
+ outDir.getAbsolutePath,
+ "-classpath",
+ join(outDir.toString, CLASSPATH)
+ ) ++ files.map("" + _)
+
+ pushTranscript(args mkString " ")
+ val captured = StreamCapture(runCommand(args, logFile))
+ if (captured.result) genPass() else {
+ logFile appendAll captured.stderr
+ genFail("java compilation failed")
+ }
+ }
+
+ def testPrompt = kind match {
+ case "res" => "nsc> "
+ case _ => "% "
+ }
+
+ def nextTestAction[T](body: => T)(failFn: PartialFunction[T, TestState]): T = {
+ val result = body
+ setLastState( if (failFn isDefinedAt result) failFn(result) else genPass() )
+ result
+ }
+ def nextTestActionExpectTrue[T](reason: String, body: => Boolean): Boolean = {
+ nextTestAction(body) { case false => genFail(reason) }
+ }
+
+ private def assembleTestCommand(outDir: File, logFile: File): List[String] = {
+ // check whether there is a ".javaopts" file
+ val argsFile = testFile changeExtension "javaopts"
+ val argString = file2String(argsFile)
+ if (argString != "")
+ NestUI.verbose("Found javaopts file '%s', using options: '%s'".format(argsFile, argString))
+
+ val testFullPath = testFile.getAbsolutePath
+
+ // 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 extras = if (isPartestDebug) List("-Dpartest.debug=true") else Nil
+ val propertyOptions = List(
+ "-Dfile.encoding=UTF-8",
+ "-Djava.library.path="+logFile.getParentFile.getAbsolutePath,
+ "-Dpartest.output="+outDir.getAbsolutePath,
+ "-Dpartest.lib="+LATEST_LIB,
+ "-Dpartest.reflect="+LATEST_REFLECT,
+ "-Dpartest.cwd="+outDir.getParent,
+ "-Dpartest.test-path="+testFullPath,
+ "-Dpartest.testname="+fileBase,
+ "-Djavacmd="+javaCmd,
+ "-Djavaccmd="+javacCmd,
+ "-Duser.language=en",
+ "-Duser.country=US"
+ ) ++ extras
+
+ val classpath = if (extraClasspath != "") join(extraClasspath, CLASSPATH) else CLASSPATH
+
+ javaCmd +: (
+ (JAVA_OPTS.split(' ') ++ extraJavaOptions.split(' ') ++ argString.split(' ')).map(_.trim).filter(_ != "").toList ++ Seq(
+ "-classpath",
+ join(outDir.toString, classpath)
+ ) ++ propertyOptions ++ Seq(
+ "scala.tools.nsc.MainGenericRunner",
+ "-usejavacp",
+ "Test",
+ "jvm"
+ )
+ )
+ }
+
+ /** Runs command redirecting standard out and
+ * error out to output file.
+ */
+ private def runCommand(args: Seq[String], outFile: File): Boolean = {
+ (Process(args) #> outFile !) == 0
+ }
+
+ private def execTest(outDir: File, logFile: File): Boolean = {
+ val cmd = assembleTestCommand(outDir, logFile)
+
+ pushTranscript(cmd.mkString(" \\\n ") + " > " + logFile.getName)
+ nextTestActionExpectTrue("non-zero exit code", runCommand(cmd, logFile)) || {
+ _transcript append logFile.fileContents
+ false
+ }
+ }
+
+ override def toString = s"""Test($testIdent, lastState = $lastState)"""
+
+ def newTestWriters() = {
+ val swr = new StringWriter
+ val wr = new PrintWriter(swr, true)
+ // diff = ""
+
+ ((swr, wr))
+ }
+
+ def fail(what: Any) = {
+ NestUI.verbose("scalac: compilation of "+what+" failed\n")
+ false
+ }
+
+ def currentDiff = (
+ if (checkFile.canRead) compareFiles(logFile, checkFile)
+ else compareContents(augmentString(file2String(logFile)).lines.toList, Nil)
+ )
+
+ val gitRunner = List("/usr/local/bin/git", "/usr/bin/git") map (f => new java.io.File(f)) find (_.canRead)
+ val gitDiffOptions = "--ignore-space-at-eol --no-index " + propOrEmpty("partest.git_diff_options")
+ // --color=always --word-diff
+
+ def gitDiff(f1: File, f2: File): Option[String] = {
+ try gitRunner map { git =>
+ val cmd = s"$git diff $gitDiffOptions $f1 $f2"
+ val diff = Process(cmd).lines_!.drop(4).map(_ + "\n").mkString
+
+ "\n" + diff
+ }
+ catch { case t: Exception => None }
+ }
+
+ /** This does something about absolute paths and file separator
+ * chars before diffing.
+ */
+ def normalizeLog() {
+ // squashing // in paths also munges line comments, so save this innovation for another time.
+ // (There are line comments in the "stub implementations" error output.)
+ //val slashes = """[/\\]+""".r
+ //def squashSlashes(s: String) = slashes replaceAllIn (s, "/")
+ def squashSlashes(s: String) = s replace ('\\', '/')
+ val base = squashSlashes(parentFile.getAbsolutePath + File.separator)
+ val quoted = """\Q%s\E""" format base
+ val baseless = (if (isWin) "(?i)" + quoted else quoted).r
+ def canonicalize(s: String) = baseless replaceAllIn (squashSlashes(s), "")
+ logFile mapInPlace canonicalize
+ }
+
+ def diffIsOk: Boolean = {
+ val diff = currentDiff
+ val ok: Boolean = (diff == "") || {
+ fileManager.updateCheck && {
+ NestUI.verbose("Updating checkfile " + checkFile)
+ checkFile writeAll file2String(logFile)
+ true
+ }
+ }
+ pushTranscript(s"diff $logFile $checkFile")
+ nextTestAction(ok) {
+ case false =>
+ // Get a word-highlighted diff from git if we can find it
+ val bestDiff = if (ok) "" else {
+ if (checkFile.canRead)
+ gitDiff(logFile, checkFile) getOrElse {
+ s"diff $logFile $checkFile\n$diff"
+ }
+ else diff
+ }
+ _transcript append bestDiff
+ genFail("output differs")
+ // TestState.fail("output differs", "output differs",
+ // genFail("output differs")
+ // TestState.Fail("output differs", bestDiff)
+ }
+ }
+
+ /** 1. Creates log file and output directory.
+ * 2. Runs script function, providing log file and output directory as arguments.
+ */
+ def runInContext(body: => Boolean): (Boolean, LogContext) = {
+ val (swr, wr) = newTestWriters()
+ val succeeded = body
+ (succeeded, LogContext(logFile, swr, wr))
+ }
+
+ /** Grouped files in group order, and lex order within each group. */
+ def groupedFiles(dir: File): List[List[File]] = {
+ val testFiles = dir.listFiles.toList filter (_.isJavaOrScala)
+ val grouped = testFiles groupBy (_.group)
+ grouped.keys.toList.sorted map (k => grouped(k) sortBy (_.getName))
+ }
+
+ def newCompiler = new DirectCompiler(fileManager)
+
+ def attemptCompile(sources: List[File]): TestState = {
+ val state = newCompiler.compile(this, flagsForCompilation(sources), sources)
+ if (!state.isOk)
+ _transcript append ("\n" + file2String(logFile))
+
+ state
+ }
+
+ // snort or scarf all the contributing flags files
+ def flagsForCompilation(sources: List[File]): List[String] = {
+ def argsplitter(s: String) = words(s) filter (_.nonEmpty)
+ val perTest = argsplitter(flagsFile.fileContents)
+ val perGroup = if (testFile.isDirectory) {
+ sources flatMap { f => SFile(Path(f) changeExtension "flags").safeSlurp map argsplitter getOrElse Nil }
+ } else Nil
+ perTest ++ perGroup
+ }
+
+ abstract class CompileRound {
+ def fs: List[File]
+ def result: TestState
+ def description: String
+
+ def fsString = fs map (_.toString stripPrefix parentFile.toString + "/") mkString " "
+ def isOk = result.isOk
+ def mkScalacString(): String = {
+ val flags = file2String(flagsFile) match {
+ case "" => ""
+ case s => " " + s
+ }
+ s"""scalac $fsString"""
+ }
+ override def toString = description + ( if (result.isOk) "" else "\n" + result.status )
+ }
+ case class OnlyJava(fs: List[File]) extends CompileRound {
+ def description = s"""javac $fsString"""
+ lazy val result = { pushTranscript(description) ; javac(fs) }
+ }
+ case class OnlyScala(fs: List[File]) extends CompileRound {
+ def description = mkScalacString()
+ lazy val result = { pushTranscript(description) ; attemptCompile(fs) }
+ }
+ case class ScalaAndJava(fs: List[File]) extends CompileRound {
+ def description = mkScalacString()
+ lazy val result = { pushTranscript(description) ; attemptCompile(fs) }
+ }
+
+ def compilationRounds(file: File): List[CompileRound] = {
+ val grouped = if (file.isDirectory) groupedFiles(file) else List(List(file))
+
+ (grouped map mixedCompileGroup).flatten
+ }
+ def mixedCompileGroup(allFiles: List[File]): List[CompileRound] = {
+ val (scalaFiles, javaFiles) = allFiles partition (_.isScala)
+ val isMixed = javaFiles.nonEmpty && scalaFiles.nonEmpty
+ val round1 = if (scalaFiles.isEmpty) None else Some(ScalaAndJava(allFiles))
+ val round2 = if (javaFiles.isEmpty) None else Some(OnlyJava(javaFiles))
+ val round3 = if (!isMixed) None else Some(OnlyScala(scalaFiles))
+
+ List(round1, round2, round3).flatten
+ }
+
+ def runNegTest() = runInContext {
+ val rounds = compilationRounds(testFile)
+
+ if (rounds forall (x => nextTestActionExpectTrue("compilation failed", x.isOk)))
+ nextTestActionExpectTrue("expected compilation failure", false)
+ else {
+ normalizeLog // put errors in a normal form
+ diffIsOk
+ }
+ }
+
+ def runTestCommon(andAlso: => Boolean): (Boolean, LogContext) = runInContext {
+ compilationRounds(testFile).forall(x => nextTestActionExpectTrue("compilation failed", x.isOk)) && andAlso
+ }
+
+ // Apache Ant 1.6 or newer
+ def ant(args: Seq[String], output: File): Boolean = {
+ val antDir = Directory(envOrElse("ANT_HOME", "/opt/ant/"))
+ val antLibDir = Directory(antDir / "lib")
+ val antLauncherPath = SFile(antLibDir / "ant-launcher.jar").path
+ val antOptions =
+ if (NestUI._verbose) List("-verbose", "-noinput")
+ else List("-noinput")
+ val cmd = javaCmd +: (
+ JAVA_OPTS.split(' ').map(_.trim).filter(_ != "") ++ Seq(
+ "-classpath",
+ antLauncherPath,
+ "org.apache.tools.ant.launch.Launcher"
+ ) ++ antOptions ++ args
+ )
+
+ runCommand(cmd, output)
+ }
+
+ def runAntTest(): (Boolean, LogContext) = {
+ val (swr, wr) = newTestWriters()
+
+ val succeeded = try {
+ val binary = "-Dbinary="+(
+ if (fileManager.LATEST_LIB endsWith "build/quick/classes/library") "quick"
+ else if (fileManager.LATEST_LIB endsWith "build/pack/lib/scala-library.jar") "pack"
+ else if (fileManager.LATEST_LIB endsWith "dists/latest/lib/scala-library.jar/") "latest"
+ else "installed"
+ )
+ val args = Array(binary, "-logfile", logFile.getPath, "-file", testFile.getPath)
+ NestUI.verbose("ant "+args.mkString(" "))
+
+ pushTranscript(s"ant ${args.mkString(" ")}")
+ nextTestActionExpectTrue("ant failed", ant(args, logFile)) && diffIsOk
+ }
+ catch { // *catch-all*
+ case e: Exception =>
+ NestUI.warning("caught "+e)
+ false
+ }
+
+ (succeeded, LogContext(logFile, swr, wr))
+ }
+
+ def extraClasspath = kind match {
+ case "specialized" => PathSettings.srcSpecLib.toString
+ case _ => ""
+ }
+ def extraJavaOptions = kind match {
+ case "instrumented" => "-javaagent:"+PathSettings.instrumentationAgentLib
+ case _ => ""
+ }
+
+ def runScalacheckTest() = runTestCommon {
+ NestUI.verbose("compilation of "+testFile+" succeeded\n")
+
+ val outURL = outDir.getAbsoluteFile.toURI.toURL
+ val logWriter = new PrintStream(new FileOutputStream(logFile), true)
+
+ Output.withRedirected(logWriter) {
+ // this classloader is test specific: its parent contains library classes and others
+ ScalaClassLoader.fromURLs(List(outURL), testRunParams.scalaCheckParentClassLoader).run("Test", Nil)
+ }
+
+ NestUI.verbose(file2String(logFile))
+ // obviously this must be improved upon
+ val lines = SFile(logFile).lines map (_.trim) filterNot (_ == "") toBuffer;
+ lines.forall(x => !x.startsWith("!")) || {
+ NestUI.normal("ScalaCheck test failed. Output:\n")
+ lines foreach (x => NestUI.normal(x + "\n"))
+ false
+ }
+ }
+
+ def runResidentTest() = {
+ // simulate resident compiler loop
+ val prompt = "\nnsc> "
+ val (swr, wr) = newTestWriters()
+
+ NestUI.verbose(this+" running test "+fileBase)
+ val dir = parentFile
+ val resFile = new File(dir, fileBase + ".res")
+
+ // run compiler in resident mode
+ // $SCALAC -d "$os_dstbase".obj -Xresident -sourcepath . "$@"
+ val sourcedir = logFile.getParentFile.getAbsoluteFile
+ val sourcepath = sourcedir.getAbsolutePath+File.separator
+ NestUI.verbose("sourcepath: "+sourcepath)
+
+ val argList = List(
+ "-d", outDir.getAbsoluteFile.getPath,
+ "-Xresident",
+ "-sourcepath", sourcepath)
+
+ // configure input/output files
+ val logOut = new FileOutputStream(logFile)
+ val logWriter = new PrintStream(logOut, true)
+ val resReader = new BufferedReader(new FileReader(resFile))
+ val logConsoleWriter = new PrintWriter(new OutputStreamWriter(logOut), true)
+
+ // create compiler
+ val settings = new Settings(workerError)
+ settings.sourcepath.value = sourcepath
+ settings.classpath.value = fileManager.CLASSPATH
+ val reporter = new ConsoleReporter(settings, scala.Console.in, logConsoleWriter)
+ val command = new CompilerCommand(argList, settings)
+ object compiler extends Global(command.settings, reporter)
+
+ def resCompile(line: String): Boolean = {
+ // NestUI.verbose("compiling "+line)
+ val cmdArgs = (line split ' ').toList map (fs => new File(dir, fs).getAbsolutePath)
+ // NestUI.verbose("cmdArgs: "+cmdArgs)
+ val sett = new Settings(workerError)
+ sett.sourcepath.value = sourcepath
+ val command = new CompilerCommand(cmdArgs, sett)
+ // "scalac " + command.files.mkString(" ")
+ pushTranscript("scalac " + command.files.mkString(" "))
+ nextTestActionExpectTrue(
+ "compilation failed",
+ command.ok && {
+ (new compiler.Run) compile command.files
+ !reporter.hasErrors
+ }
+ )
+ }
+ def loop(): Boolean = {
+ logWriter.print(prompt)
+ resReader.readLine() match {
+ case null | "" => logWriter.close() ; true
+ case line => resCompile(line) && loop()
+ }
+ }
+ // res/t687.res depends on ignoring its compilation failure
+ // and just looking at the diff, so I made them all do that
+ // because this is long enough.
+ if (!Output.withRedirected(logWriter)(try loop() finally resReader.close()))
+ setLastState(genPass())
+
+ normalizeLog // put errors in a normal form
+ (diffIsOk, LogContext(logFile, swr, wr))
+ }
+
+ def run(): TestState = {
+ if (kind == "neg" || (kind endsWith "-neg")) runNegTest()
+ else kind match {
+ case "pos" => runTestCommon(true)
+ case "ant" => runAntTest()
+ case "scalacheck" => runScalacheckTest()
+ case "res" => runResidentTest()
+ case "scalap" => runScalapTest()
+ case "script" => runScriptTest()
+ case _ => runTestCommon(execTest(outDir, logFile) && diffIsOk)
+ }
+
+ lastState
+ }
+
+ def runScalapTest() = runTestCommon {
+ val isPackageObject = testFile.getName startsWith "package"
+ val className = testFile.getName.stripSuffix(".scala").capitalize + (if (!isPackageObject) "" else ".package")
+ val loader = ScalaClassLoader.fromURLs(List(outDir.toURI.toURL), this.getClass.getClassLoader)
+ val byteCode = ByteCode forClass (loader loadClass className)
+ val result = decompileScala(byteCode.bytes, isPackageObject)
+
+ logFile writeAll result
+ diffIsOk
+ }
+ def runScriptTest() = {
+ val (swr, wr) = newTestWriters()
+
+ val args = file2String(testFile changeExtension "args")
+ val cmdFile = if (isWin) testFile changeExtension "bat" else testFile
+ val succeeded = (((cmdFile + " " + args) #> logFile !) == 0) && diffIsOk
+
+ (succeeded, LogContext(logFile, swr, wr))
+ }
+
+ def cleanup() {
+ if (lastState.isOk)
+ logFile.delete()
+ if (!isPartestDebug)
+ Directory(outDir).deleteRecursively()
+ }
+}
diff --git a/src/partest/scala/tools/partest/nest/RunnerManager.scala b/src/partest/scala/tools/partest/nest/RunnerManager.scala
index a63a81c47b..1c689714c7 100644
--- a/src/partest/scala/tools/partest/nest/RunnerManager.scala
+++ b/src/partest/scala/tools/partest/nest/RunnerManager.scala
@@ -8,8 +8,6 @@ package nest
import java.io._
import java.net.URL
-import java.util.{ Timer, TimerTask }
-
import scala.tools.nsc.Properties.{ jdkHome, javaHome, propOrElse }
import scala.util.Properties.{ envOrElse, isWin }
import scala.tools.nsc.{ Settings, CompilerCommand, Global }
@@ -22,6 +20,7 @@ import scala.collection.{ mutable, immutable }
import scala.sys.process._
import java.util.concurrent.{ Executors, TimeUnit, TimeoutException }
import PartestDefaults.{ javaCmd, javacCmd }
+import scala.tools.scalap.Main.decompileScala
class LogContext(val file: File, val writers: Option[(StringWriter, PrintWriter)])
@@ -75,690 +74,22 @@ object Output {
class RunnerManager(kind: String, val fileManager: FileManager, params: TestRunParams) {
import fileManager._
-
- val compileMgr = new CompileManager(fileManager)
fileManager.CLASSPATH += File.pathSeparator + PathSettings.scalaCheck
fileManager.CLASSPATH += File.pathSeparator + PathSettings.diffUtils // needed to put diffutils on test/partest's classpath
- private def compareFiles(f1: File, f2: File): String =
- try fileManager.compareFiles(f1, f2)
- catch { case t: Exception => t.toString }
-
- /** This does something about absolute paths and file separator
- * chars before diffing.
- */
- private def replaceSlashes(dir: File, s: String): String = {
- val base = (dir.getAbsolutePath + File.separator).replace('\\', '/')
- var regex = """\Q%s\E""" format base
- if (isWin) regex = "(?i)" + regex
- s.replace('\\', '/').replaceAll(regex, "")
- }
-
- private def workerError(msg: String): Unit = System.err.println("Error: " + msg)
-
- private 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 name = file.getAbsolutePath drop testdir.getAbsolutePath.length
- if (name.length <= totalWidth) name
- // 2. try with [...]/run/test.scala
- else file.getAbsolutePath drop filesdir.getAbsolutePath.length
- }
- NestUI.normal("[...]%s%s".format(name, " " * (totalWidth - name.length)), printer)
- }
-
- private def printInfoEnd(success: Boolean, printer: PrintWriter) {
- NestUI.normal("[", printer)
- if (success) NestUI.success(" OK ", printer)
- else NestUI.failure("FAILED", printer)
- NestUI.normal("]\n", printer)
- }
-
- private def printInfoTimeout(printer: PrintWriter) {
- NestUI.normal("[", printer)
- NestUI.failure("TIMOUT", printer)
- NestUI.normal("]\n", printer)
- }
-
- private def javac(outDir: File, files: List[File], output: File): CompilationOutcome = {
- // compile using command-line javac compiler
- val args = Seq(
- javacCmd,
- "-d",
- outDir.getAbsolutePath,
- "-classpath",
- join(outDir.toString, CLASSPATH)
- ) ++ files.map("" + _)
-
- try if (runCommand(args, output)) CompileSuccess else CompileFailed
- catch exHandler(output, "javac command failed:\n" + args.map(" " + _ + "\n").mkString + "\n", CompilerCrashed)
- }
-
- /** Runs command redirecting standard out and error out to output file.
- * Overloaded to accept a sequence of arguments.
- */
- private def runCommand(args: Seq[String], outFile: File): Boolean = {
- NestUI.verbose("running command:\n"+args.map(" " + _ + "\n").mkString)
- runCommandImpl(Process(args), outFile)
- }
-
- /** Runs command redirecting standard out and error out to output file.
- * Overloaded to accept a single string = concatenated command + arguments.
- */
- private def runCommand(command: String, outFile: File): Boolean = {
- NestUI.verbose("running command:"+command)
- runCommandImpl(Process(command), outFile)
- }
-
- private def runCommandImpl(process: => ProcessBuilder, outFile: File): Boolean = {
- val exitCode = (process #> outFile !)
- // normalize line endings
- // System.getProperty("line.separator") should be "\n" here
- // so reading a file and writing it back should convert all CRLFs to LFs
- SFile(outFile).printlnAll(SFile(outFile).lines.toList: _*)
- exitCode == 0
- }
-
- @inline private def isJava(f: File) = SFile(f) hasExtension "java"
- @inline private def isScala(f: File) = SFile(f) hasExtension "scala"
- @inline private def isJavaOrScala(f: File) = isJava(f) || isScala(f)
-
- private def outputLogFile(logFile: File) {
- val lines = SFile(logFile).lines
- if (lines.nonEmpty) {
- NestUI.normal("Log file '" + logFile + "': \n")
- lines foreach (x => NestUI.normal(x + "\n"))
- }
- }
- private def logStackTrace(logFile: File, t: Throwable, msg: String): Boolean = {
- SFile(logFile).writeAll(msg, stackTraceString(t))
- outputLogFile(logFile) // if running the test threw an exception, output log file
- false
- }
-
- private def exHandler[T](logFile: File, msg: String, value: T): PartialFunction[Throwable, T] = {
- case e: Exception => logStackTrace(logFile, e, msg) ; value
- }
-
- class Runner(testFile: File) {
- var testDiff: String = ""
- var passed: Option[Boolean] = None
-
- val fileBase = basename(testFile.getName)
- val logFile = fileManager.getLogFile(testFile, kind)
- val parent = testFile.getParentFile
- val outDir = new File(parent, "%s-%s.obj".format(fileBase, kind))
- def toDelete = if (isPartestDebug) Nil else List(
- if (passed exists (x => x)) Some(logFile) else None,
- if (outDir.isDirectory) Some(outDir) else None
- ).flatten
-
- private def createOutputDir(): File = {
- outDir.mkdirs()
- outDir
- }
-
- private def execTest(outDir: File, logFile: File, classpathPrefix: String = "", javaOpts: String = ""): Boolean = {
- // check whether there is a ".javaopts" file
- val argsFile = new File(logFile.getParentFile, fileBase + ".javaopts")
- val argString = file2String(argsFile)
- if (argString != "")
- NestUI.verbose("Found javaopts file '%s', using options: '%s'".format(argsFile, argString))
-
- val testFullPath = {
- val d = new File(logFile.getParentFile, fileBase)
- if (d.isDirectory) d.getAbsolutePath
- else {
- val f = new File(logFile.getParentFile, fileBase + ".scala")
- if (f.isFile) f.getAbsolutePath
- else ""
- }
- }
-
- // 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 extras = if (isPartestDebug) List("-Dpartest.debug=true") else Nil
- val propertyOptions = List(
- "-Dfile.encoding=UTF-8",
- "-Djava.library.path="+logFile.getParentFile.getAbsolutePath,
- "-Dpartest.output="+outDir.getAbsolutePath,
- "-Dpartest.lib="+LATEST_LIB,
- "-Dpartest.reflect="+LATEST_REFLECT,
- "-Dpartest.comp="+LATEST_COMP,
- "-Dpartest.cwd="+outDir.getParent,
- "-Dpartest.test-path="+testFullPath,
- "-Dpartest.testname="+fileBase,
- "-Djavacmd="+javaCmd,
- "-Djavaccmd="+javacCmd,
- "-Duser.language=en",
- "-Duser.country=US"
- ) ++ extras
-
- val classpath = if (classpathPrefix != "") join(classpathPrefix, CLASSPATH) else CLASSPATH
- val cmd = javaCmd +: (
- (JAVA_OPTS.split(' ') ++ javaOpts.split(' ') ++ argString.split(' ')).map(_.trim).filter(_ != "") ++ Seq(
- "-classpath",
- join(outDir.toString, classpath)
- ) ++ propertyOptions ++ Seq(
- "scala.tools.nsc.MainGenericRunner",
- "-usejavacp",
- "Test",
- "jvm"
- )
- )
-
- runCommand(cmd, logFile)
- }
-
- private def getCheckFilePath(dir: File, suffix: String) = {
- def chkFile(s: String) = (Directory(dir) / "%s%s.check".format(fileBase, s)).toFile
-
- if (chkFile("").isFile || suffix == "") chkFile("")
- else chkFile("-" + suffix)
- }
-
- private def compareOutput(dir: File, logFile: File): String = {
- val checkFile = getCheckFilePath(dir, kind)
- val diff =
- if (checkFile.canRead) compareFiles(logFile, checkFile.jfile)
- else file2String(logFile)
-
- // if check file exists, compare with log file
- if (diff != "" && fileManager.updateCheck) {
- NestUI.verbose("Updating checkfile " + checkFile.jfile)
- val toWrite = if (checkFile.exists) checkFile else getCheckFilePath(dir, "")
- toWrite writeAll file2String(logFile)
- ""
- }
- else diff
- }
-
- def newTestWriters() = {
- val swr = new StringWriter
- val wr = new PrintWriter(swr, true)
-
- ((swr, wr))
- }
-
- def diffCheck(testFile: File, diff: String) = {
- testDiff = diff
- testDiff == ""
- }
-
- /** 1. Creates log file and output directory.
- * 2. Runs script function, providing log file and output directory as arguments.
- */
- def runInContext(file: File, script: (File, File) => Boolean): (Boolean, LogContext) = {
- val (swr, wr) = newTestWriters()
- printInfoStart(file, wr)
-
- NestUI.verbose(this+" running test "+fileBase)
- val outDir = createOutputDir()
- NestUI.verbose("output directory: "+outDir)
-
- // run test-specific code
- val succeeded = try {
- if (isPartestDebug) {
- val (result, millis) = timed(script(logFile, outDir))
- fileManager.recordTestTiming(file.getPath, millis)
- result
- }
- else script(logFile, outDir)
- }
- catch exHandler(logFile, "", false)
-
- (succeeded, LogContext(logFile, swr, wr))
- }
-
- def groupedFiles(dir: File): List[List[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))).sorted)
- val noGroupSuffix = (testFiles filterNot (groups.flatten contains)).sorted
-
- noGroupSuffix :: groups filterNot (_.isEmpty)
- }
-
- def compileFilesIn(dir: File, logFile: File, outDir: File): CompilationOutcome = {
- def compileGroup(g: List[File]): CompilationOutcome = {
- val (scalaFiles, javaFiles) = g partition isScala
- val allFiles = javaFiles ++ scalaFiles
-
- /* The test can contain both java and scala files, each of which should be compiled with the corresponding
- * compiler. Since the source files can reference each other both ways (java referencing scala classes and
- * vice versa, the partest compilation routine attempts to reach a "bytecode fixpoint" between the two
- * compilers -- that's when bytecode generated by each compiler implements the signatures expected by the other.
- *
- * In theory this property can't be guaranteed, as neither compiler can know what signatures the other
- * compiler expects and how to implement them. (see SI-1240 for the full story)
- *
- * In practice, this happens in 3 steps:
- * STEP1: Feed all the files to scalac if there are also non-Scala sources.
- * It will parse java files and obtain their expected signatures and generate bytecode for scala files
- * STEP2: Feed the java files to javac if there are any.
- * It will generate the bytecode for the java files and link to the scalac-generated bytecode for scala
- * STEP3: (Re-)compile the scala sources so they link to the correct
- * java signatures, in case the signatures deduced by scalac from the source files were wrong. Since the
- * bytecode for java is already in place, we only feed the scala files to scalac so it will take the
- * java signatures from the existing javac-generated bytecode.
- * Note that no artifacts are deleted before this step.
- */
- List(1, 2, 3).foldLeft(CompileSuccess: CompilationOutcome) {
- case (CompileSuccess, 1) if scalaFiles.nonEmpty && javaFiles.nonEmpty =>
- compileMgr.attemptCompile(Some(outDir), allFiles, kind, logFile)
- case (CompileSuccess, 2) if javaFiles.nonEmpty =>
- javac(outDir, javaFiles, logFile)
- case (CompileSuccess, 3) if scalaFiles.nonEmpty =>
- // TODO: Do we actually need this? SI-1240 is known to require this, but we don't know if other tests
- // require it: https://groups.google.com/forum/?fromgroups#!topic/scala-internals/rFDKAcOKciU
- compileMgr.attemptCompile(Some(outDir), scalaFiles, kind, logFile)
-
- case (outcome, _) => outcome
- }
- }
- groupedFiles(dir).foldLeft(CompileSuccess: CompilationOutcome) {
- case (CompileSuccess, files) => compileGroup(files)
- case (outcome, _) => outcome
- }
- }
-
- def runTestCommon(file: File, expectFailure: Boolean)(
- onSuccess: (File, File) => Boolean,
- onFail: (File, File) => Unit = (_, _) => ()): (Boolean, LogContext) =
- {
- runInContext(file, (logFile: File, outDir: File) => {
- val outcome = (
- if (file.isDirectory) compileFilesIn(file, logFile, outDir)
- else compileMgr.attemptCompile(None, List(file), kind, logFile)
- )
- val result = (
- if (expectFailure) outcome.isNegative
- else outcome.isPositive
- )
-
- if (result) onSuccess(logFile, outDir)
- else { onFail(logFile, outDir) ; false }
- })
- }
-
- def runJvmTest(file: File): (Boolean, LogContext) =
- runTestCommon(file, expectFailure = false)((logFile, outDir) => {
- val dir = file.getParentFile
-
- // adding codelib.jar to the classpath
- // codelib provides the possibility to override standard reify
- // this shields the massive amount of reification tests from changes in the API
- execTest(outDir, logFile, PathSettings.srcCodeLib.toString) && {
- // cannot replace paths here since this also inverts slashes
- // which affects a bunch of tests
- //fileManager.mapFile(logFile, replaceSlashes(dir, _))
- diffCheck(file, compareOutput(dir, logFile))
- }
- })
-
- // Apache Ant 1.6 or newer
- def ant(args: Seq[String], output: File): Boolean = {
- val antDir = Directory(envOrElse("ANT_HOME", "/opt/ant/"))
- val antLibDir = Directory(antDir / "lib")
- val antLauncherPath = SFile(antLibDir / "ant-launcher.jar").path
- val antOptions =
- if (NestUI._verbose) List("-verbose", "-noinput")
- else List("-noinput")
- val cmd = javaCmd +: (
- JAVA_OPTS.split(' ').map(_.trim).filter(_ != "") ++ Seq(
- "-classpath",
- antLauncherPath,
- "org.apache.tools.ant.launch.Launcher"
- ) ++ antOptions ++ args
- )
-
- try runCommand(cmd, output)
- catch exHandler(output, "ant command '" + cmd + "' failed:\n", false)
- }
-
- def runAntTest(file: File): (Boolean, LogContext) = {
- val (swr, wr) = newTestWriters()
- printInfoStart(file, wr)
-
- NestUI.verbose(this+" running test "+fileBase)
-
- val succeeded = try {
- val binary = "-Dbinary="+(
- if (fileManager.LATEST_LIB endsWith "build/quick/classes/library") "quick"
- else if (fileManager.LATEST_LIB endsWith "build/pack/lib/scala-library.jar") "pack"
- else if (fileManager.LATEST_LIB endsWith "dists/latest/lib/scala-library.jar/") "latest"
- else "installed"
- )
- val args = Array(binary, "-logfile", logFile.path, "-file", file.path)
- NestUI.verbose("ant "+args.mkString(" "))
- ant(args, logFile) && diffCheck(file, compareOutput(file.getParentFile, logFile))
- }
- catch { // *catch-all*
- case e: Exception =>
- NestUI.verbose("caught "+e)
- false
- }
-
- (succeeded, LogContext(logFile, swr, wr))
- }
-
- def runSpecializedTest(file: File): (Boolean, LogContext) =
- runTestCommon(file, expectFailure = false)((logFile, outDir) => {
- val dir = file.getParentFile
-
- // adding the instrumented library to the classpath
- ( execTest(outDir, logFile, PathSettings.srcSpecLib.toString) &&
- diffCheck(file, compareOutput(dir, logFile))
- )
- })
-
- def runInstrumentedTest(file: File): (Boolean, LogContext) =
- runTestCommon(file, expectFailure = false)((logFile, outDir) => {
- val dir = file.getParentFile
-
- // adding the javagent option with path to instrumentation agent
- execTest(outDir, logFile, javaOpts = "-javaagent:"+PathSettings.instrumentationAgentLib) &&
- diffCheck(file, compareOutput(dir, logFile))
- })
-
- def processSingleFile(file: File): (Boolean, LogContext) = kind match {
- case "scalacheck" =>
- val succFn: (File, File) => Boolean = { (logFile, outDir) =>
- NestUI.verbose("compilation of "+file+" succeeded\n")
-
- val outURL = outDir.getAbsoluteFile.toURI.toURL
- val logWriter = new PrintStream(new FileOutputStream(logFile), true)
-
- Output.withRedirected(logWriter) {
- // this classloader is test specific: its parent contains library classes and others
- ScalaClassLoader.fromURLs(List(outURL), params.scalaCheckParentClassLoader).run("Test", Nil)
- }
-
- NestUI.verbose(file2String(logFile))
- // obviously this must be improved upon
- val lines = SFile(logFile).lines map (_.trim) filterNot (_ == "") toBuffer;
- lines.forall(x => !x.startsWith("!")) || {
- NestUI.normal("ScalaCheck test failed. Output:\n")
- lines foreach (x => NestUI.normal(x + "\n"))
- false
- }
- }
- runTestCommon(file, expectFailure = false)(
- succFn,
- (logFile, outDir) => outputLogFile(logFile)
- )
-
- case "pos" =>
- runTestCommon(file, expectFailure = false)(
- (logFile, outDir) => true,
- (_, _) => ()
- )
-
- case "neg" =>
- runTestCommon(file, expectFailure = true)((logFile, outDir) => {
- // compare log file to check file
- val dir = file.getParentFile
-
- // diff is contents of logFile
- fileManager.mapFile(logFile, replaceSlashes(dir, _))
- diffCheck(file, compareOutput(dir, logFile))
- })
-
- case "run" | "jvm" =>
- runJvmTest(file)
-
- case "specialized" =>
- runSpecializedTest(file)
-
- case "instrumented" =>
- runInstrumentedTest(file)
-
- case "presentation" =>
- runJvmTest(file) // for the moment, it's exactly the same as for a run test
-
- case "ant" =>
- runAntTest(file)
-
- case "res" => {
- // simulate resident compiler loop
- val prompt = "\nnsc> "
-
- val (swr, wr) = newTestWriters()
- printInfoStart(file, wr)
-
- NestUI.verbose(this+" running test "+fileBase)
- val dir = file.getParentFile
- val outDir = createOutputDir()
- 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 . "$@"
- val sourcedir = logFile.getParentFile.getAbsoluteFile
- val sourcepath = sourcedir.getAbsolutePath+File.separator
- NestUI.verbose("sourcepath: "+sourcepath)
-
- val argList = List(
- "-d", outDir.getAbsoluteFile.getPath,
- "-Xresident",
- "-sourcepath", sourcepath)
-
- // configure input/output files
- val logOut = new FileOutputStream(logFile)
- val logWriter = new PrintStream(logOut, true)
- val resReader = new BufferedReader(new FileReader(resFile))
- val logConsoleWriter = new PrintWriter(new OutputStreamWriter(logOut), true)
-
- // create compiler
- val settings = new Settings(workerError)
- settings.sourcepath.value = sourcepath
- settings.classpath.value = fileManager.CLASSPATH
- val reporter = new ConsoleReporter(settings, scala.Console.in, logConsoleWriter)
- val command = new CompilerCommand(argList, settings)
- object compiler extends Global(command.settings, reporter)
-
- 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(workerError)
- sett.sourcepath.value = sourcepath
- val command = new CompilerCommand(cmdArgs, sett)
- command.ok && {
- (new compiler.Run) compile command.files
- !reporter.hasErrors
- }
- }
-
- def loop(action: String => Boolean): Boolean = {
- logWriter.print(prompt)
- resReader.readLine() match {
- case null | "" => logWriter.flush() ; true
- case line => action(line) && loop(action)
- }
- }
-
- Output.withRedirected(logWriter) {
- try loop(resCompile)
- finally resReader.close()
- }
- fileManager.mapFile(logFile, replaceSlashes(dir, _))
-
- (diffCheck(file, compareOutput(dir, logFile)), LogContext(logFile, swr, wr))
- }
-
- case "shootout" =>
- val (swr, wr) = newTestWriters()
- printInfoStart(file, wr)
-
- NestUI.verbose(this+" running test "+fileBase)
- val outDir = createOutputDir()
-
- // 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(parent, fileBase+".scala.runner")
- val bodyFile = new File(parent, fileBase+".scala")
- SFile(testFile).writeAll(
- file2String(runnerFile),
- file2String(bodyFile)
- )
-
- // 4. compile testFile
- val ok = compileMgr.attemptCompile(None, List(testFile), kind, logFile) eq CompileSuccess
- NestUI.verbose("compilation of " + testFile + (if (ok) "succeeded" else "failed"))
- val result = ok && {
- execTest(outDir, logFile) && {
- NestUI.verbose(this+" finished running "+fileBase)
- diffCheck(file, compareOutput(parent, logFile))
- }
- }
-
- (result, LogContext(logFile, swr, wr))
-
- case "scalap" =>
- runInContext(file, (logFile: File, outDir: File) => {
- val sourceDir = Directory(if (file.isFile) file.getParent else file)
- val sources = sourceDir.files filter (_ hasExtension "scala") map (_.jfile) toList
- val results = sourceDir.files filter (_.name == "result.test") map (_.jfile) toList
-
- if (sources.length != 1 || results.length != 1) {
- NestUI.warning("Misconfigured scalap test directory: " + sourceDir + " \n")
- false
- }
- else {
- val resFile = results.head
- // 2. Compile source file
-
- if (!compileMgr.attemptCompile(Some(outDir), sources, kind, logFile).isPositive) {
- NestUI.normal("compilerMgr failed to compile %s to %s".format(sources mkString ", ", outDir))
- false
- }
- else {
- // 3. Decompile file and compare results
- val isPackageObject = sourceDir.name startsWith "package"
- val className = sourceDir.name.capitalize + (if (!isPackageObject) "" else ".package")
- val url = outDir.toURI.toURL
- val loader = ScalaClassLoader.fromURLs(List(url), this.getClass.getClassLoader)
- val clazz = loader.loadClass(className)
-
- val byteCode = ByteCode.forClass(clazz)
- val result = scala.tools.scalap.Main.decompileScala(byteCode.bytes, isPackageObject)
-
- SFile(logFile) writeAll result
- diffCheck(file, compareFiles(logFile, resFile))
- }
- }
- })
-
- case "script" =>
- val (swr, wr) = newTestWriters()
- printInfoStart(file, wr)
-
- 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 = file2String(argsFile)
- val succeeded = try {
- val cmdString =
- if (isWin) {
- val batchFile = new File(file.getParentFile, fileBase+".bat")
- NestUI.verbose("batchFile: "+batchFile)
- batchFile.getAbsolutePath
- }
- else file.getAbsolutePath
-
- val ok = runCommand(cmdString+argString, logFile)
- ( ok && diffCheck(file, compareOutput(file.getParentFile, logFile)) )
- }
- catch { case e: Exception => NestUI.verbose("caught "+e) ; false }
-
- (succeeded, LogContext(logFile, swr, wr))
- }
-
- private def crashContext(t: Throwable): LogContext = {
- try {
- logStackTrace(logFile, t, "Possible compiler crash during test of: " + testFile + "\n")
- LogContext(logFile)
- }
- catch { case t: Throwable => LogContext(null) }
- }
-
- def run(): (Boolean, LogContext) = {
- val result = try processSingleFile(testFile) catch { case t: Throwable => (false, crashContext(t)) }
- passed = Some(result._1)
- result
- }
-
- def reportResult(writers: Option[(StringWriter, PrintWriter)]) {
- writers foreach { case (swr, wr) =>
- if (passed.isEmpty) printInfoTimeout(wr)
- else printInfoEnd(passed.get, wr)
- wr.flush()
- swr.flush()
- NestUI.normal(swr.toString)
-
- if (passed exists (x => !x)) {
- if (fileManager.showDiff || isPartestDebug)
- NestUI.normal(testDiff)
- if (fileManager.showLog)
- showLog(logFile)
- }
- }
- toDelete foreach (_.deleteRecursively())
- }
- }
-
def runTest(testFile: File): TestState = {
- val runner = new Runner(testFile)
+ val runner = new Runner(testFile, fileManager) {
+ override def testRunParams = params
+ }
// when option "--failed" is provided execute test only if log
// is present (which means it failed before)
if (fileManager.failed && !runner.logFile.canRead)
- return TestState.Ok
-
- val (success, ctx) = runner.run()
- val state = if (success) TestState.Ok else TestState.Fail
-
- runner.reportResult(ctx.writers)
- state
- }
-
- 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) }
- }
-
- private def showLog(logFile: File) {
- file2String(logFile) match {
- case "" if logFile.canRead => ()
- case "" => NestUI.failure("Couldn't open log file: " + logFile + "\n")
- case s => NestUI.normal(s)
+ runner.genPass()
+ else {
+ val (state, elapsed) = timed(runner.run())
+ NestUI.reportTest(state)
+ runner.cleanup()
+ state
}
}
}
diff --git a/src/partest/scala/tools/partest/nest/SBTRunner.scala b/src/partest/scala/tools/partest/nest/SBTRunner.scala
index 20f9c701d5..1cf3aa858f 100644
--- a/src/partest/scala/tools/partest/nest/SBTRunner.scala
+++ b/src/partest/scala/tools/partest/nest/SBTRunner.scala
@@ -1,3 +1,6 @@
+/* NEST (New Scala Test)
+ * Copyright 2007-2013 LAMP/EPFL
+ */
package scala.tools.partest
package nest
@@ -21,7 +24,7 @@ object SBTRunner extends DirectRunner {
val testRootDir: Directory = Directory(testRootPath)
}
- def reflectiveRunTestsForFiles(kindFiles: Array[File], kind: String):java.util.Map[String, TestState] = {
+ def reflectiveRunTestsForFiles(kindFiles: Array[File], kind: String): java.util.List[TestState] = {
def failedOnlyIfRequired(files:List[File]):List[File]={
if (fileManager.failed) files filter (x => fileManager.logFileExists(x, kind)) else files
}
@@ -33,7 +36,7 @@ object SBTRunner extends DirectRunner {
scalacOptions: Seq[String] = Seq(),
justFailedTests: Boolean = false)
- def mainReflect(args: Array[String]): java.util.Map[String, String] = {
+ def mainReflect(args: Array[String]): java.util.List[TestState] = {
setProp("partest.debug", "true")
val Argument = new scala.util.matching.Regex("-(.*)")
@@ -46,7 +49,7 @@ object SBTRunner extends DirectRunner {
case x => sys.error("Unknown command line options: " + x)
}
val config = parseArgs(args, CommandLineOptions())
- fileManager.SCALAC_OPTS ++= config.scalacOptions
+ fileManager.SCALAC_OPTS = config.scalacOptions
fileManager.CLASSPATH = config.classpath getOrElse sys.error("No classpath set")
def findClasspath(jar: String, name: String): Option[String] = {
@@ -67,22 +70,14 @@ object SBTRunner extends DirectRunner {
// TODO - Make this a flag?
//fileManager.updateCheck = true
// Now run and report...
- val runs = config.tests.filterNot(_._2.isEmpty)
- (for {
- (testType, files) <- runs
- (path, result) <- reflectiveRunTestsForFiles(files,testType).asScala
- } yield (path, fixResult(result))).seq.asJava
- }
- def fixResult(result: TestState): String = result match {
- case TestState.Ok => "OK"
- case TestState.Fail => "FAIL"
- case TestState.Timeout => "TIMEOUT"
+ val runs = config.tests.filterNot(_._2.isEmpty)
+ val result = runs.toList flatMap { case (kind, files) => reflectiveRunTestsForFiles(files, kind).asScala }
+
+ result.asJava
}
+
def main(args: Array[String]): Unit = {
- val failures = (
- for ((path, result) <- mainReflect(args).asScala ; if result != TestState.Ok) yield
- path + ( if (result == TestState.Fail) " [FAILED]" else " [TIMEOUT]" )
- )
+ val failures = mainReflect(args).asScala collect { case s if !s.isOk => s.longStatus }
// Re-list all failures so we can go figure out what went wrong.
failures foreach System.err.println
if(!failures.isEmpty) sys.exit(1)
diff --git a/src/partest/scala/tools/partest/nest/StreamCapture.scala b/src/partest/scala/tools/partest/nest/StreamCapture.scala
new file mode 100644
index 0000000000..dc155b1787
--- /dev/null
+++ b/src/partest/scala/tools/partest/nest/StreamCapture.scala
@@ -0,0 +1,53 @@
+/* NEST (New Scala Test)
+ * Copyright 2007-2013 LAMP/EPFL
+ * @author Paul Phillips
+ */
+package scala.tools.partest
+package nest
+
+import java.io.{ Console => _, _ }
+
+object StreamCapture {
+ case class Captured[T](stdout: String, stderr: String, result: T) {
+ override def toString = s"""
+ |result: $result
+ |[stdout]
+ |$stdout
+ |[stderr]
+ |$stderr""".stripMargin.trim
+ }
+
+ private def mkStream = {
+ val swr = new StringWriter
+ val wr = new PrintWriter(swr, true)
+ val ostream = new PrintStream(new OutputStream { def write(b: Int): Unit = wr write b }, true) // autoFlush = true
+
+ (ostream, () => { ostream.close() ; swr.toString })
+ }
+
+ def savingSystem[T](body: => T): T = {
+ val savedOut = System.out
+ val savedErr = System.err
+ try body
+ finally {
+ System setErr savedErr
+ System setOut savedOut
+ }
+ }
+
+ def apply[T](body: => T): Captured[T] = {
+ val (outstream, stdoutFn) = mkStream
+ val (errstream, stderrFn) = mkStream
+
+ val result = savingSystem {
+ System setOut outstream
+ System setErr errstream
+ Console.withOut(outstream) {
+ Console.withErr(errstream) {
+ body
+ }
+ }
+ }
+ Captured(stdoutFn(), stderrFn(), result)
+ }
+}
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 880c6e431b..0000000000
--- a/src/partest/scala/tools/partest/nest/TestFile.scala
+++ /dev/null
@@ -1,80 +0,0 @@
-/* NEST (New Scala Test)
- * Copyright 2007-2013 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.util.ClassPath
-import scala.tools.nsc.io._
-import scala.util.Properties.{ propIsSet, propOrElse, setProp }
-
-trait TestFileCommon {
- def file: JFile
- def kind: String
-
- val dir = file.toAbsolute.parent
- val fileBase = file.stripExtension
- val flags = dir / (fileBase + ".flags") ifFile (f => f.slurp().trim)
-
- lazy val objectDir = dir / (fileBase + "-" + kind + ".obj") createDirectory true
- def setOutDirTo = objectDir
-}
-
-abstract class TestFile(val kind: String) extends TestFileCommon {
- def file: JFile
- def fileManager: FileManager
-
- def defineSettings(settings: Settings, setOutDir: Boolean) = {
- settings.classpath append dir.path
- if (setOutDir)
- settings.outputDirs setSingleOutput setOutDirTo.path
-
- // adding codelib.jar to the classpath
- // codelib provides the possibility to override standard reify
- // this shields the massive amount of reification tests from changes in the API
- settings.classpath prepend PathSettings.srcCodeLib.toString
- if (propIsSet("java.class.path")) setProp("java.class.path", PathSettings.srcCodeLib.toString + ";" + propOrElse("java.class.path", ""))
-
- // have to catch bad flags somewhere
- (flags forall (f => settings.processArgumentString(f)._1)) && {
- settings.classpath append fileManager.CLASSPATH
- true
- }
- }
-
- 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 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
-}
-case class SpecializedTestFile(file: JFile, fileManager: FileManager) extends TestFile("specialized") {
- override def defineSettings(settings: Settings, setOutDir: Boolean): Boolean = {
- super.defineSettings(settings, setOutDir) && {
- // add the instrumented library version to classpath
- settings.classpath prepend PathSettings.srcSpecLib.toString
- // @partest maintainer: if we use a custom Scala build (specified via --classpath)
- // then the classes provided by it will come earlier than instrumented.jar in the resulting classpath
- // this entire classpath business needs a thorough solution
- if (propIsSet("java.class.path")) setProp("java.class.path", PathSettings.srcSpecLib.toString + ";" + propOrElse("java.class.path", ""))
- true
- }
- }
-}
-case class PresentationTestFile(file: JFile, fileManager: FileManager) extends TestFile("presentation")
-case class AntTestFile(file: JFile, fileManager: FileManager) extends TestFile("ant")
-case class InstrumentedTestFile(file: JFile, fileManager: FileManager) extends TestFile("instrumented")
diff --git a/src/partest/scala/tools/partest/package.scala b/src/partest/scala/tools/partest/package.scala
index 2b2ce2e435..9e21b0f6ba 100644
--- a/src/partest/scala/tools/partest/package.scala
+++ b/src/partest/scala/tools/partest/package.scala
@@ -4,31 +4,102 @@
package scala.tools
-import java.io.{ FileNotFoundException, File => JFile }
-import nsc.io.{ Path, Directory, File => SFile }
-import scala.tools.util.PathResolver
-import nsc.Properties.{ propOrElse, propOrNone, propOrEmpty }
import scala.sys.process.javaVmArguments
import java.util.concurrent.Callable
+import scala.tools.partest.nest.NestUI
+import scala.tools.nsc.util.{ ScalaClassLoader, Exceptional }
-package partest {
- class TestState { }
- object TestState {
- val Ok = new TestState
- val Fail = new TestState
- val Timeout = new TestState
+package object partest {
+ type File = java.io.File
+ type SFile = scala.reflect.io.File
+ type Directory = scala.reflect.io.Directory
+ type Path = scala.reflect.io.Path
+ type PathResolver = scala.tools.util.PathResolver
+ type ClassPath[T] = scala.tools.nsc.util.ClassPath[T]
+ type StringWriter = java.io.StringWriter
+
+ val SFile = scala.reflect.io.File
+ val Directory = scala.reflect.io.Directory
+ val Path = scala.reflect.io.Path
+ val PathResolver = scala.tools.util.PathResolver
+ val ClassPath = scala.tools.nsc.util.ClassPath
+
+ val space = "\u0020"
+ val EOL = scala.compat.Platform.EOL
+ def onull(s: String) = if (s == null) "" else s
+ def oempty(xs: String*) = xs filterNot (x => x == null || x == "")
+ def ojoin(xs: String*): String = oempty(xs: _*) mkString space
+ def nljoin(xs: String*): String = oempty(xs: _*) mkString EOL
+
+ def setUncaughtHandler() = {
+ Thread.setDefaultUncaughtExceptionHandler(
+ new Thread.UncaughtExceptionHandler {
+ def uncaughtException(thread: Thread, t: Throwable) {
+ val t1 = Exceptional unwrap t
+ System.err.println(s"Uncaught exception on thread $thread: $t1")
+ t1.printStackTrace()
+ }
+ }
+ )
}
-}
-package object partest {
- import nest.NestUI
+ /** Sources have a numerical group, specified by name_7 and so on. */
+ private val GroupPattern = """.*_(\d+)""".r
+
+ implicit class FileOps(val f: File) {
+ private def sf = SFile(f)
+
+ def testIdent = {
+ f.toString split """[/\\]+""" takeRight 2 mkString "/" // e.g. pos/t1234
+ }
+
+ def mapInPlace(mapFn: String => String): Unit =
+ writeAll(fileLines.map(x => mapFn(x) + "\n"): _*)
+
+ def appendAll(strings: String*): Unit = sf.appendAll(strings: _*)
+ def writeAll(strings: String*): Unit = sf.writeAll(strings: _*)
+ def absolutePathSegments: List[String] = f.getAbsolutePath split """[/\\]+""" toList
+
+ def isJava = f.isFile && (sf hasExtension "java")
+ def isScala = f.isFile && (sf hasExtension "scala")
+ def isJavaOrScala = isJava || isScala
+
+ def extension = sf.extension
+ def hasExtension(ext: String) = sf hasExtension ext
+ def changeExtension(ext: String): File = (sf changeExtension ext).jfile
+
+ /** The group number for this source file, or -1 for no group. */
+ def group: Int =
+ sf.stripExtension match {
+ case GroupPattern(g) if g.toInt >= 0 => g.toInt
+ case _ => -1
+ }
+
+ def fileContents: String = try sf.slurp() catch { case _: java.io.FileNotFoundException => "" }
+ def fileLines: List[String] = augmentString(fileContents).lines.toList
+ }
+
+ implicit class PathOps(p: Path) extends FileOps(p.jfile) { }
+
+ implicit class Copier(val f: SFile) extends AnyVal {
+ def copyTo(dest: Path): Unit = dest.toFile writeAll f.slurp(scala.io.Codec.UTF8)
+ }
- implicit private[partest] def temporaryPath2File(x: Path): JFile = x.jfile
- implicit private[partest] def temporaryFile2Path(x: JFile): Path = Path(x)
+ implicit def temporaryPath2File(x: Path): File = x.jfile
+ implicit def stringPathToJavaFile(path: String): File = new File(path)
implicit lazy val postfixOps = scala.language.postfixOps
implicit lazy val implicitConversions = scala.language.implicitConversions
+ def fileSeparator = java.io.File.separator
+ def pathSeparator = java.io.File.pathSeparator
+
+ def pathToTestIdent(path: Path) = path.jfile.testIdent
+
+ def canonicalizeSlashes(line: String) = line.replaceAll("""[/\\]+""", "/")
+
+ def words(s: String): List[String] = (s.trim split "\\s+").toList
+
def timed[T](body: => T): (T, Long) = {
val t1 = System.currentTimeMillis
val result = body
@@ -39,15 +110,40 @@ package object partest {
def callable[T](body: => T): Callable[T] = new Callable[T] { override def call() = body }
- def file2String(f: JFile) =
- try SFile(f).slurp(scala.io.Codec.UTF8)
- catch { case _: FileNotFoundException => "" }
+ def file2String(f: File): String = f.fileContents
def basename(name: String): String = Path(name).stripExtension
- def resultsToStatistics(results: Iterable[(_, TestState)]): (Int, Int) = {
- val (files, failures) = results map (_._2 == TestState.Ok) partition (_ == true)
- (files.size, failures.size)
+ /** In order to allow for spaces in flags/options, this
+ * parses .flags, .javaopts, javacopts etc files as follows:
+ * If it is exactly one line, it is split (naively) on spaces.
+ * If it contains more than one line, each line is its own
+ * token, spaces and all.
+ */
+ def readOptionsFile(file: File): List[String] = {
+ file.fileLines match {
+ case x :: Nil => words(x)
+ case xs => xs map (_.trim)
+ }
+ }
+
+ def findProgram(name: String): Option[File] = {
+ val pathDirs = sys.env("PATH") match {
+ case null => List("/usr/local/bin", "/usr/bin", "/bin")
+ case path => path split "[:;]" filterNot (_ == "") toList
+ }
+ pathDirs.iterator map (d => new File(d, name)) find (_.canExecute)
+ }
+
+ def now = (new java.util.Date).toString
+ def elapsedString(millis: Long): String = {
+ val elapsedSecs = millis/1000
+ val elapsedMins = elapsedSecs/60
+ val elapsedHrs = elapsedMins/60
+ val dispMins = elapsedMins - elapsedHrs * 60
+ val dispSecs = elapsedSecs - elapsedMins * 60
+
+ "%02d:%02d:%02d".format(elapsedHrs, dispMins, dispSecs)
}
def vmArgString = javaVmArguments.mkString(
@@ -62,13 +158,10 @@ package object partest {
}
def showAllJVMInfo() {
- NestUI.verbose(vmArgString)
- NestUI.verbose(allPropertiesString)
+ vlog(vmArgString)
+ vlog(allPropertiesString)
}
- def isPartestDebug: Boolean =
- propOrEmpty("partest.debug") == "true"
-
import scala.language.experimental.macros
/**
@@ -117,4 +210,10 @@ package object partest {
a.tree))))),
a.tree))
}
+
+ def isPartestTerse = NestUI.isTerse
+ def isPartestDebug = NestUI.isDebug
+ def isPartestVerbose = NestUI.isVerbose
+
+ def vlog(msg: => String) = if (isPartestVerbose) System.err.println(msg)
}
diff --git a/test/build-partest.xml b/test/build-partest.xml
new file mode 100755
index 0000000000..44502ffa61
--- /dev/null
+++ b/test/build-partest.xml
@@ -0,0 +1,24 @@
+<project name="partest" basedir=".">
+ <dirname property="partest.basedir" file="${ant.file.partest}"/>
+ <property file="${partest.basedir}/included.properties"/>
+
+ <macrodef name="testSuite">
+ <attribute name="dir" default="${partest.basedir}/test"/>
+ <attribute name="srcdir" default="files"/> <!-- TODO: make targets for `pending` and other subdirs -->
+ <attribute name="colors" default="${partest.colors}"/>
+ <attribute name="scalacOpts" default="${scalac.args.optimise}"/>
+ <attribute name="kinds" default="pos neg run jvm res scalap scalacheck specialized instrumented presentation"/>
+ <sequential>
+ <property name="partest.dir" value="@{dir}" />
+ <partest srcdir="@{srcdir}"
+ kinds="@{kinds}"
+ colors="@{colors}"
+ scalacOpts="@{scalacOpts}"
+ compilationpathref="partest.classpath">
+ <compilationpath>
+ <fileset dir="${partest.dir}/files/lib" includes="*.jar" />
+ </compilationpath>
+ </partest>
+ </sequential>
+ </macrodef>
+</project>
diff --git a/test/files/neg/choices.check b/test/files/neg/choices.check
index 3e63f9999d..b114394e96 100644
--- a/test/files/neg/choices.check
+++ b/test/files/neg/choices.check
@@ -1,2 +1,2 @@
-partest error: bad flags: -Ylinearizer
+error: bad options: -Yresolve-term-conflict
one error found
diff --git a/test/files/neg/choices.flags b/test/files/neg/choices.flags
index 5464a18c5d..9718467d4c 100644
--- a/test/files/neg/choices.flags
+++ b/test/files/neg/choices.flags
@@ -1 +1 @@
--Ylinearizer \ No newline at end of file
+-Yresolve-term-conflict
diff --git a/test/files/run/reify_this.scala b/test/files/run/reify_this.scala
index ecbf394bba..c385da6360 100644
--- a/test/files/run/reify_this.scala
+++ b/test/files/run/reify_this.scala
@@ -1,11 +1,11 @@
import scala.reflect.runtime.universe._
import scala.tools.reflect.Eval
-trait Eval {
+trait Transvaal {
def eval(tree: Expr[_]) = tree.eval
}
-object Test extends App with Eval {
+object Test extends App with Transvaal {
// select a value from package
eval(reify{println("foo")})
eval(reify{println((new Object).toString == (new Object).toString)})
@@ -17,4 +17,4 @@ object Test extends App with Eval {
// select a value from module
val x = 2
eval(reify{println(x)})
-} \ No newline at end of file
+}
diff --git a/test/files/scalap/abstractClass/result.test b/test/files/scalap/abstractClass.check
index ef1daac23d..ef1daac23d 100644
--- a/test/files/scalap/abstractClass/result.test
+++ b/test/files/scalap/abstractClass.check
diff --git a/test/files/scalap/abstractClass/A.scala b/test/files/scalap/abstractClass.scala
index 19a528d5a1..19a528d5a1 100644
--- a/test/files/scalap/abstractClass/A.scala
+++ b/test/files/scalap/abstractClass.scala
diff --git a/test/files/scalap/abstractMethod/result.test b/test/files/scalap/abstractMethod.check
index 40fa02d408..40fa02d408 100644
--- a/test/files/scalap/abstractMethod/result.test
+++ b/test/files/scalap/abstractMethod.check
diff --git a/test/files/scalap/abstractMethod/A.scala b/test/files/scalap/abstractMethod.scala
index 4bedb377b3..4bedb377b3 100644
--- a/test/files/scalap/abstractMethod/A.scala
+++ b/test/files/scalap/abstractMethod.scala
diff --git a/test/files/scalap/caseClass/result.test b/test/files/scalap/caseClass.check
index 7d7aa4fd8f..7d7aa4fd8f 100644
--- a/test/files/scalap/caseClass/result.test
+++ b/test/files/scalap/caseClass.check
diff --git a/test/files/scalap/caseClass/A.scala b/test/files/scalap/caseClass.scala
index 95f9984519..95f9984519 100644
--- a/test/files/scalap/caseClass/A.scala
+++ b/test/files/scalap/caseClass.scala
diff --git a/test/files/scalap/caseObject/result.test b/test/files/scalap/caseObject.check
index 867a4b2162..867a4b2162 100644
--- a/test/files/scalap/caseObject/result.test
+++ b/test/files/scalap/caseObject.check
diff --git a/test/files/scalap/caseObject/A.scala b/test/files/scalap/caseObject.scala
index 6a3ff10d75..6a3ff10d75 100644
--- a/test/files/scalap/caseObject/A.scala
+++ b/test/files/scalap/caseObject.scala
diff --git a/test/files/scalap/cbnParam/result.test b/test/files/scalap/cbnParam.check
index 52ecb6ae66..52ecb6ae66 100644
--- a/test/files/scalap/cbnParam/result.test
+++ b/test/files/scalap/cbnParam.check
diff --git a/test/files/scalap/cbnParam/A.scala b/test/files/scalap/cbnParam.scala
index 2f366df64a..2f366df64a 100644
--- a/test/files/scalap/cbnParam/A.scala
+++ b/test/files/scalap/cbnParam.scala
diff --git a/test/files/scalap/classPrivate/result.test b/test/files/scalap/classPrivate.check
index ab2d40cdaf..ab2d40cdaf 100644
--- a/test/files/scalap/classPrivate/result.test
+++ b/test/files/scalap/classPrivate.check
diff --git a/test/files/scalap/classPrivate/A.scala b/test/files/scalap/classPrivate.scala
index 9f1bd34a6a..9f1bd34a6a 100644
--- a/test/files/scalap/classPrivate/A.scala
+++ b/test/files/scalap/classPrivate.scala
diff --git a/test/files/scalap/classWithExistential/result.test b/test/files/scalap/classWithExistential.check
index caee3fd6de..caee3fd6de 100644
--- a/test/files/scalap/classWithExistential/result.test
+++ b/test/files/scalap/classWithExistential.check
diff --git a/test/files/scalap/classWithExistential/A.scala b/test/files/scalap/classWithExistential.scala
index 4a5213f963..4a5213f963 100644
--- a/test/files/scalap/classWithExistential/A.scala
+++ b/test/files/scalap/classWithExistential.scala
diff --git a/test/files/scalap/classWithSelfAnnotation/result.test b/test/files/scalap/classWithSelfAnnotation.check
index 82bbd9e8df..82bbd9e8df 100644
--- a/test/files/scalap/classWithSelfAnnotation/result.test
+++ b/test/files/scalap/classWithSelfAnnotation.check
diff --git a/test/files/scalap/classWithSelfAnnotation/A.scala b/test/files/scalap/classWithSelfAnnotation.scala
index 9e0398622a..9e0398622a 100644
--- a/test/files/scalap/classWithSelfAnnotation/A.scala
+++ b/test/files/scalap/classWithSelfAnnotation.scala
diff --git a/test/files/scalap/covariantParam/result.test b/test/files/scalap/covariantParam.check
index f7a3c98966..f7a3c98966 100644
--- a/test/files/scalap/covariantParam/result.test
+++ b/test/files/scalap/covariantParam.check
diff --git a/test/files/scalap/covariantParam/A.scala b/test/files/scalap/covariantParam.scala
index 5b2c24d6fa..5b2c24d6fa 100644
--- a/test/files/scalap/covariantParam/A.scala
+++ b/test/files/scalap/covariantParam.scala
diff --git a/test/files/scalap/defaultParameter/result.test b/test/files/scalap/defaultParameter.check
index 0c775ea7b5..0c775ea7b5 100644
--- a/test/files/scalap/defaultParameter/result.test
+++ b/test/files/scalap/defaultParameter.check
diff --git a/test/files/scalap/defaultParameter/A.scala b/test/files/scalap/defaultParameter.scala
index d3514952f4..d3514952f4 100644
--- a/test/files/scalap/defaultParameter/A.scala
+++ b/test/files/scalap/defaultParameter.scala
diff --git a/test/files/scalap/implicitParam/result.test b/test/files/scalap/implicitParam.check
index a2cfd6092d..a2cfd6092d 100644
--- a/test/files/scalap/implicitParam/result.test
+++ b/test/files/scalap/implicitParam.check
diff --git a/test/files/scalap/implicitParam/A.scala b/test/files/scalap/implicitParam.scala
index 80657218d9..80657218d9 100644
--- a/test/files/scalap/implicitParam/A.scala
+++ b/test/files/scalap/implicitParam.scala
diff --git a/test/files/scalap/packageObject/result.test b/test/files/scalap/packageObject.check
index 5732d92958..5732d92958 100644
--- a/test/files/scalap/packageObject/result.test
+++ b/test/files/scalap/packageObject.check
diff --git a/test/files/scalap/packageObject/A.scala b/test/files/scalap/packageObject.scala
index 7e429c9935..7e429c9935 100644
--- a/test/files/scalap/packageObject/A.scala
+++ b/test/files/scalap/packageObject.scala
diff --git a/test/files/scalap/paramClauses/result.test b/test/files/scalap/paramClauses.check
index 3a141e8faf..3a141e8faf 100644
--- a/test/files/scalap/paramClauses/result.test
+++ b/test/files/scalap/paramClauses.check
diff --git a/test/files/scalap/paramClauses/A.scala b/test/files/scalap/paramClauses.scala
index f9d1917402..f9d1917402 100644
--- a/test/files/scalap/paramClauses/A.scala
+++ b/test/files/scalap/paramClauses.scala
diff --git a/test/files/scalap/paramNames/result.test b/test/files/scalap/paramNames.check
index 85e37f858d..85e37f858d 100644
--- a/test/files/scalap/paramNames/result.test
+++ b/test/files/scalap/paramNames.check
diff --git a/test/files/scalap/paramNames/A.scala b/test/files/scalap/paramNames.scala
index 7ba9ff0feb..7ba9ff0feb 100644
--- a/test/files/scalap/paramNames/A.scala
+++ b/test/files/scalap/paramNames.scala
diff --git a/test/files/scalap/sequenceParam/result.test b/test/files/scalap/sequenceParam.check
index 142d92fea3..142d92fea3 100644
--- a/test/files/scalap/sequenceParam/result.test
+++ b/test/files/scalap/sequenceParam.check
diff --git a/test/files/scalap/sequenceParam/A.scala b/test/files/scalap/sequenceParam.scala
index 86e13340b9..86e13340b9 100644
--- a/test/files/scalap/sequenceParam/A.scala
+++ b/test/files/scalap/sequenceParam.scala
diff --git a/test/files/scalap/simpleClass/result.test b/test/files/scalap/simpleClass.check
index 4fdf25d1cf..4fdf25d1cf 100644
--- a/test/files/scalap/simpleClass/result.test
+++ b/test/files/scalap/simpleClass.check
diff --git a/test/files/scalap/simpleClass/A.scala b/test/files/scalap/simpleClass.scala
index fa82e62680..fa82e62680 100644
--- a/test/files/scalap/simpleClass/A.scala
+++ b/test/files/scalap/simpleClass.scala
diff --git a/test/files/scalap/traitObject/result.test b/test/files/scalap/traitObject.check
index 104ba14f1a..104ba14f1a 100644
--- a/test/files/scalap/traitObject/result.test
+++ b/test/files/scalap/traitObject.check
diff --git a/test/files/scalap/traitObject/A.scala b/test/files/scalap/traitObject.scala
index d5f43181c1..d5f43181c1 100644
--- a/test/files/scalap/traitObject/A.scala
+++ b/test/files/scalap/traitObject.scala
diff --git a/test/files/scalap/typeAnnotations/result.test b/test/files/scalap/typeAnnotations.check
index 407b0235c6..407b0235c6 100644
--- a/test/files/scalap/typeAnnotations/result.test
+++ b/test/files/scalap/typeAnnotations.check
diff --git a/test/files/scalap/typeAnnotations/A.scala b/test/files/scalap/typeAnnotations.scala
index ff2445edc9..ff2445edc9 100644
--- a/test/files/scalap/typeAnnotations/A.scala
+++ b/test/files/scalap/typeAnnotations.scala
diff --git a/test/files/scalap/valAndVar/result.test b/test/files/scalap/valAndVar.check
index e940da9801..e940da9801 100644
--- a/test/files/scalap/valAndVar/result.test
+++ b/test/files/scalap/valAndVar.check
diff --git a/test/files/scalap/valAndVar/A.scala b/test/files/scalap/valAndVar.scala
index 2d89348401..2d89348401 100644
--- a/test/files/scalap/valAndVar/A.scala
+++ b/test/files/scalap/valAndVar.scala
diff --git a/test/files/scalap/wildcardType/result.test b/test/files/scalap/wildcardType.check
index e43261db32..e43261db32 100644
--- a/test/files/scalap/wildcardType/result.test
+++ b/test/files/scalap/wildcardType.check
diff --git a/test/files/scalap/wildcardType/A.scala b/test/files/scalap/wildcardType.scala
index 4bb0d14de5..4bb0d14de5 100644
--- a/test/files/scalap/wildcardType/A.scala
+++ b/test/files/scalap/wildcardType.scala
diff --git a/test/partest b/test/partest
index ec66f5c048..8243316cca 100755
--- a/test/partest
+++ b/test/partest
@@ -1,7 +1,8 @@
-#!/bin/sh
+#!/usr/bin/env bash
+#
##############################################################################
-# Scala test runner 2.8.0
+# Scala test runner 2.10.0
##############################################################################
# (c) 2002-2013 LAMP/EPFL
#
@@ -10,6 +11,16 @@
# PARTICULAR PURPOSE.
##############################################################################
+# Use tput to detect color-capable terminal.
+term_colors=$(tput colors 2>/dev/null)
+if [[ $? == 0 ]] && [[ $term_colors -gt 2 ]]; then
+ git_diff_options="--color=always --word-diff"
+ color_opts="-Dpartest.colors=$term_colors"
+else
+ unset color_opts
+ git_diff_options="--nocolor"
+fi
+
cygwin=false;
darwin=false;
case "`uname`" in
@@ -98,6 +109,7 @@ fi
"${JAVACMD:=java}" \
$JAVA_OPTS -cp "$EXT_CLASSPATH" \
${partestDebugStr} \
+ "$color_opts" \
-Dscala.home="${SCALA_HOME}" \
-Dpartest.javacmd="${JAVACMD}" \
-Dpartest.java_opts="${JAVA_OPTS}" \