|
| 1 | +package scala.tools.xsbt |
| 2 | + |
| 3 | +import org.junit.Test |
| 4 | +import org.junit.Assert._ |
| 5 | +import xsbti.api._ |
| 6 | + |
| 7 | +class ExtractAPITest extends BridgeTesting { |
| 8 | + |
| 9 | + @Test |
| 10 | + def `ExtractAPI should extract children of a sealed class`(): Unit = { |
| 11 | + def compileAndGetFooClassApi(src: String): ClassLike = { |
| 12 | + val apis = extractApisFromSrc(src) |
| 13 | + val FooApi = apis.find(_.name() == "Foo").get |
| 14 | + FooApi |
| 15 | + } |
| 16 | + |
| 17 | + val src1 = |
| 18 | + """|sealed abstract class Foo |
| 19 | + |case class C1(x: Int) extends Foo |
| 20 | + |""".stripMargin |
| 21 | + val fooClassApi1 = compileAndGetFooClassApi(src1) |
| 22 | + val src2 = |
| 23 | + """|sealed abstract class Foo |
| 24 | + |case class C1(x: Int) extends Foo |
| 25 | + |case class C2(x: Int) extends Foo |
| 26 | + |""".stripMargin |
| 27 | + val fooClassApi2 = compileAndGetFooClassApi(src2) |
| 28 | + assertFalse(SameAPI(fooClassApi1, fooClassApi2)) |
| 29 | + } |
| 30 | + |
| 31 | + @Test |
| 32 | + def `ExtractAPI should extract correctly the definition type of a package object`(): Unit = { |
| 33 | + val src = "package object foo".stripMargin |
| 34 | + val apis = extractApisFromSrc(src) |
| 35 | + val Seq(fooClassApi) = apis.toSeq |
| 36 | + assertEquals(fooClassApi.definitionType, DefinitionType.PackageModule) |
| 37 | + } |
| 38 | + |
| 39 | + @Test |
| 40 | + def `ExtractAPI should extract nested classes`(): Unit = { |
| 41 | + val src = |
| 42 | + """class A { |
| 43 | + | class B |
| 44 | + |}""".stripMargin |
| 45 | + val apis = extractApisFromSrc(src).map(c => c.name -> c).toMap |
| 46 | + assertEquals(apis.keys, Set("A", "A.B")) |
| 47 | + } |
| 48 | + |
| 49 | + @Test |
| 50 | + def `ExtractAPI should not extract local classes`(): Unit = { |
| 51 | + val src = |
| 52 | + """class A |
| 53 | + |class B |
| 54 | + |class C { def foo: Unit = { class Inner2 extends B } } |
| 55 | + |class D { def foo: Unit = { new B {} } }""".stripMargin |
| 56 | + val apis = extractApisFromSrc(src).map(c => c.name -> c).toMap |
| 57 | + assertEquals(apis.keys, Set("A", "B", "C", "D")) |
| 58 | + } |
| 59 | + |
| 60 | + @Test |
| 61 | + def `ExtractAPI should extract flat (without members) api for a nested class`(): Unit = { |
| 62 | + def compileAndGetFooClassApi(src: String): ClassLike = { |
| 63 | + val apis = extractApisFromSrc(src) |
| 64 | + val FooApi = apis.find(_.name() == "Foo").get |
| 65 | + FooApi |
| 66 | + } |
| 67 | + |
| 68 | + val src1 = |
| 69 | + """class Foo { |
| 70 | + | class A |
| 71 | + |}""".stripMargin |
| 72 | + val fooClassApi1 = compileAndGetFooClassApi(src1) |
| 73 | + val src2 = |
| 74 | + """class Foo { |
| 75 | + | class A { |
| 76 | + | def foo: Int = 123 |
| 77 | + | } |
| 78 | + |}""".stripMargin |
| 79 | + val fooClassApi2 = compileAndGetFooClassApi(src2) |
| 80 | + assertTrue(SameAPI(fooClassApi1, fooClassApi2)) |
| 81 | + } |
| 82 | + |
| 83 | + @Test |
| 84 | + def `ExtractAPI should extract private classes`(): Unit = { |
| 85 | + val src = |
| 86 | + """private class A |
| 87 | + |class B { private class Inner1 extends A } |
| 88 | + |""".stripMargin |
| 89 | + val apis = extractApisFromSrc(src).map(c => c.name -> c).toMap |
| 90 | + assertEquals(apis.keys, Set("A", "B", "B.Inner1")) |
| 91 | + } |
| 92 | + |
| 93 | + @Test |
| 94 | + def `ExtractAPI should give stable names to members of existential types in method signatures`(): Unit = { |
| 95 | + def compileAndGetFooMethodApi(src: String): Def = { |
| 96 | + val sourceApi = extractApisFromSrc(src) |
| 97 | + val FooApi = sourceApi.find(_.name() == "Foo").get |
| 98 | + val fooMethodApi = FooApi.structure().declared().find(_.name == "foo").get |
| 99 | + fooMethodApi.asInstanceOf[Def] |
| 100 | + } |
| 101 | + |
| 102 | + val src1 = |
| 103 | + """class Box[T] |
| 104 | + |class Foo { |
| 105 | + | def foo: Box[_] = null |
| 106 | + |}""".stripMargin |
| 107 | + val fooMethodApi1 = compileAndGetFooMethodApi(src1) |
| 108 | + val src2 = |
| 109 | + """class Box[T] |
| 110 | + |class Foo { |
| 111 | + | def bar: Box[_] = null |
| 112 | + | def foo: Box[_] = null |
| 113 | + |}""".stripMargin |
| 114 | + val fooMethodApi2 = compileAndGetFooMethodApi(src2) |
| 115 | + assertTrue("APIs are not the same.", SameAPI.apply(fooMethodApi1, fooMethodApi2)) |
| 116 | + } |
| 117 | + |
| 118 | + /** |
| 119 | + * Checks if representation of the inherited Namer class (with a declared self variable) in Global.Foo |
| 120 | + * is stable between compiling from source and unpickling. We compare extracted APIs of Global when Global |
| 121 | + * is compiled together with Namers or Namers is compiled first and then Global refers |
| 122 | + * to Namers by unpickling types from class files. |
| 123 | + */ |
| 124 | + @Test |
| 125 | + def `ExtractAPI should make a stable representation of a self variable that has no self type`(): Unit = { |
| 126 | + def selectNamer(apis: Set[ClassLike]): ClassLike = { |
| 127 | + // TODO: this doesn't work yet because inherited classes are not extracted |
| 128 | + apis.find(_.name == "Global.Foo.Namer").get |
| 129 | + } |
| 130 | + |
| 131 | + val src1 = |
| 132 | + """|class Namers { |
| 133 | + | class Namer { thisNamer => } |
| 134 | + |} |
| 135 | + |""".stripMargin |
| 136 | + val src2 = |
| 137 | + """|class Global { |
| 138 | + | class Foo extends Namers |
| 139 | + |} |
| 140 | + |""".stripMargin |
| 141 | + val apis = |
| 142 | + extractApisFromSrcs(List(src1, src2), List(src2)) |
| 143 | + val _ :: src2Api1 :: src2Api2 :: Nil = apis.toList |
| 144 | + val namerApi1 = selectNamer(src2Api1) |
| 145 | + val namerApi2 = selectNamer(src2Api2) |
| 146 | + assertTrue(SameAPI(namerApi1, namerApi2)) |
| 147 | + } |
| 148 | + |
| 149 | + @Test |
| 150 | + def `ExtractAPI should make a different representation for an inherited class`(): Unit = { |
| 151 | + val src = |
| 152 | + """|class A[T] { |
| 153 | + | abstract class AA { def t: T } |
| 154 | + |} |
| 155 | + |class B extends A[Int] |
| 156 | + """.stripMargin |
| 157 | + val apis = extractApisFromSrc(src).map(a => a.name -> a).toMap |
| 158 | + assertEquals(apis.keySet, Set("A", "A.AA", "B", "B.AA")) |
| 159 | + assertNotEquals(apis("A.AA"), apis("B.AA")) |
| 160 | + } |
| 161 | + |
| 162 | + @Test |
| 163 | + def `ExtractAPI should handle package objects and type companions`(): Unit = { |
| 164 | + val src = |
| 165 | + """|package object abc { |
| 166 | + | type BuildInfoKey = BuildInfoKey.Entry[_] |
| 167 | + | object BuildInfoKey { |
| 168 | + | sealed trait Entry[A] |
| 169 | + | } |
| 170 | + |} |
| 171 | + """.stripMargin |
| 172 | + val apis = extractApisFromSrc(src).map(a => a.name -> a).toMap |
| 173 | + assertEquals(apis.keySet, Set("abc.package", "abc.BuildInfoKey", "abc.BuildInfoKey.Entry")) |
| 174 | + } |
| 175 | + |
| 176 | + /** |
| 177 | + * Checks if self type is properly extracted in various cases of declaring a self type |
| 178 | + * with our without a self variable. |
| 179 | + */ |
| 180 | + @Test |
| 181 | + def `ExtractAPI should represent a self type correctly`(): Unit = { |
| 182 | + val srcX = "trait X" |
| 183 | + val srcY = "trait Y" |
| 184 | + val srcC1 = "class C1 { this: C1 => }" |
| 185 | + val srcC2 = "class C2 { thisC: C2 => }" |
| 186 | + val srcC3 = "class C3 { this: X => }" |
| 187 | + val srcC4 = "class C4 { thisC: X => }" |
| 188 | + val srcC5 = "class C5 extends AnyRef with X with Y { self: X with Y => }" |
| 189 | + val srcC6 = "class C6 extends AnyRef with X { self: X with Y => }" |
| 190 | + val srcC7 = "class C7 { _ => }" |
| 191 | + val srcC8 = "class C8 { self => }" |
| 192 | + val apis = extractApisFromSrcs( |
| 193 | + List(srcX, srcY, srcC1, srcC2, srcC3, srcC4, srcC5, srcC6, srcC7, srcC8) |
| 194 | + ).map(_.head) |
| 195 | + val emptyType = EmptyType.of() |
| 196 | + |
| 197 | + def hasSelfType(c: ClassLike): Boolean = |
| 198 | + c.selfType != emptyType |
| 199 | + |
| 200 | + val (withSelfType, withoutSelfType) = apis.partition(hasSelfType) |
| 201 | + assertEquals(withSelfType.map(_.name).toSet, Set("C3", "C4", "C5", "C6")) |
| 202 | + assertEquals(withoutSelfType.map(_.name).toSet, Set("X", "Y", "C1", "C2", "C7", "C8")) |
| 203 | + } |
| 204 | +} |
0 commit comments