|
1 | 1 | package com.typesafe.genjavadoc
|
2 | 2 |
|
3 |
| -import org.scalatest.{Matchers, WordSpec} |
4 |
| -import org.scalatest.matchers.{MatchResult, Matcher} |
5 | 3 | import java.net.URLClassLoader
|
6 | 4 | import java.lang.reflect.Modifier
|
7 | 5 |
|
| 6 | +import org.junit.Test |
| 7 | +import org.junit.Assert._ |
| 8 | + |
8 | 9 | import util._
|
9 | 10 |
|
10 | 11 | object SignatureSpec {
|
@@ -44,119 +45,114 @@ object SignatureSpec {
|
44 | 45 |
|
45 | 46 | }
|
46 | 47 |
|
47 |
| -class SignatureSpec extends WordSpec with Matchers { |
| 48 | +class SignatureSpec { |
48 | 49 |
|
49 | 50 | import SignatureSpec._
|
50 | 51 |
|
51 |
| - "The generated java files" must { |
52 |
| - |
53 |
| - "contain the same methods and classes as the original Scala files" in { |
54 |
| - val doc = IO.tempDir("java") |
55 |
| - val docPath = doc.getAbsolutePath |
| 52 | + @Test def `the generated java files contain the same methods and classes as the original Scala files`(): Unit = { |
| 53 | + val doc = IO.tempDir("java") |
| 54 | + val docPath = doc.getAbsolutePath |
56 | 55 |
|
57 |
| - val scalac = new GenJavadocCompiler(Seq( |
58 |
| - s"genjavadoc:out=$docPath", |
59 |
| - "genjavadoc:suppressSynthetic=false" |
60 |
| - )) |
| 56 | + val scalac = new GenJavadocCompiler(Seq( |
| 57 | + s"genjavadoc:out=$docPath", |
| 58 | + "genjavadoc:suppressSynthetic=false" |
| 59 | + )) |
61 | 60 |
|
62 |
| - val javaSources = expectedClasses.map{cls => |
63 |
| - docPath + "/" + cls.replace(".", "/") + ".java" |
64 |
| - } |
65 |
| - val javac = new JavaCompiler |
66 |
| - |
67 |
| - scalac.compile(BasicSpec.sources) |
68 |
| - javac.compile(javaSources) |
69 |
| - |
70 |
| - val scalaCL = new URLClassLoader(Array(scalac.target.getAbsoluteFile.toURI.toURL), classOf[List[_]].getClassLoader) |
71 |
| - val javaCL = new URLClassLoader(Array(javac.target.getAbsoluteFile.toURI.toURL), classOf[List[_]].getClassLoader) |
72 |
| - |
73 |
| - val accProtLvl = Map(1 -> 1, 2 -> 3, 4 -> 2) |
74 |
| - |
75 |
| - /* |
76 |
| - * This translation is necessary for the evil hack that allows things |
77 |
| - * nested in nested objects to be accepted by Javadoc: while the emitted |
78 |
| - * Java code compiles, the name mangling of javac and scalac differs for |
79 |
| - * such nestings, which means that it is impossible to express the Scalac |
80 |
| - * generated byte-code in valid Java source. To make things compile |
81 |
| - * nonetheless we just accept that the types here are nonsense, but they |
82 |
| - * are not usable from Java anyway. |
83 |
| - */ |
84 |
| - val exception = "(akka.rk.buh.is.it.A\\$[C1D]+\\$)\\$" |
85 |
| - val replacemnt = "$1" |
86 |
| - |
87 |
| - def check(jn: String): Unit = { |
88 |
| - val jc: Class[_] = javaCL.loadClass(jn) |
89 |
| - val sc: Class[_] = scalaCL.loadClass(jn.replaceAll(exception, replacemnt)) |
90 |
| - |
91 |
| - def matchJava(j: Set[String]) = Matcher { (s: Traversable[String]) ⇒ |
92 |
| - MatchResult(s == j, |
93 |
| - s"Scala: \n" + |
| 61 | + val javaSources = expectedClasses.map{cls => |
| 62 | + docPath + "/" + cls.replace(".", "/") + ".java" |
| 63 | + } |
| 64 | + val javac = new JavaCompiler |
| 65 | + |
| 66 | + scalac.compile(BasicSpec.sources) |
| 67 | + javac.compile(javaSources) |
| 68 | + |
| 69 | + val scalaCL = new URLClassLoader(Array(scalac.target.getAbsoluteFile.toURI.toURL), classOf[List[_]].getClassLoader) |
| 70 | + val javaCL = new URLClassLoader(Array(javac.target.getAbsoluteFile.toURI.toURL), classOf[List[_]].getClassLoader) |
| 71 | + |
| 72 | + val accProtLvl = Map(1 -> 1, 2 -> 3, 4 -> 2) |
| 73 | + |
| 74 | + /* |
| 75 | + * This translation is necessary for the evil hack that allows things |
| 76 | + * nested in nested objects to be accepted by Javadoc: while the emitted |
| 77 | + * Java code compiles, the name mangling of javac and scalac differs for |
| 78 | + * such nestings, which means that it is impossible to express the Scalac |
| 79 | + * generated byte-code in valid Java source. To make things compile |
| 80 | + * nonetheless we just accept that the types here are nonsense, but they |
| 81 | + * are not usable from Java anyway. |
| 82 | + */ |
| 83 | + val exception = "(akka.rk.buh.is.it.A\\$[C1D]+\\$)\\$" |
| 84 | + val replacement = "$1" |
| 85 | + |
| 86 | + def check(jn: String): Unit = { |
| 87 | + val jc: Class[_] = javaCL.loadClass(jn) |
| 88 | + val sc: Class[_] = scalaCL.loadClass(jn.replaceAll(exception, replacement)) |
| 89 | + |
| 90 | + def matchJava(s: Set[String], j: Set[String]) = { |
| 91 | + val msg = s"Scala: \n" + |
94 | 92 | s" ${s.mkString("\n ")} \n" +
|
95 | 93 | s"did not match Java: \n" +
|
96 | 94 | s" ${j.mkString("\n ")}\n" +
|
97 |
| - s"(in $jc)", |
98 |
| - s"$s matched $j (in $jc)") |
99 |
| - } |
100 |
| - |
101 |
| - val jm = getMethods(jc, filter = false) |
102 |
| - val sm = getMethods(sc, filter = true) |
103 |
| - printIfNotEmpty(sm -- jm, "missing methods:") |
104 |
| - printIfNotEmpty(jm -- sm, "extraneous methods:") |
105 |
| - sm should matchJava(jm) |
106 |
| - |
107 |
| - val jsub = getClasses(jc, filter = false) |
108 |
| - val ssub = getClasses(sc, filter = true) |
109 |
| - printIfNotEmpty(ssub.keySet -- jsub.keySet, "missing classes:") |
110 |
| - printIfNotEmpty(jsub.keySet -- ssub.keySet, "extraneous classes:") |
111 |
| - ssub.keySet should matchJava(jsub.keySet) |
112 |
| - |
113 |
| - for (n ← ssub.keys) { |
114 |
| - val js = jsub(n) |
115 |
| - val ss = ssub(n) |
116 |
| - |
117 |
| - def beEqual[T: Manifest](t: T) = Matcher { (u: T) ⇒ MatchResult(u == t, s"$u was not equal $t (in $n)", s"$u was equal $t (in $n)") } |
118 |
| - def beAtLeast(t: Int) = Matcher { (u: Int) ⇒ MatchResult(u >= t, s"$u was < $t (in $n)", s"$u was >= $t (in $n)") } |
119 |
| - |
120 |
| - (js.getModifiers & ~15) should beEqual(ss.getModifiers & ~15) |
121 |
| - (ss.getModifiers & 8) should beAtLeast(js.getModifiers & 8) // older Scala versions (2.10) were more STATIC ... |
122 |
| - accProtLvl(js.getModifiers & 7) should beAtLeast(accProtLvl(ss.getModifiers & 7)) |
123 |
| - js.getInterfaces.toList.map(_.getName) should beEqual(ss.getInterfaces.toList.map(_.getName)) |
124 |
| - js.isInterface should beEqual(ss.isInterface) |
125 |
| - if (!js.isInterface()) |
126 |
| - js.getSuperclass.getName should beEqual(ss.getSuperclass.getName) |
127 |
| - check(js.getName) |
128 |
| - } |
| 95 | + s"(in $jc)" |
| 96 | + assertEquals(msg, s, j) |
129 | 97 | }
|
130 | 98 |
|
131 |
| - def printIfNotEmpty(s: Set[String], msg: String): Unit = if (s.nonEmpty) { |
132 |
| - println(msg) |
133 |
| - s.toList.sorted foreach println |
134 |
| - } |
135 |
| - |
136 |
| - def getMethods(c: Class[_], filter: Boolean): Set[String] = { |
137 |
| - c.getDeclaredMethods.filterNot(x ⇒ filter && (defaultFilteredStrings.exists { s => x.getName.contains(s) } |
138 |
| - || javaKeywords.contains(x.getName) |
139 |
| - || x.getName == "$init$" // These synthetic methods show up in 2.12.0-M4+ even though they are not in the generated Java sources |
140 |
| - || (filter && Modifier.isStatic(x.getModifiers) && x.getName.endsWith("$")) // These synthetic static methods appear since 2.12.0-M5+ as companions to default methods |
141 |
| - || startsWithNumber.findFirstIn(x.getName).isDefined)) |
142 |
| - .map(_.toGenericString) |
143 |
| - .map(_.replace("default ", "abstract ")) // Scala 2.12.0-M4+ creates default methods for trait method implementations. We treat them as abstract for now. |
144 |
| - .map(_.replaceAll(exception, replacemnt)) |
145 |
| - .toSet |
| 99 | + val jm = getMethods(jc, filter = false) |
| 100 | + val sm = getMethods(sc, filter = true) |
| 101 | + printIfNotEmpty(sm -- jm, "missing methods:") |
| 102 | + printIfNotEmpty(jm -- sm, "extraneous methods:") |
| 103 | + matchJava(sm, jm) |
| 104 | + |
| 105 | + val jsub = getClasses(jc, filter = false) |
| 106 | + val ssub = getClasses(sc, filter = true) |
| 107 | + printIfNotEmpty(ssub.keySet -- jsub.keySet, "missing classes:") |
| 108 | + printIfNotEmpty(jsub.keySet -- ssub.keySet, "extraneous classes:") |
| 109 | + matchJava(ssub.keySet, jsub.keySet) |
| 110 | + |
| 111 | + for (n ← ssub.keys) { |
| 112 | + val js = jsub(n) |
| 113 | + val ss = ssub(n) |
| 114 | + |
| 115 | + def beEqual[T](u: T, t: T) = assertEquals(s"$u was not equal $t (in $n)", t, u) |
| 116 | + def beAtLeast(u: Int, t: Int) = assertTrue(s"$u was < $t (in $n)", u >= t) |
| 117 | + |
| 118 | + beEqual(js.getModifiers & ~15, ss.getModifiers & ~15) |
| 119 | + beAtLeast(ss.getModifiers & 8, js.getModifiers & 8) // older Scala versions (2.10) were more STATIC ... |
| 120 | + beAtLeast(accProtLvl(js.getModifiers & 7), accProtLvl(ss.getModifiers & 7)) |
| 121 | + beEqual(js.getInterfaces.toList.map(_.getName), ss.getInterfaces.toList.map(_.getName)) |
| 122 | + beEqual(js.isInterface, ss.isInterface) |
| 123 | + if (!js.isInterface()) |
| 124 | + beEqual(js.getSuperclass.getName, ss.getSuperclass.getName) |
| 125 | + check(js.getName) |
146 | 126 | }
|
| 127 | + } |
147 | 128 |
|
148 |
| - def getClasses(c: Class[_], filter: Boolean): Map[String, Class[_]] = { |
149 |
| - c.getDeclaredClasses.collect { |
150 |
| - case x if (!filter || !(x.getName contains "anon")) => x.getName.replaceAll(exception, replacemnt) -> x |
151 |
| - }.toMap |
152 |
| - } |
| 129 | + def printIfNotEmpty(s: Set[String], msg: String): Unit = if (s.nonEmpty) { |
| 130 | + println(msg) |
| 131 | + s.toList.sorted foreach println |
| 132 | + } |
153 | 133 |
|
| 134 | + def getMethods(c: Class[_], filter: Boolean): Set[String] = { |
| 135 | + c.getDeclaredMethods.filterNot(x ⇒ filter && (defaultFilteredStrings.exists { s => x.getName.contains(s) } |
| 136 | + || javaKeywords.contains(x.getName) |
| 137 | + || x.getName == "$init$" // These synthetic methods show up in 2.12.0-M4+ even though they are not in the generated Java sources |
| 138 | + || (filter && Modifier.isStatic(x.getModifiers) && x.getName.endsWith("$")) // These synthetic static methods appear since 2.12.0-M5+ as companions to default methods |
| 139 | + || startsWithNumber.findFirstIn(x.getName).isDefined)) |
| 140 | + .map(_.toGenericString) |
| 141 | + .map(_.replace("default ", "abstract ")) // Scala 2.12.0-M4+ creates default methods for trait method implementations. We treat them as abstract for now. |
| 142 | + .map(_.replaceAll(exception, replacement)) |
| 143 | + .toSet |
| 144 | + } |
154 | 145 |
|
155 |
| - for (className <- expectedClasses) { |
156 |
| - check(className) |
157 |
| - } |
| 146 | + def getClasses(c: Class[_], filter: Boolean): Map[String, Class[_]] = { |
| 147 | + c.getDeclaredClasses.collect { |
| 148 | + case x if (!filter || !(x.getName contains "anon")) => x.getName.replaceAll(exception, replacement) -> x |
| 149 | + }.toMap |
158 | 150 | }
|
159 | 151 |
|
| 152 | + |
| 153 | + for (className <- expectedClasses) { |
| 154 | + check(className) |
| 155 | + } |
160 | 156 | }
|
161 | 157 |
|
162 | 158 | }
|
0 commit comments