Skip to content

Commit c17925f

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 3cb9c2d commit c17925f

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,11 +19,21 @@ 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.
@@ -31,29 +44,16 @@ class TestServer(testFolder: Path, workspaces: List[Workspace]) {
3144
.map(elem => '"' + elem.toString.replace('\\', '/') + '"')
3245
.mkString("[ ", ", ", " ]")
3346

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

5050
s"""{
5151
| "id" : "${workspace.name}",
5252
| "compilerVersion" : "${BuildInfo.ideTestsCompilerVersion}",
5353
| "compilerArguments" : ${showSeq(BuildInfo.ideTestsCompilerArguments)},
54-
| "sourceDirectories" : ${showSeq(sourceDirectory :: Nil)},
55-
| "dependencyClasspath" : ${showSeq(dependencyClasspath)},
56-
| "classDirectory" : "${classDirectory(workspace).toString.replace('\\','/')}"
54+
| "sourceDirectories" : ${showSeq(sourceDirectory(workspace, wipe = false) :: Nil)},
55+
| "dependencyClasspath" : ${showSeq(dependencyClasspath(workspace))},
56+
| "classDirectory" : "${classDirectory(workspace, wipe = false).toString.replace('\\','/')}"
5757
|}
5858
|""".stripMargin
5959
}
@@ -81,7 +81,7 @@ class TestServer(testFolder: Path, workspaces: List[Workspace]) {
8181
* @return the file opened
8282
*/
8383
def openCode(code: String, workspace: Workspace, fileName: String): TestFile = {
84-
val testFile = new TestFile(workspace.name + sep + fileName)
84+
val testFile = new TestFile(workspace.name + separator + fileName)
8585
val dotdp = new DidOpenTextDocumentParams()
8686
val tdi = new TextDocumentItem()
8787
tdi.setUri(testFile.uri)
@@ -91,4 +91,50 @@ class TestServer(testFolder: Path, workspaces: List[Workspace]) {
9191
testFile
9292
}
9393

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

0 commit comments

Comments
 (0)