Compare module metadata in JVM ABI consistency tests

This commit is contained in:
vladislav.grechko
2024-02-06 12:13:49 +01:00
committed by Space Team
parent ba217ad688
commit b74501ee93
9 changed files with 200 additions and 18 deletions
@@ -5,19 +5,22 @@
package org.jetbrains.kotlin.abicmp.checkers
import org.jetbrains.kotlin.abicmp.reports.MetadataPropertyReport
import org.jetbrains.kotlin.abicmp.reports.NamedDiffEntry
import kotlin.metadata.KmConstructor
import kotlin.metadata.KmFunction
import kotlin.metadata.KmProperty
import kotlin.metadata.KmTypeAlias
import org.jetbrains.kotlin.abicmp.reports.MetadataPropertyReport
import org.jetbrains.kotlin.abicmp.reports.NamedDiffEntry
import kotlin.metadata.jvm.KmModule
import kotlin.metadata.jvm.KmPackageParts
import kotlin.metadata.jvm.UnstableMetadataApi
interface GenericMetadataChecker<T> : Checker {
fun check(metadata1: T, metadata2: T, report: MetadataPropertyReport)
}
abstract class GenericMetadataPropertyChecker<T>(name: String) :
PropertyChecker<String, T>("class.metadata.$name"),
PropertyChecker<String, T>(name),
GenericMetadataChecker<T> {
override fun check(metadata1: T, metadata2: T, report: MetadataPropertyReport) {
@@ -29,22 +32,34 @@ abstract class GenericMetadataPropertyChecker<T>(name: String) :
}
}
@OptIn(UnstableMetadataApi::class)
fun moduleMetadataPropertyChecker(name: String, propertyGetter: (KmModule) -> String) =
object : GenericMetadataPropertyChecker<KmModule>("module.metadata.$name") {
override fun getProperty(node: KmModule) = propertyGetter(node)
}
@OptIn(UnstableMetadataApi::class)
fun packagePartsPropertyChecker(name: String, propertyGetter: (KmPackageParts) -> String) =
object : GenericMetadataPropertyChecker<KmPackageParts>("module.metadata.$name") {
override fun getProperty(node: KmPackageParts) = propertyGetter(node)
}
fun constructorMetadataPropertyChecker(name: String, propertyGetter: (KmConstructor) -> String) =
object : GenericMetadataPropertyChecker<KmConstructor>("constructor.$name") {
object : GenericMetadataPropertyChecker<KmConstructor>("class.metadata.constructor.$name") {
override fun getProperty(node: KmConstructor) = propertyGetter(node)
}
fun functionMetadataPropertyChecker(name: String, propertyGetter: (KmFunction) -> String) =
object : GenericMetadataPropertyChecker<KmFunction>("function.$name") {
object : GenericMetadataPropertyChecker<KmFunction>("class.metadata.function.$name") {
override fun getProperty(node: KmFunction) = propertyGetter(node)
}
fun typeAliasMetadataPropertyChecker(name: String, propertyGetter: (KmTypeAlias) -> String) =
object : GenericMetadataPropertyChecker<KmTypeAlias>("typeAlias.$name") {
object : GenericMetadataPropertyChecker<KmTypeAlias>("class.metadata.typeAlias.$name") {
override fun getProperty(node: KmTypeAlias) = propertyGetter(node)
}
fun propertyMetadataPropertyChecker(name: String, propertyGetter: (KmProperty) -> String) =
object : GenericMetadataPropertyChecker<KmProperty>("property.$name") {
object : GenericMetadataPropertyChecker<KmProperty>("class.metadata.property.$name") {
override fun getProperty(node: KmProperty) = propertyGetter(node)
}
@@ -8,9 +8,9 @@ package org.jetbrains.kotlin.abicmp.reports
import org.jetbrains.kotlin.abicmp.tag
import java.io.PrintWriter
class MetadataPropertyReport(val id: String, val header1: String, val header2: String) : ComparisonReport {
open class MetadataPropertyReport(val id: String, val header1: String, val header2: String) : ComparisonReport {
private val propertyDiffs = ArrayList<NamedDiffEntry>()
protected val propertyDiffs = ArrayList<NamedDiffEntry>()
override fun isEmpty() = propertyDiffs.isEmpty()
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.abicmp.reports
import java.io.PrintWriter
class ModuleMetadataReport(header1: String, header2: String) : MetadataPropertyReport("MODULE METADATA", header1, header2) {
private val packagePartsReports = ArrayList<MetadataPropertyReport>()
override fun isEmpty() = propertyDiffs.isEmpty() && getFilteredPackagePartsReports().isEmpty()
override fun writeAsHtml(output: PrintWriter) {
if (isEmpty()) return
super.writeAsHtml(output)
packagePartsReports.forEach { it.writeAsHtml(output) }
}
fun TextTreeBuilderContext.appendModuleMetadataReport() {
node("MODULE METADATA") {
appendNamedDiffEntries(header1, header2, propertyDiffs, "Property")
for (report in getFilteredPackagePartsReports()) {
with(report) { appendReport() }
}
}
}
fun packagePartsReport(id: String) = MetadataPropertyReport(id, header1, header2).also { packagePartsReports.add(it) }
private fun getFilteredPackagePartsReports() = packagePartsReports.filter { !it.isEmpty() }.sortedBy { it.id }
}
@@ -153,6 +153,23 @@ private val allPackageMetadataCheckers = listOf(
fileFacadeMetadataListChecker("localDelegatedProperties") { loadLocalDelegatedProperties(it).keys.toList() }
)
@OptIn(UnstableMetadataApi::class)
private val allPackagePartsMetadataCheckers = listOf(
packagePartsPropertyChecker("multiFileParts") {
it.multiFileClassParts.toList().map { filePart -> "(${filePart.first}, ${filePart.second})" }.toList().sorted()
.joinToString(prefix = "[", postfix = "]")
},
packagePartsPropertyChecker("fileFacades") { it.fileFacades.toList().sorted().joinToString(prefix = "[", postfix = "]") }
)
@OptIn(UnstableMetadataApi::class)
private val allModuleMetadataCheckers = listOf(
moduleMetadataPropertyChecker("optionalAnnotations") {
it.optionalAnnotationClasses.map { clazz -> clazz.name }.sorted().joinToString(prefix = "[", postfix = "]")
},
moduleMetadataPropertyChecker("packageParts") { it.packageParts.keys.toList().sorted().joinToString(prefix = "[", postfix = "]") }
)
private val allMultifileClassFacadeMetadataCheckers = listOf(
multiFileClassFacadeMetadataListChecker("partClassNames") { it.partClassNames }
)
@@ -190,6 +207,7 @@ inline fun checkerConfiguration(b: CheckerConfigurationBuilder.() -> Unit): Chec
return builder.build()
}
@OptIn(UnstableMetadataApi::class)
class CheckerConfiguration(private val enabledExclusively: Set<String>, private val disabled: Set<String>) {
private fun <T : Checker> List<T>.filterOutDisabled() = filter { it.isEnabled() }
@@ -206,6 +224,8 @@ class CheckerConfiguration(private val enabledExclusively: Set<String>, private
val enabledMultifileClassFacadeMetadataCheckers = allMultifileClassFacadeMetadataCheckers.filterOutDisabled()
val enabledMultifileClassPartMetadataCheckers = allMultifileClassPartMetadataCheckers.filterOutDisabled()
val enabledAllSyntheticClassMetadataCheckers = allSyntheticClassMetadataCheckers.filterOutDisabled()
val enabledModuleMetadataCheckers = allModuleMetadataCheckers.filterOutDisabled()
val enabledPackagePartsMetadataCheckers = allPackagePartsMetadataCheckers.filterOutDisabled()
private fun Checker.isEnabled(): Boolean {
if (enabledExclusively.isNotEmpty() && name !in enabledExclusively) return false
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.abicmp.tasks
import org.jetbrains.kotlin.abicmp.reports.ModuleMetadataReport
import kotlin.metadata.jvm.KotlinModuleMetadata
import kotlin.metadata.jvm.UnstableMetadataApi
@OptIn(UnstableMetadataApi::class)
class ModuleMetadataTask(
private val configuration: CheckerConfiguration,
metadata1Bytes: ByteArray,
metadata2Bytes: ByteArray,
private val report: ModuleMetadataReport,
) : Runnable {
private val metadata1 = metadata1Bytes.toKmModule()
private val metadata2 = metadata2Bytes.toKmModule()
override fun run() {
for (checker in configuration.enabledModuleMetadataCheckers) {
checker.check(metadata1, metadata2, report)
}
checkPackageParts()
}
private fun checkPackageParts() {
val packageParts1 = metadata1.packageParts
val packageParts2 = metadata2.packageParts
val commonIds = packageParts1.keys.intersect(packageParts2.keys).sorted()
for (id in commonIds) {
val packagePart1 = packageParts1[id]!!
val packagePart2 = packageParts2[id]!!
val packagePartsReport = report.packagePartsReport(id)
PackagePartsMetadataTask(configuration, packagePart1, packagePart2, packagePartsReport).run()
}
}
}
@OptIn(UnstableMetadataApi::class)
private fun ByteArray.toKmModule() = KotlinModuleMetadata.read(this).kmModule
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.abicmp.tasks
import org.jetbrains.kotlin.abicmp.reports.MetadataPropertyReport
import kotlin.metadata.jvm.KmPackageParts
import kotlin.metadata.jvm.UnstableMetadataApi
@OptIn(UnstableMetadataApi::class)
class PackagePartsMetadataTask(
private val configuration: CheckerConfiguration,
private val packagePart1: KmPackageParts,
private val packagePart2: KmPackageParts,
private val packagePartsReport: MetadataPropertyReport
): Runnable {
override fun run() {
for (checker in configuration.enabledPackagePartsMetadataCheckers) {
checker.check(packagePart1, packagePart2, packagePartsReport)
}
}
}