diff --git a/compiler/test/dotty/tools/TestSources.scala b/compiler/test/dotty/tools/TestSources.scala index 8b593394214e..d6c35250614b 100644 --- a/compiler/test/dotty/tools/TestSources.scala +++ b/compiler/test/dotty/tools/TestSources.scala @@ -10,6 +10,21 @@ import dotty.Properties object TestSources { + private val isWorkingDirectoryInsideCompiler = Paths.get(".").toAbsolutePath.normalize.endsWith("compiler") + + def rootPath(): Path = + if isWorkingDirectoryInsideCompiler + then Paths.get("..") + else Paths.get(".") + + def getPath(relative: String): Path = + if isWorkingDirectoryInsideCompiler + then Paths.get("..", relative) + // important to not do `get(".", relative)` (or equivalently `rootPath().resolve(relative)`), + // the rest of the testing framework depends on exact paths, + // so "error in ./x.scala" and "error in x.scala" aren't considered equivalent. + else Paths.get(relative) + // pos tests lists def posFromTastyExcludelistFile: String = "compiler/test/dotc/pos-from-tasty.excludelist" @@ -85,7 +100,7 @@ object TestSources { // load lists private def loadList(path: String): List[String] = { - val list = Files.readAllLines(Paths.get(path)) + val list = Files.readAllLines(getPath(path)) .iterator() .asScala .map(_.trim) // allow indentation diff --git a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala index c2febc8417c2..2c220d49cc85 100644 --- a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala @@ -186,7 +186,7 @@ class BootstrappedOnlyCompilationTests { // 1. hack with absolute path for -Xplugin // 2. copy `pluginFile` to destination def compileFilesInDir(dir: String, run: Boolean = false): CompilationTest = { - val outDir = defaultOutputDir + "testPlugins/" + val outDir = new java.io.File(defaultOutputDir, "testPlugins") val sourceDir = new java.io.File(dir) val dirs = sourceDir.listFiles.toList.filter(_.isDirectory) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 4e2a68db6798..e111e8d7a6b3 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -4,35 +4,26 @@ package dotc import scala.language.unsafeNulls -import org.junit.{ Test, BeforeClass, AfterClass, Ignore } -import org.junit.Assert._ -import org.junit.Assume._ -import org.junit.experimental.categories.Category - -import java.io.File -import java.nio.file._ -import java.util.stream.{ Stream => JStream } -import scala.jdk.CollectionConverters._ -import scala.util.matching.Regex -import scala.concurrent.duration._ -import TestSources.sources -import TestSources.scoverageIgnoreExcludelisted -import reporting.TestReporter -import vulpix._ -import dotty.tools.dotc.config.ScalaSettings -import dotty.tools.dotc.coverage.Serializer +import org.junit.{ Test, AfterClass } +import org.junit.Assume.* + +import java.nio.file.* +import scala.concurrent.duration.* + +import dotty.tools.dotc.reporting.TestReporter +import dotty.tools.vulpix.* class CompilationTests { - import ParallelTesting._ - import TestConfiguration._ - import CompilationTests._ + import ParallelTesting.* + import TestConfiguration.* + import CompilationTests.* import CompilationTest.aggregateTests // Positive tests ------------------------------------------------------------ @Test def pos: Unit = { implicit val testGroup: TestGroup = TestGroup("compilePos") - var tests = List( + val tests = List( compileFilesInDir("tests/pos", defaultOptions.and("-Wsafe-init", "-Wunused:all", "-Wshadow:private-shadow", "-Wshadow:type-parameter-shadow"), FileFilter.include(TestSources.posLintingAllowlist)), compileFilesInDir("tests/pos", defaultOptions.and("-Wsafe-init"), FileFilter.exclude(TestSources.posLintingAllowlist)), compileFilesInDir("tests/pos-deep-subtype", allowDeepSubtypes), @@ -279,7 +270,7 @@ class CompilationTests { locally { val group = TestGroup("checkInitGlobal/tastySource") val tastSourceOptions = defaultOptions.and("-Ysafe-init-global") - val outDirLib = defaultOutputDir + group + "/A/tastySource/A" + val outDirLib = Paths.get(defaultOutputDir.getAbsolutePath, group.name,"A", "tastySource", "A").toString // Set -sourceroot such that the source code cannot be found by the compiler val libOptions = tastSourceOptions.and("-sourceroot", "tests/init-global/special") @@ -311,8 +302,8 @@ class CompilationTests { locally { val i12128Group = TestGroup("checkInit/i12128") val i12128Options = options.without("-Werror") - val outDir1 = defaultOutputDir + i12128Group + "/Reflect_1/i12128/Reflect_1" - val outDir2 = defaultOutputDir + i12128Group + "/Macro_2/i12128/Macro_2" + val outDir1 = Paths.get(defaultOutputDir.getAbsolutePath, i12128Group.name, "Reflect_1", "i12128", "Reflect_1").toString + val outDir2 = Paths.get(defaultOutputDir.getAbsolutePath, i12128Group.name, "Macro_2", "i12128", "Macro_2").toString val tests = List( withCoverage(compileFile("tests/init/special/i12128/Reflect_1.scala", i12128Options)(using i12128Group).keepOutput), @@ -331,9 +322,9 @@ class CompilationTests { val tastyErrorGroup = TestGroup("checkInit/tasty-error/val-or-defdef") val tastyErrorOptions = options.without("-Werror") - val classA0 = defaultOutputDir + tastyErrorGroup + "/A/v0/A" - val classA1 = defaultOutputDir + tastyErrorGroup + "/A/v1/A" - val classB1 = defaultOutputDir + tastyErrorGroup + "/B/v1/B" + val classA0 = Paths.get(defaultOutputDir.getAbsolutePath, tastyErrorGroup.name, "A", "v0", "A").toString + val classA1 = Paths.get(defaultOutputDir.getAbsolutePath, tastyErrorGroup.name, "A", "v1", "A").toString + val classB1 = Paths.get(defaultOutputDir.getAbsolutePath, tastyErrorGroup.name, "B", "v1", "B").toString val tests = List( withCoverage(compileFile("tests/init/tasty-error/val-or-defdef/v1/A.scala", tastyErrorOptions)(using tastyErrorGroup).keepOutput), @@ -355,10 +346,10 @@ class CompilationTests { val tastyErrorGroup = TestGroup("checkInit/tasty-error/typedef") val tastyErrorOptions = options.without("-Werror").without("-Ycheck:all") - val classC = defaultOutputDir + tastyErrorGroup + "/C/typedef/C" - val classA0 = defaultOutputDir + tastyErrorGroup + "/A/v0/A" - val classA1 = defaultOutputDir + tastyErrorGroup + "/A/v1/A" - val classB1 = defaultOutputDir + tastyErrorGroup + "/B/v1/B" + val classC = Paths.get(defaultOutputDir.getAbsolutePath, tastyErrorGroup.name, "C", "typedef", "C").toString + val classA0 = Paths.get(defaultOutputDir.getAbsolutePath, tastyErrorGroup.name, "A", "v0", "A").toString + val classA1 = Paths.get(defaultOutputDir.getAbsolutePath, tastyErrorGroup.name, "A", "v1", "A").toString + val classB1 = Paths.get(defaultOutputDir.getAbsolutePath, tastyErrorGroup.name, "B", "v1", "B").toString val tests = List( withCoverage(compileFile("tests/init/tasty-error/typedef/C.scala", tastyErrorOptions)(using tastyErrorGroup).keepOutput), diff --git a/compiler/test/dotty/tools/dotc/Playground.scala b/compiler/test/dotty/tools/dotc/Playground.scala index 40a24c0408cf..1edcad113859 100644 --- a/compiler/test/dotty/tools/dotc/Playground.scala +++ b/compiler/test/dotty/tools/dotc/Playground.scala @@ -1,13 +1,23 @@ package dotty.tools.dotc -import dotty.tools.vulpix._ +import dotty.tools.vulpix.* import org.junit.Test import org.junit.Ignore +// For ease of debugging individual tests @Ignore class Playground: - import TestConfiguration._ - import CompilationTests._ + import TestConfiguration.* + import CompilationTests.* + import CompilationTest.aggregateTests @Test def example: Unit = - implicit val testGroup: TestGroup = TestGroup("playground") - compileFile("tests/playground/example.scala", defaultOptions).checkCompile() + implicit val testGroup: TestGroup = TestGroup("single-test") + // can add, e.g., .and("-some-option") + val options = defaultOptions + // can also use `compileDir` (single test as a dir), `compileFilesInDir` (all tests within a dir) + val test = compileFile("tests/pos/tuple-filter.scala", options) + // or `RunTestWithCoverage` for "run" tests with output, or `WarnTestWithCoverage` for "warn" tests with warnings + type TestKind = PosTestWithCoverage + val compilationTest = withCoverage(aggregateTests(test)) + runWithCoverageOrFallback[TestKind](compilationTest, testGroup.name) + diff --git a/compiler/test/dotty/tools/dotc/TastyBootstrapTests.scala b/compiler/test/dotty/tools/dotc/TastyBootstrapTests.scala index 49a5c52310ae..e1124881e4ee 100644 --- a/compiler/test/dotty/tools/dotc/TastyBootstrapTests.scala +++ b/compiler/test/dotty/tools/dotc/TastyBootstrapTests.scala @@ -37,16 +37,16 @@ class TastyBootstrapTests { val dotty2Group = TestGroup("tastyBootstrap/dotty2") // Make sure that the directory is clean - dotty.tools.io.Directory(defaultOutputDir + "tastyBootstrap").deleteRecursively() + dotty.tools.io.Directory(new java.io.File(defaultOutputDir, "tastyBootstrap").getAbsolutePath).deleteRecursively() val opt = TestFlags( List( // compile with bootstrapped library on cp: - defaultOutputDir + libGroup + "/lib/", + Paths.get(defaultOutputDir.getAbsolutePath, libGroup.name, "lib").toString, // and bootstrapped tasty-core: - defaultOutputDir + tastyCoreGroup + "/tastyCore/", + Paths.get(defaultOutputDir.getAbsolutePath, tastyCoreGroup.name, "tastyCore").toString, // as well as bootstrapped compiler: - defaultOutputDir + dotty1Group + "/dotty1/", + Paths.get(defaultOutputDir.getAbsolutePath, dotty1Group.name, "dotty1").toString, // and the other compiler dependencies: Properties.compilerInterface, Properties.scalaLibrary, Properties.scalaAsm, Properties.dottyInterfaces, Properties.jlineTerminal, Properties.jlineReader, diff --git a/compiler/test/dotty/tools/dotc/coverage/CoverageTests.scala b/compiler/test/dotty/tools/dotc/coverage/CoverageTests.scala index 3e384b31ce07..27b84396bd66 100644 --- a/compiler/test/dotty/tools/dotc/coverage/CoverageTests.scala +++ b/compiler/test/dotty/tools/dotc/coverage/CoverageTests.scala @@ -8,7 +8,6 @@ import org.junit.experimental.categories.Category import dotty.{BootstrappedOnlyTests, Properties} import dotty.tools.vulpix.* import dotty.tools.vulpix.TestConfiguration.* -import dotty.tools.dotc.Main import dotty.tools.dotc.reporting.TestReporter import java.nio.file.{FileSystems, Files, Path, Paths, StandardCopyOption} @@ -16,7 +15,6 @@ import scala.jdk.CollectionConverters.* import scala.util.Properties.userDir import scala.language.unsafeNulls import scala.collection.mutable.Buffer -import dotty.tools.dotc.util.DiffUtil import java.nio.charset.StandardCharsets import java.util.stream.Collectors diff --git a/compiler/test/dotty/tools/dotc/printing/PrintingTest.scala b/compiler/test/dotty/tools/dotc/printing/PrintingTest.scala index ebb6de8596aa..451fe87c8501 100644 --- a/compiler/test/dotty/tools/dotc/printing/PrintingTest.scala +++ b/compiler/test/dotty/tools/dotc/printing/PrintingTest.scala @@ -26,9 +26,9 @@ import java.io.File class PrintingTest { def options(phase: String, flags: List[String]) = - val outDir = ParallelTesting.defaultOutputDir + "printing" + File.pathSeparator - File(outDir).mkdirs() - List(s"-Vprint:$phase", "-color:never", "-nowarn", "-d", outDir, "-classpath", TestConfiguration.basicClasspath) ::: flags + val outDir = new File(ParallelTesting.defaultOutputDir, "printing") + outDir.mkdirs() + List(s"-Vprint:$phase", "-color:never", "-nowarn", "-d", outDir.getAbsolutePath, "-classpath", TestConfiguration.basicClasspath) ::: flags private def compileFile(path: JPath, phase: String): Boolean = { val baseFilePath = path.toString.stripSuffix(".scala").stripSuffix(".java") diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index d5fec91101ef..cb2ebd8ca847 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -162,7 +162,7 @@ trait ParallelTesting extends RunnerOrchestration with CoverageSupport: final override def toString: String = sourceFiles match { case Array(f) => f.getPath - case _ => outDir.getPath.stripPrefix(defaultOutputDir).stripPrefix(name).stripPrefix("/") + case _ => outDir.getPath.stripPrefix(defaultOutputDirName).stripPrefix(name).stripPrefix("/") } } @@ -507,11 +507,16 @@ trait ParallelTesting extends RunnerOrchestration with CoverageSupport: def scalacOptions = toolArgs.getOrElse(ToolName.Scalac, Nil) def javacOptions = toolArgs.getOrElse(ToolName.Javac, Nil) - val flags = flags0 + var flags = flags0 .and(scalacOptions*) .and("-d", targetDir.getPath) .withClasspath(targetDir.getPath) + // We must set -sourceroot for SemanticDB extraction to work properly inside an IDE, + // but we have many existing coverage tests that assume it is not set, so as a workaround: + if !flags.all.contains("-coverage-out") then + flags = flags.and("-sourceroot", TestSources.rootPath().toAbsolutePath.toString) + def compileWithJavac(fs: Array[String]) = if (fs.nonEmpty) { val fullArgs = Array( "-encoding", StandardCharsets.UTF_8.name, @@ -1409,24 +1414,23 @@ trait ParallelTesting extends RunnerOrchestration with CoverageSupport: end CompilationTest /** Create out directory for directory `d` */ - def createOutputDirsForDir(d: JFile, sourceDir: JFile, outDir: String): JFile = { - val targetDir = new JFile(outDir + s"${sourceDir.getName}/${d.getName}") + def createOutputDirsForDir(d: JFile, sourceDir: JFile, outDir: JFile): 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 = { + private def createOutputDirsForFile(file: JFile, sourceDir: JFile, outDir: JFile): JFile = { val uniqueSubdir = file.getName.substring(0, file.getName.lastIndexOf('.')) - val targetDir = new JFile(outDir + s"${sourceDir.getName}${JFile.separatorChar}$uniqueSubdir") + val targetDir = new JFile(outDir, s"${sourceDir.getName}${JFile.separatorChar}$uniqueSubdir") targetDir.mkdirs() targetDir } /** Make sure that directory string is as expected */ - private def checkRequirements(f: String, sourceDir: JFile, outDir: String): Unit = { + private def checkRequirements(f: String, sourceDir: JFile, outDir: JFile): Unit = { require(sourceDir.isDirectory && sourceDir.exists, "passed non-directory to `compileFilesInDir`: " + sourceDir) - require(outDir.last == JFile.separatorChar, "please specify an `outDir` with a trailing file separator") } /** Separates directories from files and returns them as `(dirs, files)` */ @@ -1440,11 +1444,10 @@ trait ParallelTesting extends RunnerOrchestration with CoverageSupport: /** Compiles a single file from the string path `f` using the supplied flags */ def compileFile(f: String, flags: TestFlags)(implicit testGroup: TestGroup): CompilationTest = { - val sourceFile = new JFile(f) + val sourceFile = TestSources.getPath(f).toFile val parent = sourceFile.getParentFile val outDir = - defaultOutputDir + testGroup + JFile.separator + - sourceFile.getName.substring(0, sourceFile.getName.lastIndexOf('.')) + JFile.separator + new JFile(new JFile(defaultOutputDir, testGroup.name), sourceFile.getName.substring(0, sourceFile.getName.lastIndexOf('.'))) require( sourceFile.exists && !sourceFile.isDirectory && @@ -1469,8 +1472,8 @@ trait ParallelTesting extends RunnerOrchestration with CoverageSupport: * can be used for randomization. */ def compileDir(f: String, flags: TestFlags, randomOrder: Option[Int] = None, recursive: Boolean = true)(using testGroup: TestGroup): CompilationTest = { - val outDir = defaultOutputDir + testGroup + JFile.separator - val sourceDir = new JFile(f) + val outDir = new JFile(defaultOutputDir, testGroup.name) + val sourceDir = TestSources.getPath(f).toFile checkRequirements(f, sourceDir, outDir) def flatten(f: JFile): Array[JFile] = @@ -1488,7 +1491,7 @@ trait ParallelTesting extends RunnerOrchestration with CoverageSupport: } // Directories in which to compile all containing files with `flags`: - val targetDir = new JFile(outDir + JFile.separator + sourceDir.getName + JFile.separator) + val targetDir = new JFile(outDir, sourceDir.getName) targetDir.mkdirs() val target = JointCompilationSource(s"compiling '$f' in test '$testGroup'", randomized, flags, targetDir) @@ -1500,10 +1503,8 @@ trait ParallelTesting extends RunnerOrchestration with CoverageSupport: * dissociated */ def compileList(testName: String, files: List[String], flags: TestFlags)(implicit testGroup: TestGroup): CompilationTest = { - val outDir = defaultOutputDir + testGroup + JFile.separator + testName + JFile.separator - // Directories in which to compile all containing files with `flags`: - val targetDir = new JFile(outDir) + val targetDir = new JFile(new JFile(defaultOutputDir, testGroup.name), testName) targetDir.mkdirs() assert(targetDir.exists, s"couldn't create target directory: $targetDir") @@ -1531,8 +1532,8 @@ trait ParallelTesting extends RunnerOrchestration with CoverageSupport: * the same name as the directory (with the file extension `.check`) */ def compileFilesInDir(f: String, flags: TestFlags, fileFilter: FileFilter = FileFilter.NoFilter)(implicit testGroup: TestGroup): CompilationTest = { - val outDir = defaultOutputDir + testGroup + JFile.separator - val sourceDir = new JFile(f) + val outDir = new JFile(defaultOutputDir, testGroup.name) + val sourceDir = TestSources.getPath(f).toFile checkRequirements(f, sourceDir, outDir) val (dirs, files) = compilationTargets(sourceDir, fileFilter) @@ -1571,9 +1572,8 @@ trait ParallelTesting extends RunnerOrchestration with CoverageSupport: * Tests in the first part of the tuple must be executed before the second. * Both testsRequires explicit delete(). */ - def compileTastyInDir(f: String, flags0: TestFlags, fromTastyFilter: FileFilter)( - implicit testGroup: TestGroup): TastyCompilationTest = { - val outDir = defaultOutputDir + testGroup + JFile.separator + def compileTastyInDir(f: String, flags0: TestFlags, fromTastyFilter: FileFilter)(implicit testGroup: TestGroup): TastyCompilationTest = { + val outDir = new JFile(defaultOutputDir, testGroup.name) val flags = flags0 `and` "-Yretain-trees" val sourceDir = new JFile(f) checkRequirements(f, sourceDir, outDir) @@ -1636,7 +1636,7 @@ trait ParallelTesting extends RunnerOrchestration with CoverageSupport: val semanticDbFlag = "-Xsemanticdb" assert(!flags.options.contains(bestEffortFlag), "Best effort compilation flag should not be added manually") - val outDir = defaultOutputDir + testGroup + JFile.separator + val outDir = new JFile(defaultOutputDir, testGroup.name) val sourceDir = new JFile(f) checkRequirements(f, sourceDir, outDir) @@ -1739,7 +1739,7 @@ trait ParallelTesting extends RunnerOrchestration with CoverageSupport: val step1SourceFiles = step1SourceDir.listFiles val step2SourceFiles = step2SourceDir.listFiles - val outDir = defaultOutputDir + testGroup + JFile.separator + dir.getName().toString + JFile.separator + val outDir = new JFile(new JFile(defaultOutputDir, testGroup.name), dir.getName) val step1OutDir = createOutputDirsForDir(step1SourceDir, step1SourceDir, outDir) val step2OutDir = createOutputDirsForDir(step2SourceDir, step2SourceDir, outDir) @@ -1825,7 +1825,7 @@ trait ParallelTesting extends RunnerOrchestration with CoverageSupport: * tests. */ def compileShallowFilesInDir(f: String, flags: TestFlags)(implicit testGroup: TestGroup): CompilationTest = { - val outDir = defaultOutputDir + testGroup + JFile.separator + val outDir = new JFile(defaultOutputDir, testGroup.name) val sourceDir = new JFile(f) checkRequirements(f, sourceDir, outDir) @@ -1851,7 +1851,8 @@ trait ParallelTesting extends RunnerOrchestration with CoverageSupport: object ParallelTesting: - def defaultOutputDir: String = "out"+JFile.separator + def defaultOutputDirName: String = "out" + JFile.separator + def defaultOutputDir: JFile = TestSources.getPath(defaultOutputDirName).toFile def isSourceFile(f: JFile): Boolean = { val name = f.getName diff --git a/tests/init-global/special/tastySource/B.check b/tests/init-global/special/tastySource/B.check index ece8bc60b7d4..2161dfd0c54f 100644 --- a/tests/init-global/special/tastySource/B.check +++ b/tests/init-global/special/tastySource/B.check @@ -5,8 +5,10 @@ |Reading mutable state of other static objects is forbidden as it breaks initialization-time irrelevance. Calling trace: |├── object B: [ B.scala:1 ] |│ ^ - |├── (no source) [ tastySource/A.scala:2 ] - |├── (no source) [ tastySource/A.scala:4 ] + |├── def foo(fn: => Int) = bar(fn) [ A.scala:2 ] + |│ ^^^^^^^ + |├── def bar(fn: => Int) = fn [ A.scala:4 ] + |│ ^^ |├── var y = A.foo(bar) * 2 [ B.scala:2 ] |│ ^^^ |└── def bar = C.n * 3 // warn [ B.scala:4 ]