Skip to content

Commit 38d6973

Browse files
committed
IDE tests: compile dependent workspaces
When writing IDE tests that simulate multi-project setups, we need to compile the sources of the workspaces that are depended on, because the dependent workspaces require their class files. This commit changes the `TestServer` so that workspaces that are depended on are compiled during the initialization of the server. When there are no dependency relation between workspaces, no compilation is performed.
1 parent 705992b commit 38d6973

File tree

2 files changed

+73
-25
lines changed

2 files changed

+73
-25
lines changed

language-server/test/dotty/tools/languageserver/util/CodeTester.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ class CodeTester(workspaces: List[Workspace]) {
1818
private val sources = for { workspace <- workspaces
1919
source <- workspace.sources } yield (workspace, source)
2020

21-
private val files = sources.zipWithIndex.map { case ((workspace, source), i) =>
22-
testServer.openCode(source.text, workspace, s"Source${i}.scala")
23-
}
21+
private val files =
22+
for { workspace <- workspaces
23+
(source, id) <- workspace.sources.zipWithIndex } yield {
24+
testServer.openCode(source.text, workspace, s"Source${id}.scala")
25+
}
2426

2527
private val positions: PositionContext = getPositions(files)
2628

language-server/test/dotty/tools/languageserver/util/server/TestServer.scala

Lines changed: 68 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package dotty.tools.languageserver.util.server
22

33
import java.io.PrintWriter
4-
import java.io.File.{separator => sep}
4+
import java.io.File.{pathSeparator, separator}
55
import java.net.URI
66
import java.nio.file.{Files, Path}
77
import java.util
88

9+
import dotty.tools.dotc.Main
10+
import dotty.tools.dotc.reporting.{Reporter, ThrowingReporter}
11+
import dotty.tools.io.Directory
912
import dotty.tools.languageserver.DottyLanguageServer
1013
import dotty.tools.languageserver.util.Code.Workspace
1114
import org.eclipse.lsp4j.{ DidOpenTextDocumentParams, InitializeParams, InitializeResult, TextDocumentItem}
@@ -16,41 +19,38 @@ class TestServer(testFolder: Path, workspaces: List[Workspace]) {
1619
init()
1720

1821
private[this] def init(): InitializeResult = {
22+
var compiledWorkspaces: Set[Workspace] = Set.empty
23+
24+
/** Compile the dependencies of the given workspace, and then the workspace. */
25+
def compileWorkspaceAndDependencies(workspace: Workspace): Unit =
26+
if (!compiledWorkspaces.contains(workspace)) {
27+
workspace.dependsOn.foreach(compileWorkspaceAndDependencies)
28+
compileWorkspace(workspace)
29+
compiledWorkspaces += workspace
30+
}
31+
1932
/**
2033
* Set up given workspace, return JSON config.
2134
*
22-
* This creates the necessary directories to hold the classes and sources. Some values
23-
* are passed via sbt-buildinfo, such as the classpath containing the scala and dotty libaries.
35+
* If the workspace has dependencies, these dependencies are compiled. The classfiles of the
36+
* dependent workspaces are put on the classpath of this workspace.
2437
*
2538
* @param workspace The workspace to configure.
2639
* @return A JSON object representing the configuration for this workspace.
2740
*/
2841
def workspaceSetup(workspace: Workspace): String = {
2942
def showSeq[T](lst: Seq[T]): String = lst.map(elem => '"' + elem.toString + '"').mkString("[ ", ", ", " ]")
3043

31-
def classDirectory(workspace: Workspace): Path = {
32-
val path = testFolder.resolve(workspace.name).resolve("out")
33-
Files.createDirectories(path)
34-
path.toAbsolutePath
35-
}
36-
37-
val dependencyClasspath =
38-
BuildInfo.ideTestsDependencyClasspath.map(_.getAbsolutePath) ++
39-
workspace.dependsOn.map(w => classDirectory(w).toString)
40-
41-
val sourceDirectory: Path = {
42-
val path = TestFile.sourceDir.resolve(workspace.name).toAbsolutePath
43-
Files.createDirectories(path)
44-
path
45-
}
44+
// Compile all the dependencies of this workspace
45+
workspace.dependsOn.foreach(compileWorkspaceAndDependencies)
4646

4747
s"""{
4848
| "id" : "${workspace.name}",
4949
| "compilerVersion" : "${BuildInfo.ideTestsCompilerVersion}",
5050
| "compilerArguments" : ${showSeq(BuildInfo.ideTestsCompilerArguments)},
51-
| "sourceDirectories" : ${showSeq(sourceDirectory :: Nil)},
52-
| "dependencyClasspath" : ${showSeq(dependencyClasspath)},
53-
| "classDirectory" : "${classDirectory(workspace)}"
51+
| "sourceDirectories" : ${showSeq(sourceDirectory(workspace, wipe = false) :: Nil)},
52+
| "dependencyClasspath" : ${showSeq(dependencyClasspath(workspace))},
53+
| "classDirectory" : "${classDirectory(workspace, wipe = false)}"
5454
|}
5555
|""".stripMargin
5656
}
@@ -78,7 +78,7 @@ class TestServer(testFolder: Path, workspaces: List[Workspace]) {
7878
* @return the file opened
7979
*/
8080
def openCode(code: String, workspace: Workspace, fileName: String): TestFile = {
81-
val testFile = new TestFile(workspace.name + sep + fileName)
81+
val testFile = new TestFile(workspace.name + separator + fileName)
8282
val dotdp = new DidOpenTextDocumentParams()
8383
val tdi = new TextDocumentItem()
8484
tdi.setUri(testFile.uri)
@@ -88,4 +88,50 @@ class TestServer(testFolder: Path, workspaces: List[Workspace]) {
8888
testFile
8989
}
9090

91+
private def classDirectory(workspace: Workspace, wipe: Boolean): Path = {
92+
val path = testFolder.resolve(workspace.name).resolve("out")
93+
if (wipe) {
94+
Directory(path).deleteRecursively()
95+
Files.createDirectories(path)
96+
}
97+
path.toAbsolutePath
98+
}
99+
100+
private def dependencyClasspath(workspace: Workspace) =
101+
BuildInfo.ideTestsDependencyClasspath.map(_.getAbsolutePath) ++
102+
workspace.dependsOn.map(w => classDirectory(w, wipe = false).toString)
103+
104+
private def sourceDirectory(workspace: Workspace, wipe: Boolean): Path = {
105+
val path = TestFile.sourceDir.resolve(workspace.name).toAbsolutePath
106+
if (wipe) {
107+
Directory(path).deleteRecursively()
108+
Files.createDirectories(path)
109+
}
110+
path
111+
}
112+
113+
/**
114+
* Sets up the sources of the given workspace, creates the necessary directories
115+
* and compile the sources.
116+
*
117+
* @param workspace The workspace to set up.
118+
*/
119+
private def compileWorkspace(workspace: Workspace): Unit = {
120+
val sourcesDir = sourceDirectory(workspace, wipe = true)
121+
val sources = workspace.sources.zipWithIndex.map { case (src, id) =>
122+
val path = sourcesDir.resolve(s"Source${id}.scala").toAbsolutePath
123+
Files.write(path, src.text.getBytes("UTF-8"))
124+
path.toString
125+
}
126+
127+
val compileOptions =
128+
sources.toArray ++
129+
Array(
130+
"-classpath", dependencyClasspath(workspace).mkString(pathSeparator),
131+
"-d", classDirectory(workspace, wipe = true).toString
132+
)
133+
val reporter = new ThrowingReporter(Reporter.NoReporter)
134+
Main.process(compileOptions, reporter)
135+
}
136+
91137
}

0 commit comments

Comments
 (0)