aboutsummaryrefslogtreecommitdiff
path: root/sbt-bridge
diff options
context:
space:
mode:
authorGuillaume Martres <smarter@ubuntu.com>2017-03-14 18:52:23 +0100
committerGuillaume Martres <smarter@ubuntu.com>2017-03-24 17:51:24 +0100
commitd9f98c2848dbb0ae18ae75159761598fdac894c3 (patch)
treebca5fb32309daf2433289090a26bca01cfcb444b /sbt-bridge
parent6099195216e36efff4ab7175881c6e32e52aae2f (diff)
downloaddotty-d9f98c2848dbb0ae18ae75159761598fdac894c3.tar.gz
dotty-d9f98c2848dbb0ae18ae75159761598fdac894c3.tar.bz2
dotty-d9f98c2848dbb0ae18ae75159761598fdac894c3.zip
sbt-bridge: Synchronize unit tests with sbt 0.13.14
Also fix a bug where the compiler output for the tests ended up in the wrong directory, causing some new tests from 0.13.14 to fail.
Diffstat (limited to 'sbt-bridge')
-rw-r--r--sbt-bridge/test/xsbt/ExtractAPISpecification.scala80
-rw-r--r--sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala39
-rw-r--r--sbt-bridge/test/xsbt/ScalaCompilerForUnitTesting.scala47
-rw-r--r--sbt-bridge/test/xsbti/TestCallback.scala2
4 files changed, 148 insertions, 20 deletions
diff --git a/sbt-bridge/test/xsbt/ExtractAPISpecification.scala b/sbt-bridge/test/xsbt/ExtractAPISpecification.scala
index f5af67e45..4b3b2c51a 100644
--- a/sbt-bridge/test/xsbt/ExtractAPISpecification.scala
+++ b/sbt-bridge/test/xsbt/ExtractAPISpecification.scala
@@ -2,9 +2,8 @@
package xsbt
import org.junit.runner.RunWith
-import xsbti.api.ClassLike
-import xsbti.api.Def
-import xsbt.api.ShowAPI
+import xsbti.api._
+import xsbt.api.DefaultShowAPI
import org.specs2.mutable.Specification
import org.specs2.runner.JUnitRunner
@@ -17,7 +16,7 @@ class ExtractAPISpecification extends Specification {
def stableExistentialNames: Boolean = {
def compileAndGetFooMethodApi(src: String): Def = {
- val compilerForTesting = new ScalaCompilerForUnitTesting
+ val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = false)
val sourceApi = compilerForTesting.extractApiFromSrc(src)
val FooApi = sourceApi.definitions().find(_.name() == "Foo").get.asInstanceOf[ClassLike]
val fooMethodApi = FooApi.structure().declared().find(_.name == "foo").get
@@ -38,8 +37,81 @@ class ExtractAPISpecification extends Specification {
|
}""".stripMargin
val fooMethodApi2 = compileAndGetFooMethodApi(src2)
+
fooMethodApi1 == fooMethodApi2
// Fails because xsbt.api is compiled with Scala 2.10
// SameAPI.apply(fooMethodApi1, fooMethodApi2)
}
+
+ /**
+ * Checks if representation of the inherited Namer class (with a declared self variable) in Global.Foo
+ * is stable between compiling from source and unpickling. We compare extracted APIs of Global when Global
+ * is compiled together with Namers or Namers is compiled first and then Global refers
+ * to Namers by unpickling types from class files.
+ *
+ * See https://github.com/sbt/sbt/issues/2504
+ */
+ "Self variable and no self type" in {
+ def selectNamer(api: SourceAPI): ClassLike = {
+ def selectClass(defs: Iterable[Definition], name: String): ClassLike = defs.collectFirst {
+ case cls: ClassLike if cls.name == name => cls
+ }.get
+ val global = selectClass(api.definitions, "Global")
+ val foo = selectClass(global.structure.declared, "Global.Foo")
+ selectClass(foo.structure.inherited, "Namers.Namer")
+ }
+ val src1 =
+ """|class Namers {
+ | class Namer { thisNamer => }
+ |}
+ |""".stripMargin
+ val src2 =
+ """|class Global {
+ | class Foo extends Namers
+ |}
+ |""".stripMargin
+ val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = false)
+ val apis = compilerForTesting.extractApisFromSrcs(reuseCompilerInstance = false)(List(src1, src2), List(src2))
+ val _ :: src2Api1 :: src2Api2 :: Nil = apis.toList
+ val namerApi1 = selectNamer(src2Api1)
+ val namerApi2 = selectNamer(src2Api2)
+
+ DefaultShowAPI(namerApi1) == DefaultShowAPI(namerApi2)
+ // Fails because xsbt.api is compiled with Scala 2.10
+ // SameAPI(namerApi1, namerApi2)
+ }
+
+ /**
+ * Checks if self type is properly extracted in various cases of declaring a self type
+ * with our without a self variable.
+ */
+ "Self type" in {
+ def collectFirstClass(defs: Array[Definition]): ClassLike = defs.collectFirst {
+ case c: ClassLike => c
+ }.get
+ val srcX = "trait X"
+ val srcY = "trait Y"
+ val srcC1 = "class C1 { this: C1 => }"
+ val srcC2 = "class C2 { thisC: C2 => }"
+ val srcC3 = "class C3 { this: X => }"
+ val srcC4 = "class C4 { thisC: X => }"
+ val srcC5 = "class C5 extends AnyRef with X with Y { self: X with Y => }"
+ val srcC6 = "class C6 extends AnyRef with X { self: X with Y => }"
+ // val srcC7 = "class C7 { _ => }" // DOTTY: Syntax not supported
+ val srcC8 = "class C8 { self => }"
+ val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = false)
+ val apis = compilerForTesting.extractApisFromSrcs(reuseCompilerInstance = true)(
+ List(srcX, srcY, srcC1, srcC2, srcC3, srcC4, srcC5, srcC6, srcC8)
+ ).map(x => collectFirstClass(x.definitions))
+ val emptyType = new EmptyType
+ def hasSelfType(c: ClassLike): Boolean =
+ c.selfType != emptyType
+ val (withSelfType, withoutSelfType) = apis.partition(hasSelfType)
+ // DOTTY: In the scalac ExtractAPI phase, the self-type is only
+ // extracted if it differs from the type of the class for stability
+ // reasons. This isn't necessary in dotty because we always pickle
+ // the self type.
+ withSelfType.map(_.name).toSet === Set("C1", "C2", "C3", "C4", "C5", "C6", "C8")
+ withoutSelfType.map(_.name).toSet === Set("X", "Y")
+ }
}
diff --git a/sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala b/sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala
index ed463a3e6..6cff284fe 100644
--- a/sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala
+++ b/sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala
@@ -75,6 +75,7 @@ class ExtractUsedNamesSpecification extends Specification {
|}""".stripMargin
val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true)
val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB)
+
// DOTTY TODO: "Int" is not actually used, but we collect it because
// it's the inferred return type so it appears in a TypeTree
// We could avoid this by checking if the untyped tree has a return type
@@ -84,6 +85,44 @@ class ExtractUsedNamesSpecification extends Specification {
usedNames === expectedNames
}
+ "extract names in the types of trees" in {
+ val src1 = """|class X0
+ |class X1 extends X0
+ |class Y
+ |class A {
+ | type T >: X1 <: X0
+ |}
+ |class M
+ |class N
+ |class P0
+ |class P1 extends P0
+ |object B {
+ | type S = Y
+ | val lista: List[A] = ???
+ | val at: A#T = ???
+ | val as: S = ???
+ | def foo(m: M): N = ???
+ | def bar[Param >: P1 <: P0](p: Param): Param = ???
+ |}""".stripMargin
+ val src2 = """|object Test {
+ | val x = B.lista
+ | val y = B.at
+ | val z = B.as
+ | B.foo(???)
+ | B.bar(???)
+ |}""".stripMargin
+ val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true)
+ val usedNames = compilerForTesting.extractUsedNamesFromSrc(src1, src2)
+ val expectedNames = standardNames ++ Set("Test", "Test$", "B", "B$",
+ "Predef", "Predef$", "$qmark$qmark$qmark", "Nothing",
+ "lista", "List", "A",
+ "at", "T", "X1", "X0",
+ "as", "S", "Y",
+ "foo", "M", "N",
+ "bar", "P1", "P0")
+ usedNames === expectedNames
+ }
+
// test for https://github.com/gkossakowski/sbt/issues/3
"used names from the same compilation unit" in {
val src = "class A { def foo: Int = 0; def bar: Int = foo }"
diff --git a/sbt-bridge/test/xsbt/ScalaCompilerForUnitTesting.scala b/sbt-bridge/test/xsbt/ScalaCompilerForUnitTesting.scala
index 409729023..a6b9fa65e 100644
--- a/sbt-bridge/test/xsbt/ScalaCompilerForUnitTesting.scala
+++ b/sbt-bridge/test/xsbt/ScalaCompilerForUnitTesting.scala
@@ -7,7 +7,7 @@ import _root_.scala.tools.nsc.reporters.ConsoleReporter
import _root_.scala.tools.nsc.Settings
import xsbti._
import xsbti.api.SourceAPI
-import sbt.IO.withTemporaryDirectory
+import sbt.IO._
import xsbti.api.ClassLike
import xsbti.api.Definition
import xsbti.api.Def
@@ -21,7 +21,7 @@ import ScalaCompilerForUnitTesting.ExtractedSourceDependencies
* Provides common functionality needed for unit tests that require compiling
* source code using Scala compiler.
*/
-class ScalaCompilerForUnitTesting(nameHashing: Boolean = false) {
+class ScalaCompilerForUnitTesting(nameHashing: Boolean, includeSynthToNameHashing: Boolean = false) {
import scala.language.reflectiveCalls
/**
@@ -33,6 +33,15 @@ class ScalaCompilerForUnitTesting(nameHashing: Boolean = false) {
analysisCallback.apis(tempSrcFile)
}
+ /**
+ * Compiles given source code using Scala compiler and returns API representation
+ * extracted by ExtractAPI class.
+ */
+ def extractApisFromSrcs(reuseCompilerInstance: Boolean)(srcs: List[String]*): Seq[SourceAPI] = {
+ val (tempSrcFiles, analysisCallback) = compileSrcs(srcs.toList, reuseCompilerInstance)
+ tempSrcFiles.map(analysisCallback.apis)
+ }
+
def extractUsedNamesFromSrc(src: String): Set[String] = {
val (Seq(tempSrcFile), analysisCallback) = compileSrcs(src)
analysisCallback.usedNames(tempSrcFile)
@@ -66,7 +75,7 @@ class ScalaCompilerForUnitTesting(nameHashing: Boolean = false) {
def extractDependenciesFromSrcs(srcs: List[Map[Symbol, String]]): ExtractedSourceDependencies = {
val rawGroupedSrcs = srcs.map(_.values.toList)
val symbols = srcs.flatMap(_.keys)
- val (tempSrcFiles, testCallback) = compileSrcs(rawGroupedSrcs)
+ val (tempSrcFiles, testCallback) = compileSrcs(rawGroupedSrcs, reuseCompilerInstance = true)
val fileToSymbol = (tempSrcFiles zip symbols).toMap
val memberRefFileDeps = testCallback.sourceDependencies collect {
@@ -109,19 +118,31 @@ class ScalaCompilerForUnitTesting(nameHashing: Boolean = false) {
* useful to compile macros, which cannot be used in the same compilation run that
* defines them.
*
+ * The `reuseCompilerInstance` parameter controls whether the same Scala compiler instance
+ * is reused between compiling source groups. Separate compiler instances can be used to
+ * test stability of API representation (with respect to pickling) or to test handling of
+ * binary dependencies.
+ *
* The sequence of temporary files corresponding to passed snippets and analysis
* callback is returned as a result.
*/
- private def compileSrcs(groupedSrcs: List[List[String]]): (Seq[File], TestCallback) = {
- withTemporaryDirectory { temp =>
- val analysisCallback = new TestCallback(nameHashing)
+ private def compileSrcs(groupedSrcs: List[List[String]],
+ reuseCompilerInstance: Boolean): (Seq[File], TestCallback) = {
+ // withTemporaryDirectory { temp =>
+ {
+ val temp = createTemporaryDirectory
+ val analysisCallback = new TestCallback(nameHashing, includeSynthToNameHashing)
val classesDir = new File(temp, "classes")
classesDir.mkdir()
- // val (compiler, ctx) = prepareCompiler(classesDir, analysisCallback, classesDir.toString)
+ lazy val commonCompilerInstanceAndCtx = prepareCompiler(classesDir, analysisCallback, classesDir.toString)
val files = for ((compilationUnit, unitId) <- groupedSrcs.zipWithIndex) yield {
- val (compiler, ctx) = prepareCompiler(classesDir, analysisCallback, classesDir.toString)
+ // use a separate instance of the compiler for each group of sources to
+ // have an ability to test for bugs in instability between source and pickled
+ // representation of types
+ val (compiler, ctx) = if (reuseCompilerInstance) commonCompilerInstanceAndCtx else
+ prepareCompiler(classesDir, analysisCallback, classesDir.toString)
val run = compiler.newRun(ctx)
val srcFiles = compilationUnit.toSeq.zipWithIndex map {
case (src, i) =>
@@ -132,7 +153,7 @@ class ScalaCompilerForUnitTesting(nameHashing: Boolean = false) {
run.compile(srcFilePaths)
- srcFilePaths.foreach(f => new File(f).delete)
+ // srcFilePaths.foreach(f => new File(f).delete)
srcFiles
}
(files.flatten.toSeq, analysisCallback)
@@ -140,7 +161,7 @@ class ScalaCompilerForUnitTesting(nameHashing: Boolean = false) {
}
private def compileSrcs(srcs: String*): (Seq[File], TestCallback) = {
- compileSrcs(List(srcs.toList))
+ compileSrcs(List(srcs.toList), reuseCompilerInstance = true)
}
private def prepareSrcFile(baseDir: File, fileName: String, src: String): File = {
@@ -151,10 +172,6 @@ class ScalaCompilerForUnitTesting(nameHashing: Boolean = false) {
private def prepareCompiler(outputDir: File, analysisCallback: AnalysisCallback, classpath: String = ".") = {
val args = Array.empty[String]
- object output extends SingleOutput {
- def outputDirectory: File = outputDir
- override def toString = s"SingleOutput($outputDirectory)"
- }
import dotty.tools.dotc._
import dotty.tools.dotc.core.Contexts._
@@ -171,7 +188,7 @@ class ScalaCompilerForUnitTesting(nameHashing: Boolean = false) {
}
}
val ctx = (new ContextBase).initialCtx.fresh.setSbtCallback(analysisCallback)
- driver.getCompiler(Array("-classpath", classpath, "-usejavacp"), ctx)
+ driver.getCompiler(Array("-classpath", classpath, "-usejavacp", "-d", outputDir.getAbsolutePath), ctx)
}
private object ConsoleReporter extends Reporter {
diff --git a/sbt-bridge/test/xsbti/TestCallback.scala b/sbt-bridge/test/xsbti/TestCallback.scala
index b849e1a80..99c8d963d 100644
--- a/sbt-bridge/test/xsbti/TestCallback.scala
+++ b/sbt-bridge/test/xsbti/TestCallback.scala
@@ -6,7 +6,7 @@ import scala.collection.mutable.ArrayBuffer
import xsbti.api.SourceAPI
import xsbti.DependencyContext._
-class TestCallback(override val nameHashing: Boolean = false) extends AnalysisCallback
+class TestCallback(override val nameHashing: Boolean, override val includeSynthToNameHashing: Boolean) extends AnalysisCallback
{
val sourceDependencies = new ArrayBuffer[(File, File, DependencyContext)]
val binaryDependencies = new ArrayBuffer[(File, String, File, DependencyContext)]