Improve usability of PublicApiTest:
* No need to manually create tests for new modules (scans list of modules)
* Pass test when used with "-Poverwrite.output=true"
diff --git a/binary-compatibility-validator/test/PublicApiTest.kt b/binary-compatibility-validator/test/PublicApiTest.kt
index 5fe5c1e..0384bf7 100644
--- a/binary-compatibility-validator/test/PublicApiTest.kt
+++ b/binary-compatibility-validator/test/PublicApiTest.kt
@@ -5,124 +5,62 @@
package kotlinx.coroutines.experimental.tools
import org.junit.*
-import org.junit.rules.*
+import org.junit.runner.*
+import org.junit.runners.*
import java.io.*
+import java.util.*
import java.util.jar.*
+import kotlin.collections.ArrayList
-class PublicApiTest {
+@RunWith(Parameterized::class)
+class PublicApiTest(
+ private val rootDir: String,
+ private val moduleName: String
+) {
+ companion object {
+ private val apiProps = ClassLoader.getSystemClassLoader()
+ .getResource("api.properties").openStream().use { Properties().apply { load(it) } }
+ private val nonPublicPackages = apiProps.getProperty("packages.internal")!!.split(" ")
- /*
- * How to add a test for your module kotlinx-coroutines-foo?
- *
- * Dump public declarations via PublicApiDump.kt and create file
- * reference-public-api/kotlinx-coroutines-foo.txt with dumped declarations.
- *
- * Then add test:
- *
- * @Test
- * fun kotlinxCorountesFoo() { // <- name pattern should match txt file from reference-public-api
- * snapshotAPIAndCompare($relative_path_to_module)
- * }
- */
-
- @Rule
- @JvmField
- val testName = TestName()
-
- @Test
- fun kotlinxCoroutinesCore() {
- snapshotAPIAndCompare("core/kotlinx-coroutines-core", nonPublicPackages = listOf(
- "kotlinx.coroutines.experimental.internal",
- "kotlinx.coroutines.experimental.scheduling"))
+ @Parameterized.Parameters(name = "{1}")
+ @JvmStatic
+ fun modules(): List<Array<Any>> {
+ val moduleRoots = apiProps.getProperty("module.roots").split(" ")
+ val moduleMarker = apiProps.getProperty("module.marker")!!
+ val moduleIgnore = apiProps.getProperty("module.ignore")!!.split(" ").toSet()
+ val modules = ArrayList<Array<Any>>()
+ for (rootDir in moduleRoots) {
+ File("../$rootDir").listFiles( FileFilter { it.isDirectory })?.forEach { dir ->
+ if (dir.name !in moduleIgnore && File(dir, moduleMarker).exists()) {
+ modules += arrayOf<Any>(rootDir, dir.name)
+ }
+ }
+ }
+ return modules
+ }
}
@Test
- fun kotlinxCoroutinesReactive() {
- snapshotAPIAndCompare("reactive/kotlinx-coroutines-reactive")
- }
-
- @Test
- fun kotlinxCoroutinesReactor() {
- snapshotAPIAndCompare("reactive/kotlinx-coroutines-reactor")
- }
-
- @Test
- fun kotlinxCoroutinesRx1() {
- snapshotAPIAndCompare("reactive/kotlinx-coroutines-rx1")
- }
-
- @Test
- fun kotlinxCoroutinesRx2() {
- snapshotAPIAndCompare("reactive/kotlinx-coroutines-rx2")
- }
-
- @Test
- fun kotlinxCoroutinesGuava() {
- snapshotAPIAndCompare("integration/kotlinx-coroutines-guava")
- }
-
- @Test
- fun kotlinxCoroutinesJdk8() {
- snapshotAPIAndCompare("integration/kotlinx-coroutines-jdk8")
- }
-
-
- @Test
- fun kotlinxCoroutinesNio() {
- snapshotAPIAndCompare("integration/kotlinx-coroutines-nio")
- }
-
- @Test
- fun kotlinxCoroutinesQuasar() {
- snapshotAPIAndCompare("integration/kotlinx-coroutines-quasar")
- }
-
- @Test
- fun kotlinxCoroutinesAndroid() {
- snapshotAPIAndCompare("ui/kotlinx-coroutines-android")
- }
-
-
- @Test
- fun kotlinxCoroutinesJavafx() {
- snapshotAPIAndCompare("ui/kotlinx-coroutines-javafx")
- }
-
- @Test
- fun kotlinxCoroutinesSwing() {
- snapshotAPIAndCompare("ui/kotlinx-coroutines-swing")
- }
-
- private fun snapshotAPIAndCompare(basePath: String, jarPattern: String = basePath.substring(basePath.indexOf("/") + 1),
- publicPackages: List<String> = emptyList(), nonPublicPackages: List<String> = emptyList()) {
- val base = File("../$basePath/build/libs").absoluteFile.normalize()
- val jarFile = getJarPath(base, jarPattern)
- val kotlinJvmMappingsFiles = listOf(base.resolve("../visibilities.json"))
-
- val publicPackagePrefixes = publicPackages.map { it.replace('.', '/') + '/' }
+ fun testApi() {
+ val libsDir = File("../$rootDir/$moduleName/build/libs").absoluteFile.normalize()
+ val jarFile = getJarPath(libsDir)
+ val kotlinJvmMappingsFiles = listOf(libsDir.resolve("../visibilities.json"))
val visibilities =
kotlinJvmMappingsFiles
- .map { readKotlinVisibilities(it).filterKeys { name -> publicPackagePrefixes.none { name.startsWith(it) } } }
+ .map { readKotlinVisibilities(it) }
.reduce { m1, m2 -> m1 + m2 }
-
val api = getBinaryAPI(JarFile(jarFile), visibilities).filterOutNonPublic(nonPublicPackages)
-
- val target = File("reference-public-api")
- .resolve(testName.methodName.replaceCamelCaseWithDashedLowerCase() + ".txt")
-
- api.dumpAndCompareWith(target)
+ api.dumpAndCompareWith(File("reference-public-api").resolve("$moduleName.txt"))
}
- private fun getJarPath(base: File, jarPattern: String, kotlinVersion: String? = null): File {
- val versionPattern = kotlinVersion?.let { "-" + Regex.escape(it) } ?: ".+"
- val regex = Regex("$jarPattern$versionPattern\\.jar")
- val files = (base.listFiles() ?: throw Exception("Cannot list files in $base"))
+ private fun getJarPath(libsDir: File): File {
+ val regex = Regex("$moduleName-.+\\.jar")
+ val files = (libsDir.listFiles() ?: throw Exception("Cannot list files in $libsDir"))
.filter { it.name.let {
it matches regex
&& !it.endsWith("-sources.jar")
&& !it.endsWith("-javadoc.jar")
&& !it.endsWith("-tests.jar")} }
-
- return files.singleOrNull() ?: throw Exception("No single file matching $regex in $base:\n${files.joinToString("\n")}")
+ return files.singleOrNull() ?: throw Exception("No single file matching $regex in $libsDir:\n${files.joinToString("\n")}")
}
}