Skip to content

Commit ab71732

Browse files
committed
Add compiler bridge unit tests from sbt 0.13
1 parent e434e89 commit ab71732

File tree

6 files changed

+547
-2
lines changed

6 files changed

+547
-2
lines changed

bridge/build.sbt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ lazy val root = (project in file(".")).
66
libraryDependencies := Seq(
77
"org.scala-lang" %% "dotty" % "0.1-SNAPSHOT",
88
"org.scala-sbt" % "interface" % sbtVersion.value,
9-
"org.scala-sbt" % "api" % sbtVersion.value % "test"
9+
"org.scala-sbt" % "api" % sbtVersion.value % "test",
10+
"org.specs2" %% "specs2" % "2.3.11" % "test"
1011
),
1112
publishArtifact in packageDoc := false,
1213
version := "0.1-SNAPSHOT",
@@ -15,5 +16,8 @@ lazy val root = (project in file(".")).
1516
scalaVersion := "2.11.5",
1617
// Ideally, the sources should be published with crossPaths := false and the
1718
// binaries with crossPaths := true, but I haven't figured out how to do this.
18-
crossPaths := false
19+
crossPaths := false,
20+
21+
fork in Test := true,
22+
parallelExecution in Test := false
1923
)
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/** Adapted from https://github.com/sbt/sbt/blob/0.13/compile/interface/src/test/scala/xsbt/DependencySpecification.scala */
2+
package xsbt
3+
4+
import org.junit.runner.RunWith
5+
import xsbti.api.ClassLike
6+
import xsbti.api.Def
7+
import xsbt.api.SameAPI
8+
import org.specs2.mutable.Specification
9+
import org.specs2.runner.JUnitRunner
10+
11+
import ScalaCompilerForUnitTesting.ExtractedSourceDependencies
12+
13+
@RunWith(classOf[JUnitRunner])
14+
class DependencySpecification extends Specification {
15+
16+
"Extracted source dependencies from public members" in {
17+
val sourceDependencies = extractSourceDependenciesPublic
18+
val memberRef = sourceDependencies.memberRef
19+
val inheritance = sourceDependencies.inheritance
20+
memberRef('A) === Set.empty
21+
inheritance('A) === Set.empty
22+
memberRef('B) === Set('A, 'D)
23+
inheritance('B) === Set('D)
24+
memberRef('C) === Set('A)
25+
inheritance('C) === Set.empty
26+
memberRef('D) === Set.empty
27+
inheritance('D) === Set.empty
28+
memberRef('E) === Set.empty
29+
inheritance('E) === Set.empty
30+
memberRef('F) === Set('A, 'B, 'C, 'D, 'E, 'G)
31+
inheritance('F) === Set('A, 'E)
32+
memberRef('H) === Set('B, 'E, 'G)
33+
// aliases and applied type constructors are expanded so we have inheritance dependency on B
34+
inheritance('H) === Set('B, 'E)
35+
}
36+
37+
"Extracted source dependencies from private members" in {
38+
val sourceDependencies = extractSourceDependenciesPrivate
39+
val memberRef = sourceDependencies.memberRef
40+
val inheritance = sourceDependencies.inheritance
41+
memberRef('A) === Set.empty
42+
inheritance('A) === Set.empty
43+
memberRef('B) === Set.empty
44+
inheritance('B) === Set.empty
45+
memberRef('C) === Set('A)
46+
inheritance('C) === Set('A)
47+
memberRef('D) === Set('B)
48+
inheritance('D) === Set('B)
49+
}
50+
51+
"Extracted source dependencies with trait as first parent" in {
52+
val sourceDependencies = extractSourceDependenciesTraitAsFirstPatent
53+
val memberRef = sourceDependencies.memberRef
54+
val inheritance = sourceDependencies.inheritance
55+
memberRef('A) === Set.empty
56+
inheritance('A) === Set.empty
57+
memberRef('B) === Set('A)
58+
inheritance('B) === Set('A)
59+
// verify that memberRef captures the oddity described in documentation of `Relations.inheritance`
60+
// we are mainly interested whether dependency on A is captured in `memberRef` relation so
61+
// the invariant that says that memberRef is superset of inheritance relation is preserved
62+
memberRef('C) === Set('A, 'B)
63+
inheritance('C) === Set('A, 'B)
64+
// same as above but indirect (C -> B -> A), note that only A is visible here
65+
memberRef('D) === Set('A, 'C)
66+
inheritance('D) === Set('A, 'C)
67+
}
68+
69+
/*
70+
"Extracted source dependencies from macro arguments" in {
71+
val sourceDependencies = extractSourceDependenciesFromMacroArgument
72+
val memberRef = sourceDependencies.memberRef
73+
val inheritance = sourceDependencies.inheritance
74+
75+
memberRef('A) === Set('B, 'C)
76+
inheritance('A) === Set.empty
77+
memberRef('B) === Set.empty
78+
inheritance('B) === Set.empty
79+
memberRef('C) === Set.empty
80+
inheritance('C) === Set.empty
81+
}
82+
*/
83+
84+
private def extractSourceDependenciesPublic: ExtractedSourceDependencies = {
85+
val srcA = "class A"
86+
val srcB = "class B extends D[A]"
87+
val srcC = """|class C {
88+
| def a: A = null
89+
|}""".stripMargin
90+
val srcD = "class D[T]"
91+
val srcE = "trait E[T]"
92+
val srcF = "trait F extends A with E[D[B]] { self: G.MyC => }"
93+
val srcG = "object G { type T[x] = B ; type MyC = C }"
94+
// T is a type constructor [x]B
95+
// B extends D
96+
// E verifies the core type gets pulled out
97+
val srcH = "trait H extends G.T[Int] with (E[Int] @unchecked)"
98+
99+
val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true)
100+
val sourceDependencies = compilerForTesting.extractDependenciesFromSrcs('A -> srcA, 'B -> srcB, 'C -> srcC,
101+
'D -> srcD, 'E -> srcE, 'F -> srcF, 'G -> srcG, 'H -> srcH)
102+
sourceDependencies
103+
}
104+
105+
private def extractSourceDependenciesPrivate: ExtractedSourceDependencies = {
106+
val srcA = "class A"
107+
val srcB = "class B"
108+
val srcC = "class C { private class Inner1 extends A }"
109+
val srcD = "class D { def foo: Unit = { class Inner2 extends B } }"
110+
111+
val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true)
112+
val sourceDependencies =
113+
compilerForTesting.extractDependenciesFromSrcs('A -> srcA, 'B -> srcB, 'C -> srcC, 'D -> srcD)
114+
sourceDependencies
115+
}
116+
117+
private def extractSourceDependenciesTraitAsFirstPatent: ExtractedSourceDependencies = {
118+
val srcA = "class A"
119+
val srcB = "trait B extends A"
120+
val srcC = "trait C extends B"
121+
val srcD = "class D extends C"
122+
123+
val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true)
124+
val sourceDependencies =
125+
compilerForTesting.extractDependenciesFromSrcs('A -> srcA, 'B -> srcB, 'C -> srcC, 'D -> srcD)
126+
sourceDependencies
127+
}
128+
129+
/*
130+
private def extractSourceDependenciesFromMacroArgument: ExtractedSourceDependencies = {
131+
val srcA = "class A { println(B.printTree(C.foo)) }"
132+
val srcB = """
133+
|import scala.language.experimental.macros
134+
|import scala.reflect.macros._
135+
|object B {
136+
| def printTree(arg: Any) = macro printTreeImpl
137+
| def printTreeImpl(c: Context)(arg: c.Expr[Any]): c.Expr[String] = {
138+
| val argStr = arg.tree.toString
139+
| val literalStr = c.universe.Literal(c.universe.Constant(argStr))
140+
| c.Expr[String](literalStr)
141+
| }
142+
|}""".stripMargin
143+
val srcC = "object C { val foo = 1 }"
144+
145+
val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true)
146+
val sourceDependencies =
147+
compilerForTesting.extractDependenciesFromSrcs(List(Map('B -> srcB, 'C -> srcC), Map('A -> srcA)))
148+
sourceDependencies
149+
}
150+
*/
151+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/** Adapted from https://github.com/sbt/sbt/blob/0.13/compile/interface/src/test/scala/xsbt/ExtractAPISpecification.scala */
2+
package xsbt
3+
4+
import org.junit.runner.RunWith
5+
import xsbti.api.ClassLike
6+
import xsbti.api.Def
7+
import xsbt.api.ShowAPI
8+
import org.specs2.mutable.Specification
9+
import org.specs2.runner.JUnitRunner
10+
11+
@RunWith(classOf[JUnitRunner])
12+
class ExtractAPISpecification extends Specification {
13+
14+
"Existential types in method signatures" should {
15+
"have stable names" in { stableExistentialNames }
16+
}
17+
18+
def stableExistentialNames: Boolean = {
19+
def compileAndGetFooMethodApi(src: String): Def = {
20+
val compilerForTesting = new ScalaCompilerForUnitTesting
21+
val sourceApi = compilerForTesting.extractApiFromSrc(src)
22+
val FooApi = sourceApi.definitions().find(_.name() == "Foo").get.asInstanceOf[ClassLike]
23+
val fooMethodApi = FooApi.structure().declared().find(_.name == "foo").get
24+
fooMethodApi.asInstanceOf[Def]
25+
}
26+
val src1 = """
27+
|class Box[T]
28+
|class Foo {
29+
| def foo: Box[_] = null
30+
|
31+
}""".stripMargin
32+
val fooMethodApi1 = compileAndGetFooMethodApi(src1)
33+
val src2 = """
34+
|class Box[T]
35+
|class Foo {
36+
| def bar: Box[_] = null
37+
| def foo: Box[_] = null
38+
|
39+
}""".stripMargin
40+
val fooMethodApi2 = compileAndGetFooMethodApi(src2)
41+
fooMethodApi1 == fooMethodApi2
42+
// Fails because xsbt.api is compiled with Scala 2.10
43+
// SameAPI.apply(fooMethodApi1, fooMethodApi2)
44+
}
45+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/** Adapted from https://github.com/sbt/sbt/blob/0.13/compile/interface/src/test/scala/xsbt/ExtractUsedNamesSpecification.scala */
2+
package xsbt
3+
4+
import org.junit.runner.RunWith
5+
import xsbti.api.ClassLike
6+
import xsbti.api.Def
7+
import xsbti.api.Package
8+
import xsbt.api.SameAPI
9+
import org.junit.runners.JUnit4
10+
11+
import org.specs2.mutable.Specification
12+
13+
@RunWith(classOf[JUnit4])
14+
class ExtractUsedNamesSpecification extends Specification {
15+
16+
/**
17+
* Standard names that appear in every compilation unit that has any class
18+
* definition.
19+
*/
20+
private val standardNames = Set(
21+
// All class extend Object
22+
"Object",
23+
// All class have a default constructor called <init>
24+
"<init>",
25+
// the return type of the default constructor is Unit
26+
"Unit"
27+
)
28+
29+
"imported name" in {
30+
val src = """
31+
|package a { class A }
32+
|package b {
33+
| import a.{A => A2}
34+
|}""".stripMargin
35+
val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true)
36+
val usedNames = compilerForTesting.extractUsedNamesFromSrc(src)
37+
val expectedNames = standardNames ++ Set("a", "A", "A2", "b")
38+
usedNames === expectedNames
39+
}
40+
41+
// test covers https://github.com/gkossakowski/sbt/issues/6
42+
"names in type tree" in {
43+
val srcA = """|
44+
|package a {
45+
| class A {
46+
| class C { class D }
47+
| }
48+
| class B[T]
49+
| class BB
50+
|}""".stripMargin
51+
val srcB = """|
52+
|package b {
53+
| abstract class X {
54+
| def foo: a.A#C#D
55+
| def bar: a.B[a.BB]
56+
| }
57+
|}""".stripMargin
58+
val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true)
59+
val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB)
60+
// DOTTY: unlike the scalac sbt phase, this does not contain "X", I believe this is safe
61+
// TODO: report issue against sbt suggesting that they do the same
62+
val expectedNames = standardNames ++ Set("a", "A", "B", "C", "D", "b", "BB")
63+
usedNames === expectedNames
64+
}
65+
66+
// test for https://github.com/gkossakowski/sbt/issues/5
67+
"symbolic names" in {
68+
val srcA = """|
69+
|class A {
70+
| def `=`: Int = 3
71+
|}""".stripMargin
72+
val srcB = """|
73+
|class B {
74+
| def foo(a: A) = a.`=`
75+
|}""".stripMargin
76+
val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true)
77+
val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB)
78+
// DOTTY TODO: "Int" is not actually used, but we collect it because
79+
// it's the inferred return type so it appears in a TypeTree
80+
// We could avoid this by checking if the untyped tree has a return type
81+
// but is it worth it? Revisit this after https://github.com/sbt/sbt/issues/1104
82+
// has landed.
83+
val expectedNames = standardNames ++ Set("A", "a", "$eq", "Int")
84+
usedNames === expectedNames
85+
}
86+
87+
// test for https://github.com/gkossakowski/sbt/issues/3
88+
"used names from the same compilation unit" in {
89+
val src = "class A { def foo: Int = 0; def bar: Int = foo }"
90+
val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true)
91+
val usedNames = compilerForTesting.extractUsedNamesFromSrc(src)
92+
val expectedNames = standardNames ++ Set("A", "foo", "Int")
93+
usedNames === expectedNames
94+
}
95+
96+
// pending test for https://issues.scala-lang.org/browse/SI-7173
97+
"names of constants" in {
98+
val src = "class A { final val foo = 12; def bar: Int = foo }"
99+
val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true)
100+
val usedNames = compilerForTesting.extractUsedNamesFromSrc(src)
101+
val expectedNames = standardNames ++ Set("A", "foo", "Int")
102+
usedNames === expectedNames
103+
}
104+
105+
// pending test for https://github.com/gkossakowski/sbt/issues/4
106+
// TODO: we should fix it by having special treatment of `selectDynamic` and `applyDynamic` calls
107+
"names from method calls on Dynamic" in {
108+
val srcA = """|import scala.language.dynamics
109+
|class A extends Dynamic {
110+
| def selectDynamic(name: String): Int = name.length
111+
|}""".stripMargin
112+
val srcB = "class B { def foo(a: A): Int = a.bla }"
113+
val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true)
114+
val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB)
115+
val expectedNames = standardNames ++ Set("B", "A", "a", "Int", "selectDynamic", "bla")
116+
usedNames === expectedNames
117+
}.pendingUntilFixed("Call to Dynamic is desugared in type checker so Select nodes is turned into string literal.")
118+
119+
}

0 commit comments

Comments
 (0)