Skip to content

Commit a9be390

Browse files
authored
Revive tests for sbt-bridge (#16659)
* `reuseCompilerInstance` option was removed, as now, when we trigger compilation directly via the bridge, a new instance of the compiler is created for each group of sources. However, it looks like this previously available distinction was never used effectively * `objectStandardNames` in expected test results were removed because now the fact that all objects extend `Serializable` is not visible in the typer but added during bytecode generation
2 parents 4afb0fc + 9ad1fdb commit a9be390

10 files changed

+108
-98
lines changed

project/Build.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,15 +1054,13 @@ object Build {
10541054
// with the bootstrapped library on the classpath.
10551055
lazy val `scala3-sbt-bridge-tests` = project.in(file("sbt-bridge/test")).
10561056
dependsOn(dottyCompiler(Bootstrapped) % Test).
1057+
dependsOn(`scala3-sbt-bridge`).
10571058
settings(commonBootstrappedSettings).
10581059
settings(
10591060
Compile / sources := Seq(),
10601061
Test / scalaSource := baseDirectory.value,
10611062
Test / javaSource := baseDirectory.value,
1062-
1063-
// Tests disabled until zinc-api-info cross-compiles with 2.13,
1064-
// alternatively we could just copy in sources the part of zinc-api-info we need.
1065-
Test / sources := Seq()
1063+
libraryDependencies += ("org.scala-sbt" %% "zinc-apiinfo" % "1.8.0" % Test).cross(CrossVersion.for3Use2_13)
10661064
)
10671065

10681066
lazy val `scala3-language-server` = project.in(file("language-server")).

sbt-bridge/test/xsbt/ExtractAPISpecification.scala

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,8 @@ class ExtractAPISpecification {
147147
|""".stripMargin
148148
val compilerForTesting = new ScalaCompilerForUnitTesting
149149
val apis =
150-
compilerForTesting.extractApisFromSrcs(reuseCompilerInstance = false)(List(src1, src2),
151-
List(src2))
152-
val _ :: src2Api1 :: src2Api2 :: Nil = apis.toList
150+
compilerForTesting.extractApisFromSrcs(List(src1, src2), List(src2))
151+
val _ :: src2Api1 :: src2Api2 :: Nil = apis.toList: @unchecked
153152
val namerApi1 = selectNamer(src2Api1)
154153
val namerApi2 = selectNamer(src2Api2)
155154
assertTrue(SameAPI(namerApi1, namerApi2))
@@ -202,7 +201,7 @@ class ExtractAPISpecification {
202201
val srcC8 = "class C8 { self => }"
203202
val compilerForTesting = new ScalaCompilerForUnitTesting
204203
val apis = compilerForTesting
205-
.extractApisFromSrcs(reuseCompilerInstance = true)(
204+
.extractApisFromSrcs(
206205
List(srcX, srcY, srcC1, srcC2, srcC3, srcC4, srcC5, srcC6, srcC8)
207206
)
208207
.map(_.head)

sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,10 @@ class ExtractUsedNamesSpecification {
7979
val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB, srcC, srcD)
8080
val scalaVersion = scala.util.Properties.versionNumberString
8181
val namesA = standardNames ++ Set("Nothing", "Any")
82-
val namesAX = standardNames ++ objectStandardNames ++ Set("x", "T", "A", "Nothing", "Any", "scala")
82+
val namesAX = standardNames ++ Set("x", "T", "A", "Nothing", "Any")
8383
val namesB = Set("A", "Int", "A;init;", "Unit")
84-
val namesC = objectStandardNames ++ Set("B;init;", "B", "Unit")
85-
val namesD = standardNames ++ objectStandardNames ++ Set("C", "X", "foo", "Int", "T")
84+
val namesC = Set("B;init;", "B", "Unit")
85+
val namesD = standardNames ++ Set("C", "X", "foo", "Int", "T")
8686
assertEquals(namesA, usedNames("A"))
8787
assertEquals(namesAX, usedNames("A.X"))
8888
assertEquals(namesB, usedNames("B"))
@@ -131,13 +131,13 @@ class ExtractUsedNamesSpecification {
131131
val compilerForTesting = new ScalaCompilerForUnitTesting
132132
val usedNames = compilerForTesting.extractUsedNamesFromSrc(src1, src2)
133133
val expectedNames_lista =
134-
standardNames ++ objectStandardNames ++ Set("B", "lista", "List", "A")
134+
standardNames ++ Set("B", "lista", "List", "A")
135135
val expectedNames_at =
136-
standardNames ++ objectStandardNames ++ Set("B", "at", "A", "T", "X0", "X1")
136+
standardNames ++ Set("B", "at", "A", "T", "X0", "X1")
137137
val expectedNames_as =
138-
standardNames ++ objectStandardNames ++ Set("B", "as", "S", "Y")
138+
standardNames ++ Set("B", "as", "S", "Y")
139139
val expectedNames_foo =
140-
standardNames ++ objectStandardNames ++
140+
standardNames ++
141141
Set("B",
142142
"foo",
143143
"M",
@@ -146,7 +146,7 @@ class ExtractUsedNamesSpecification {
146146
"???",
147147
"Nothing")
148148
val expectedNames_bar =
149-
standardNames ++ objectStandardNames ++
149+
standardNames ++
150150
Set("B",
151151
"bar",
152152
"P1",
@@ -174,7 +174,7 @@ class ExtractUsedNamesSpecification {
174174
|""".stripMargin
175175
val compilerForTesting = new ScalaCompilerForUnitTesting
176176
val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcFoo, srcBar)
177-
val expectedNames = standardNames ++ objectStandardNames ++ Set("Outer", "TypeInner", "Inner", "Int")
177+
val expectedNames = standardNames ++ Set("Outer", "TypeInner", "Inner", "Int")
178178
assertEquals(expectedNames, usedNames("Bar"))
179179
}
180180

@@ -227,7 +227,7 @@ class ExtractUsedNamesSpecification {
227227
def findPatMatUsages(in: String): Set[String] = {
228228
val compilerForTesting = new ScalaCompilerForUnitTesting
229229
val (_, callback) =
230-
compilerForTesting.compileSrcs(List(List(sealedClass, in)), reuseCompilerInstance = false)
230+
compilerForTesting.compileSrcs(List(List(sealedClass, in)))
231231
val clientNames = callback.usedNamesAndScopes.view.filterKeys(!_.startsWith("base."))
232232

233233
val names: Set[String] = clientNames.flatMap {
@@ -309,9 +309,4 @@ class ExtractUsedNamesSpecification {
309309
// the return type of the default constructor is Unit
310310
"Unit"
311311
)
312-
313-
private val objectStandardNames = Set(
314-
// all Dotty objects extend scala.Serializable
315-
"scala", "Serializable"
316-
)
317312
}

sbt-bridge/test/xsbt/ScalaCompilerForUnitTesting.scala

Lines changed: 26 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
/** Adapted from https://github.com/sbt/sbt/blob/0.13/compile/interface/src/test/scala/xsbt/ScalaCompilerForUnitTesting.scala */
22
package xsbt
33

4-
import xsbti.compile.SingleOutput
4+
import xsbti.compile.{CompileProgress, SingleOutput}
55
import java.io.File
66
import xsbti._
77
import sbt.io.IO
88
import xsbti.api.{ ClassLike, Def, DependencyContext }
99
import DependencyContext._
1010
import xsbt.api.SameAPI
1111
import sbt.internal.util.ConsoleLogger
12+
import dotty.tools.io.PlainFile.toPlainFile
13+
import dotty.tools.xsbt.CompilerBridge
1214

1315
import TestCallback.ExtractedClassDependencies
1416

@@ -32,8 +34,8 @@ class ScalaCompilerForUnitTesting {
3234
* Compiles given source code using Scala compiler and returns API representation
3335
* extracted by ExtractAPI class.
3436
*/
35-
def extractApisFromSrcs(reuseCompilerInstance: Boolean)(srcs: List[String]*): Seq[Seq[ClassLike]] = {
36-
val (tempSrcFiles, analysisCallback) = compileSrcs(srcs.toList, reuseCompilerInstance)
37+
def extractApisFromSrcs(srcs: List[String]*): Seq[Seq[ClassLike]] = {
38+
val (tempSrcFiles, analysisCallback) = compileSrcs(srcs.toList)
3739
tempSrcFiles.map(analysisCallback.apis)
3840
}
3941

@@ -91,7 +93,7 @@ class ScalaCompilerForUnitTesting {
9193
* file system-independent way of testing dependencies between source code "files".
9294
*/
9395
def extractDependenciesFromSrcs(srcs: List[List[String]]): ExtractedClassDependencies = {
94-
val (_, testCallback) = compileSrcs(srcs, reuseCompilerInstance = true)
96+
val (_, testCallback) = compileSrcs(srcs)
9597

9698
val memberRefDeps = testCallback.classDependencies collect {
9799
case (target, src, DependencyByMemberRef) => (src, target)
@@ -117,79 +119,53 @@ class ScalaCompilerForUnitTesting {
117119
* useful to compile macros, which cannot be used in the same compilation run that
118120
* defines them.
119121
*
120-
* The `reuseCompilerInstance` parameter controls whether the same Scala compiler instance
121-
* is reused between compiling source groups. Separate compiler instances can be used to
122-
* test stability of API representation (with respect to pickling) or to test handling of
123-
* binary dependencies.
124-
*
125122
* The sequence of temporary files corresponding to passed snippets and analysis
126123
* callback is returned as a result.
127124
*/
128-
def compileSrcs(groupedSrcs: List[List[String]],
129-
reuseCompilerInstance: Boolean): (Seq[File], TestCallback) = {
130-
// withTemporaryDirectory { temp =>
131-
{
125+
def compileSrcs(groupedSrcs: List[List[String]]): (Seq[File], TestCallback) = {
132126
val temp = IO.createTemporaryDirectory
133127
val analysisCallback = new TestCallback
134128
val classesDir = new File(temp, "classes")
135129
classesDir.mkdir()
136130

137-
lazy val commonCompilerInstanceAndCtx = prepareCompiler(classesDir, analysisCallback, classesDir.toString)
131+
val bridge = new CompilerBridge
138132

139133
val files = for ((compilationUnit, unitId) <- groupedSrcs.zipWithIndex) yield {
140-
// use a separate instance of the compiler for each group of sources to
141-
// have an ability to test for bugs in instability between source and pickled
142-
// representation of types
143-
val (compiler, ctx) = if (reuseCompilerInstance) commonCompilerInstanceAndCtx else
144-
prepareCompiler(classesDir, analysisCallback, classesDir.toString)
145-
val run = compiler.newRun(ctx)
146-
val srcFiles = compilationUnit.toSeq.zipWithIndex map {
147-
case (src, i) =>
134+
val srcFiles = compilationUnit.toSeq.zipWithIndex.map {
135+
(src, i) =>
148136
val fileName = s"Test-$unitId-$i.scala"
149137
prepareSrcFile(temp, fileName, src)
150138
}
151-
val srcFilePaths = srcFiles.map(srcFile => srcFile.getAbsolutePath).toList
152139

153-
run.compile(srcFilePaths)
140+
val virtualSrcFiles = srcFiles.map(file => TestVirtualFile(file.toPath)).toArray
141+
val classesDirPath = classesDir.getAbsolutePath.toString
142+
val output = new SingleOutput:
143+
def getOutputDirectory() = classesDir
144+
145+
bridge.run(
146+
virtualSrcFiles.toArray,
147+
new TestDependencyChanges,
148+
Array("-Yforce-sbt-phases", "-classpath", classesDirPath, "-usejavacp", "-d", classesDirPath),
149+
output,
150+
analysisCallback,
151+
new TestReporter,
152+
new CompileProgress {},
153+
new TestLogger
154+
)
154155

155-
// srcFilePaths.foreach(f => new File(f).delete)
156156
srcFiles
157157
}
158158
(files.flatten.toSeq, analysisCallback)
159-
}
160159
}
161160

162161
def compileSrcs(srcs: String*): (Seq[File], TestCallback) = {
163-
compileSrcs(List(srcs.toList), reuseCompilerInstance = true)
162+
compileSrcs(List(srcs.toList))
164163
}
165164

166165
private def prepareSrcFile(baseDir: File, fileName: String, src: String): File = {
167166
val srcFile = new File(baseDir, fileName)
168167
IO.write(srcFile, src)
169168
srcFile
170169
}
171-
172-
private def prepareCompiler(outputDir: File, analysisCallback: AnalysisCallback, classpath: String = ".") = {
173-
val args = Array.empty[String]
174-
175-
import dotty.tools.dotc.{Compiler, Driver}
176-
import dotty.tools.dotc.core.Contexts._
177-
178-
val driver = new TestDriver
179-
val ctx = (new ContextBase).initialCtx.fresh.setSbtCallback(analysisCallback)
180-
driver.getCompiler(Array("-classpath", classpath, "-usejavacp", "-d", outputDir.getAbsolutePath), ctx)
181-
}
182-
183-
private object ConsoleReporter extends Reporter {
184-
def reset(): Unit = ()
185-
def hasErrors: Boolean = false
186-
def hasWarnings: Boolean = false
187-
def printWarnings(): Unit = ()
188-
def problems(): Array[xsbti.Problem] = Array.empty
189-
def log(problem: xsbti.Problem): Unit = println(problem.message)
190-
def comment(pos: Position, msg: String): Unit = ()
191-
def printSummary(): Unit = ()
192-
}
193-
194170
}
195171

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package xsbt
2+
3+
import xsbti.compile.*
4+
5+
class TestDependencyChanges extends DependencyChanges:
6+
def isEmpty(): Boolean = ???
7+
def modifiedBinaries(): Array[java.io.File] = ???
8+
def modifiedClasses(): Array[String] = ???
9+
def modifiedLibraries(): Array[xsbti.VirtualFileRef] = ???

sbt-bridge/test/xsbt/TestDriver.scala

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

sbt-bridge/test/xsbt/TestLogger.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package xsbt
2+
3+
import java.util.function.Supplier
4+
5+
import xsbti.*
6+
7+
class TestLogger extends Logger:
8+
override def debug(msg: Supplier[String]): Unit = ()
9+
override def error(msg: Supplier[String]): Unit = ()
10+
override def info(msg: Supplier[String]): Unit = ()
11+
override def warn(msg: Supplier[String]): Unit = ()
12+
override def trace(exception: Supplier[Throwable]): Unit = ()
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package xsbt
2+
3+
import xsbti.*
4+
5+
class TestReporter extends Reporter:
6+
private val allProblems = collection.mutable.ListBuffer.empty[Problem]
7+
def comment(position: Position, msg: String): Unit = ()
8+
def hasErrors(): Boolean = allProblems.exists(_.severity == Severity.Error)
9+
def hasWarnings(): Boolean = allProblems.exists(_.severity == Severity.Warn)
10+
def log(problem: Problem): Unit = allProblems.append(problem)
11+
def printSummary(): Unit = ()
12+
def problems(): Array[Problem] = allProblems.toArray
13+
def reset(): Unit = allProblems.clear()
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package xsbt
2+
3+
import xsbti.PathBasedFile
4+
import java.nio.file.{Files, Path}
5+
import scala.io.Source
6+
import scala.io.Codec
7+
8+
class TestVirtualFile(path: Path) extends PathBasedFile:
9+
override def contentHash(): Long = ???
10+
override def input(): java.io.InputStream = Files.newInputStream(path)
11+
override def id(): String = name()
12+
override def name(): String = path.toFile.getName
13+
override def names(): Array[String] = ???
14+
override def toPath(): Path = path

sbt-bridge/test/xsbti/TestCallback.scala

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
package xsbti
33

44
import java.io.File
5+
import java.nio.file.Path
56
import scala.collection.mutable.ArrayBuffer
7+
import xsbti.VirtualFileRef
68
import xsbti.api.ClassLike
79
import xsbti.api.DependencyContext
810
import DependencyContext._
@@ -24,25 +26,28 @@ class TestCallback extends AnalysisCallback
2426
assert(!apis.contains(source), s"startSource can be called only once per source file: $source")
2527
apis(source) = Seq.empty
2628
}
29+
override def startSource(source: VirtualFile): Unit = ???
2730

2831
override def binaryDependency(binary: File, name: String, fromClassName: String, source: File, context: DependencyContext): Unit = {
2932
binaryDependencies += ((binary, name, fromClassName, source, context))
3033
}
34+
override def binaryDependency(binary: Path, name: String, fromClassName: String, source: VirtualFileRef, context: DependencyContext): Unit = ???
3135

32-
def generatedNonLocalClass(source: File,
36+
override def generatedNonLocalClass(source: File,
3337
module: File,
3438
binaryClassName: String,
3539
srcClassName: String): Unit = {
3640
products += ((source, module))
3741
classNames(source) += ((srcClassName, binaryClassName))
3842
()
3943
}
44+
override def generatedNonLocalClass(source: VirtualFileRef, module: Path, binaryClassName: String, srcClassName: String): Unit = ???
4045

41-
def generatedLocalClass(source: File, module: File): Unit = {
46+
override def generatedLocalClass(source: File, module: File): Unit = {
4247
products += ((source, module))
4348
()
4449
}
45-
50+
override def generatedLocalClass(source: VirtualFileRef, module: Path): Unit = ???
4651

4752
override def classDependency(onClassName: String, sourceClassName: String, context: DependencyContext): Unit = {
4853
if (onClassName != sourceClassName) classDependencies += ((onClassName, sourceClassName, context))
@@ -51,15 +56,23 @@ class TestCallback extends AnalysisCallback
5156
override def usedName(className: String, name: String, scopes: EnumSet[UseScope]): Unit = {
5257
usedNamesAndScopes(className) += TestUsedName(name, scopes)
5358
}
59+
5460
override def api(source: File, classApi: ClassLike): Unit = {
5561
apis(source) = classApi +: apis(source)
5662
}
63+
override def api(source: VirtualFileRef, classApi: ClassLike): Unit = ???
64+
5765
override def problem(category: String, pos: xsbti.Position, message: String, severity: xsbti.Severity, reported: Boolean): Unit = ()
5866
override def dependencyPhaseCompleted(): Unit = ()
5967
override def apiPhaseCompleted(): Unit = ()
6068
override def enabled(): Boolean = true
61-
def mainClass(source: File, className: String): Unit = ()
6269

70+
override def mainClass(source: File, className: String): Unit = ()
71+
override def mainClass(source: VirtualFileRef, className: String): Unit = ???
72+
73+
override def classesInOutputJar(): java.util.Set[String] = ???
74+
override def getPickleJarPair(): java.util.Optional[xsbti.T2[Path, Path]] = ???
75+
override def isPickleJava(): Boolean = ???
6376
}
6477

6578
object TestCallback {
@@ -78,14 +91,8 @@ object TestCallback {
7891
}
7992

8093
private def pairsToMultiMap[A, B](pairs: collection.Seq[(A, B)]): Map[A, Set[B]] = {
81-
import scala.collection.mutable.{ HashMap, MultiMap }
82-
val emptyMultiMap = new HashMap[A, scala.collection.mutable.Set[B]] with MultiMap[A, B]
83-
val multiMap = pairs.foldLeft(emptyMultiMap) {
84-
case (acc, (key, value)) =>
85-
acc.addBinding(key, value)
86-
}
87-
// convert all collections to immutable variants
88-
multiMap.toMap.view.mapValues(_.toSet).toMap.withDefaultValue(Set.empty)
94+
pairs.groupBy(_._1).view.mapValues(values => values.map(_._2).toSet)
95+
.toMap.withDefaultValue(Set.empty)
8996
}
9097
}
9198
}

0 commit comments

Comments
 (0)