Skip to content

Commit 45633c1

Browse files
committed
Run tests for partest
1 parent 258775e commit 45633c1

File tree

9 files changed

+156
-41
lines changed

9 files changed

+156
-41
lines changed

project/Build.scala

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import sbt.Keys._
22
import sbt._
33
import java.io.{ RandomAccessFile, File }
44
import java.nio.channels.FileLock
5+
import scala.reflect.io.Path
56

67
object DottyBuild extends Build {
78

@@ -42,16 +43,16 @@ object DottyBuild extends Build {
4243
// to get Scala 2.11
4344
resolvers += Resolver.sonatypeRepo("releases"),
4445

45-
// get reflect and xml onboard
46-
libraryDependencies ++= Seq("org.scala-lang" % "scala-reflect" % scalaVersion.value,
47-
"org.scala-lang.modules" %% "scala-xml" % "1.0.1",
48-
"me.d-d" % "scala-compiler" % "2.11.5-20150506-175515-8fc7635b56",
46+
// get libraries onboard
47+
partestDeps := Seq("me.d-d" % "scala-compiler" % "2.11.5-20150506-175515-8fc7635b56",
48+
"org.scala-lang" % "scala-reflect" % scalaVersion.value,
49+
"org.scala-lang" % "scala-library" % scalaVersion.value % "test"),
50+
libraryDependencies ++= partestDeps.value,
51+
libraryDependencies ++= Seq("org.scala-lang.modules" %% "scala-xml" % "1.0.1",
4952
"org.scala-lang.modules" %% "scala-partest" % "1.0.5" % "test",
53+
"com.novocode" % "junit-interface" % "0.11" % "test",
5054
"jline" % "jline" % "2.12"),
5155

52-
// get junit onboard
53-
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test",
54-
5556
// scalac options
5657
scalacOptions in Global ++= Seq("-feature", "-deprecation", "-language:_"),
5758

@@ -67,9 +68,18 @@ object DottyBuild extends Build {
6768
// otherwise it just executes the tests directly
6869
lockPartestFile := {
6970
val partestLockFile = "." + File.separator + "tests" + File.separator + "partest.lock"
70-
partestLock = new RandomAccessFile(partestLockFile, "rw").getChannel.tryLock
71+
try {
72+
partestLock = new RandomAccessFile(partestLockFile, "rw").getChannel.tryLock
73+
} catch {
74+
case ex: java.nio.channels.OverlappingFileLockException => // locked already
75+
}
76+
},
77+
runPartestRunner <<= Def.taskDyn {
78+
val jars = Seq((packageBin in Compile).value.getAbsolutePath) ++
79+
getJarPaths(partestDeps.value, ivyPaths.value.ivyHome)
80+
val dottyJars = "-dottyJars " + jars.length + " " + jars.mkString(" ")
81+
runTask(Test, "dotty.partest.DPConsoleRunner", dottyJars)
7182
},
72-
runPartestRunner <<= runTask(Test, "dotty.partest.DPConsoleRunner", "") dependsOn (test in Test),
7383

7484
// Adjust classpath for running dotty
7585
mainClass in (Compile, run) := Some("dotty.tools.dotc.Main"),
@@ -103,7 +113,7 @@ object DottyBuild extends Build {
103113

104114
tuning ::: agentOptions ::: travis_build ::: fullpath
105115
}
106-
) ++ addCommandAlias("partest", ";lockPartestFile;runPartestRunner")
116+
) ++ addCommandAlias("partest", ";test:compile;lockPartestFile;test:test;runPartestRunner")
107117

108118
lazy val dotty = Project(id = "dotty", base = file("."), settings = defaults)
109119

@@ -156,5 +166,17 @@ object DottyBuild extends Build {
156166

157167
lazy val lockPartestFile = TaskKey[Unit]("lockPartestFile", "Creates the file lock on ./tests/partest.lock")
158168
lazy val runPartestRunner = TaskKey[Unit]("runPartestRunner", "Runs partests")
159-
169+
lazy val partestDeps = SettingKey[Seq[ModuleID]]("partestDeps", "Finds jars for partest dependencies")
170+
171+
def getJarPaths(modules: Seq[ModuleID], ivyHome: Option[File]): Seq[String] = ivyHome match {
172+
case Some(home) =>
173+
modules.map({ module =>
174+
val file = Path(home) / Path("cache") /
175+
Path(module.organization) / Path(module.name) / Path("jars") /
176+
Path(module.name + "-" + module.revision + ".jar")
177+
if (!file.isFile) throw new RuntimeException("ERROR: sbt getJarPaths: dependency jar not found: " + file)
178+
else file.jfile.getAbsolutePath
179+
})
180+
case None => throw new RuntimeException("ERROR: sbt getJarPaths: ivyHome not defined")
181+
}
160182
}

test/dotc/tests.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class tests extends CompilerTest {
3535
val posDir = testsDir + "pos/"
3636
val posSpecialDir = testsDir + "pos-special/"
3737
val negDir = testsDir + "neg/"
38+
val runDir = testsDir + "run/"
3839
val newDir = testsDir + "new/"
3940

4041
val sourceDir = "./src/"
@@ -136,7 +137,12 @@ class tests extends CompilerTest {
136137
@Test def neg_instantiateAbstract = compileFile(negDir, "instantiateAbstract", xerrors = 8)
137138
@Test def neg_selfInheritance = compileFile(negDir, "selfInheritance", xerrors = 5)
138139
*/
139-
@Test def dotty = compileDir(toolsDir, "", "-deep" :: allowDeepSubtypes ++ twice) // note the -deep argument
140+
@Test def run_hello = runFile(runDir, "hello")
141+
@Test def run_lazyVals = runFile(runDir, "lazyVals")
142+
143+
144+
@Test def dotty = compileDir(dottyDir, "tools", "-deep" :: allowDeepSubtypes ++ twice) // note the -deep argument
145+
140146
/*
141147
@Test def dotc_ast = compileDir(dotcDir, "ast")
142148
@Test def dotc_config = compileDir(dotcDir, "config")

test/dotty/partest/DPConsoleRunner.scala

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,57 @@
44

55
package dotty.partest
66

7+
import scala.reflect.io.AbstractFile
78
import scala.tools.partest._
89
import scala.tools.partest.nest._
10+
import scala.util.matching.Regex
11+
import tools.nsc.io.{ File => NSCFile }
912
import java.io.File
1013
import java.net.URLClassLoader
1114

1215
/** Runs dotty partest from the Console, discovering test sources in
1316
* DPConfig.testRoot that have been generated automatically by
14-
* DPPrepJUnitRunner. Use `sbt test` to run.
17+
* DPPrepJUnitRunner. Use `sbt partest` to run.
1518
*/
1619
object DPConsoleRunner {
1720
def main(args: Array[String]): Unit = {
18-
new DPConsoleRunner(args mkString (" ")).runPartest
21+
// unfortunately sbt runTask passes args as single string
22+
val jarFinder = """-dottyJars (\d*) (.*)""".r
23+
val (jarList, otherArgs) = args.toList.partition(jarFinder.findFirstIn(_).isDefined)
24+
val extraJars = jarList match {
25+
case Nil => sys.error("Error: DPConsoleRunner needs \"-dottyJars <jarCount> <jars>*\".")
26+
case jarFinder(nr, jarString) :: Nil =>
27+
val jars = jarString.split(" ").toList
28+
if (jars.length.toString != nr)
29+
sys.error("Error: DPConsoleRunner found wrong number of dottyJars: " + jars + ", expected: " + nr + ". Make sure the path doesn't contain any spaces.")
30+
else jars
31+
case list => sys.error("Error: DPConsoleRunner found several -dottyJars options: " + list)
32+
}
33+
new DPConsoleRunner(otherArgs mkString (" "), extraJars).runPartest
1934
}
2035
}
2136

2237
// console runner has a suite runner which creates a test runner for each test
23-
class DPConsoleRunner(args: String) extends ConsoleRunner(args) {
38+
class DPConsoleRunner(args: String, extraJars: List[String]) extends ConsoleRunner(args) {
2439
override val suiteRunner = new DPSuiteRunner (
2540
testSourcePath = optSourcePath getOrElse DPConfig.testRoot,
26-
fileManager = null, // new FileManager(ClassPath split PathResolver.Environment.javaUserClassPath map (Path(_))), // the script sets up our classpath for us via ant
41+
fileManager = new DottyFileManager(extraJars),
2742
updateCheck = optUpdateCheck,
2843
failed = optFailed)
2944

3045
override def run = {}
3146
def runPartest = super.run
3247
}
3348

49+
class DottyFileManager(extraJars: List[String]) extends FileManager(Nil) {
50+
lazy val extraJarList = extraJars.map(NSCFile(_))
51+
override lazy val libraryUnderTest = Path(extraJars.find(_.contains("scala-library")).getOrElse(""))
52+
override lazy val reflectUnderTest = Path(extraJars.find(_.contains("scala-reflect")).getOrElse(""))
53+
override lazy val compilerUnderTest = Path(extraJars.find(_.contains("dotty")).getOrElse(""))
54+
}
55+
3456
class DPSuiteRunner(testSourcePath: String, // relative path, like "files", or "pending"
35-
fileManager: FileManager,
57+
fileManager: DottyFileManager,
3658
updateCheck: Boolean,
3759
failed: Boolean,
3860
javaCmdPath: String = PartestDefaults.javaCmd,
@@ -83,7 +105,7 @@ extends SuiteRunner(testSourcePath, fileManager, updateCheck, failed, javaCmdPat
83105
// but it doesn't seem to be used anywhere
84106
}
85107

86-
class DPTestRunner(testFile: File, suiteRunner: SuiteRunner) extends nest.Runner(testFile, suiteRunner) {
108+
class DPTestRunner(testFile: File, suiteRunner: DPSuiteRunner) extends nest.Runner(testFile, suiteRunner) {
87109
// override to provide DottyCompiler
88110
override def newCompiler = new dotty.partest.DPDirectCompiler(this)
89111

@@ -117,7 +139,6 @@ class DPTestRunner(testFile: File, suiteRunner: SuiteRunner) extends nest.Runner
117139
case class CompSucceeded() extends NegTestState
118140

119141
def nerrIsOk(reason: String) = {
120-
import scala.util.matching.Regex
121142
val nerrFinder = """compilation failed with (\d+) errors""".r
122143
reason match {
123144
case nerrFinder(found) =>
@@ -187,4 +208,8 @@ class DPTestRunner(testFile: File, suiteRunner: SuiteRunner) extends nest.Runner
187208
def description = s"dotc $fsString"
188209
lazy val result = { pushTranscript(description) ; attemptCompile(fs) }
189210
}
211+
212+
// override to add dotty and scala jars to classpath
213+
override def extraClasspath = suiteRunner.fileManager.asInstanceOf[DottyFileManager].extraJarList ::: super.extraClasspath
214+
190215
}

test/partest

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/usr/bin/env bash
2+
# partest error message references partest script to update check files, but
3+
# doesn't work for dotty because we don't know where tests came from.
4+
5+
if [ $1='--update-check' ];
6+
then
7+
echo """ERROR: Since dotty partest runs on generated files, please update the check
8+
files in the original location (run tests) or update the expected error count
9+
(neg tests) in the test file."
10+
else
11+
echo "This script doesn't launch partest, please use sbt partest instead."
12+
fi

test/test/CompilerTest.scala

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import dotty.tools.dotc.{Main, Bench, Driver}
55
import dotty.tools.dotc.reporting.Reporter
66
import scala.collection.mutable.ListBuffer
77
import scala.reflect.io.{ Path, Directory, File => SFile }
8-
import scala.tools.partest.nest.FileManager
8+
import scala.tools.partest.nest.{ FileManager, NestUI }
99
import java.io.{ RandomAccessFile, File => JFile }
1010

1111
import org.junit.Test
@@ -19,15 +19,19 @@ import org.junit.Test
1919
* generated sources.
2020
*
2121
* Through overriding the partestableXX methods, tests can always be run as
22-
* JUnit compiler tests.
22+
* JUnit compiler tests. Run tests cannot be run by JUnit, only by partest.
2323
*
24-
* A test can either be a file or a directory. The test is in a parent
25-
* directory that determines the kind of test:
24+
* A test can either be a file or a directory. Partest will generate a
25+
* <test>-<kind>.log file with output of failed tests. Partest reads compiler
26+
* flags and the number of errors expected from a neg test from <test>.flags
27+
* and <test>.nerr files (also generated). The test is in a parent directory
28+
* that determines the kind of test:
2629
* - pos: checks that compilation succeeds
2730
* - neg: checks that compilation fails with the given number of errors
28-
* (- run: compilation succeeds and running generates the given output.)
29-
* For partest, compiler flags and the number of errors expected from a neg
30-
* test are read from test.flags and test.nerr files (also generated).
31+
* - run: compilation succeeds, partest: test run generates the output in
32+
* <test>.check. Run tests always need to be:
33+
* object Test { def main(args: Array[String]): Unit = ... }
34+
*
3135
*/
3236
abstract class CompilerTest extends DottyTest {
3337

@@ -60,8 +64,11 @@ abstract class CompilerTest extends DottyTest {
6064
CompilerTest.init
6165

6266
/** Always run with JUnit. */
63-
def compileLine(cmdLine: String, xerrors: Int = 0)(implicit defaultOptions: List[String]): Unit =
67+
def compileLine(cmdLine: String, xerrors: Int = 0)(implicit defaultOptions: List[String]): Unit = {
68+
if (generatePartestFiles)
69+
NestUI.echoWarning("WARNING: compileLine will always run with JUnit, no partest files generated.")
6470
compileArgs(cmdLine.split("\n"), xerrors)
71+
}
6572

6673
/** Compiles the given code file.
6774
*
@@ -73,12 +80,15 @@ abstract class CompilerTest extends DottyTest {
7380
* @param extension the file extension, .scala by default
7481
* @param defaultOptions more arguments to the compiler
7582
*/
76-
def compileFile(prefix: String, fileName: String, args: List[String] = Nil, xerrors: Int = 0, extension: String = ".scala")
83+
def compileFile(prefix: String, fileName: String, args: List[String] = Nil, xerrors: Int = 0,
84+
extension: String = ".scala", runTest: Boolean = false)
7785
(implicit defaultOptions: List[String]): Unit = {
7886
if (!generatePartestFiles || !partestableFile(prefix, fileName, extension, args ++ defaultOptions, xerrors)) {
87+
if (runTest)
88+
NestUI.echoWarning(s"WARNING: run tests can only be run by partest, JUnit just verifies compilation: $prefix$fileName$extension")
7989
compileArgs((s"$prefix$fileName$extension" :: args).toArray, xerrors)
8090
} else {
81-
val kind = testKind(xerrors)
91+
val kind = testKind(prefix, xerrors, runTest)
8292
println(s"generating partest files for test file: $prefix$fileName$extension of kind $kind")
8393

8494
val sourceFile = new JFile(prefix + fileName + extension)
@@ -90,11 +100,17 @@ abstract class CompilerTest extends DottyTest {
90100
}
91101
}
92102
}
103+
def runFile(prefix: String, fileName: String, args: List[String] = Nil, xerrors: Int = 0,
104+
extension: String = ".scala")(implicit defaultOptions: List[String]): Unit =
105+
compileFile(prefix, fileName, args, xerrors, extension, true)
93106

94107
/** Compiles the code files in the given directory together. If args starts
95108
* with "-deep", all files in subdirectories (and so on) are included. */
96-
def compileDir(prefix: String, dirName: String, args: List[String] = Nil, xerrors: Int = 0)(implicit defaultOptions: List[String]): Unit = {
109+
def compileDir(prefix: String, dirName: String, args: List[String] = Nil, xerrors: Int = 0, runTest: Boolean = false)
110+
(implicit defaultOptions: List[String]): Unit = {
97111
if (!generatePartestFiles || !partestableDir(prefix, dirName, args ++ defaultOptions, xerrors)) {
112+
if (runTest)
113+
NestUI.echoWarning(s"WARNING: run tests can only be run by partest, JUnit just verifies compilation: $prefix$dirName")
98114
val dir = Directory(prefix + dirName)
99115
val (files, normArgs) = args match {
100116
case "-deep" :: args1 => (dir.deepFiles, args1)
@@ -107,18 +123,24 @@ abstract class CompilerTest extends DottyTest {
107123
case "-deep" :: args1 => (flattenDir(prefix, dirName), args1 ++ defaultOptions, "deep")
108124
case _ => (new JFile(prefix + dirName), args ++ defaultOptions, "shallow")
109125
}
110-
val kind = testKind(xerrors)
126+
val kind = testKind(prefix, xerrors, runTest)
111127
println(s"generating partest files for test directory ($deep): $prefix$dirName of kind $kind")
112128

113129
if (sourceDir.exists) {
114130
val firstDest = Directory(DPConfig.testRoot + JFile.separator + kind + JFile.separator + dirName)
115-
computeDestAndCopyFiles(sourceDir, firstDest, kind, args ++ defaultOptions, xerrors.toString)
116-
if (deep == "deep") sourceDir.delete
131+
computeDestAndCopyFiles(sourceDir, firstDest, kind, flags, xerrors.toString)
132+
if (deep == "deep") {
133+
sourceDir.listFiles.foreach(_.delete)
134+
sourceDir.delete
135+
}
117136
} else {
118137
throw new java.io.FileNotFoundException(s"Unable to locate test dir $prefix$dirName")
119138
}
120139
}
121140
}
141+
def runDir(prefix: String, dirName: String, args: List[String] = Nil, xerrors: Int = 0)
142+
(implicit defaultOptions: List[String]): Unit =
143+
compileDir(prefix, dirName, args, xerrors, true)
122144

123145
/** Compiles each source in the directory path separately by calling
124146
* compileFile resp. compileDir. */
@@ -161,10 +183,18 @@ abstract class CompilerTest extends DottyTest {
161183
}
162184

163185
// In particular, don't copy flags from scalac tests
164-
private val extensionsToCopy = scala.collection.immutable.HashSet("scala", "java")
186+
private val extensionsToCopy = scala.collection.immutable.HashSet("scala", "java", "check")
165187

166188
/** Determines what kind of test to run. */
167-
private def testKind(xerrors: Int) = if (xerrors > 0) "neg" else "pos"
189+
private def testKind(prefixDir: String, xerrors: Int, runTest: Boolean) = {
190+
if (runTest) "run"
191+
else if (xerrors > 0) "neg"
192+
else if (prefixDir.endsWith("run" + JFile.separator)) {
193+
NestUI.echoWarning("WARNING: test is being run as pos test despite being in a run directory. " +
194+
"Use runFile/runDir instead of compileFile/compileDir to do a run test")
195+
"pos"
196+
} else "pos"
197+
}
168198

169199
/** The three possibilities: no generated sources exist yet, the same sources
170200
* exist already, different sources exist. */
@@ -184,7 +214,7 @@ abstract class CompilerTest extends DottyTest {
184214
val flags = oldFlags.map(f => if (f == oldOutput) partestOutput else f)
185215

186216
getExisting(dest).isDifferent(source, flags, nerr) match {
187-
case NotExists => copyFiles(source, dest, partestOutput, flags, nerr)
217+
case NotExists => copyFiles(source, dest, partestOutput, flags, nerr, kind)
188218
case ExistsSame => // nothing else to do
189219
case ExistsDifferent =>
190220
val nextDest = dest.parent / (dest match {
@@ -195,8 +225,8 @@ abstract class CompilerTest extends DottyTest {
195225
}
196226
}
197227

198-
/** Copies the test sources and creates flags, nerr and output files. */
199-
private def copyFiles(sourceFile: Path, dest: Path, partestOutput: String, flags: List[String], nerr: String) = {
228+
/** Copies the test sources. Creates flags, nerr, check and output files. */
229+
private def copyFiles(sourceFile: Path, dest: Path, partestOutput: String, flags: List[String], nerr: String, kind: String) = {
200230
recCopyFiles(sourceFile, dest)
201231

202232
new JFile(partestOutput).mkdirs
@@ -205,17 +235,24 @@ abstract class CompilerTest extends DottyTest {
205235
dest.changeExtension("flags").createFile(true).writeAll(flags.mkString(" "))
206236
if (nerr != "0")
207237
dest.changeExtension("nerr").createFile(true).writeAll(nerr)
238+
sourceFile.changeExtension("check").ifFile({ check =>
239+
if (kind == "run")
240+
FileManager.copyFile(check.jfile, dest.changeExtension("check").jfile)
241+
else
242+
NestUI.echoWarning(s"WARNING: ignoring $check for test kind $kind")
243+
})
244+
208245
}
209246

210-
/** Recursively copy over files and directories, excluding extensions that
211-
* aren't in extensionsToCopy. */
247+
/** Recursively copy over source files and directories, excluding extensions
248+
* that aren't in extensionsToCopy. */
212249
private def recCopyFiles(sourceFile: Path, dest: Path): Unit = {
213250
processFileDir(sourceFile, { sf =>
214251
if (extensionsToCopy.contains(sf.extension)) {
215252
dest.parent.jfile.mkdirs
216253
FileManager.copyFile(sourceFile.jfile, dest.jfile)
217254
} else {
218-
println(s"warning: ignoring $sf")
255+
NestUI.echoWarning(s"WARNING: ignoring $sf")
219256
}
220257
}, { sdir =>
221258
dest.jfile.mkdirs

tests/run/hello.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
hello dotty!

tests/run/hello.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object Test {
2+
def main(args: Array[String]): Unit = println("hello dotty!")
3+
}

tests/run/lazyVals.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
42

0 commit comments

Comments
 (0)