aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2017-03-29 15:39:00 +0200
committerGitHub <noreply@github.com>2017-03-29 15:39:00 +0200
commit4757a6b470f022bd3178fba843f33638d24ee735 (patch)
tree2e39266f0426e47dcf21c004d1b2371aa1504e06
parent9ba9b147fb8d7894761e2f2fb18b08601981db20 (diff)
parent08d75b5b1bcdb7b1051831e7a2282f1fbb896267 (diff)
downloaddotty-4757a6b470f022bd3178fba843f33638d24ee735.tar.gz
dotty-4757a6b470f022bd3178fba843f33638d24ee735.tar.bz2
dotty-4757a6b470f022bd3178fba843f33638d24ee735.zip
Merge pull request #2125 from dotty-staging/topic/kill-partest
Operation Kill Partest (part 1)
-rw-r--r--.drone.yml10
-rw-r--r--.drone.yml.sig2
-rw-r--r--compiler/src/dotty/tools/dotc/Compiler.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala3
-rw-r--r--compiler/src/dotty/tools/dotc/reporting/Reporter.scala2
-rw-r--r--compiler/test/dotc/comptest.scala33
-rw-r--r--compiler/test/dotc/tests.scala14
-rw-r--r--compiler/test/dotty/partest/DPConsoleRunner.scala24
-rw-r--r--compiler/test/dotty/tools/StdLibSources.scala17
-rw-r--r--compiler/test/dotty/tools/dotc/CompilationTests.scala312
-rw-r--r--compiler/test/dotty/tools/dotc/CompilerTest.scala17
-rw-r--r--compiler/test/dotty/tools/dotc/ParallelSummaryReport.java67
-rw-r--r--compiler/test/dotty/tools/dotc/ParallelTestTests.scala55
-rw-r--r--compiler/test/dotty/tools/dotc/ParallelTesting.scala1062
-rw-r--r--compiler/test/dotty/tools/dotc/repl/TestREPL.scala23
-rw-r--r--compiler/test/dotty/tools/dotc/reporting/TestReporter.scala108
-rw-r--r--compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala14
-rw-r--r--doc-tool/test/DottyDocTest.scala40
-rw-r--r--doc-tool/test/WhitelistedStdLib.scala8
-rw-r--r--out/.keep0
-rw-r--r--project/Build.scala138
-rw-r--r--tests/neg/i941.scala2
-rw-r--r--tests/neg/instantiateAbstract.scala2
-rw-r--r--tests/neg/t1625.scala (renamed from tests/neg/varargsInMethodsT1625/allParamsAreVarArgs.scala)0
-rw-r--r--tests/neg/t1625b.scala3
-rw-r--r--tests/neg/t1625c.scala (renamed from tests/neg/varargsInMethodsT1625/classConstructorVarArgs.scala)0
-rw-r--r--tests/neg/t1625d.scala (renamed from tests/neg/varargsInMethodsT1625/curriedNegExample.scala)0
-rw-r--r--tests/neg/t1625e.scala (renamed from tests/neg/varargsInMethodsT1625/firstParamIsVarArgs.scala)0
-rw-r--r--tests/neg/varargsInMethodsT1625/caseClassConstructorVarArgs.scala3
-rw-r--r--tests/partest-test/negAnnotWrongLine.scala3
-rw-r--r--tests/partest-test/negMissingAnnot.scala1
-rw-r--r--tests/partest-test/negNoPositionAnnots.scala5
-rw-r--r--tests/partest-test/negTooManyAnnots.scala3
-rw-r--r--tests/partest-test/posFail1Error.scala3
-rw-r--r--tests/partest-test/runDiffOutput1.check5
-rw-r--r--tests/partest-test/runDiffOutput1.scala9
-rw-r--r--tests/partest-test/runWrongOutput1.check6
-rw-r--r--tests/partest-test/runWrongOutput1.scala9
-rw-r--r--tests/partest-test/runWrongOutput2.check3
-rw-r--r--tests/partest-test/runWrongOutput2.scala9
-rw-r--r--tests/partest-test/stackOverflow.scala7
-rw-r--r--tests/pos/i851.java2
-rw-r--r--tests/pos/t1625.scala (renamed from tests/pos/varargsInMethodsT1625/curriedPosExample.scala)0
-rw-r--r--tests/pos/t1625b.scala (renamed from tests/pos/varargsInMethodsT1625/onlyOneVarArgs.scala)0
-rw-r--r--tests/run/getclass.check4
-rw-r--r--tests/run/getclass.scala6
-rw-r--r--tests/run/origins.check6
-rw-r--r--tests/run/origins.flags1
-rw-r--r--tests/run/origins.scala21
-rw-r--r--tests/run/shutdownhooks.check3
-rw-r--r--tests/run/shutdownhooks.scala37
51 files changed, 1868 insertions, 236 deletions
diff --git a/.drone.yml b/.drone.yml
index 83309ef4e..75f7d89de 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -34,7 +34,9 @@ pipeline:
matrix:
TEST:
- - ;test;dotty-bin-tests/test
- - ;publishLocal;dotty-bootstrapped/test
- - partest-only-no-bootstrap --show-diff --verbose
- - partest-only --show-diff --verbose
+ - ;set testOptions in LocalProject("dotty-compiler") += Tests.Argument(TestFrameworks.JUnit, "--exclude-categories=dotty.tools.dotc.ParallelTesting") ;test ;dotty-bin-tests/test
+ - ;set testOptions in LocalProject("dotty-compiler-bootstrapped") += Tests.Argument(TestFrameworks.JUnit, "--exclude-categories=dotty.tools.dotc.ParallelTesting") ;publishLocal ;dotty-bootstrapped/test
+ - ;set testOptions in LocalProject("dotty-compiler") += Tests.Argument(TestFrameworks.JUnit, "--exclude-categories=dotty.tools.dotc.ParallelTesting") ;partest-only-no-bootstrap --show-diff --verbose
+ - ;set testOptions in LocalProject("dotty-compiler-bootstrapped") += Tests.Argument(TestFrameworks.JUnit, "--exclude-categories=dotty.tools.dotc.ParallelTesting") ;partest-only --show-diff --verbose
+ - ;dotty-compiler/testOnly dotty.tools.dotc.CompilationTests
+ - ;publishLocal ;dotty-bootstrapped/testOnly dotty.tools.dotc.CompilationTests
diff --git a/.drone.yml.sig b/.drone.yml.sig
index 1093928f0..7f5049f14 100644
--- a/.drone.yml.sig
+++ b/.drone.yml.sig
@@ -1 +1 @@
-eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgdGVzdDoKICAgIGltYWdlOiBsYW1wZXBmbC9kb3R0eTpsYXRlc3QKICAgIHB1bGw6IHRydWUKICAgIGNvbW1hbmRzOgogICAgICAtIGxuIC1zIC92YXIvY2FjaGUvZHJvbmUvc2NhbGEtc2NhbGEgc2NhbGEtc2NhbGEKICAgICAgLSBsbiAtcyAvdmFyL2NhY2hlL2Ryb25lL2l2eTIgIiRIT01FLy5pdnkyIgogICAgICAtIC4vc2NyaXB0cy91cGRhdGUtc2NhbGEtbGlicmFyeQogICAgICAtIHNidCAtSi1YbXg0MDk2bSAtSi1YWDpSZXNlcnZlZENvZGVDYWNoZVNpemU9NTEybSAtSi1YWDpNYXhNZXRhc3BhY2VTaXplPTEwMjRtIC1EZG90dHkuZHJvbmUubWVtPTQwOTZtICIke1RFU1R9IgogICAgd2hlbjoKICAgICAgYnJhbmNoOgogICAgICAgIGV4Y2x1ZGU6IGdoLXBhZ2VzCgogIGRvY3VtZW50YXRpb246CiAgICBpbWFnZTogbGFtcGVwZmwvZG90dHk6bGF0ZXN0CiAgICBwdWxsOiB0cnVlCiAgICBjb21tYW5kczoKICAgICAgLSAuL3Byb2plY3Qvc2NyaXB0cy9nZW5Eb2NzICIke1RFU1R9IiAkQk9UX1BBU1MKICAgIHdoZW46CiAgICAgIGJyYW5jaDogbWFzdGVyCgogIGdpdHRlcjoKICAgIGltYWdlOiBwbHVnaW5zL2dpdHRlcgogICAgd2hlbjoKICAgICAgYnJhbmNoOiBtYXN0ZXIKICAgICAgc3RhdHVzOiBjaGFuZ2VkCgogIHNsYWNrOgogICAgaW1hZ2U6IHBsdWdpbnMvc2xhY2sKICAgIGNoYW5uZWw6IGRvdHR5CiAgICB3aGVuOgogICAgICBicmFuY2g6IG1hc3RlcgogICAgICBzdGF0dXM6IGNoYW5nZWQKCm1hdHJpeDoKICBURVNUOgogICAgLSA7dGVzdDtkb3R0eS1iaW4tdGVzdHMvdGVzdAogICAgLSA7cHVibGlzaExvY2FsO2RvdHR5LWJvb3RzdHJhcHBlZC90ZXN0CiAgICAtIHBhcnRlc3Qtb25seS1uby1ib290c3RyYXAgLS1zaG93LWRpZmYgLS12ZXJib3NlCiAgICAtIHBhcnRlc3Qtb25seSAtLXNob3ctZGlmZiAtLXZlcmJvc2UK.VRqZiSgeE6OumPlEvs4TWfxIHNOEVjR_ZmyBmapxZ-U \ No newline at end of file
+eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgdGVzdDoKICAgIGltYWdlOiBsYW1wZXBmbC9kb3R0eTpsYXRlc3QKICAgIHB1bGw6IHRydWUKICAgIGNvbW1hbmRzOgogICAgICAtIGxuIC1zIC92YXIvY2FjaGUvZHJvbmUvc2NhbGEtc2NhbGEgc2NhbGEtc2NhbGEKICAgICAgLSBsbiAtcyAvdmFyL2NhY2hlL2Ryb25lL2l2eTIgIiRIT01FLy5pdnkyIgogICAgICAtIC4vc2NyaXB0cy91cGRhdGUtc2NhbGEtbGlicmFyeQogICAgICAtIHNidCAtSi1YbXg0MDk2bSAtSi1YWDpSZXNlcnZlZENvZGVDYWNoZVNpemU9NTEybSAtSi1YWDpNYXhNZXRhc3BhY2VTaXplPTEwMjRtIC1EZG90dHkuZHJvbmUubWVtPTQwOTZtICIke1RFU1R9IgogICAgd2hlbjoKICAgICAgYnJhbmNoOgogICAgICAgIGV4Y2x1ZGU6IGdoLXBhZ2VzCgogIGRvY3VtZW50YXRpb246CiAgICBpbWFnZTogbGFtcGVwZmwvZG90dHk6bGF0ZXN0CiAgICBwdWxsOiB0cnVlCiAgICBjb21tYW5kczoKICAgICAgLSAuL3Byb2plY3Qvc2NyaXB0cy9nZW5Eb2NzICIke1RFU1R9IiAkQk9UX1BBU1MKICAgIHdoZW46CiAgICAgIGJyYW5jaDogbWFzdGVyCgogIGdpdHRlcjoKICAgIGltYWdlOiBwbHVnaW5zL2dpdHRlcgogICAgd2hlbjoKICAgICAgYnJhbmNoOiBtYXN0ZXIKICAgICAgc3RhdHVzOiBjaGFuZ2VkCgogIHNsYWNrOgogICAgaW1hZ2U6IHBsdWdpbnMvc2xhY2sKICAgIGNoYW5uZWw6IGRvdHR5CiAgICB3aGVuOgogICAgICBicmFuY2g6IG1hc3RlcgogICAgICBzdGF0dXM6IGNoYW5nZWQKCm1hdHJpeDoKICBURVNUOgogICAgLSA7c2V0IHRlc3RPcHRpb25zIGluIExvY2FsUHJvamVjdCgiZG90dHktY29tcGlsZXIiKSArPSBUZXN0cy5Bcmd1bWVudChUZXN0RnJhbWV3b3Jrcy5KVW5pdCwgIi0tZXhjbHVkZS1jYXRlZ29yaWVzPWRvdHR5LnRvb2xzLmRvdGMuUGFyYWxsZWxUZXN0aW5nIikgO3Rlc3QgO2RvdHR5LWJpbi10ZXN0cy90ZXN0CiAgICAtIDtzZXQgdGVzdE9wdGlvbnMgaW4gTG9jYWxQcm9qZWN0KCJkb3R0eS1jb21waWxlci1ib290c3RyYXBwZWQiKSArPSBUZXN0cy5Bcmd1bWVudChUZXN0RnJhbWV3b3Jrcy5KVW5pdCwgIi0tZXhjbHVkZS1jYXRlZ29yaWVzPWRvdHR5LnRvb2xzLmRvdGMuUGFyYWxsZWxUZXN0aW5nIikgO3B1Ymxpc2hMb2NhbCA7ZG90dHktYm9vdHN0cmFwcGVkL3Rlc3QKICAgIC0gO3NldCB0ZXN0T3B0aW9ucyBpbiBMb2NhbFByb2plY3QoImRvdHR5LWNvbXBpbGVyIikgKz0gVGVzdHMuQXJndW1lbnQoVGVzdEZyYW1ld29ya3MuSlVuaXQsICItLWV4Y2x1ZGUtY2F0ZWdvcmllcz1kb3R0eS50b29scy5kb3RjLlBhcmFsbGVsVGVzdGluZyIpIDtwYXJ0ZXN0LW9ubHktbm8tYm9vdHN0cmFwIC0tc2hvdy1kaWZmIC0tdmVyYm9zZQogICAgLSA7c2V0IHRlc3RPcHRpb25zIGluIExvY2FsUHJvamVjdCgiZG90dHktY29tcGlsZXItYm9vdHN0cmFwcGVkIikgKz0gVGVzdHMuQXJndW1lbnQoVGVzdEZyYW1ld29ya3MuSlVuaXQsICItLWV4Y2x1ZGUtY2F0ZWdvcmllcz1kb3R0eS50b29scy5kb3RjLlBhcmFsbGVsVGVzdGluZyIpIDtwYXJ0ZXN0LW9ubHkgLS1zaG93LWRpZmYgLS12ZXJib3NlCiAgICAtIDtkb3R0eS1jb21waWxlci90ZXN0T25seSBkb3R0eS50b29scy5kb3RjLkNvbXBpbGF0aW9uVGVzdHMKICAgIC0gO3B1Ymxpc2hMb2NhbCA7ZG90dHktYm9vdHN0cmFwcGVkL3Rlc3RPbmx5IGRvdHR5LnRvb2xzLmRvdGMuQ29tcGlsYXRpb25UZXN0cwo.b9x4iSh27OqWMUT8eR6uiK0OH_eERpnKIaMglF8hKYA \ No newline at end of file
diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala
index efde897cd..0d3fb5821 100644
--- a/compiler/src/dotty/tools/dotc/Compiler.scala
+++ b/compiler/src/dotty/tools/dotc/Compiler.scala
@@ -129,7 +129,7 @@ class Compiler {
.setMode(Mode.ImplicitsEnabled)
.setTyperState(new MutableTyperState(ctx.typerState, ctx.typerState.reporter, isCommittable = true))
.setFreshNames(new FreshNameCreator.Default)
- ctx.initialize()(start) // re-initialize the base context with start
+ ctx.initialize()(start) // re-initialize the base context with start
def addImport(ctx: Context, refFn: () => TermRef) =
ctx.fresh.setImportInfo(ImportInfo.rootImport(refFn)(ctx))
(start.setRunInfo(new RunInfo(start)) /: defn.RootImportFns)(addImport)
diff --git a/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala b/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala
index 95f468995..9942b9ab9 100644
--- a/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala
+++ b/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala
@@ -17,9 +17,6 @@ class ConsoleReporter(
import MessageContainer._
- /** maximal number of error messages to be printed */
- protected def ErrorLimit = 100
-
/** Prints the message. */
def printMessage(msg: String): Unit = { writer.print(msg + "\n"); writer.flush() }
diff --git a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala
index b2c7abec9..77ea1884d 100644
--- a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala
+++ b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala
@@ -63,7 +63,7 @@ trait Reporting { this: Context =>
|This can be achieved by adding the import clause 'import $fqname'
|or by setting the compiler option -language:$feature.
|See the Scala docs for value $fqname for a discussion
- |why the feature $req be explicitly enabled."""
+ |why the feature $req be explicitly enabled.""".stripMargin
}
}
diff --git a/compiler/test/dotc/comptest.scala b/compiler/test/dotc/comptest.scala
index 5ae1823e8..dce002c81 100644
--- a/compiler/test/dotc/comptest.scala
+++ b/compiler/test/dotc/comptest.scala
@@ -1,24 +1,29 @@
package dotc
-import dotty.tools.dotc.CompilerTest
+import dotty.tools.dotc.ParallelTesting
-object comptest extends CompilerTest {
+object comptest extends ParallelTesting {
- override val generatePartestFiles = false
- val defaultOutputDir: String = ""
+ def isInteractive = true
+ def testFilter = None
+
+ implicit val defaultOutputDir: String = "."
val posDir = "./tests/pos/"
val negDir = "./tests/neg/"
val dotcDir = "./src/dotty/"
- def main(args: Array[String]) =
- compileList("comptest", List(
- dotcDir + "tools/dotc/CompilationUnit.scala",
- dotcDir + "tools/dotc/core/Types.scala",
- dotcDir + "tools/dotc/ast/Trees.scala"), List(
- "#runs", "2",
- "-Ylog:frontend",
- "-Xprompt"))(Nil)
-
-// compileDir(dotcDir + "tools/dotc/", "printing", List("-Xprompt", "-Ylog:frontend", "#runs", "2", "-uniqid"))
+ def main(args: Array[String]): Unit =
+ compileList(
+ "comptest",
+ List(
+ dotcDir + "tools/dotc/CompilationUnit.scala",
+ dotcDir + "tools/dotc/core/Types.scala",
+ dotcDir + "tools/dotc/ast/Trees.scala"
+ ),
+ Array(
+ "-Ylog:frontend",
+ "-Xprompt"
+ )
+ )
}
diff --git a/compiler/test/dotc/tests.scala b/compiler/test/dotc/tests.scala
index 3ebf7f2c4..1c80767ee 100644
--- a/compiler/test/dotc/tests.scala
+++ b/compiler/test/dotc/tests.scala
@@ -85,7 +85,6 @@ class tests extends CompilerTest {
val negDir = testsDir + "neg/"
val runDir = testsDir + "run/"
val newDir = testsDir + "new/"
- val replDir = testsDir + "repl/"
val javaDir = testsDir + "pos-java-interop/"
val sourceDir = "./src/"
@@ -173,7 +172,6 @@ class tests extends CompilerTest {
@Test def pos_utf16 = compileFile(posSpecialDir, "utf16encoded", explicitUTF16)
@Test def new_all = compileFiles(newDir, twice)
- @Test def repl_all = replFiles(replDir)
@Test def neg_all = compileFiles(negDir, verbose = true, compileSubDirs = false)
@Test def neg_typedIdents() = compileDir(negDir, "typedIdents")
@@ -205,18 +203,6 @@ class tests extends CompilerTest {
private val stdlibFiles: List[String] = StdLibSources.whitelisted
- @Test def checkWBLists = {
- val stdlibFilesBlackListed = StdLibSources.blacklisted
-
- val duplicates = stdlibFilesBlackListed.groupBy(x => x).filter(_._2.size > 1).filter(_._2.size > 1)
- val msg = duplicates.map(x => s"'${x._1}' appears ${x._2.size} times").mkString(s"Duplicate entries in ${StdLibSources.blacklistFile}:\n", "\n", "\n")
- assertTrue(msg, duplicates.isEmpty)
-
- val filesNotInStdLib = stdlibFilesBlackListed.toSet -- StdLibSources.all
- val msg2 = filesNotInStdLib.map(x => s"'$x'").mkString(s"Entries in ${StdLibSources.blacklistFile} where not found:\n", "\n", "\n")
- assertTrue(msg2, filesNotInStdLib.isEmpty)
- }
-
@Test def compileStdLib = compileList("compileStdLib", stdlibFiles, "-migration" :: "-Yno-inline" :: scala2mode)
@Test def compileMixed = compileLine(
"""../tests/pos/B.scala
diff --git a/compiler/test/dotty/partest/DPConsoleRunner.scala b/compiler/test/dotty/partest/DPConsoleRunner.scala
index aa926efe2..3362d7a59 100644
--- a/compiler/test/dotty/partest/DPConsoleRunner.scala
+++ b/compiler/test/dotty/partest/DPConsoleRunner.scala
@@ -90,18 +90,18 @@ extends SuiteRunner(testSourcePath, fileManager, updateCheck, failed, javaCmdPat
}
/** Some tests require a limitation of resources, tests which are compiled
- * with one or more of the flags in this list will be run with
- * `limitedThreads`. This is necessary because some test flags require a lot
- * of memory when running the compiler and may exhaust the available memory
- * when run in parallel with too many other tests.
- *
- * This number could be increased on the CI, but might fail locally if
- * scaled too extreme - override with:
- *
- * ```
- * -Ddotty.tests.limitedThreads=X
- * ```
- */
+ * with one or more of the flags in this list will be run with
+ * `limitedThreads`. This is necessary because some test flags require a lot
+ * of memory when running the compiler and may exhaust the available memory
+ * when run in parallel with too many other tests.
+ *
+ * This number could be increased on the CI, but might fail locally if
+ * scaled too extreme - override with:
+ *
+ * ```
+ * -Ddotty.tests.limitedThreads=X
+ * ```
+ */
def limitResourceFlags = List("-Ytest-pickler")
private val limitedThreads = sys.props.get("dotty.tests.limitedThreads").getOrElse("2")
diff --git a/compiler/test/dotty/tools/StdLibSources.scala b/compiler/test/dotty/tools/StdLibSources.scala
index e3da36f22..0c1121058 100644
--- a/compiler/test/dotty/tools/StdLibSources.scala
+++ b/compiler/test/dotty/tools/StdLibSources.scala
@@ -1,8 +1,9 @@
package dotty.tools
import java.io.File
-
import scala.io.Source
+import org.junit.Test
+import org.junit.Assert._
object StdLibSources {
@@ -57,3 +58,17 @@ object StdLibSources {
.toList
}
+
+class StdLibSources {
+ @Test def checkWBLists = {
+ val stdlibFilesBlackListed = StdLibSources.blacklisted
+
+ val duplicates = stdlibFilesBlackListed.groupBy(x => x).filter(_._2.size > 1).filter(_._2.size > 1)
+ val msg = duplicates.map(x => s"'${x._1}' appears ${x._2.size} times").mkString(s"Duplicate entries in ${StdLibSources.blacklistFile}:\n", "\n", "\n")
+ assertTrue(msg, duplicates.isEmpty)
+
+ val filesNotInStdLib = stdlibFilesBlackListed.toSet -- StdLibSources.all
+ val msg2 = filesNotInStdLib.map(x => s"'$x'").mkString(s"Entries in ${StdLibSources.blacklistFile} where not found:\n", "\n", "\n")
+ assertTrue(msg2, filesNotInStdLib.isEmpty)
+ }
+}
diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala
new file mode 100644
index 000000000..788e30aa3
--- /dev/null
+++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala
@@ -0,0 +1,312 @@
+package dotty
+package tools
+package dotc
+
+import org.junit.Test
+import java.io.{ File => JFile }
+import org.junit.experimental.categories.Category
+
+import scala.util.matching.Regex
+
+@Category(Array(classOf[ParallelTesting]))
+class CompilationTests extends ParallelSummaryReport with ParallelTesting {
+ import CompilationTests._
+
+ def isInteractive: Boolean = !sys.env.contains("DRONE")
+
+ def testFilter: Option[Regex] = sys.props.get("dotty.partest.filter").map(r => new Regex(r))
+
+ // Positive tests ------------------------------------------------------------
+
+ @Test def compilePos: Unit = {
+ compileList("compileStdLib", StdLibSources.whitelisted, scala2Mode.and("-migration", "-Yno-inline")) +
+ compileFilesInDir("../tests/pos", defaultOptions)
+ }.checkCompile()
+
+ @Test def compilePosScala2: Unit =
+ compileFilesInDir("../tests/pos-scala2", scala2Mode).checkCompile()
+
+ @Test def compilePosMixedFlags: Unit = {
+ compileFile("../tests/pos/nullarify.scala", defaultOptions.and("-Ycheck:nullarify")) +
+ compileFile("../tests/pos-scala2/rewrites.scala", scala2Mode.and("-rewrite")).copyToTarget() +
+ compileFile("../tests/pos-special/t8146a.scala", allowDeepSubtypes) +
+ compileFile("../tests/pos-special/utf8encoded.scala", explicitUTF8) +
+ compileFile("../tests/pos-special/utf16encoded.scala", explicitUTF16) +
+ compileList(
+ "compileMixed",
+ List(
+ "../tests/pos/B.scala",
+ "../scala-scala/src/library/scala/collection/immutable/Seq.scala",
+ "../scala-scala/src/library/scala/collection/parallel/ParSeq.scala",
+ "../scala-scala/src/library/scala/package.scala",
+ "../scala-scala/src/library/scala/collection/GenSeqLike.scala",
+ "../scala-scala/src/library/scala/collection/SeqLike.scala",
+ "../scala-scala/src/library/scala/collection/generic/GenSeqFactory.scala"
+ ),
+ defaultOptions
+ ) +
+ compileFilesInDir("../tests/pos-special/spec-t5545", defaultOptions) +
+ compileFile("../scala-scala/src/library/scala/collection/immutable/IndexedSeq.scala", defaultOptions) +
+ compileFile("../scala-scala/src/library/scala/collection/parallel/mutable/ParSetLike.scala", defaultOptions) +
+ compileList(
+ "parSetSubset",
+ List(
+ "../scala-scala/src/library/scala/collection/parallel/mutable/ParSetLike.scala",
+ "../scala-scala/src/library/scala/collection/parallel/mutable/ParSet.scala",
+ "../scala-scala/src/library/scala/collection/mutable/SetLike.scala"
+ ),
+ scala2Mode
+ )
+ }.checkCompile()
+
+ @Test def compileCoreNoCheck: Unit =
+ compileDir("../compiler/src/dotty/tools/dotc/core", noCheckOptions ++ classPath).checkCompile()
+
+ @Test def compileDotcInternals: Unit = {
+ compileDir("../compiler/src/dotty/tools/dotc/ast", defaultOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/config", defaultOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/core", allowDeepSubtypes) +
+ compileDir("../compiler/src/dotty/tools/dotc/transform", allowDeepSubtypes) +
+ compileDir("../compiler/src/dotty/tools/dotc/parsing", defaultOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/printing", defaultOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/reporting", defaultOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/typer", defaultOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/util", defaultOptions) +
+ compileDir("../compiler/src/dotty/tools/io", defaultOptions)
+ }.checkCompile()
+
+ @Test def posTwice: Unit = {
+ compileFile("../tests/pos/Labels.scala", defaultOptions) +
+ compileFilesInDir("../tests/pos-java-interop", defaultOptions) +
+ compileFile("../tests/pos/t2168.scala", defaultOptions) +
+ compileFile("../tests/pos/erasure.scala", defaultOptions) +
+ compileFile("../tests/pos/Coder.scala", defaultOptions) +
+ compileFile("../tests/pos/blockescapes.scala", defaultOptions) +
+ compileFile("../tests/pos/collections.scala", defaultOptions) +
+ compileFile("../tests/pos/functions1.scala", defaultOptions) +
+ compileFile("../tests/pos/implicits1.scala", defaultOptions) +
+ compileFile("../tests/pos/inferred.scala", defaultOptions) +
+ compileFile("../tests/pos/Patterns.scala", defaultOptions) +
+ compileFile("../tests/pos/selftypes.scala", defaultOptions) +
+ compileFile("../tests/pos/varargs.scala", defaultOptions) +
+ compileFile("../tests/pos/vararg-pattern.scala", defaultOptions) +
+ compileFile("../tests/pos/opassign.scala", defaultOptions) +
+ compileFile("../tests/pos/typedapply.scala", defaultOptions) +
+ compileFile("../tests/pos/nameddefaults.scala", defaultOptions) +
+ compileFile("../tests/pos/desugar.scala", defaultOptions) +
+ compileFile("../tests/pos/sigs.scala", defaultOptions) +
+ compileFile("../tests/pos/typers.scala", defaultOptions) +
+ compileDir("../tests/pos/typedIdents", defaultOptions) +
+ compileFile("../tests/pos/assignments.scala", defaultOptions) +
+ compileFile("../tests/pos/packageobject.scala", defaultOptions) +
+ compileFile("../tests/pos/overloaded.scala", defaultOptions) +
+ compileFile("../tests/pos/overrides.scala", defaultOptions) +
+ compileDir("../tests/pos/java-override", defaultOptions) +
+ compileFile("../tests/pos/templateParents.scala", defaultOptions) +
+ compileFile("../tests/pos/overloadedAccess.scala", defaultOptions) +
+ compileFile("../tests/pos/approximateUnion.scala", defaultOptions) +
+ compileFilesInDir("../tests/pos/tailcall", defaultOptions) +
+ compileShallowFilesInDir("../tests/pos/pos_valueclasses", defaultOptions) +
+ compileFile("../tests/pos/subtyping.scala", defaultOptions) +
+ compileFile("../tests/pos/i0239.scala", defaultOptions) +
+ compileFile("../tests/pos/anonClassSubtyping.scala", defaultOptions) +
+ compileFile("../tests/pos/extmethods.scala", defaultOptions) +
+ compileFile("../tests/pos/companions.scala", defaultOptions) +
+ compileList(
+ "testNonCyclic",
+ List(
+ "../compiler/src/dotty/tools/dotc/CompilationUnit.scala",
+ "../compiler/src/dotty/tools/dotc/core/Types.scala",
+ "../compiler/src/dotty/tools/dotc/ast/Trees.scala"
+ ),
+ defaultOptions.and("-Xprompt")
+ ) +
+ compileList(
+ "testIssue34",
+ List(
+ "../compiler/src/dotty/tools/dotc/config/Properties.scala",
+ "../compiler/src/dotty/tools/dotc/config/PathResolver.scala"
+ ),
+ defaultOptions.and("-Xprompt")
+ )
+ }.times(2).checkCompile()
+
+ // New tests -----------------------------------------------------------------
+
+ @Test def compileNew: Unit =
+ compileFilesInDir("../tests/new", defaultOptions).checkCompile()
+
+ // Negative tests ------------------------------------------------------------
+
+ @Test def compileNeg: Unit =
+ compileShallowFilesInDir("../tests/neg", defaultOptions).checkExpectedErrors()
+
+ @Test def compileNegCustomFlags: Unit = {
+ compileFile("../tests/neg/customArgs/typers.scala", allowDoubleBindings) +
+ compileFile("../tests/neg/customArgs/overrideClass.scala", scala2Mode) +
+ compileFile("../tests/neg/customArgs/autoTuplingTest.scala", defaultOptions.and("-language:noAutoTupling")) +
+ compileFile("../tests/neg/customArgs/i1050.scala", defaultOptions.and("-strict")) +
+ compileFile("../tests/neg/customArgs/i1240.scala", allowDoubleBindings) +
+ compileFile("../tests/neg/customArgs/i2002.scala", allowDoubleBindings) +
+ compileFile("../tests/neg/customArgs/nopredef.scala", defaultOptions.and("-Yno-predef")) +
+ compileFile("../tests/neg/customArgs/noimports.scala", defaultOptions.and("-Yno-imports")) +
+ compileFile("../tests/neg/customArgs/noimports2.scala", defaultOptions.and("-Yno-imports")) +
+ compileFile("../tests/neg/tailcall/t1672b.scala", defaultOptions) +
+ compileFile("../tests/neg/tailcall/t3275.scala", defaultOptions) +
+ compileFile("../tests/neg/tailcall/t6574.scala", defaultOptions) +
+ compileFile("../tests/neg/tailcall/tailrec.scala", defaultOptions) +
+ compileFile("../tests/neg/tailcall/tailrec-2.scala", defaultOptions) +
+ compileFile("../tests/neg/tailcall/tailrec-3.scala", defaultOptions) +
+ compileDir("../tests/neg/typedIdents", defaultOptions)
+ }.checkExpectedErrors()
+
+ // Run tests -----------------------------------------------------------------
+
+ @Test def runAll: Unit =
+ compileFilesInDir("../tests/run", defaultOptions).checkRuns()
+
+ // Pickling Tests ------------------------------------------------------------
+ //
+ // Pickling tests are very memory intensive and as such need to be run with a
+ // lower level of concurrency as to not kill their running VMs
+
+ @Test def testPickling1: Unit = {
+ compileFilesInDir("../tests/new", picklingOptions) +
+ compileFilesInDir("../tests/pickling", picklingOptions) +
+ compileDir("../library/src/dotty/runtime", picklingOptions) +
+ compileDir("../compiler/src/dotty/tools/backend/jvm", picklingOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/ast", picklingOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/core", picklingOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/config", picklingOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/parsing", picklingOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/printing", picklingOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/repl", picklingOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/rewrite", picklingOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/transform", picklingOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/typer", picklingOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/util", picklingOptions) +
+ compileDir("../compiler/src/dotty/tools/io", picklingOptions) +
+ compileFile("../tests/pos/pickleinf.scala", picklingOptions)
+ }.limitThreads(4).checkCompile()
+
+ @Test def testPickling2: Unit = {
+ compileDir("../compiler/src/dotty/tools/dotc/core/classfile", picklingOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/core/tasty", picklingOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/core/unpickleScala2", picklingOptions)
+ }.limitThreads(4).checkCompile()
+
+ @Test def testPickling3: Unit = {
+ compileDir("../compiler/src/dotty/tools", picklingOptions)
+ }.limitThreads(4).checkCompile()
+
+ @Test def testPickling4: Unit = {
+ compileDir("../compiler/src/dotty/tools/dotc", picklingOptions)
+ }.limitThreads(4).checkCompile()
+
+ /** The purpose of this test is two-fold, being able to compile dotty
+ * bootstrapped, and making sure that TASTY can link against a compiled
+ * version of Dotty
+ */
+ @Test def tastyBootstrap: Unit = {
+ val opt = Array(
+ "-classpath",
+ // compile with bootstrapped library on cp:
+ defaultOutputDir + "lib$1/src/:" +
+ // as well as bootstrapped compiler:
+ defaultOutputDir + "dotty1$1/dotty/:" +
+ Jars.dottyInterfaces
+ )
+
+ def lib =
+ compileDir("../library/src",
+ allowDeepSubtypes.and("-Ycheck-reentrant", "-strict", "-priorityclasspath", defaultOutputDir))
+
+ def dotty1 =
+ compileDir("../compiler/src/dotty", opt)
+
+ def dotty2 =
+ compileShallowFilesInDir("../compiler/src/dotty", opt)
+
+ {
+ lib.keepOutput :: dotty1.keepOutput :: {
+ dotty2 +
+ compileShallowFilesInDir("../compiler/src/dotty/tools", opt) +
+ compileShallowFilesInDir("../compiler/src/dotty/tools/dotc", opt) +
+ compileShallowFilesInDir("../compiler/src/dotty/tools/dotc/ast", opt) +
+ compileShallowFilesInDir("../compiler/src/dotty/tools/dotc/config", opt) +
+ compileShallowFilesInDir("../compiler/src/dotty/tools/dotc/parsing", opt) +
+ compileShallowFilesInDir("../compiler/src/dotty/tools/dotc/printing", opt) +
+ compileShallowFilesInDir("../compiler/src/dotty/tools/dotc/repl", opt) +
+ compileShallowFilesInDir("../compiler/src/dotty/tools/dotc/reporting", opt) +
+ compileShallowFilesInDir("../compiler/src/dotty/tools/dotc/rewrite", opt) +
+ compileShallowFilesInDir("../compiler/src/dotty/tools/dotc/transform", opt) +
+ compileShallowFilesInDir("../compiler/src/dotty/tools/dotc/typer", opt) +
+ compileShallowFilesInDir("../compiler/src/dotty/tools/dotc/util", opt)
+ } :: Nil
+ }.map(_.checkCompile()).foreach(_.delete())
+ }
+}
+
+object CompilationTests {
+ implicit val defaultOutputDir: String = "../out/"
+
+ implicit class RichStringArray(val xs: Array[String]) extends AnyVal {
+ def and(args: String*): Array[String] = {
+ val argsArr: Array[String] = args.toArray
+ xs ++ argsArr
+ }
+ }
+
+ val noCheckOptions = Array(
+ "-pagewidth", "120",
+ "-color:never"
+ )
+
+ val checkOptions = Array(
+ "-Yno-deep-subtypes",
+ "-Yno-double-bindings",
+ "-Yforce-sbt-phases"
+ )
+
+ val classPath = {
+ val paths = Jars.dottyTestDeps map { p =>
+ val file = new JFile(p)
+ assert(
+ file.exists,
+ s"""|File "$p" couldn't be found. Run `packageAll` from build tool before
+ |testing.
+ |
+ |If running without sbt, test paths need to be setup environment variables:
+ |
+ | - DOTTY_LIBRARY
+ | - DOTTY_COMPILER
+ | - DOTTY_INTERFACES
+ | - DOTTY_EXTRAS
+ |
+ |Where these all contain locations, except extras which is a colon
+ |separated list of jars.
+ |
+ |When compiling with eclipse, you need the sbt-interfaces jar, put
+ |it in extras."""
+ )
+ file.getAbsolutePath
+ } mkString (":")
+
+ Array("-classpath", paths)
+ }
+
+ private val yCheckOptions = Array("-Ycheck:tailrec,resolveSuper,mixin,restoreScopes,labelDef")
+
+ val defaultOptions = noCheckOptions ++ checkOptions ++ yCheckOptions ++ classPath
+ val allowDeepSubtypes = defaultOptions diff Array("-Yno-deep-subtypes")
+ val allowDoubleBindings = defaultOptions diff Array("-Yno-double-bindings")
+ val picklingOptions = defaultOptions ++ Array(
+ "-Xprint-types",
+ "-Ytest-pickler",
+ "-Ystop-after:pickler",
+ "-Yprintpos"
+ )
+ val scala2Mode = defaultOptions ++ Array("-language:Scala2")
+ val explicitUTF8 = defaultOptions ++ Array("-encoding", "UTF8")
+ val explicitUTF16 = defaultOptions ++ Array("-encoding", "UTF16")
+}
diff --git a/compiler/test/dotty/tools/dotc/CompilerTest.scala b/compiler/test/dotty/tools/dotc/CompilerTest.scala
index db12994f4..f35f9f919 100644
--- a/compiler/test/dotty/tools/dotc/CompilerTest.scala
+++ b/compiler/test/dotty/tools/dotc/CompilerTest.scala
@@ -237,23 +237,6 @@ abstract class CompilerTest {
}
}
- def replFile(prefix: String, fileName: String): Unit = {
- val path = s"$prefix$fileName"
- val f = new PlainFile(path)
- val repl = new TestREPL(new String(f.toCharArray))
- repl.process(Array[String]())
- repl.check()
- }
-
- def replFiles(path: String): Unit = {
- val dir = Directory(path)
- val fileNames = dir.files.toArray.map(_.jfile.getName).filter(_ endsWith ".check")
- for (name <- fileNames) {
- log(s"testing $path$name")
- replFile(path, name)
- }
- }
-
// ========== HELPERS =============
private def expectedErrors(filePaths: List[String]): List[ErrorsInFile] = if (filePaths.exists(isNegTest(_))) filePaths.map(getErrors(_)) else Nil
diff --git a/compiler/test/dotty/tools/dotc/ParallelSummaryReport.java b/compiler/test/dotty/tools/dotc/ParallelSummaryReport.java
new file mode 100644
index 000000000..9214e7d25
--- /dev/null
+++ b/compiler/test/dotty/tools/dotc/ParallelSummaryReport.java
@@ -0,0 +1,67 @@
+package dotty.tools.dotc;
+
+import org.junit.BeforeClass;
+import org.junit.AfterClass;
+import java.util.ArrayDeque;
+
+import dotty.tools.dotc.reporting.TestReporter;
+import dotty.tools.dotc.reporting.TestReporter$;
+
+/** Note that while `ParallelTesting` runs in parallel, JUnit tests cannot with
+ * this class
+ */
+public class ParallelSummaryReport {
+ private static TestReporter rep = TestReporter.reporter(-1);
+ private static ArrayDeque<String> failedTests = new ArrayDeque<>();
+ private static ArrayDeque<String> reproduceInstructions = new ArrayDeque<>();
+ private static int passed;
+ private static int failed;
+
+ public final static void reportFailed() {
+ failed++;
+ }
+
+ public final static void reportPassed() {
+ passed++;
+ }
+
+ public final static void addFailedTest(String msg) {
+ failedTests.offer(msg);
+ }
+
+ public final static void addReproduceInstruction(String msg) {
+ reproduceInstructions.offer(msg);
+ }
+
+ @BeforeClass public final static void setup() {
+ rep = TestReporter.reporter(-1);
+ failedTests = new ArrayDeque<>();
+ reproduceInstructions = new ArrayDeque<>();
+ }
+
+ @AfterClass public final static void teardown() {
+ rep.echo(
+ "\n================================================================================" +
+ "\nTest Report" +
+ "\n================================================================================" +
+ "\n" +
+ passed + " passed, " + failed + " failed, " + (passed + failed) + " total" +
+ "\n"
+ );
+
+ failedTests
+ .stream()
+ .map(x -> " " + x)
+ .forEach(rep::echo);
+
+ rep.flushToStdErr();
+
+ rep.echo("");
+
+ reproduceInstructions
+ .stream()
+ .forEach(rep::echo);
+
+ if (failed > 0) rep.flushToFile();
+ }
+}
diff --git a/compiler/test/dotty/tools/dotc/ParallelTestTests.scala b/compiler/test/dotty/tools/dotc/ParallelTestTests.scala
new file mode 100644
index 000000000..9964be036
--- /dev/null
+++ b/compiler/test/dotty/tools/dotc/ParallelTestTests.scala
@@ -0,0 +1,55 @@
+package dotty
+package tools
+package dotc
+
+import org.junit.Assert._
+import org.junit.Test
+
+import scala.util.control.NonFatal
+
+class ParallelTestTests extends ParallelTesting {
+ import CompilationTests._
+
+ def isInteractive = !sys.env.contains("DRONE")
+ def testFilter = None
+
+ @Test def missingFile: Unit =
+ try {
+ compileFile("../tests/partest-test/i-dont-exist.scala", defaultOptions).expectFailure.checkExpectedErrors()
+ fail("didn't fail properly")
+ }
+ catch {
+ case _: IllegalArgumentException => // pass!
+ case NonFatal(_) => fail("wrong exception thrown")
+ }
+
+ @Test def pos1Error: Unit =
+ compileFile("../tests/partest-test/posFail1Error.scala", defaultOptions).expectFailure.checkCompile()
+
+ @Test def negMissingAnnot: Unit =
+ compileFile("../tests/partest-test/negMissingAnnot.scala", defaultOptions).expectFailure.checkExpectedErrors()
+
+ @Test def negAnnotWrongLine: Unit =
+ compileFile("../tests/partest-test/negAnnotWrongLine.scala", defaultOptions).expectFailure.checkExpectedErrors()
+
+ @Test def negTooManyAnnots: Unit =
+ compileFile("../tests/partest-test/negTooManyAnnots.scala", defaultOptions).expectFailure.checkExpectedErrors()
+
+ @Test def negNoPositionAnnot: Unit =
+ compileFile("../tests/partest-test/negNoPositionAnnots.scala", defaultOptions).expectFailure.checkExpectedErrors()
+
+ @Test def runCompileFail: Unit =
+ compileFile("../tests/partest-test/posFail1Error.scala", defaultOptions).expectFailure.checkRuns()
+
+ @Test def runWrongOutput1: Unit =
+ compileFile("../tests/partest-test/runWrongOutput1.scala", defaultOptions).expectFailure.checkRuns()
+
+ @Test def runWrongOutput2: Unit =
+ compileFile("../tests/partest-test/runWrongOutput2.scala", defaultOptions).expectFailure.checkRuns()
+
+ @Test def runDiffOutput1: Unit =
+ compileFile("../tests/partest-test/runDiffOutput1.scala", defaultOptions).expectFailure.checkRuns()
+
+ @Test def runStackOverflow: Unit =
+ compileFile("../tests/partest-test/stackOverflow.scala", defaultOptions).expectFailure.checkRuns()
+}
diff --git a/compiler/test/dotty/tools/dotc/ParallelTesting.scala b/compiler/test/dotty/tools/dotc/ParallelTesting.scala
new file mode 100644
index 000000000..30679de9e
--- /dev/null
+++ b/compiler/test/dotty/tools/dotc/ParallelTesting.scala
@@ -0,0 +1,1062 @@
+package dotty
+package tools
+package dotc
+
+import java.io.{ File => JFile }
+import java.text.SimpleDateFormat
+import java.util.HashMap
+import java.lang.reflect.InvocationTargetException
+import java.nio.file.StandardCopyOption.REPLACE_EXISTING
+import java.nio.file.{ Files, Path, Paths, NoSuchFileException }
+import java.util.concurrent.{ Executors => JExecutors, TimeUnit, TimeoutException }
+
+import scala.io.Source
+import scala.util.control.NonFatal
+import scala.util.Try
+import scala.collection.mutable
+import scala.util.matching.Regex
+
+import core.Contexts._
+import reporting.{ Reporter, TestReporter }
+import reporting.diagnostic.MessageContainer
+import interfaces.Diagnostic.ERROR
+import dotc.util.DiffUtil
+
+/** A parallel testing suite whose goal is to integrate nicely with JUnit
+ *
+ * This trait can be mixed in to offer parallel testing to compile runs. When
+ * using this, you should be running your JUnit tests **sequentially**, as the
+ * test suite itself runs with a high level of concurrency.
+ */
+trait ParallelTesting {
+
+ import ParallelTesting._
+ import ParallelSummaryReport._
+
+ /** If the running environment supports an interactive terminal, each `Test`
+ * will be run with a progress bar and real time feedback
+ */
+ def isInteractive: Boolean
+
+ /** A regex which is used to filter which tests to run, if `None` will run
+ * all tests
+ */
+ def testFilter: Option[Regex]
+
+ /** A test source whose files or directory of files is to be compiled
+ * in a specific way defined by the `Test`
+ */
+ private sealed trait TestSource { self =>
+ def name: String
+ def outDir: JFile
+ def flags: Array[String]
+
+ /** Adds the flags specified in `newFlags0` if they do not already exist */
+ def withFlags(newFlags0: String*) = {
+ val newFlags = newFlags0.toArray
+ if (!flags.containsSlice(newFlags)) self match {
+ case self: JointCompilationSource =>
+ self.copy(flags = flags ++ newFlags)
+ case self: SeparateCompilationSource =>
+ self.copy(flags = flags ++ newFlags)
+ }
+ else self
+ }
+
+ /** Generate the instructions to redo the test from the command line */
+ def buildInstructions(errors: Int, warnings: Int): String = {
+ val sb = new StringBuilder
+ val maxLen = 80
+ var lineLen = 0
+
+ sb.append(s"\n\nTest compiled with $errors error(s) and $warnings warning(s), the test can be reproduced by running:")
+ sb.append("\n\n./bin/dotc ")
+ flags.foreach { arg =>
+ if (lineLen > maxLen) {
+ sb.append(" \\\n ")
+ lineLen = 4
+ }
+ sb.append(arg)
+ lineLen += arg.length
+ sb += ' '
+ }
+
+ self match {
+ case JointCompilationSource(_, files, _, _) => {
+ files.map(_.getAbsolutePath).foreach { path =>
+ sb.append("\\\n ")
+ sb.append(path)
+ sb += ' '
+ }
+ sb.toString + "\n\n"
+ }
+ case self: SeparateCompilationSource => {
+ val command = sb.toString
+ val fsb = new StringBuilder(command)
+ self.compilationGroups.foreach { files =>
+ files.map(_.getPath).foreach { path =>
+ fsb.append("\\\n ")
+ lineLen = 8
+ fsb.append(path)
+ fsb += ' '
+ }
+ fsb.append("\n\n")
+ fsb.append(command)
+ }
+ fsb.toString + "\n\n"
+ }
+ }
+ }
+ }
+
+ /** A group of files that may all be compiled together, with the same flags
+ * and output directory
+ */
+ private final case class JointCompilationSource(
+ name: String,
+ files: Array[JFile],
+ flags: Array[String],
+ outDir: JFile
+ ) extends TestSource {
+ def sourceFiles: Array[JFile] = files.filter(isSourceFile)
+
+ override def toString() = outDir.toString
+ }
+
+ /** A test source whose files will be compiled separately according to their
+ * suffix `_X`
+ */
+ private final case class SeparateCompilationSource(
+ name: String,
+ dir: JFile,
+ flags: Array[String],
+ outDir: JFile
+ ) extends TestSource {
+
+ /** Get the files grouped by `_X` as a list of groups, files missing this
+ * suffix will be put into the same group
+ *
+ * Filters out all none source files
+ */
+ def compilationGroups: List[Array[JFile]] =
+ dir
+ .listFiles
+ .groupBy { file =>
+ val name = file.getName
+ Try {
+ val potentialNumber = name
+ .substring(0, name.lastIndexOf('.'))
+ .reverse.takeWhile(_ != '_').reverse
+
+ potentialNumber.toInt.toString
+ }
+ .toOption
+ .getOrElse("")
+ }
+ .toList.sortBy(_._1).map(_._2.filter(isSourceFile))
+ }
+
+ /** Each `Test` takes the `testSources` and performs the compilation and assertions
+ * according to the implementing class "neg", "run" or "pos".
+ */
+ private abstract class Test(testSources: List[TestSource], times: Int, threadLimit: Option[Int], suppressAllOutput: Boolean) {
+
+ /** Actual compilation run logic, the test behaviour is defined here */
+ protected def compilationRunnable(testSource: TestSource): Runnable
+
+ /** All testSources left after filtering out */
+ private val filteredSources =
+ if (!testFilter.isDefined) testSources
+ else testSources.filter {
+ case JointCompilationSource(_, files, _, _) =>
+ files.exists(file => testFilter.get.findFirstIn(file.getAbsolutePath).isDefined)
+ case SeparateCompilationSource(_, dir, _, _) =>
+ testFilter.get.findFirstIn(dir.getAbsolutePath).isDefined
+ }
+
+ /** Total amount of test sources being compiled by this test */
+ val sourceCount = filteredSources.length
+
+ private[this] var _errorCount = 0
+ def errorCount: Int = synchronized { _errorCount }
+
+ private[this] var _testSourcesCompiled = 0
+ private def testSourcesCompiled : Int = synchronized { _testSourcesCompiled }
+
+ /** Complete the current compilation with the amount of errors encountered */
+ protected final def registerCompilation(errors: Int) = synchronized {
+ _testSourcesCompiled += 1
+ _errorCount += errors
+ }
+
+ private[this] var _failed = false
+ /** Fail the current test */
+ protected[this] final def fail(): Unit = synchronized { _failed = true }
+ def didFail: Boolean = _failed
+
+ protected def echoBuildInstructions(reporter: TestReporter, testSource: TestSource, err: Int, war: Int) = {
+ val errorMsg = testSource.buildInstructions(reporter.errorCount, reporter.warningCount)
+ addFailureInstruction(errorMsg)
+ failTestSource(testSource)
+ }
+
+ /** Instructions on how to reproduce failed test source compilations */
+ private[this] val reproduceInstructions = mutable.ArrayBuffer.empty[String]
+ protected final def addFailureInstruction(ins: String): Unit =
+ synchronized { reproduceInstructions.append(ins) }
+
+ /** The test sources that failed according to the implementing subclass */
+ private[this] val failedTestSources = mutable.ArrayBuffer.empty[String]
+ protected final def failTestSource(testSource: TestSource) = synchronized {
+ failedTestSources.append(testSource.name + " failed")
+ fail()
+ }
+
+ /** Prints to `System.err` if we're not suppressing all output */
+ protected def echo(msg: String): Unit =
+ if (!suppressAllOutput) System.err.println(msg)
+
+ /** A single `Runnable` that prints a progress bar for the curent `Test` */
+ private def createProgressMonitor: Runnable = new Runnable {
+ def run(): Unit = {
+ val start = System.currentTimeMillis
+ var tCompiled = testSourcesCompiled
+ while (tCompiled < sourceCount) {
+ val timestamp = (System.currentTimeMillis - start) / 1000
+ val progress = (tCompiled.toDouble / sourceCount * 40).toInt
+ print(
+ "[" + ("=" * (math.max(progress - 1, 0))) +
+ (if (progress > 0) ">" else "") +
+ (" " * (39 - progress)) +
+ s"] compiling ($tCompiled/$sourceCount, ${timestamp}s)\r"
+ )
+ Thread.sleep(100)
+ tCompiled = testSourcesCompiled
+ }
+ // println, otherwise no newline and cursor at start of line
+ println(
+ s"[=======================================] compiled ($sourceCount/$sourceCount, " +
+ s"${(System.currentTimeMillis - start) / 1000}s) "
+ )
+ }
+ }
+
+ /** Wrapper function to make sure that the compiler itself did not crash -
+ * if it did, the test should automatically fail.
+ */
+ protected def tryCompile(testSource: TestSource)(op: => Unit): Unit =
+ try op catch {
+ case NonFatal(e) => {
+ // if an exception is thrown during compilation, the complete test
+ // run should fail
+ failTestSource(testSource)
+ e.printStackTrace()
+ registerCompilation(1)
+ throw e
+ }
+ }
+
+ protected def compile(files0: Array[JFile], flags0: Array[String], suppressErrors: Boolean, targetDir: JFile): TestReporter = {
+
+ val flags = flags0 ++ Array("-d", targetDir.getAbsolutePath)
+
+ def flattenFiles(f: JFile): Array[JFile] =
+ if (f.isDirectory) f.listFiles.flatMap(flattenFiles)
+ else Array(f)
+
+ val files: Array[JFile] = files0.flatMap(flattenFiles)
+
+ def findJarFromRuntime(partialName: String) = {
+ val urls = ClassLoader.getSystemClassLoader.asInstanceOf[java.net.URLClassLoader].getURLs.map(_.getFile.toString)
+ urls.find(_.contains(partialName)).getOrElse {
+ throw new java.io.FileNotFoundException(
+ s"""Unable to locate $partialName on classpath:\n${urls.toList.mkString("\n")}"""
+ )
+ }
+ }
+
+ def addOutDir(xs: Array[String]): Array[String] = {
+ val (beforeCp, cpAndAfter) = xs.toList.span(_ != "-classpath")
+ if (cpAndAfter.nonEmpty) {
+ val (cp :: cpArg :: rest) = cpAndAfter
+ (beforeCp ++ (cp :: (cpArg + s":${targetDir.getAbsolutePath}") :: rest)).toArray
+ }
+ else (beforeCp ++ ("-classpath" :: targetDir.getAbsolutePath :: Nil)).toArray
+ }
+
+ def compileWithJavac(fs: Array[String]) = if (fs.nonEmpty) {
+ val scalaLib = findJarFromRuntime("scala-library-2.")
+ val fullArgs = Array(
+ "javac",
+ "-classpath",
+ s".:$scalaLib:${targetDir.getAbsolutePath}"
+ ) ++ flags.takeRight(2) ++ fs
+
+ Runtime.getRuntime.exec(fullArgs).waitFor() == 0
+ } else true
+
+ val reporter = TestReporter.parallelReporter(this, logLevel =
+ if (suppressErrors || suppressAllOutput) ERROR + 1 else ERROR)
+ val driver =
+ if (times == 1) new Driver { def newCompiler(implicit ctx: Context) = new Compiler }
+ else new Driver {
+ def newCompiler(implicit ctx: Context) = new Compiler
+
+ private def ntimes(n: Int)(op: Int => Reporter): Reporter =
+ (emptyReporter /: (1 to n)) ((_, i) => op(i))
+
+ override def doCompile(comp: Compiler, files: List[String])(implicit ctx: Context) =
+ ntimes(times) { run =>
+ val start = System.nanoTime()
+ val rep = super.doCompile(comp, files)
+ ctx.echo(s"\ntime run $run: ${(System.nanoTime - start) / 1000000}ms")
+ rep
+ }
+ }
+
+ val allArgs = addOutDir(flags)
+ driver.process(allArgs ++ files.map(_.getAbsolutePath), reporter = reporter)
+
+ val javaFiles = files.filter(_.getName.endsWith(".java")).map(_.getAbsolutePath)
+ assert(compileWithJavac(javaFiles), s"java compilation failed for ${javaFiles.mkString(", ")}")
+
+ reporter
+ }
+
+ private[ParallelTesting] def executeTestSuite(): this.type = {
+ assert(_testSourcesCompiled == 0, "not allowed to re-use a `CompileRun`")
+
+ if (filteredSources.nonEmpty) {
+ val pool = threadLimit match {
+ case Some(i) => JExecutors.newWorkStealingPool(i)
+ case None => JExecutors.newWorkStealingPool()
+ }
+
+ if (isInteractive && !suppressAllOutput) pool.submit(createProgressMonitor)
+
+ filteredSources.foreach { target =>
+ pool.submit(compilationRunnable(target))
+ }
+
+ pool.shutdown()
+ if (!pool.awaitTermination(10, TimeUnit.MINUTES))
+ throw new TimeoutException("Compiling targets timed out")
+
+ if (didFail) {
+ reportFailed()
+ failedTestSources.toSet.foreach(addFailedTest)
+ reproduceInstructions.iterator.foreach(addReproduceInstruction)
+ }
+ else reportPassed()
+ }
+ else echo {
+ testFilter
+ .map(r => s"""No files matched regex "$r" in test""")
+ .getOrElse("No tests available under target - erroneous test?")
+ }
+
+ this
+ }
+ }
+
+ private final class PosTest(testSources: List[TestSource], times: Int, threadLimit: Option[Int], suppressAllOutput: Boolean)
+ extends Test(testSources, times, threadLimit, suppressAllOutput) {
+ protected def compilationRunnable(testSource: TestSource): Runnable = new Runnable {
+ def run(): Unit = tryCompile(testSource) {
+ testSource match {
+ case testSource @ JointCompilationSource(_, files, flags, outDir) => {
+ val reporter = compile(testSource.sourceFiles, flags, false, outDir)
+ registerCompilation(reporter.errorCount)
+
+ if (reporter.errorCount > 0)
+ echoBuildInstructions(reporter, testSource, reporter.errorCount, reporter.warningCount)
+ }
+
+ case testSource @ SeparateCompilationSource(_, dir, flags, outDir) => {
+ val reporters = testSource.compilationGroups.map(files => compile(files, flags, false, outDir))
+ val errorCount = reporters.foldLeft(0) { (acc, reporter) =>
+ if (reporter.errorCount > 0)
+ echoBuildInstructions(reporter, testSource, reporter.errorCount, reporter.warningCount)
+
+ acc + reporter.errorCount
+ }
+
+ registerCompilation(errorCount)
+
+ if (errorCount > 0) failTestSource(testSource)
+ }
+ }
+
+ }
+ }
+ }
+
+ private final class RunTest(testSources: List[TestSource], times: Int, threadLimit: Option[Int], suppressAllOutput: Boolean)
+ extends Test(testSources, times, threadLimit, suppressAllOutput) {
+ private def runMain(dir: JFile, testSource: TestSource): Array[String] = {
+ def renderStackTrace(ex: Throwable): String =
+ ex.getStackTrace
+ .takeWhile(_.getMethodName != "invoke0")
+ .mkString(" ", "\n ", "")
+
+ import java.io.ByteArrayOutputStream
+ import java.net.{ URL, URLClassLoader }
+
+ val printStream = new ByteArrayOutputStream
+ try {
+ // Do classloading magic and running here:
+ val ucl = new URLClassLoader(Array(dir.toURI.toURL))
+ val cls = ucl.loadClass("Test")
+ val meth = cls.getMethod("main", classOf[Array[String]])
+
+ Console.withOut(printStream) {
+ meth.invoke(null, Array("jvm")) // partest passes at least "jvm" as an arg
+ }
+ }
+ catch {
+ case ex: NoSuchMethodException =>
+ echo(s"test in '$dir' did not contain method: ${ex.getMessage}\n${renderStackTrace(ex.getCause)}")
+ failTestSource(testSource)
+
+ case ex: ClassNotFoundException =>
+ echo(s"test in '$dir' did not contain class: ${ex.getMessage}\n${renderStackTrace(ex.getCause)}")
+ failTestSource(testSource)
+
+ case ex: InvocationTargetException =>
+ echo(s"An exception ocurred when running main: ${ex.getCause}\n${renderStackTrace(ex.getCause)}")
+ failTestSource(testSource)
+ }
+ printStream.toString("utf-8").lines.toArray
+ }
+
+ private def verifyOutput(checkFile: JFile, dir: JFile, testSource: TestSource, warnings: Int) = {
+ val outputLines = runMain(dir, testSource)
+ val checkLines = Source.fromFile(checkFile).getLines.toArray
+
+ def linesMatch =
+ outputLines
+ .zip(checkLines)
+ .forall { case (x, y) => x == y }
+
+ if (outputLines.length != checkLines.length || !linesMatch) {
+ // Print diff to files and summary:
+ val diff = outputLines.zip(checkLines).map { case (act, exp) =>
+ DiffUtil.mkColoredCodeDiff(exp, act, true)
+ }.mkString("\n")
+ val msg = s"\nOutput from run test '$checkFile' did not match expected, output:\n$diff\n"
+ echo(msg)
+ addFailureInstruction(msg)
+
+ // Print build instructions to file and summary:
+ val buildInstr = testSource.buildInstructions(0, warnings)
+ addFailureInstruction(buildInstr)
+
+ // Fail target:
+ failTestSource(testSource)
+ }
+ }
+
+ protected def compilationRunnable(testSource: TestSource): Runnable = new Runnable {
+ def run(): Unit = tryCompile(testSource) {
+ val (errorCount, warningCount, hasCheckFile, verifier: Function0[Unit]) = testSource match {
+ case testSource @ JointCompilationSource(_, files, flags, outDir) => {
+ val checkFile = files.flatMap { file =>
+ if (file.isDirectory) Nil
+ else {
+ val fname = file.getAbsolutePath.reverse.dropWhile(_ != '.').reverse + "check"
+ val checkFile = new JFile(fname)
+ if (checkFile.exists) List(checkFile)
+ else Nil
+ }
+ }.headOption
+ val reporter = compile(testSource.sourceFiles, flags, false, outDir)
+
+ if (reporter.errorCount > 0)
+ echoBuildInstructions(reporter, testSource, reporter.errorCount, reporter.warningCount)
+
+ registerCompilation(reporter.errorCount)
+ (reporter.errorCount, reporter.warningCount, checkFile.isDefined, () => verifyOutput(checkFile.get, outDir, testSource, reporter.warningCount))
+ }
+
+ case testSource @ SeparateCompilationSource(_, dir, flags, outDir) => {
+ val checkFile = new JFile(dir.getAbsolutePath.reverse.dropWhile(_ == '/').reverse + ".check")
+ val (errorCount, warningCount) =
+ testSource
+ .compilationGroups
+ .map(compile(_, flags, false, outDir))
+ .foldLeft((0,0)) { case ((errors, warnings), reporter) =>
+ if (reporter.errorCount > 0)
+ echoBuildInstructions(reporter, testSource, reporter.errorCount, reporter.warningCount)
+
+ (errors + reporter.errorCount, warnings + reporter.warningCount)
+ }
+
+ if (errorCount > 0) fail()
+
+ registerCompilation(errorCount)
+ (errorCount, warningCount, checkFile.exists, () => verifyOutput(checkFile, outDir, testSource, warningCount))
+ }
+ }
+
+ if (errorCount == 0 && hasCheckFile) verifier()
+ else if (errorCount == 0) runMain(testSource.outDir, testSource)
+ else if (errorCount > 0) {
+ echo(s"\nCompilation failed for: '$testSource'")
+ val buildInstr = testSource.buildInstructions(errorCount, warningCount)
+ addFailureInstruction(buildInstr)
+ failTestSource(testSource)
+ }
+ }
+ }
+ }
+
+ private final class NegTest(testSources: List[TestSource], times: Int, threadLimit: Option[Int], suppressAllOutput: Boolean)
+ extends Test(testSources, times, threadLimit, suppressAllOutput) {
+ protected def compilationRunnable(testSource: TestSource): Runnable = new Runnable {
+ def run(): Unit = tryCompile(testSource) {
+ // In neg-tests we allow two types of error annotations,
+ // "nopos-error" which doesn't care about position and "error" which
+ // has to be annotated on the correct line number.
+ //
+ // We collect these in a map `"file:row" -> numberOfErrors`, for
+ // nopos errors we save them in `"file" -> numberOfNoPosErrors`
+ def getErrorMapAndExpectedCount(files: Array[JFile]): (HashMap[String, Integer], Int) = {
+ val errorMap = new HashMap[String, Integer]()
+ var expectedErrors = 0
+ files.filter(_.getName.endsWith(".scala")).foreach { file =>
+ Source.fromFile(file).getLines.zipWithIndex.foreach { case (line, lineNbr) =>
+ val errors = line.sliding("// error".length).count(_.mkString == "// error")
+ if (errors > 0)
+ errorMap.put(s"${file.getAbsolutePath}:${lineNbr}", errors)
+
+ val noposErrors = line.sliding("// nopos-error".length).count(_.mkString == "// nopos-error")
+ if (noposErrors > 0) {
+ val nopos = errorMap.get("nopos")
+ val existing: Integer = if (nopos eq null) 0 else nopos
+ errorMap.put("nopos", noposErrors + existing)
+ }
+
+ expectedErrors += noposErrors + errors
+ }
+ }
+
+ (errorMap, expectedErrors)
+ }
+
+ def getMissingExpectedErrors(errorMap: HashMap[String, Integer], reporterErrors: Iterator[MessageContainer]) = !reporterErrors.forall { error =>
+ val key = if (error.pos.exists) {
+ val fileName = error.pos.source.file.toString
+ s"$fileName:${error.pos.line}"
+
+ } else "nopos"
+
+ val errors = errorMap.get(key)
+
+ if (errors ne null) {
+ if (errors == 1) errorMap.remove(key)
+ else errorMap.put(key, errors - 1)
+ true
+ }
+ else {
+ echo {
+ s"Error reported in ${error.pos.source}, but no annotation found"
+ }
+ false
+ }
+ }
+
+ val (expectedErrors, actualErrors, hasMissingAnnotations, errorMap) = testSource match {
+ case testSource @ JointCompilationSource(_, files, flags, outDir) => {
+ val sourceFiles = testSource.sourceFiles
+ val (errorMap, expectedErrors) = getErrorMapAndExpectedCount(sourceFiles)
+ val reporter = compile(sourceFiles, flags, true, outDir)
+ val actualErrors = reporter.errorCount
+
+ (expectedErrors, actualErrors, () => getMissingExpectedErrors(errorMap, reporter.errors), errorMap)
+ }
+
+ case testSource @ SeparateCompilationSource(_, dir, flags, outDir) => {
+ val compilationGroups = testSource.compilationGroups
+ val (errorMap, expectedErrors) = getErrorMapAndExpectedCount(compilationGroups.toArray.flatten)
+ val reporters = compilationGroups.map(compile(_, flags, true, outDir))
+ val actualErrors = reporters.foldLeft(0)(_ + _.errorCount)
+ val errors = reporters.iterator.flatMap(_.errors)
+ (expectedErrors, actualErrors, () => getMissingExpectedErrors(errorMap, errors), errorMap)
+ }
+ }
+
+ if (expectedErrors != actualErrors) {
+ echo {
+ s"\nWrong number of errors encountered when compiling $testSource, expected: $expectedErrors, actual: $actualErrors\n"
+ }
+ failTestSource(testSource)
+ }
+ else if (hasMissingAnnotations()) {
+ echo {
+ s"\nErrors found on incorrect row numbers when compiling $testSource"
+ }
+ failTestSource(testSource)
+ }
+ else if (!errorMap.isEmpty) {
+ echo {
+ s"\nExpected error(s) have {<error position>=<unreported error>}: $errorMap"
+ }
+ failTestSource(testSource)
+ }
+
+ registerCompilation(actualErrors)
+ }
+ }
+ }
+
+ /** The `CompilationTest` is the main interface to `ParallelTesting`, it
+ * can be instantiated via one of the following methods:
+ *
+ * - `compileFile`
+ * - `compileDir`
+ * - `compileList`
+ * - `compileFilesInDir`
+ * - `compileShallowFilesInDir`
+ *
+ * Each compilation test can then be turned into either a "pos", "neg" or
+ * "run" test:
+ *
+ * ```
+ * compileFile("../tests/pos/i1103.scala", opts).pos()
+ * ```
+ *
+ * These tests can be customized before calling one of the execution
+ * methods, for instance:
+ *
+ * ```
+ * compileFile("../tests/pos/i1103.scala", opts).times(2).verbose.pos()
+ * ```
+ *
+ * Which would compile `i1103.scala` twice with the verbose flag as a "pos"
+ * test.
+ *
+ * pos tests
+ * =========
+ * Pos tests verify that the compiler is able to compile the given
+ * `TestSource`s and that they generate no errors or exceptions during
+ * compilation
+ *
+ * neg tests
+ * =========
+ * Neg tests are expected to generate a certain amount of errors - but not
+ * crash the compiler. In each `.scala` file, you specifiy the line on which
+ * the error will be generated, e.g:
+ *
+ * ```
+ * val x: String = 1 // error
+ * ```
+ *
+ * if a line generates multiple errors, you need to annotate it multiple
+ * times. For a line that generates two errors:
+ *
+ * ```
+ * val y: String = { val y1: String = 1; 2 } // error // error
+ * ```
+ *
+ * Certain errors have no position, if you need to check these annotate the
+ * file anywhere with `// nopos-error`
+ *
+ * run tests
+ * =========
+ * Run tests are a superset of pos tests, they both verify compilation and
+ * that the compiler does not crash. In addition, run tests verify that the
+ * tests are able to run as expected.
+ *
+ * Run tests need to have the following form:
+ *
+ * ```
+ * object Test {
+ * def main(args: Array[String]): Unit = ()
+ * }
+ * ```
+ *
+ * This is because the runner instantiates the `Test` class and calls the
+ * main method.
+ *
+ * Other definitions are allowed in the same file, but the file needs to at
+ * least have the `Test` object with a `main` method.
+ *
+ * To verify output you may use `.check` files. These files should share the
+ * name of the file or directory that they are testing. For instance:
+ *
+ * ```none
+ * .
+ * └── tests
+ * ├── i1513.scala
+ * └── i1513.check
+ * ```
+ *
+ * If you are testing a directory under separate compilation, you would
+ * have:
+ *
+ * ```none
+ * .
+ * └── tests
+ * ├── myTestDir
+ * │ ├── T_1.scala
+ * │ ├── T_2.scala
+ * │ └── T_3.scala
+ * └── myTestDir.check
+ * ```
+ *
+ * In the above example, `i1513.scala` and one of the files `T_X.scala`
+ * would contain a `Test` object with a main method.
+ *
+ * Composing tests
+ * ===============
+ * Since this is a parallel test suite, it is essential to be able to
+ * compose tests to take advantage of the concurrency. This is done using
+ * the `+` function. This function will make sure that tests being combined
+ * are compatible according to the `require`s in `+`.
+ */
+ final class CompilationTest private (
+ private[ParallelTesting] val targets: List[TestSource],
+ private[ParallelTesting] val times: Int,
+ private[ParallelTesting] val shouldDelete: Boolean,
+ private[ParallelTesting] val threadLimit: Option[Int],
+ private[ParallelTesting] val shouldFail: Boolean
+ ) {
+ import org.junit.Assert.fail
+
+ private[ParallelTesting] def this(target: TestSource) =
+ this(List(target), 1, true, None, false)
+
+ private[ParallelTesting] def this(targets: List[TestSource]) =
+ this(targets, 1, true, None, false)
+
+ /** Compose test targets from `this` with `other`
+ *
+ * It does this, only if the two tests are compatible. Otherwise it throws
+ * an `IllegalArgumentException`.
+ *
+ * Grouping tests together like this allows us to take advantage of the
+ * concurrency offered by this test suite as each call to an executing
+ * method (`pos()` / `checkExpectedErrors()`/ `run()`) will spin up a thread pool with the
+ * maximum allowed level of concurrency. Doing this for only a few targets
+ * does not yield any real benefit over sequential compilation.
+ *
+ * As such, each `CompilationTest` should contain as many targets as
+ * possible.
+ */
+ def +(other: CompilationTest) = {
+ require(other.times == times, "can't combine tests that are meant to be benchmark compiled")
+ require(other.shouldDelete == shouldDelete, "can't combine tests that differ on deleting output")
+ require(other.shouldFail == shouldFail, "can't combine tests that have different expectations on outcome")
+ new CompilationTest(targets ++ other.targets, times, shouldDelete, threadLimit, shouldFail)
+ }
+
+ /** Creates a "pos" test run, which makes sure that all tests pass
+ * compilation without generating errors and that they do not crash the
+ * compiler
+ */
+ def checkCompile(): this.type = {
+ val test = new PosTest(targets, times, threadLimit, shouldFail).executeTestSuite()
+
+ if (!shouldFail && test.didFail) {
+ fail(s"Expected no errors when compiling, but found: ${test.errorCount}")
+ }
+ else if (shouldFail && !test.didFail) {
+ fail("Pos test should have failed, but didn't")
+ }
+
+ cleanup()
+ }
+
+ /** Creates a "neg" test run, which makes sure that each test generates the
+ * correct amount of errors at the correct positions. It also makes sure
+ * that none of these tests crash the compiler
+ */
+ def checkExpectedErrors(): this.type = {
+ val test = new NegTest(targets, times, threadLimit, shouldFail).executeTestSuite()
+
+ if (!shouldFail && test.didFail) {
+ fail("Neg test shouldn't have failed, but did")
+ }
+ else if (shouldFail && !test.didFail) {
+ fail("Neg test should have failed, but did not")
+ }
+
+ cleanup()
+ }
+
+ /** Creates a "run" test run, which is a superset of "pos". In addition to
+ * making sure that all tests pass compilation and that they do not crash
+ * the compiler; it also makes sure that all tests can run with the
+ * expected output
+ */
+ def checkRuns(): this.type = {
+ val test = new RunTest(targets, times, threadLimit, shouldFail).executeTestSuite()
+
+ if (!shouldFail && test.didFail) {
+ fail("Run test failed, but should not")
+ }
+ else if (shouldFail && !test.didFail) {
+ fail("Run test should have failed, but did not")
+ }
+
+ cleanup()
+ }
+
+ /** Deletes output directories and files */
+ private def cleanup(): this.type = {
+ if (shouldDelete) delete()
+ this
+ }
+
+ /** Copies `file` to `dir` - taking into account if `file` is a directory,
+ * and if so copying recursively
+ */
+ private def copyToDir(dir: JFile, file: JFile): JFile = {
+ val target = Paths.get(dir.getAbsolutePath, file.getName)
+ Files.copy(file.toPath, target, REPLACE_EXISTING)
+ if (file.isDirectory) file.listFiles.map(copyToDir(target.toFile, _))
+ target.toFile
+ }
+
+ /** Builds a new `CompilationTest` where we have copied the target files to
+ * the out directory. This is needed for tests that modify the original
+ * source, such as `-rewrite` tests
+ */
+ def copyToTarget(): CompilationTest = new CompilationTest (
+ targets.map {
+ case target @ JointCompilationSource(_, files, _, outDir) =>
+ target.copy(files = files.map(copyToDir(outDir,_)))
+ case target @ SeparateCompilationSource(_, dir, _, outDir) =>
+ target.copy(dir = copyToDir(outDir, dir))
+ },
+ times, shouldDelete, threadLimit, shouldFail
+ )
+
+ /** Builds a `CompilationTest` which performs the compilation `i` times on
+ * each target
+ */
+ def times(i: Int): CompilationTest =
+ new CompilationTest(targets, i, shouldDelete, threadLimit, shouldFail)
+
+ /** Builds a `Compilationtest` which passes the verbose flag and logs the
+ * classpath
+ */
+ def verbose: CompilationTest = new CompilationTest(
+ targets.map(t => t.withFlags("-verbose", "-Ylog-classpath")),
+ times, shouldDelete, threadLimit, shouldFail
+ )
+
+ /** Builds a `CompilationTest` which keeps the generated output files
+ *
+ * This is needed for tests like `tastyBootstrap` which relies on first
+ * compiling a certain part of the project and then compiling a second
+ * part which depends on the first
+ */
+ def keepOutput: CompilationTest =
+ new CompilationTest(targets, times, false, threadLimit, shouldFail)
+
+ /** Builds a `CompilationTest` with a limited level of concurrency with
+ * maximum `i` threads
+ */
+ def limitThreads(i: Int): CompilationTest =
+ new CompilationTest(targets, times, shouldDelete, Some(i), shouldFail)
+
+ /** Builds a `CompilationTest` where the executed test is expected to fail
+ *
+ * This behaviour is mainly needed for the tests that test the test suite.
+ */
+ def expectFailure: CompilationTest =
+ new CompilationTest(targets, times, shouldDelete, threadLimit, true)
+
+ /** Delete all output files generated by this `CompilationTest` */
+ def delete(): Unit = targets.foreach(t => delete(t.outDir))
+
+ private def delete(file: JFile): Unit = {
+ if (file.isDirectory) file.listFiles.foreach(delete)
+ try Files.delete(file.toPath)
+ catch {
+ case _: NoSuchFileException => // already deleted, everything's fine
+ }
+ }
+ }
+
+ /** Create out directory for directory `d` */
+ private def createOutputDirsForDir(d: JFile, sourceDir: JFile, outDir: String): JFile = {
+ val targetDir = new JFile(outDir + s"${sourceDir.getName}/${d.getName}")
+ targetDir.mkdirs()
+ targetDir
+ }
+
+ /** Create out directory for `file` */
+ private def createOutputDirsForFile(file: JFile, sourceDir: JFile, outDir: String): JFile = {
+ val uniqueSubdir = file.getName.substring(0, file.getName.lastIndexOf('.'))
+ val targetDir = new JFile(outDir + s"${sourceDir.getName}/$uniqueSubdir")
+ targetDir.mkdirs()
+ targetDir
+ }
+
+ /** Make sure that directory string is as expected */
+ private def checkRequirements(f: String, sourceDir: JFile, outDir: String): Unit = {
+ require(sourceDir.isDirectory && sourceDir.exists, "passed non-directory to `compileFilesInDir`")
+ require(outDir.last == '/', "please specify an `outDir` with a trailing slash")
+ }
+
+ /** Separates directories from files and returns them as `(dirs, files)` */
+ private def compilationTargets(sourceDir: JFile): (List[JFile], List[JFile]) =
+ sourceDir.listFiles.foldLeft((List.empty[JFile], List.empty[JFile])) { case ((dirs, files), f) =>
+ if (f.isDirectory) (f :: dirs, files)
+ else if (f.getName.endsWith(".check")) (dirs, files)
+ else if (f.getName.endsWith(".flags")) (dirs, files)
+ else (dirs, f :: files)
+ }
+
+ /** Gets the name of the calling method via reflection.
+ *
+ * It does this in a way that needs to work both with the bootstrapped dotty
+ * and the non-bootstrapped version. Since the two compilers generate
+ * different bridges, we first need to filter out methods with the same name
+ * (bridges) - and then find the `@Test` method in our extending class
+ */
+ private def getCallingMethod(): String = {
+ val seen = mutable.Set.empty[String]
+ Thread.currentThread.getStackTrace
+ .filter { elem =>
+ if (seen.contains(elem.getMethodName)) false
+ else { seen += elem.getMethodName; true }
+ }
+ .find { elem =>
+ val callingClass = Class.forName(elem.getClassName)
+ classOf[ParallelTesting].isAssignableFrom(callingClass) &&
+ elem.getFileName != "ParallelTesting.scala"
+ }
+ .map(_.getMethodName)
+ .getOrElse {
+ throw new IllegalStateException("Unable to reflectively find calling method")
+ }
+ }
+
+ /** Compiles a single file from the string path `f` using the supplied flags */
+ def compileFile(f: String, flags: Array[String])(implicit outDirectory: String): CompilationTest = {
+ val callingMethod = getCallingMethod
+ val sourceFile = new JFile(f)
+ val parent = sourceFile.getParentFile
+ val outDir =
+ outDirectory + callingMethod + "/" +
+ sourceFile.getName.substring(0, sourceFile.getName.lastIndexOf('.')) + "/"
+
+ require(
+ sourceFile.exists && !sourceFile.isDirectory &&
+ (parent ne null) && parent.exists && parent.isDirectory,
+ s"Source file: $f, didn't exist"
+ )
+
+ val target = JointCompilationSource(
+ callingMethod,
+ Array(sourceFile),
+ flags,
+ createOutputDirsForFile(sourceFile, parent, outDir)
+ )
+ new CompilationTest(target)
+ }
+
+ /** Compiles a directory `f` using the supplied `flags`. This method does
+ * deep compilation, that is - it compiles all files and subdirectories
+ * contained within the directory `f`.
+ */
+ def compileDir(f: String, flags: Array[String])(implicit outDirectory: String): CompilationTest = {
+ val callingMethod = getCallingMethod
+ val outDir = outDirectory + callingMethod + "/"
+ val sourceDir = new JFile(f)
+ checkRequirements(f, sourceDir, outDir)
+
+ def flatten(f: JFile): Array[JFile] =
+ if (f.isDirectory) f.listFiles.flatMap(flatten)
+ else Array(f)
+
+ // Directories in which to compile all containing files with `flags`:
+ val targetDir = new JFile(outDir + "/" + sourceDir.getName + "/")
+ targetDir.mkdirs()
+
+ val target = JointCompilationSource(callingMethod, flatten(sourceDir), flags, targetDir)
+ new CompilationTest(target)
+ }
+
+ /** Compiles all `files` together as a single compilation run. It is given a
+ * `testName` since files can be in separate directories and or be otherwise
+ * dissociated
+ */
+ def compileList(testName: String, files: List[String], flags: Array[String])(implicit outDirectory: String): CompilationTest = {
+ val callingMethod = getCallingMethod
+ val outDir = outDirectory + callingMethod + "/" + testName + "/"
+
+ // Directories in which to compile all containing files with `flags`:
+ val targetDir = new JFile(outDir)
+ targetDir.mkdirs()
+ assert(targetDir.exists, s"couldn't create target directory: $targetDir")
+
+ val target = JointCompilationSource(callingMethod, files.map(new JFile(_)).toArray, flags, targetDir)
+
+ // Create a CompilationTest and let the user decide whether to execute a pos or a neg test
+ new CompilationTest(target)
+ }
+
+ /** This function compiles the files and folders contained within directory
+ * `f` in a specific way.
+ *
+ * - Each file is compiled separately as a single compilation run
+ * - Each directory is compiled as a `SeparateCompilationTaret`, in this
+ * target all files are grouped according to the file suffix `_X` where `X`
+ * is a number. These groups are then ordered in ascending order based on
+ * the value of `X` and each group is compiled one after the other.
+ *
+ * For this function to work as expected, we use the same convention for
+ * directory layout as the old partest. That is:
+ *
+ * - Single files can have an associated check-file with the same name (but
+ * with file extension `.check`)
+ * - Directories can have an associated check-file, where the check file has
+ * the same name as the directory (with the file extension `.check`)
+ */
+ def compileFilesInDir(f: String, flags: Array[String])(implicit outDirectory: String): CompilationTest = {
+ val callingMethod = getCallingMethod
+ val outDir = outDirectory + callingMethod + "/"
+ val sourceDir = new JFile(f)
+ checkRequirements(f, sourceDir, outDir)
+
+ val (dirs, files) = compilationTargets(sourceDir)
+
+ val targets =
+ files.map(f => JointCompilationSource(callingMethod, Array(f), flags, createOutputDirsForFile(f, sourceDir, outDir))) ++
+ dirs.map(dir => SeparateCompilationSource(callingMethod, dir, flags, createOutputDirsForDir(dir, sourceDir, outDir)))
+
+ // Create a CompilationTest and let the user decide whether to execute a pos or a neg test
+ new CompilationTest(targets)
+ }
+
+ /** This function behaves similar to `compileFilesInDir` but it ignores
+ * sub-directories and as such, does **not** perform separate compilation
+ * tests.
+ */
+ def compileShallowFilesInDir(f: String, flags: Array[String])(implicit outDirectory: String): CompilationTest = {
+ val callingMethod = getCallingMethod
+ val outDir = outDirectory + callingMethod + "/"
+ val sourceDir = new JFile(f)
+ checkRequirements(f, sourceDir, outDir)
+
+ val (_, files) = compilationTargets(sourceDir)
+
+ val targets = files.map { file =>
+ JointCompilationSource(callingMethod, Array(file), flags, createOutputDirsForFile(file, sourceDir, outDir))
+ }
+
+ // Create a CompilationTest and let the user decide whether to execute a pos or a neg test
+ new CompilationTest(targets)
+ }
+}
+
+object ParallelTesting {
+ def isSourceFile(f: JFile): Boolean = {
+ val name = f.getName
+ name.endsWith(".scala") || name.endsWith(".java")
+ }
+}
diff --git a/compiler/test/dotty/tools/dotc/repl/TestREPL.scala b/compiler/test/dotty/tools/dotc/repl/TestREPL.scala
index a38abcbab..131a88ab1 100644
--- a/compiler/test/dotty/tools/dotc/repl/TestREPL.scala
+++ b/compiler/test/dotty/tools/dotc/repl/TestREPL.scala
@@ -5,6 +5,8 @@ package repl
import core.Contexts.Context
import collection.mutable
import java.io.StringWriter
+import dotty.tools.io.{ PlainFile, Directory }
+import org.junit.Test
/** A subclass of REPL used for testing.
* It takes a transcript of a REPL session in `script`. The transcript
@@ -62,3 +64,24 @@ class TestREPL(script: String) extends REPL {
}
}
}
+
+class REPLTests {
+ def replFile(prefix: String, fileName: String): Unit = {
+ val path = s"$prefix$fileName"
+ val f = new PlainFile(path)
+ val repl = new TestREPL(new String(f.toCharArray))
+ repl.process(Array[String]())
+ repl.check()
+ }
+
+ def replFiles(path: String): Unit = {
+ val dir = Directory(path)
+ val fileNames = dir.files.toArray.map(_.jfile.getName).filter(_ endsWith ".check")
+ for (name <- fileNames) {
+ println(s"testing $path$name")
+ replFile(path, name)
+ }
+ }
+
+ @Test def replAll = replFiles("../tests/repl/")
+}
diff --git a/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala b/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala
index 169908c4f..521cf9576 100644
--- a/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala
+++ b/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala
@@ -2,35 +2,57 @@ package dotty.tools
package dotc
package reporting
+import java.io.{ PrintWriter, File => JFile, FileOutputStream }
+import java.text.SimpleDateFormat
+import java.util.Date
+
import scala.collection.mutable
+
import util.SourcePosition
import core.Contexts._
import Reporter._
-import java.io.PrintWriter
import diagnostic.{ Message, MessageContainer, NoExplanation }
import diagnostic.messages._
+import interfaces.Diagnostic.{ ERROR, WARNING, INFO }
-class TestReporter(writer: PrintWriter) extends Reporter
-with UniqueMessagePositions with HideNonSensicalMessages {
-
+class TestReporter protected (outWriter: PrintWriter, filePrintln: String => Unit, logLevel: Int)
+extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with MessageRendering {
import MessageContainer._
- /** maximal number of error messages to be printed */
- protected def ErrorLimit = 100
+ protected final val _errorBuf = mutable.ArrayBuffer.empty[MessageContainer]
+ final def errors: Iterator[MessageContainer] = _errorBuf.iterator
+
+ protected final val _messageBuf = mutable.ArrayBuffer.empty[String]
- def printPos(pos: SourcePosition): Unit =
+ final def flushToFile(): Unit =
+ _messageBuf.iterator.foreach(filePrintln)
+
+ final def flushToStdErr(): Unit =
+ _messageBuf.iterator.foreach(System.err.println)
+
+ final def inlineInfo(pos: SourcePosition): String =
if (pos.exists) {
- if (pos.outer.exists) {
- writer.println(s"\ninlined at ${pos.outer}:\n")
- printPos(pos.outer)
- }
+ if (pos.outer.exists)
+ s"\ninlined at ${pos.outer}:\n" + inlineInfo(pos.outer)
+ else ""
}
+ else ""
+
+ def echo(msg: String) =
+ _messageBuf.append(msg)
/** Prints the message with the given position indication. */
- def printMessageAndPos(msg: String, pos: SourcePosition)(implicit ctx: Context): Unit = {
- val posStr = s"${pos.line + 1}: "
- writer.println(posStr + msg)
- printPos(pos)
+ def printMessageAndPos(m: MessageContainer, extra: String)(implicit ctx: Context): Unit = {
+ val msg = messageAndPos(m.contained, m.pos, diagnosticLevel(m))
+ val extraInfo = inlineInfo(m.pos)
+
+ if (m.level >= logLevel) {
+ outWriter.println(msg)
+ if (extraInfo.nonEmpty) outWriter.println(extraInfo)
+ }
+
+ _messageBuf.append(msg)
+ if (extraInfo.nonEmpty) _messageBuf.append(extraInfo)
}
override def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
@@ -41,11 +63,61 @@ with UniqueMessagePositions with HideNonSensicalMessages {
}
m match {
- case m: Error =>
- printMessageAndPos(m.contained.kind + extra, m.pos)
+ case m: Error => {
+ _errorBuf.append(m)
+ printMessageAndPos(m, extra)
+ }
case w: Warning =>
- printMessageAndPos(w.contained.kind + extra, w.pos)
+ printMessageAndPos(w, extra)
case _ =>
}
}
}
+
+object TestReporter {
+ private[this] val logWriter = {
+ val df = new SimpleDateFormat("yyyy-MM-dd-HH:mm")
+ val timestamp = df.format(new Date)
+ new PrintWriter(new FileOutputStream(new JFile(s"../tests-$timestamp.log"), true))
+ }
+
+ def writeToLog(str: String) = {
+ logWriter.println(str)
+ logWriter.flush()
+ }
+
+ def parallelReporter(lock: AnyRef, logLevel: Int): TestReporter = new TestReporter(
+ new PrintWriter(Console.err, true),
+ str => lock.synchronized {
+ logWriter.println(str)
+ logWriter.flush()
+ },
+ logLevel
+ )
+
+ def reporter(logLevel: Int): TestReporter = new TestReporter(
+ new PrintWriter(Console.err, true),
+ writeToLog,
+ logLevel
+ )
+
+ def simplifiedReporter(writer: PrintWriter): TestReporter = new TestReporter(
+ writer,
+ writeToLog,
+ WARNING
+ ) {
+ /** Prints the message with the given position indication in a simplified manner */
+ override def printMessageAndPos(m: MessageContainer, extra: String)(implicit ctx: Context): Unit = {
+ val msg = s"${m.pos.line + 1}: " + m.contained.kind + extra
+ val extraInfo = inlineInfo(m.pos)
+
+ writer.println(msg)
+ _messageBuf.append(msg)
+
+ if (extraInfo.nonEmpty) {
+ writer.println(extraInfo)
+ _messageBuf.append(extraInfo)
+ }
+ }
+ }
+}
diff --git a/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala b/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala
index d9a5d8a38..eff86e6e7 100644
--- a/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala
+++ b/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala
@@ -1,21 +1,23 @@
-package test.transform
+package dotty
+package tools
+package dotc
+package transform
import java.io._
import scala.io.Source._
import scala.reflect.io.Directory
import org.junit.Test
-import dotty.tools.dotc.Main
-import dotty.tools.dotc.reporting.TestReporter
+import reporting.TestReporter
class PatmatExhaustivityTest {
val testsDir = "../tests/patmat"
// stop-after: patmatexhaust-huge.scala crash compiler
- val options = List("-color:never", "-Ystop-after:splitter", "-Ycheck-all-patmat") ++ (new dotc.tests).classPath
+ val options = List("-color:never", "-Ystop-after:splitter", "-Ycheck-all-patmat") ++ CompilationTests.classPath
private def compileFile(file: File) = {
val stringBuffer = new StringWriter()
- val reporter = new TestReporter(new PrintWriter(stringBuffer))
+ val reporter = TestReporter.simplifiedReporter(new PrintWriter(stringBuffer))
try {
Main.process((file.getPath::options).toArray, reporter, null)
@@ -38,7 +40,7 @@ class PatmatExhaustivityTest {
/** A single test with multiple files grouped in a folder */
private def compileDir(file: File) = {
val stringBuffer = new StringWriter()
- val reporter = new TestReporter(new PrintWriter(stringBuffer))
+ val reporter = TestReporter.simplifiedReporter(new PrintWriter(stringBuffer))
val files = Directory(file.getPath).list.toList
.filter(f => f.extension == "scala" || f.extension == "java" )
diff --git a/doc-tool/test/DottyDocTest.scala b/doc-tool/test/DottyDocTest.scala
index 4202dca73..9e7b70c8f 100644
--- a/doc-tool/test/DottyDocTest.scala
+++ b/doc-tool/test/DottyDocTest.scala
@@ -9,8 +9,13 @@ import dotc.typer.FrontEnd
import dottydoc.core.{ DocASTPhase, ContextDottydoc }
import model.Package
import dotty.tools.dottydoc.util.syntax._
+import dotc.reporting.{ StoreReporter, MessageRendering }
+import dotc.interfaces.Diagnostic.ERROR
+import org.junit.Assert.fail
-trait DottyDocTest {
+import java.io.{ BufferedWriter, OutputStreamWriter }
+
+trait DottyDocTest extends MessageRendering {
dotty.tools.dotc.parsing.Scanners // initialize keywords
implicit val ctx: FreshContext = {
@@ -26,6 +31,7 @@ trait DottyDocTest {
ctx.settings.classpath,
dotty.Jars.dottyLib
)
+ ctx.setReporter(new StoreReporter(ctx.reporter))
base.initialize()(ctx)
ctx
}
@@ -36,17 +42,47 @@ trait DottyDocTest {
def phaseName = "assertionPhase"
override def run(implicit ctx: Context): Unit =
assertion(ctx.docbase.packages)
+ if (ctx.reporter.hasErrors) {
+ System.err.println("reporter had errors:")
+ ctx.reporter.removeBufferedMessages.foreach { msg =>
+ System.err.println {
+ messageAndPos(msg.contained, msg.pos, diagnosticLevel(msg))
+ }
+ }
+ }
}) :: Nil
override def phases =
super.phases ++ assertionPhase
}
+ private def callingMethod: String =
+ Thread.currentThread.getStackTrace.find {
+ _.getMethodName match {
+ case "checkSource" | "callingMethod" | "getStackTrace" | "currentThread" =>
+ false
+ case _ =>
+ true
+ }
+ }
+ .map(_.getMethodName)
+ .getOrElse {
+ throw new IllegalStateException("couldn't get calling method via reflection")
+ }
+
+ private def sourceFileFromString(name: String, contents: String): SourceFile = {
+ val virtualFile = new scala.reflect.io.VirtualFile(name)
+ val writer = new BufferedWriter(new OutputStreamWriter(virtualFile.output, "UTF-8"))
+ writer.write(contents)
+ writer.close()
+ new SourceFile(virtualFile, scala.io.Codec.UTF8)
+ }
+
def checkSource(source: String)(assertion: Map[String, Package] => Unit): Unit = {
val c = compilerWithChecker(assertion)
c.rootContext(ctx)
val run = c.newRun
- run.compile(source)
+ run.compileSources(sourceFileFromString(callingMethod, source) :: Nil)
}
def checkFiles(sources: List[String])(assertion: Map[String, Package] => Unit): Unit = {
diff --git a/doc-tool/test/WhitelistedStdLib.scala b/doc-tool/test/WhitelistedStdLib.scala
index 9092d1ded..d59457605 100644
--- a/doc-tool/test/WhitelistedStdLib.scala
+++ b/doc-tool/test/WhitelistedStdLib.scala
@@ -6,18 +6,16 @@ import org.junit.Assert._
class TestWhitelistedCollections extends DottyDocTest {
- @Test def arrayHasDocumentation =
+ @Test def arrayAndImmutableHasDocumentation =
checkFiles(TestWhitelistedCollections.files) { packages =>
val array =
packages("scala")
.children.find(_.path.mkString(".") == "scala.Array")
.get
- assert(array.comment.get.body.length > 0)
- }
+ assert(array.comment.get.body.length > 0,
+ "scala.Array didn't have any documentation")
- @Test def traitImmutableHasDocumentation =
- checkFiles(TestWhitelistedCollections.files) { packages =>
val imm =
packages("scala")
.children.find(_.path.mkString(".") == "scala.Immutable")
diff --git a/out/.keep b/out/.keep
deleted file mode 100644
index e69de29bb..000000000
--- a/out/.keep
+++ /dev/null
diff --git a/project/Build.scala b/project/Build.scala
index ce560d8e4..bb02416cc 100644
--- a/project/Build.scala
+++ b/project/Build.scala
@@ -47,6 +47,9 @@ object Build {
// Spawns a repl with the correct classpath
lazy val repl = inputKey[Unit]("run the REPL with correct classpath")
+ // Run tests with filter
+ lazy val filterTest = inputKey[Unit]("runs integration test with the supplied filter")
+
// Used to compile files similar to ./bin/dotc script
lazy val dotc =
inputKey[Unit]("run the compiler using the correct classpath, or the user supplied classpath")
@@ -276,23 +279,57 @@ object Build {
com.typesafe.sbteclipse.plugin.EclipsePlugin.EclipseKeys.withSource := true,
// get libraries onboard
- partestDeps := Seq(scalaCompiler,
- "org.scala-lang" % "scala-reflect" % scalacVersion,
- "org.scala-lang" % "scala-library" % scalacVersion % "test"),
- libraryDependencies ++= partestDeps.value,
- libraryDependencies ++= Seq("org.scala-lang.modules" %% "scala-xml" % "1.0.1",
- "org.scala-lang.modules" %% "scala-partest" % "1.0.11" % "test",
- "com.novocode" % "junit-interface" % "0.11" % "test"),
-
resolvers += Resolver.typesafeIvyRepo("releases"), // For org.scala-sbt:interface
- libraryDependencies += "org.scala-sbt" % "interface" % sbtVersion.value,
+ libraryDependencies ++= Seq(scalaCompiler,
+ "org.scala-sbt" % "interface" % sbtVersion.value,
+ "org.scala-lang.modules" %% "scala-xml" % "1.0.1",
+ "com.novocode" % "junit-interface" % "0.11" % "test",
+ "org.scala-lang" % "scala-reflect" % scalacVersion,
+ "org.scala-lang" % "scala-library" % scalacVersion % "test"),
+
+ // start partest specific settings:
+ libraryDependencies += "org.scala-lang.modules" %% "scala-partest" % "1.0.11" % "test",
+ testOptions in Test += Tests.Cleanup({ () => partestLockFile.delete }),
+ // this option is needed so that partest doesn't run
+ partestDeps := Seq(
+ scalaCompiler,
+ "org.scala-lang" % "scala-reflect" % scalacVersion,
+ "org.scala-lang" % "scala-library" % scalacVersion % "test"
+ ),
+ lockPartestFile := {
+ // When this file is present, running `test` generates the files for
+ // partest. Otherwise it just executes the tests directly.
+ val lockDir = partestLockFile.getParentFile
+ lockDir.mkdirs
+ // Cannot have concurrent partests as they write to the same directory.
+ if (lockDir.list.size > 0)
+ throw new RuntimeException("ERROR: sbt partest: another partest is already running, pid in lock file: " + lockDir.list.toList.mkString(" "))
+ partestLockFile.createNewFile
+ partestLockFile.deleteOnExit
+ },
+ runPartestRunner := Def.inputTaskDyn {
+ // Magic! This is both an input task and a dynamic task. Apparently
+ // command line arguments get passed to the last task in an aliased
+ // sequence (see partest alias below), so this works.
+ val args = Def.spaceDelimited("<arg>").parsed
+ val jars = List(
+ (packageBin in Compile).value.getAbsolutePath,
+ packageAll.value("dotty-library"),
+ packageAll.value("dotty-interfaces")
+ ) ++ getJarPaths(partestDeps.value, ivyPaths.value.ivyHome)
+ val dottyJars =
+ s"""-dottyJars ${jars.length + 2} dotty.jar dotty-lib.jar ${jars.mkString(" ")}"""
+ // Provide the jars required on the classpath of run tests
+ runTask(Test, "dotty.partest.DPConsoleRunner", dottyJars + " " + args.mkString(" "))
+ }.evaluated,
+ // end partest specific settings
// enable improved incremental compilation algorithm
incOptions := incOptions.value.withNameHashing(true),
// For convenience, change the baseDirectory when running the compiler
baseDirectory in (Compile, run) := baseDirectory.value / "..",
- // .. but not when running partest
+ // .. but not when running test
baseDirectory in (Test, run) := baseDirectory.value,
repl := Def.inputTaskDyn {
@@ -303,6 +340,14 @@ object Build {
)
}.evaluated,
+ filterTest := Def.inputTaskDyn {
+ val args: Seq[String] = spaceDelimited("<arg>").parsed
+ testOptions := Seq()
+ (testOnly in Test).toTask(
+ " dotty.tools.dotc.CompilationTests -- -Ddotty.partest.filter=" + args.head
+ )
+ }.evaluated,
+
// Override run to be able to run compiled classfiles
dotr := {
val args: Seq[String] = spaceDelimited("<arg>").parsed
@@ -343,34 +388,6 @@ object Build {
TestFrameworks.JUnit, "-a", "-v",
"--run-listener=dotty.tools.ContextEscapeDetector"
),
- testOptions in Test += Tests.Cleanup({ () => partestLockFile.delete }),
-
- lockPartestFile := {
- // When this file is present, running `test` generates the files for
- // partest. Otherwise it just executes the tests directly.
- val lockDir = partestLockFile.getParentFile
- lockDir.mkdirs
- // Cannot have concurrent partests as they write to the same directory.
- if (lockDir.list.size > 0)
- throw new RuntimeException("ERROR: sbt partest: another partest is already running, pid in lock file: " + lockDir.list.toList.mkString(" "))
- partestLockFile.createNewFile
- partestLockFile.deleteOnExit
- },
- runPartestRunner := Def.inputTaskDyn {
- // Magic! This is both an input task and a dynamic task. Apparently
- // command line arguments get passed to the last task in an aliased
- // sequence (see partest alias below), so this works.
- val args = Def.spaceDelimited("<arg>").parsed
- val jars = List(
- (packageBin in Compile).value.getAbsolutePath,
- packageAll.value("dotty-library"),
- packageAll.value("dotty-interfaces")
- ) ++ getJarPaths(partestDeps.value, ivyPaths.value.ivyHome)
- val dottyJars =
- s"""-dottyJars ${jars.length + 2} dotty.jar dotty-lib.jar ${jars.mkString(" ")}"""
- // Provide the jars required on the classpath of run tests
- runTask(Test, "dotty.partest.DPConsoleRunner", dottyJars + " " + args.mkString(" "))
- }.evaluated,
/* Add the sources of scalajs-ir.
* To guarantee that dotty can bootstrap without depending on a version
@@ -458,6 +475,31 @@ object Build {
}
)
+ // Partest tasks
+ lazy val partestDeps =
+ SettingKey[Seq[ModuleID]]("partestDeps", "Finds jars for partest dependencies")
+ lazy val runPartestRunner =
+ InputKey[Unit]("runPartestRunner", "Runs partest")
+ lazy val lockPartestFile =
+ TaskKey[Unit]("lockPartestFile", "Creates the lock file at ./tests/locks/partest-<pid>.lock")
+ lazy val partestLockFile =
+ new File("." + File.separator + "tests" + File.separator + "locks" + File.separator + s"partest-$pid.lock")
+
+ def pid = java.lang.Long.parseLong(java.lang.management.ManagementFactory.getRuntimeMXBean().getName().split("@")(0))
+
+ def getJarPaths(modules: Seq[ModuleID], ivyHome: Option[File]): Seq[String] = ivyHome match {
+ case Some(home) =>
+ modules.map({ module =>
+ val file = Path(home) / Path("cache") /
+ Path(module.organization) / Path(module.name) / Path("jars") /
+ Path(module.name + "-" + module.revision + ".jar")
+ if (!file.isFile) throw new RuntimeException("ERROR: sbt getJarPaths: dependency jar not found: " + file)
+ else file.jfile.getAbsolutePath
+ })
+ case None => throw new RuntimeException("ERROR: sbt getJarPaths: ivyHome not defined")
+ }
+ // end partest tasks
+
lazy val `dotty-compiler` = project.in(file("compiler")).
dependsOn(`dotty-interfaces`).
dependsOn(`dotty-library`).
@@ -793,26 +835,6 @@ object DottyInjectedPlugin extends AutoPlugin {
)
)
- // Partest tasks
- lazy val lockPartestFile = TaskKey[Unit]("lockPartestFile", "Creates the lock file at ./tests/locks/partest-<pid>.lock")
- lazy val partestLockFile = new File("." + File.separator + "tests" + File.separator + "locks" + File.separator + s"partest-$pid.lock")
- def pid = java.lang.Long.parseLong(java.lang.management.ManagementFactory.getRuntimeMXBean().getName().split("@")(0))
-
- lazy val runPartestRunner = InputKey[Unit]("runPartestRunner", "Runs partest")
-
- lazy val partestDeps = SettingKey[Seq[ModuleID]]("partestDeps", "Finds jars for partest dependencies")
- def getJarPaths(modules: Seq[ModuleID], ivyHome: Option[File]): Seq[String] = ivyHome match {
- case Some(home) =>
- modules.map({ module =>
- val file = Path(home) / Path("cache") /
- Path(module.organization) / Path(module.name) / Path("jars") /
- Path(module.name + "-" + module.revision + ".jar")
- if (!file.isFile) throw new RuntimeException("ERROR: sbt getJarPaths: dependency jar not found: " + file)
- else file.jfile.getAbsolutePath
- })
- case None => throw new RuntimeException("ERROR: sbt getJarPaths: ivyHome not defined")
- }
-
// Compile with dotty
lazy val compileWithDottySettings = {
inConfig(Compile)(inTask(compile)(Defaults.runnerTask) ++ Seq(
diff --git a/tests/neg/i941.scala b/tests/neg/i941.scala
index 2643c2546..fc29cc27d 100644
--- a/tests/neg/i941.scala
+++ b/tests/neg/i941.scala
@@ -1,7 +1,7 @@
object Test {
def bar(tl: => String) = {
- val x = tl _ //error
+ val x = tl _ // error
val y = x _ // error
val s: String = x() // error
}
diff --git a/tests/neg/instantiateAbstract.scala b/tests/neg/instantiateAbstract.scala
index 10eeac64d..a2ff38ef4 100644
--- a/tests/neg/instantiateAbstract.scala
+++ b/tests/neg/instantiateAbstract.scala
@@ -15,7 +15,7 @@ object Test {
@scala.annotation.Annotation type T = String // error
@scala.annotation.Annotation val x = 1 // error
- @scala.annotation.Annotation def f = 1 //error
+ @scala.annotation.Annotation def f = 1 // error
(1: @scala.annotation.Annotation) // error
diff --git a/tests/neg/varargsInMethodsT1625/allParamsAreVarArgs.scala b/tests/neg/t1625.scala
index aabb1ecea..aabb1ecea 100644
--- a/tests/neg/varargsInMethodsT1625/allParamsAreVarArgs.scala
+++ b/tests/neg/t1625.scala
diff --git a/tests/neg/t1625b.scala b/tests/neg/t1625b.scala
new file mode 100644
index 000000000..f2c544f42
--- /dev/null
+++ b/tests/neg/t1625b.scala
@@ -0,0 +1,3 @@
+object T5 {
+ case class Abc(x: String*, c: String*) // error // error: varargs parameter must come last AND found: String* required: String
+}
diff --git a/tests/neg/varargsInMethodsT1625/classConstructorVarArgs.scala b/tests/neg/t1625c.scala
index 74595cb7d..74595cb7d 100644
--- a/tests/neg/varargsInMethodsT1625/classConstructorVarArgs.scala
+++ b/tests/neg/t1625c.scala
diff --git a/tests/neg/varargsInMethodsT1625/curriedNegExample.scala b/tests/neg/t1625d.scala
index 616ea0539..616ea0539 100644
--- a/tests/neg/varargsInMethodsT1625/curriedNegExample.scala
+++ b/tests/neg/t1625d.scala
diff --git a/tests/neg/varargsInMethodsT1625/firstParamIsVarArgs.scala b/tests/neg/t1625e.scala
index eceb7e696..eceb7e696 100644
--- a/tests/neg/varargsInMethodsT1625/firstParamIsVarArgs.scala
+++ b/tests/neg/t1625e.scala
diff --git a/tests/neg/varargsInMethodsT1625/caseClassConstructorVarArgs.scala b/tests/neg/varargsInMethodsT1625/caseClassConstructorVarArgs.scala
deleted file mode 100644
index 8f8a4fcf6..000000000
--- a/tests/neg/varargsInMethodsT1625/caseClassConstructorVarArgs.scala
+++ /dev/null
@@ -1,3 +0,0 @@
-object T5 {
- case class Abc(x: String*, c: String*) // error //error: varargs parameter must come last AND found: String* required: String
-} \ No newline at end of file
diff --git a/tests/partest-test/negAnnotWrongLine.scala b/tests/partest-test/negAnnotWrongLine.scala
new file mode 100644
index 000000000..06ba0a2c3
--- /dev/null
+++ b/tests/partest-test/negAnnotWrongLine.scala
@@ -0,0 +1,3 @@
+object Foo { // error
+ def bar: Int = "LOL"
+}
diff --git a/tests/partest-test/negMissingAnnot.scala b/tests/partest-test/negMissingAnnot.scala
new file mode 100644
index 000000000..acffb37a3
--- /dev/null
+++ b/tests/partest-test/negMissingAnnot.scala
@@ -0,0 +1 @@
+class Foo extends Bar
diff --git a/tests/partest-test/negNoPositionAnnots.scala b/tests/partest-test/negNoPositionAnnots.scala
new file mode 100644
index 000000000..7f3ce7e34
--- /dev/null
+++ b/tests/partest-test/negNoPositionAnnots.scala
@@ -0,0 +1,5 @@
+object Foo {
+ def bar: Int = "LOL"
+
+ // nopos-error
+}
diff --git a/tests/partest-test/negTooManyAnnots.scala b/tests/partest-test/negTooManyAnnots.scala
new file mode 100644
index 000000000..5dbd2fd75
--- /dev/null
+++ b/tests/partest-test/negTooManyAnnots.scala
@@ -0,0 +1,3 @@
+object Test {
+ def foo: Int = "LOL" // error // error
+}
diff --git a/tests/partest-test/posFail1Error.scala b/tests/partest-test/posFail1Error.scala
new file mode 100644
index 000000000..c8b565498
--- /dev/null
+++ b/tests/partest-test/posFail1Error.scala
@@ -0,0 +1,3 @@
+object Test extends Bar {
+ def main(args: Array[String]): Unit = ()
+}
diff --git a/tests/partest-test/runDiffOutput1.check b/tests/partest-test/runDiffOutput1.check
new file mode 100644
index 000000000..6234030de
--- /dev/null
+++ b/tests/partest-test/runDiffOutput1.check
@@ -0,0 +1,5 @@
+1
+2
+4
+4
+5
diff --git a/tests/partest-test/runDiffOutput1.scala b/tests/partest-test/runDiffOutput1.scala
new file mode 100644
index 000000000..32cf6f5b6
--- /dev/null
+++ b/tests/partest-test/runDiffOutput1.scala
@@ -0,0 +1,9 @@
+object Test {
+ def main(args: Array[String]): Unit = {
+ println(1)
+ println(2)
+ println(3)
+ println(4)
+ println(5)
+ }
+}
diff --git a/tests/partest-test/runWrongOutput1.check b/tests/partest-test/runWrongOutput1.check
new file mode 100644
index 000000000..b414108e8
--- /dev/null
+++ b/tests/partest-test/runWrongOutput1.check
@@ -0,0 +1,6 @@
+1
+2
+3
+4
+5
+6
diff --git a/tests/partest-test/runWrongOutput1.scala b/tests/partest-test/runWrongOutput1.scala
new file mode 100644
index 000000000..32cf6f5b6
--- /dev/null
+++ b/tests/partest-test/runWrongOutput1.scala
@@ -0,0 +1,9 @@
+object Test {
+ def main(args: Array[String]): Unit = {
+ println(1)
+ println(2)
+ println(3)
+ println(4)
+ println(5)
+ }
+}
diff --git a/tests/partest-test/runWrongOutput2.check b/tests/partest-test/runWrongOutput2.check
new file mode 100644
index 000000000..01e79c32a
--- /dev/null
+++ b/tests/partest-test/runWrongOutput2.check
@@ -0,0 +1,3 @@
+1
+2
+3
diff --git a/tests/partest-test/runWrongOutput2.scala b/tests/partest-test/runWrongOutput2.scala
new file mode 100644
index 000000000..32cf6f5b6
--- /dev/null
+++ b/tests/partest-test/runWrongOutput2.scala
@@ -0,0 +1,9 @@
+object Test {
+ def main(args: Array[String]): Unit = {
+ println(1)
+ println(2)
+ println(3)
+ println(4)
+ println(5)
+ }
+}
diff --git a/tests/partest-test/stackOverflow.scala b/tests/partest-test/stackOverflow.scala
new file mode 100644
index 000000000..b3132cc19
--- /dev/null
+++ b/tests/partest-test/stackOverflow.scala
@@ -0,0 +1,7 @@
+object Test {
+ def foo: Int = bar
+ def bar: Int = foo
+
+ def main(args: Array[String]): Unit =
+ println(foo)
+}
diff --git a/tests/pos/i851.java b/tests/pos/i851.java
index 39e0bf350..cfd86ccc4 100644
--- a/tests/pos/i851.java
+++ b/tests/pos/i851.java
@@ -5,4 +5,4 @@ interface J<T> extends I<T> {
};
}
-class C<T> extends J<T> {} \ No newline at end of file
+class C<T> implements J<T> {}
diff --git a/tests/pos/varargsInMethodsT1625/curriedPosExample.scala b/tests/pos/t1625.scala
index c614f2dee..c614f2dee 100644
--- a/tests/pos/varargsInMethodsT1625/curriedPosExample.scala
+++ b/tests/pos/t1625.scala
diff --git a/tests/pos/varargsInMethodsT1625/onlyOneVarArgs.scala b/tests/pos/t1625b.scala
index 834960b4a..834960b4a 100644
--- a/tests/pos/varargsInMethodsT1625/onlyOneVarArgs.scala
+++ b/tests/pos/t1625b.scala
diff --git a/tests/run/getclass.check b/tests/run/getclass.check
index 9d88762f4..ea73f6127 100644
--- a/tests/run/getclass.check
+++ b/tests/run/getclass.check
@@ -22,5 +22,5 @@ class [D
class [Lscala.collection.immutable.List;
Functions:
-class Test$$$Lambda$1
-class Test$$$Lambda$2
+class Test$$$Lambda$
+class Test$$$Lambda$
diff --git a/tests/run/getclass.scala b/tests/run/getclass.scala
index 7a13a61a2..b74e1b202 100644
--- a/tests/run/getclass.scala
+++ b/tests/run/getclass.scala
@@ -35,8 +35,8 @@ object Test {
println("\nFunctions:")
// FunctionN.getClass.toString has form of "class Test$$$Lambda$N/1349414238",
- // but number (1349414238) depends on environment
- println(f1.getClass.toString.takeWhile(_ != '/'))
- println(f2.getClass.toString.takeWhile(_ != '/'))
+ // but "N/1349414238" depends on environment
+ println(f1.getClass.toString.take("class Test$$$Lambda$".length))
+ println(f2.getClass.toString.take("class Test$$$Lambda$".length))
}
}
diff --git a/tests/run/origins.check b/tests/run/origins.check
deleted file mode 100644
index 54d58296b..000000000
--- a/tests/run/origins.check
+++ /dev/null
@@ -1,6 +0,0 @@
-
->> Origins tag 'boop' logged 65 calls from 3 distinguished sources.
-
- 50 Test$.$anonfun$f3$1(origins.scala:16)
- 10 Test$.$anonfun$f2$1(origins.scala:15)
- 5 Test$.$anonfun$f1$1(origins.scala:14)
diff --git a/tests/run/origins.flags b/tests/run/origins.flags
deleted file mode 100644
index 690753d80..000000000
--- a/tests/run/origins.flags
+++ /dev/null
@@ -1 +0,0 @@
--no-specialization -Ydelambdafy:inline \ No newline at end of file
diff --git a/tests/run/origins.scala b/tests/run/origins.scala
deleted file mode 100644
index 6529351d3..000000000
--- a/tests/run/origins.scala
+++ /dev/null
@@ -1,21 +0,0 @@
-import scala.reflect.internal.util.Origins
-
-package goxbox {
- object Socks {
- val origins = Origins("boop")
-
- def boop(x: Int): Int = origins { 5 }
- }
-}
-
-object Test {
- import goxbox.Socks.boop
-
- def f1() = 1 to 5 map boop
- def f2() = 1 to 10 map boop
- def f3() = 1 to 50 map boop
-
- def main(args: Array[String]): Unit = {
- f1() ; f2() ; f3()
- }
-}
diff --git a/tests/run/shutdownhooks.check b/tests/run/shutdownhooks.check
deleted file mode 100644
index 29956956e..000000000
--- a/tests/run/shutdownhooks.check
+++ /dev/null
@@ -1,3 +0,0 @@
-Fooblitzky!
-main#shutdown.
-Test#shutdown.
diff --git a/tests/run/shutdownhooks.scala b/tests/run/shutdownhooks.scala
deleted file mode 100644
index 5f512a391..000000000
--- a/tests/run/shutdownhooks.scala
+++ /dev/null
@@ -1,37 +0,0 @@
-object Test {
- scala.sys.addShutdownHook {
- Thread.sleep(1000)
- println("Test#shutdown.")
- }
-
- def daemon() = {
- val t = new Thread {
- override def run(): Unit = {
- Thread.sleep(10000)
- println("Hallelujah!") // should not see this
- }
- }
- t.setDaemon(true)
- t.start()
- t
- }
-
- def nonDaemon() = {
- val t = new Thread {
- override def run(): Unit = {
- Thread.sleep(100)
- println("Fooblitzky!")
- }
- }
- t.start()
- t
- }
-
- def main(args: Array[String]): Unit = {
- daemon()
- nonDaemon()
- scala.sys.addShutdownHook {
- println("main#shutdown.")
- }
- }
-}