Skip to content

Commit 41d6eb1

Browse files
committed
Add run testing capabilities
1 parent 4078f2c commit 41d6eb1

File tree

9 files changed

+142
-92
lines changed

9 files changed

+142
-92
lines changed

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,36 @@ import org.junit.Test
66
import java.io.{ File => JFile }
77

88
class CompilationTests extends ParallelTesting {
9-
import CompilationTests.{ defaultOutputDir, defaultOptions }
9+
import CompilationTests.{ defaultOutputDir, defaultOptions, picklingOptions }
10+
11+
// Positive tests ------------------------------------------------------------
1012

1113
@Test def compilePos =
1214
compileFilesInDir("../tests/pos", defaultOptions).pos
1315

16+
// Negative tests ------------------------------------------------------------
17+
1418
@Test def compileNeg =
1519
compileShallowFilesInDir("../tests/neg", defaultOptions).neg
20+
21+
// Run tests -----------------------------------------------------------------
22+
23+
@Test def runArraycopy =
24+
compileFile("../tests/run/arraycopy.scala", defaultOptions).run
25+
26+
@Test def runAll =
27+
compileFilesInDir("../tests/run", defaultOptions).run
28+
29+
// Pickling Tests ------------------------------------------------------------
30+
31+
@Test def testPickling =
32+
compileFilesInDir("../tests/pickling", picklingOptions).pos
33+
34+
@Test def testPicklingAst =
35+
compileFilesInDir("../compiler/src/dotty/tools/dotc/ast", picklingOptions).pos
36+
37+
@Test def testPicklingInf =
38+
compileFile("../tests/pos/pickleinf.scala", picklingOptions).pos
1639
}
1740

1841
object CompilationTests {
@@ -58,4 +81,13 @@ object CompilationTests {
5881
private val yCheckOptions = Array("-Ycheck:tailrec,resolveSuper,mixin,restoreScopes,labelDef")
5982

6083
val defaultOptions = noCheckOptions ++ checkOptions ++ yCheckOptions ++ classPath
84+
val allowDeepSubtypes = defaultOptions diff Array("-Yno-deep-subtypes")
85+
val allowDoubleBindings = defaultOptions diff Array("-Yno-double-bindings")
86+
87+
val picklingOptions = defaultOptions ++ Array(
88+
"-Xprint-types",
89+
"-Ytest-pickler",
90+
"-Ystop-after:pickler",
91+
"-Yprintpos"
92+
)
6193
}

compiler/test/dotty/tools/dotc/ParallelTesting.scala

Lines changed: 104 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import core.Contexts._
99
import reporting.{ Reporter, UniqueMessagePositions, HideNonSensicalMessages, MessageRendering }
1010
import reporting.diagnostic.MessageContainer
1111
import interfaces.Diagnostic.ERROR
12+
import java.lang.reflect.InvocationTargetException
1213
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
1314
import java.nio.file.{ Files, Path, Paths }
1415
import java.util.concurrent.{ Executors => JExecutors, TimeUnit }
@@ -31,6 +32,10 @@ trait ParallelTesting {
3132
_errors += newErrors
3233
}
3334

35+
private[this] var _failed = false
36+
final protected[this] def fail(): Unit = synchronized { _failed = true }
37+
def didFail: Boolean = _failed
38+
3439
private def statusRunner: Runnable = new Runnable {
3540
def run(): Unit = {
3641
val start = System.currentTimeMillis
@@ -85,6 +90,9 @@ trait ParallelTesting {
8590
}
8691
catch {
8792
case NonFatal(e) => {
93+
// if an exception is thrown during compilation, the complete test
94+
// run should fail
95+
fail()
8896
System.err.println(s"\n${e.getMessage}\n")
8997
completeCompilation(1)
9098
throw e
@@ -93,13 +101,77 @@ trait ParallelTesting {
93101
}
94102
}
95103

96-
private final class NegCompileRun(targetDirs: List[JFile], fromDir: String, flags: Array[String])
104+
private final class RunCompileRun(targetDirs: List[JFile], fromDir: String, flags: Array[String])
97105
extends CompileRun(targetDirs, fromDir, flags) {
98-
private[this] var _failed = false
99-
private[this] def fail(): Unit = _failed = true
106+
private def verifyOutput(checkFile: JFile, dir: JFile) = try {
107+
// Do classloading magic and running here:
108+
import java.net.{ URL, URLClassLoader }
109+
import java.io.ByteArrayOutputStream
110+
val ucl = new URLClassLoader(Array(dir.toURI.toURL))
111+
val cls = ucl.loadClass("Test")
112+
val meth = cls.getMethod("main", classOf[Array[String]])
113+
114+
val printStream = new ByteArrayOutputStream
115+
Console.withOut(printStream) {
116+
meth.invoke(null, Array("jvm")) // partest passes at least "jvm" as an arg
117+
}
100118

101-
def didFail: Boolean = _failed
119+
val outputLines = printStream.toString("utf-8").lines.toArray
120+
val checkLines = Source.fromFile(checkFile).getLines.toArray
121+
122+
def linesMatch =
123+
outputLines
124+
.zip(checkLines)
125+
.forall { case (x, y) => x == y }
126+
127+
if (outputLines.length != checkLines.length || !linesMatch) {
128+
System.err.println {
129+
s"\nOutput from run test '$dir' did not match expected, output:\n${outputLines.mkString("\n")}"
130+
}
131+
fail()
132+
}
133+
}
134+
catch {
135+
case _: NoSuchMethodException =>
136+
System.err.println(s"\ntest in '$dir' did not contain a main method")
137+
fail()
138+
139+
case _: ClassNotFoundException =>
140+
System.err.println(s"\ntest in '$dir' did was not contained within a `Test` object")
141+
fail()
142+
143+
case _: InvocationTargetException =>
144+
System.err.println(s"\nTest in '$dir' might be using args(X) where X > 0")
145+
fail()
146+
}
102147

148+
protected def compilationRunnable(dir: JFile): Runnable = new Runnable {
149+
def run(): Unit =
150+
try {
151+
val sourceFiles = dir.listFiles.filter(f => f.getName.endsWith(".scala") || f.getName.endsWith(".java"))
152+
val checkFile = dir.listFiles.find(_.getName.endsWith(".check"))
153+
val reporter = compile(sourceFiles, flags ++ Array("-d", dir.getAbsolutePath), false)
154+
completeCompilation(reporter.errorCount)
155+
156+
if (reporter.errorCount == 0 && checkFile.isDefined) verifyOutput(checkFile.get, dir)
157+
else if (reporter.errorCount > 0) {
158+
System.err.println(s"\nCompilation failed for: '$dir'")
159+
fail()
160+
}
161+
}
162+
catch {
163+
case NonFatal(e) => {
164+
System.err.println(s"\n$e\n${e.getMessage}")
165+
completeCompilation(1)
166+
fail()
167+
throw e
168+
}
169+
}
170+
}
171+
}
172+
173+
private final class NegCompileRun(targetDirs: List[JFile], fromDir: String, flags: Array[String])
174+
extends CompileRun(targetDirs, fromDir, flags) {
103175
protected def compilationRunnable(dir: JFile): Runnable = new Runnable {
104176
def run(): Unit =
105177
try {
@@ -188,10 +260,6 @@ trait ParallelTesting {
188260
}
189261
}
190262

191-
private val driver = new Driver {
192-
override def newCompiler(implicit ctx: Context) = new Compiler
193-
}
194-
195263
private class DaftReporter(suppress: Boolean)
196264
extends Reporter with UniqueMessagePositions with HideNonSensicalMessages
197265
with MessageRendering {
@@ -206,7 +274,13 @@ trait ParallelTesting {
206274
}
207275
}
208276

209-
private def compile(files: Array[JFile], flags: Array[String], suppressErrors: Boolean): DaftReporter = {
277+
private def compile(files0: Array[JFile], flags: Array[String], suppressErrors: Boolean): DaftReporter = {
278+
279+
def flattenFiles(f: JFile): Array[JFile] =
280+
if (f.isDirectory) f.listFiles.flatMap(flattenFiles)
281+
else Array(f)
282+
283+
val files: Array[JFile] = files0.flatMap(flattenFiles)
210284

211285
def findJarFromRuntime(partialName: String) = {
212286
val urls = ClassLoader.getSystemClassLoader.asInstanceOf[java.net.URLClassLoader].getURLs.map(_.getFile.toString)
@@ -231,11 +305,11 @@ trait ParallelTesting {
231305
compileWithJavac(files.filter(_.getName.endsWith(".java")).map(_.getAbsolutePath))
232306

233307
val reporter = new DaftReporter(suppress = suppressErrors)
308+
val driver = new Driver { def newCompiler(implicit ctx: Context) = new Compiler }
234309
driver.process(flags ++ files.map(_.getAbsolutePath), reporter = reporter)
235310
reporter
236311
}
237312

238-
239313
class CompilationTest(targetDirs: List[JFile], fromDir: String, flags: Array[String]) {
240314
def pos: Unit = {
241315
val run = new PosCompileRun(targetDirs, fromDir, flags).execute()
@@ -246,6 +320,11 @@ trait ParallelTesting {
246320
!(new NegCompileRun(targetDirs, fromDir, flags).execute().didFail),
247321
s"Wrong number of errors encountered when compiling $fromDir"
248322
)
323+
324+
def run: Unit = assert(
325+
!(new RunCompileRun(targetDirs, fromDir, flags).execute().didFail),
326+
s"Run tests failed for test $fromDir"
327+
)
249328
}
250329

251330
private def toCompilerDirFromDir(d: JFile, sourceDir: JFile, outDir: String): JFile = {
@@ -257,13 +336,19 @@ trait ParallelTesting {
257336
}
258337

259338
private def toCompilerDirFromFile(file: JFile, sourceDir: JFile, outDir: String): JFile = {
260-
val uniqueSubdir = file.getName.substring(0, file.getName.lastIndexOf('.'))
261-
val targetDir = new JFile(outDir + s"${sourceDir.getName}/$uniqueSubdir")
262-
// create if not exists
263-
targetDir.mkdirs()
264-
// copy file to dir:
265-
copyToDir(targetDir, file)
266-
targetDir
339+
val uniqueSubdir = file.getName.substring(0, file.getName.lastIndexOf('.'))
340+
val targetDir = new JFile(outDir + s"${sourceDir.getName}/$uniqueSubdir")
341+
if (file.getName.endsWith(".java") || file.getName.endsWith(".scala")) {
342+
// create if not exists
343+
targetDir.mkdirs()
344+
// copy file to dir:
345+
copyToDir(targetDir, file)
346+
347+
// copy checkfile if it exists
348+
val checkFile = new JFile(file.getAbsolutePath.substring(0, file.getAbsolutePath.lastIndexOf('.')) + ".check")
349+
if (checkFile.exists) copyToDir(targetDir, checkFile)
350+
}
351+
targetDir
267352
}
268353

269354
private def copyToDir(dir: JFile, file: JFile): Unit = {
@@ -279,10 +364,11 @@ trait ParallelTesting {
279364
private def compilationTargets(sourceDir: JFile): (List[JFile], List[JFile]) =
280365
sourceDir.listFiles.foldLeft((List.empty[JFile], List.empty[JFile])) { case ((dirs, files), f) =>
281366
if (f.isDirectory) (f :: dirs, files)
367+
else if (f.getName.endsWith(".check")) (dirs, files)
282368
else (dirs, f :: files)
283369
}
284370

285-
def compileFileInDir(f: String, flags: Array[String])(implicit outDirectory: String): CompilationTest = {
371+
def compileFile(f: String, flags: Array[String])(implicit outDirectory: String): CompilationTest = {
286372
// each calling method gets its own unique output directory, in which we
287373
// place the dir being compiled:
288374
val callingMethod = Thread.currentThread.getStackTrace.apply(3).getMethodName

tests/run/getclass.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@ class [D
2222
class [Lscala.collection.immutable.List;
2323

2424
Functions:
25-
class Test$$$Lambda$1
26-
class Test$$$Lambda$2
25+
class Test$$$Lambda$
26+
class Test$$$Lambda$

tests/run/getclass.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ object Test {
3535

3636
println("\nFunctions:")
3737
// FunctionN.getClass.toString has form of "class Test$$$Lambda$N/1349414238",
38-
// but number (1349414238) depends on environment
39-
println(f1.getClass.toString.takeWhile(_ != '/'))
40-
println(f2.getClass.toString.takeWhile(_ != '/'))
38+
// but "N/1349414238" depends on environment
39+
println(f1.getClass.toString.take("class Test$$$Lambda$".length))
40+
println(f2.getClass.toString.take("class Test$$$Lambda$".length))
4141
}
4242
}

tests/run/origins.check

Lines changed: 0 additions & 6 deletions
This file was deleted.

tests/run/origins.flags

Lines changed: 0 additions & 1 deletion
This file was deleted.

tests/run/origins.scala

Lines changed: 0 additions & 21 deletions
This file was deleted.

tests/run/shutdownhooks.check

Lines changed: 0 additions & 3 deletions
This file was deleted.

tests/run/shutdownhooks.scala

Lines changed: 0 additions & 37 deletions
This file was deleted.

0 commit comments

Comments
 (0)