Skip to content

Commit a0fa33d

Browse files
committed
Merge pull request #554 from vsalvis/vsalvis-partest-run
Run tests for partest
2 parents 2da8afd + f64762b commit a0fa33d

File tree

11 files changed

+213
-87
lines changed

11 files changed

+213
-87
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,5 @@ classes/
2828

2929
# Partest
3030
tests/partest-generated/
31+
tests/locks/
3132
/test-classes/

project/Build.scala

Lines changed: 66 additions & 39 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

@@ -16,8 +17,6 @@ object DottyBuild extends Build {
1617
// "-XX:+HeapDumpOnOutOfMemoryError", "-Xmx1g", "-Xss2m"
1718
)
1819

19-
var partestLock: FileLock = null
20-
2120
val defaults = Defaults.defaultSettings ++ Seq(
2221
scalaVersion in Global := "2.11.5",
2322
version in Global := "0.1-SNAPSHOT",
@@ -42,16 +41,16 @@ object DottyBuild extends Build {
4241
// to get Scala 2.11
4342
resolvers += Resolver.sonatypeRepo("releases"),
4443

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",
44+
// get libraries onboard
45+
partestDeps := Seq("me.d-d" % "scala-compiler" % "2.11.5-20150506-175515-8fc7635b56",
46+
"org.scala-lang" % "scala-reflect" % scalaVersion.value,
47+
"org.scala-lang" % "scala-library" % scalaVersion.value % "test"),
48+
libraryDependencies ++= partestDeps.value,
49+
libraryDependencies ++= Seq("org.scala-lang.modules" %% "scala-xml" % "1.0.1",
4950
"org.scala-lang.modules" %% "scala-partest" % "1.0.5" % "test",
51+
"com.novocode" % "junit-interface" % "0.11" % "test",
5052
"jline" % "jline" % "2.12"),
5153

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

@@ -62,14 +61,26 @@ object DottyBuild extends Build {
6261

6362
// enable verbose exception messages for JUnit
6463
testOptions in Test += Tests.Argument(TestFrameworks.JUnit, "-a", "-v", "--run-listener=test.ContextEscapeDetector"),
65-
testOptions in Test += Tests.Cleanup({ () => if (partestLock != null) partestLock.release }),
66-
// when this file is locked, running test generates the files for partest
67-
// otherwise it just executes the tests directly
64+
testOptions in Test += Tests.Cleanup({ () => partestLockFile.delete }),
65+
6866
lockPartestFile := {
69-
val partestLockFile = "." + File.separator + "tests" + File.separator + "partest.lock"
70-
partestLock = new RandomAccessFile(partestLockFile, "rw").getChannel.tryLock
67+
// When this file is present, running `test` generates the files for
68+
// partest. Otherwise it just executes the tests directly.
69+
val lockDir = partestLockFile.getParentFile
70+
lockDir.mkdirs
71+
// Cannot have concurrent partests as they write to the same directory.
72+
if (lockDir.list.size > 0)
73+
throw new RuntimeException("ERROR: sbt partest: another partest is already running, pid in lock file: " + lockDir.list.toList.mkString(" "))
74+
partestLockFile.createNewFile
75+
partestLockFile.deleteOnExit
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+
// Provide the jars required on the classpath of run tests
82+
runTask(Test, "dotty.partest.DPConsoleRunner", dottyJars)
7183
},
72-
runPartestRunner <<= runTask(Test, "dotty.partest.DPConsoleRunner", "") dependsOn (test in Test),
7384

7485
// Adjust classpath for running dotty
7586
mainClass in (Compile, run) := Some("dotty.tools.dotc.Main"),
@@ -79,31 +90,31 @@ object DottyBuild extends Build {
7990

8091
// http://grokbase.com/t/gg/simple-build-tool/135ke5y90p/sbt-setting-jvm-boot-paramaters-for-scala
8192
javaOptions <++= (managedClasspath in Runtime, packageBin in Compile) map { (attList, bin) =>
82-
// put the Scala {library, reflect} in the classpath
83-
val path = for {
84-
file <- attList.map(_.data)
85-
path = file.getAbsolutePath
86-
} yield "-Xbootclasspath/p:" + path
87-
// dotty itself needs to be in the bootclasspath
88-
val fullpath = ("-Xbootclasspath/a:" + bin) :: path.toList
89-
// System.err.println("BOOTPATH: " + fullpath)
90-
91-
val travis_build = // propagate if this is a travis build
92-
if (sys.props.isDefinedAt(TRAVIS_BUILD))
93-
List(s"-D$TRAVIS_BUILD=${sys.props(TRAVIS_BUILD)}") ::: travisMemLimit
94-
else
95-
List()
96-
97-
val tuning =
98-
if (sys.props.isDefinedAt("Oshort"))
99-
// Optimize for short-running applications, see https://github.com/lampepfl/dotty/issues/222
100-
List("-XX:+TieredCompilation", "-XX:TieredStopAtLevel=1")
93+
// put the Scala {library, reflect} in the classpath
94+
val path = for {
95+
file <- attList.map(_.data)
96+
path = file.getAbsolutePath
97+
} yield "-Xbootclasspath/p:" + path
98+
// dotty itself needs to be in the bootclasspath
99+
val fullpath = ("-Xbootclasspath/a:" + bin) :: path.toList
100+
// System.err.println("BOOTPATH: " + fullpath)
101+
102+
val travis_build = // propagate if this is a travis build
103+
if (sys.props.isDefinedAt(TRAVIS_BUILD))
104+
List(s"-D$TRAVIS_BUILD=${sys.props(TRAVIS_BUILD)}") ::: travisMemLimit
101105
else
102106
List()
103107

104-
tuning ::: agentOptions ::: travis_build ::: fullpath
108+
val tuning =
109+
if (sys.props.isDefinedAt("Oshort"))
110+
// Optimize for short-running applications, see https://github.com/lampepfl/dotty/issues/222
111+
List("-XX:+TieredCompilation", "-XX:TieredStopAtLevel=1")
112+
else
113+
List()
114+
115+
("-DpartestParentID=" + pid) :: tuning ::: agentOptions ::: travis_build ::: fullpath
105116
}
106-
) ++ addCommandAlias("partest", ";lockPartestFile;runPartestRunner")
117+
) ++ addCommandAlias("partest", ";test:compile;lockPartestFile;test:test;runPartestRunner")
107118

108119
lazy val dotty = Project(id = "dotty", base = file("."), settings = defaults)
109120

@@ -154,7 +165,23 @@ object DottyBuild extends Build {
154165
lazy val benchmarks = Project(id = "dotty-bench", settings = benchmarkSettings,
155166
base = file("bench")) dependsOn(dotty % "compile->test")
156167

157-
lazy val lockPartestFile = TaskKey[Unit]("lockPartestFile", "Creates the file lock on ./tests/partest.lock")
158-
lazy val runPartestRunner = TaskKey[Unit]("runPartestRunner", "Runs partests")
159-
168+
// Partest tasks
169+
lazy val lockPartestFile = TaskKey[Unit]("lockPartestFile", "Creates the lock file at ./tests/locks/partest-<pid>.lock")
170+
lazy val partestLockFile = new File("." + File.separator + "tests" + File.separator + "locks" + File.separator + s"partest-$pid.lock")
171+
def pid = java.lang.Long.parseLong(java.lang.management.ManagementFactory.getRuntimeMXBean().getName().split("@")(0))
172+
173+
lazy val runPartestRunner = TaskKey[Unit]("runPartestRunner", "Runs partest")
174+
175+
lazy val partestDeps = SettingKey[Seq[ModuleID]]("partestDeps", "Finds jars for partest dependencies")
176+
def getJarPaths(modules: Seq[ModuleID], ivyHome: Option[File]): Seq[String] = ivyHome match {
177+
case Some(home) =>
178+
modules.map({ module =>
179+
val file = Path(home) / Path("cache") /
180+
Path(module.organization) / Path(module.name) / Path("jars") /
181+
Path(module.name + "-" + module.revision + ".jar")
182+
if (!file.isFile) throw new RuntimeException("ERROR: sbt getJarPaths: dependency jar not found: " + file)
183+
else file.jfile.getAbsolutePath
184+
})
185+
case None => throw new RuntimeException("ERROR: sbt getJarPaths: ivyHome not defined")
186+
}
160187
}

test/dotc/tests.scala

Lines changed: 11 additions & 4 deletions
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/"
@@ -44,7 +45,8 @@ class tests extends CompilerTest {
4445
val coreDir = dotcDir + "core/"
4546

4647
@Test def pickle_pickleOK = compileDir(testsDir, "pickling", testPickling)
47-
@Test def pickle_pickling = compileDir(coreDir, "pickling", testPickling)
48+
// This directory doesn't exist anymore
49+
// @Test def pickle_pickling = compileDir(coreDir, "pickling", testPickling)
4850
@Test def pickle_ast = compileDir(dotcDir, "ast", testPickling)
4951

5052
//@Test def pickle_core = compileDir(dotcDir, "core", testPickling, xerrors = 2) // two spurious comparison errors in Types and TypeOps
@@ -136,14 +138,19 @@ class tests extends CompilerTest {
136138
@Test def neg_instantiateAbstract = compileFile(negDir, "instantiateAbstract", xerrors = 8)
137139
@Test def neg_selfInheritance = compileFile(negDir, "selfInheritance", xerrors = 5)
138140

139-
@Test def dotty = compileDir(toolsDir, "", "-deep" :: allowDeepSubtypes ++ twice) // note the -deep argument
141+
@Test def run_hello = runFile(runDir, "hello")
142+
@Test def run_lazyVals = runFile(runDir, "lazyVals")
143+
144+
145+
@Test def dotty = compileDir(dottyDir, "tools", "-deep" :: allowDeepSubtypes ++ twice) // note the -deep argument
146+
140147

141148
@Test def dotc_ast = compileDir(dotcDir, "ast")
142149
@Test def dotc_config = compileDir(dotcDir, "config")
143150
@Test def dotc_core = compileDir(dotcDir, "core")("-Yno-double-bindings" :: allowDeepSubtypes)// twice omitted to make tests run faster
144151

145-
146-
@Test def dotc_core_pickling = compileDir(coreDir, "pickling")(allowDeepSubtypes)// twice omitted to make tests run faster
152+
// This directory doesn't exist anymore
153+
// @Test def dotc_core_pickling = compileDir(coreDir, "pickling")(allowDeepSubtypes)// twice omitted to make tests run faster
147154

148155
@Test def dotc_transform = compileDir(dotcDir, "transform")// twice omitted to make tests run faster
149156

test/dotty/partest/DPConsoleRunner.scala

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,59 @@
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. If additional jars are
18+
* required by some run tests, add them to partestDeps in the sbt Build.scala.
1519
*/
1620
object DPConsoleRunner {
1721
def main(args: Array[String]): Unit = {
18-
new DPConsoleRunner(args mkString (" ")).runPartest
22+
// unfortunately sbt runTask passes args as single string
23+
// extra jars for run tests are passed with -dottyJars <count> <jar1> <jar2> ...
24+
val jarFinder = """-dottyJars (\d*) (.*)""".r
25+
val (jarList, otherArgs) = args.toList.partition(jarFinder.findFirstIn(_).isDefined)
26+
val extraJars = jarList match {
27+
case Nil => sys.error("Error: DPConsoleRunner needs \"-dottyJars <jarCount> <jars>*\".")
28+
case jarFinder(nr, jarString) :: Nil =>
29+
val jars = jarString.split(" ").toList
30+
if (jars.length.toString != nr)
31+
sys.error("Error: DPConsoleRunner found wrong number of dottyJars: " + jars + ", expected: " + nr + ". Make sure the path doesn't contain any spaces.")
32+
else jars
33+
case list => sys.error("Error: DPConsoleRunner found several -dottyJars options: " + list)
34+
}
35+
new DPConsoleRunner(otherArgs mkString (" "), extraJars).runPartest
1936
}
2037
}
2138

2239
// console runner has a suite runner which creates a test runner for each test
23-
class DPConsoleRunner(args: String) extends ConsoleRunner(args) {
40+
class DPConsoleRunner(args: String, extraJars: List[String]) extends ConsoleRunner(args) {
2441
override val suiteRunner = new DPSuiteRunner (
2542
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
43+
fileManager = new DottyFileManager(extraJars),
2744
updateCheck = optUpdateCheck,
2845
failed = optFailed)
2946

3047
override def run = {}
3148
def runPartest = super.run
3249
}
3350

51+
class DottyFileManager(extraJars: List[String]) extends FileManager(Nil) {
52+
lazy val extraJarList = extraJars.map(NSCFile(_))
53+
override lazy val libraryUnderTest = Path(extraJars.find(_.contains("scala-library")).getOrElse(""))
54+
override lazy val reflectUnderTest = Path(extraJars.find(_.contains("scala-reflect")).getOrElse(""))
55+
override lazy val compilerUnderTest = Path(extraJars.find(_.contains("dotty")).getOrElse(""))
56+
}
57+
3458
class DPSuiteRunner(testSourcePath: String, // relative path, like "files", or "pending"
35-
fileManager: FileManager,
59+
fileManager: DottyFileManager,
3660
updateCheck: Boolean,
3761
failed: Boolean,
3862
javaCmdPath: String = PartestDefaults.javaCmd,
@@ -83,7 +107,7 @@ extends SuiteRunner(testSourcePath, fileManager, updateCheck, failed, javaCmdPat
83107
// but it doesn't seem to be used anywhere
84108
}
85109

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

@@ -117,7 +141,6 @@ class DPTestRunner(testFile: File, suiteRunner: SuiteRunner) extends nest.Runner
117141
case class CompSucceeded() extends NegTestState
118142

119143
def nerrIsOk(reason: String) = {
120-
import scala.util.matching.Regex
121144
val nerrFinder = """compilation failed with (\d+) errors""".r
122145
reason match {
123146
case nerrFinder(found) =>
@@ -187,4 +210,8 @@ class DPTestRunner(testFile: File, suiteRunner: SuiteRunner) extends nest.Runner
187210
def description = s"dotc $fsString"
188211
lazy val result = { pushTranscript(description) ; attemptCompile(fs) }
189212
}
213+
214+
// override to add dotty and scala jars to classpath
215+
override def extraClasspath = suiteRunner.fileManager.asInstanceOf[DottyFileManager].extraJarList ::: super.extraClasspath
216+
190217
}

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

0 commit comments

Comments
 (0)