[Commonizer] Rework test infrastructure to compare metadata instead of descriptors
This commit is contained in:
@@ -36,6 +36,8 @@ dependencies {
|
||||
|
||||
testImplementation(commonDep("junit:junit"))
|
||||
testImplementation(projectTests(":compiler:tests-common"))
|
||||
testImplementation(project(":kotlinx-metadata-klib")) { isTransitive = false }
|
||||
testImplementation(project(":kotlinx-metadata")) { isTransitive = false }
|
||||
}
|
||||
|
||||
val runCommonizer by tasks.registering(JavaExec::class) {
|
||||
|
||||
+4
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2021 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.
|
||||
*/
|
||||
|
||||
@@ -15,11 +15,8 @@ import org.jetbrains.kotlin.konan.file.File as KFile
|
||||
/**
|
||||
* Provides access to metadata using default compiler's routine.
|
||||
*/
|
||||
// TODO: move to a separate module (kotlin-native-utils-metadata?) to share with C-interop tool?
|
||||
class TrivialLibraryProvider(
|
||||
private val library: MetadataLibrary
|
||||
) : KlibModuleMetadata.MetadataLibraryProvider {
|
||||
|
||||
// TODO: extract to a separate module (kotlin-native-utils-metadata?) to share with C-interop tool?
|
||||
class KotlinMetadataLibraryProvider(private val library: MetadataLibrary) : KlibModuleMetadata.MetadataLibraryProvider {
|
||||
override val moduleHeaderData: ByteArray
|
||||
get() = library.moduleHeaderData
|
||||
|
||||
@@ -34,7 +31,7 @@ class TrivialLibraryProvider(
|
||||
check(libraryPath.exists()) { "Library does not exist: $libraryPath" }
|
||||
|
||||
val library = resolveSingleFileKlib(KFile(libraryPath.absolutePath), strategy = ToolingSingleFileKlibResolveStrategy)
|
||||
return KlibModuleMetadata.read(TrivialLibraryProvider(library))
|
||||
return KlibModuleMetadata.read(KotlinMetadataLibraryProvider(library))
|
||||
}
|
||||
}
|
||||
}
|
||||
+61
-60
@@ -11,8 +11,68 @@ import org.jetbrains.kotlin.descriptors.commonizer.utils.KNI_BRIDGE_FUNCTION_PRE
|
||||
import java.util.*
|
||||
import kotlin.reflect.KProperty0
|
||||
|
||||
// TODO: move to kotlinx-metadata library?
|
||||
// TODO: extract to kotlinx-metadata-klib library?
|
||||
class MetadataDeclarationsComparator(private val config: Config = Config.Default) {
|
||||
|
||||
interface Config {
|
||||
val rootPathElement: String
|
||||
get() = "<root>"
|
||||
|
||||
/**
|
||||
* Certain auxiliary metadata entities may be intentionally excluded from comparison.
|
||||
* Ex: Kotlin/Native interface bridge functions.
|
||||
*/
|
||||
fun shouldCheckDeclaration(declaration: Any): Boolean =
|
||||
when (declaration) {
|
||||
is KmFunction -> !declaration.name.startsWith(KNI_BRIDGE_FUNCTION_PREFIX)
|
||||
else -> true
|
||||
}
|
||||
|
||||
companion object Default : Config
|
||||
}
|
||||
|
||||
sealed class Result {
|
||||
object Success : Result() {
|
||||
override fun toString() = "Success"
|
||||
}
|
||||
|
||||
class Failure(val mismatches: Collection<Mismatch>) : Result() {
|
||||
init {
|
||||
check(mismatches.isNotEmpty())
|
||||
}
|
||||
|
||||
override fun toString() = "Failure (${mismatches.size} mismatches)"
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Mismatch {
|
||||
abstract val kind: String
|
||||
abstract val name: String
|
||||
abstract val path: List<String>
|
||||
|
||||
// an entity has different non-nullable values
|
||||
data class DifferentValues(
|
||||
override val kind: String,
|
||||
override val name: String,
|
||||
override val path: List<String>,
|
||||
val valueA: Any,
|
||||
val valueB: Any
|
||||
) : Mismatch()
|
||||
|
||||
// an entity is missing at one side and present at another side,
|
||||
// or: an entity has nullable value at one side and non-nullable value at another side
|
||||
data class MissingEntity(
|
||||
override val kind: String,
|
||||
override val name: String,
|
||||
override val path: List<String>,
|
||||
val existentValue: Any,
|
||||
val missingInA: Boolean
|
||||
) : Mismatch() {
|
||||
val missingInB: Boolean
|
||||
get() = !missingInA
|
||||
}
|
||||
}
|
||||
|
||||
private val mismatches = mutableListOf<Mismatch>()
|
||||
|
||||
private class Context(pathElement: String, parent: Context? = null) {
|
||||
@@ -831,62 +891,3 @@ class MetadataDeclarationsComparator(private val config: Config = Config.Default
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Config {
|
||||
val rootPathElement: String
|
||||
get() = "<root>"
|
||||
|
||||
/**
|
||||
* Certain auxiliary metadata entities may be intentionally excluded from comparison.
|
||||
* Ex: Kotlin/Native interface bridge functions.
|
||||
*/
|
||||
fun shouldCheckDeclaration(declaration: Any): Boolean =
|
||||
when (declaration) {
|
||||
is KmFunction -> !declaration.name.startsWith(KNI_BRIDGE_FUNCTION_PREFIX)
|
||||
else -> true
|
||||
}
|
||||
|
||||
companion object Default : Config
|
||||
}
|
||||
|
||||
sealed class Result {
|
||||
object Success : Result() {
|
||||
override fun toString() = "Success"
|
||||
}
|
||||
|
||||
class Failure(val mismatches: Collection<Mismatch>) : Result() {
|
||||
init {
|
||||
check(mismatches.isNotEmpty())
|
||||
}
|
||||
|
||||
override fun toString() = "Failure (${mismatches.size} mismatches)"
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Mismatch {
|
||||
abstract val kind: String
|
||||
abstract val name: String
|
||||
abstract val path: List<String>
|
||||
|
||||
// an entity has different non-nullable values
|
||||
data class DifferentValues(
|
||||
override val kind: String,
|
||||
override val name: String,
|
||||
override val path: List<String>,
|
||||
val valueA: Any,
|
||||
val valueB: Any
|
||||
) : Mismatch()
|
||||
|
||||
// an entity is missing at one side and present at another side,
|
||||
// or: an entity has nullable value at one side and non-nullable value at another side
|
||||
data class MissingEntity(
|
||||
override val kind: String,
|
||||
override val name: String,
|
||||
override val path: List<String>,
|
||||
val presentValue: Any,
|
||||
val missingInA: Boolean
|
||||
) : Mismatch() {
|
||||
val missingInB: Boolean
|
||||
get() = !missingInA
|
||||
}
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.descriptors.commonizer.metadata.utils
|
||||
|
||||
import kotlinx.metadata.klib.KlibModuleMetadata
|
||||
|
||||
class SerializedMetadataLibraryProvider(
|
||||
override val moduleHeaderData: ByteArray,
|
||||
fragments: List<List<ByteArray>>,
|
||||
fragmentNames: List<String>
|
||||
) : KlibModuleMetadata.MetadataLibraryProvider {
|
||||
private val fragmentMap: Map<String, Map<String, ByteArray>>
|
||||
|
||||
init {
|
||||
check(fragments.size == fragmentNames.size)
|
||||
|
||||
fragmentMap = fragmentNames.mapIndexed { fragmentIndex, fragmentName ->
|
||||
// fragmentName is package FQ name, fragmentShortName is right-most part of package FQ name
|
||||
val fragmentShortName = fragmentName.substringAfterLast('.')
|
||||
|
||||
val fragmentParts = fragments[fragmentIndex]
|
||||
val digitCount = fragmentParts.size.toString().length
|
||||
|
||||
// N.B. the same fragment part numbering scheme as in org.jetbrains.kotlin.library.impl.MetadataWriterImpl
|
||||
val fragmentPartMap = fragmentParts.mapIndexed { partIndex, part ->
|
||||
val partName = partIndex.toString().padStart(digitCount, '0') + "_" + fragmentShortName
|
||||
partName to part
|
||||
}.toMap()
|
||||
|
||||
fragmentName to fragmentPartMap
|
||||
}.toMap()
|
||||
}
|
||||
|
||||
override fun packageMetadataParts(fqName: String): Set<String> {
|
||||
return fragmentMap.getValue(fqName).keys
|
||||
}
|
||||
|
||||
override fun packageMetadata(fqName: String, partName: String): ByteArray {
|
||||
return fragmentMap.getValue(fqName).getValue(partName)
|
||||
}
|
||||
}
|
||||
+28
-16
@@ -11,11 +11,14 @@ import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.analyzer.common.CommonDependenciesContainer
|
||||
import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices
|
||||
import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory
|
||||
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataMonolithicSerializer
|
||||
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
|
||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
@@ -30,6 +33,7 @@ import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBo
|
||||
import org.jetbrains.kotlin.descriptors.impl.FunctionDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.library.SerializedMetadata
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.platform.CommonPlatforms
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
@@ -74,25 +78,21 @@ abstract class AbstractCommonizationFromSourcesTest : KtUsefulTestCase() {
|
||||
val sharedTarget: SharedTarget = analyzedModules.sharedTarget
|
||||
assertEquals(sharedTarget, result.sharedTarget)
|
||||
|
||||
val sharedModuleAsExpected: ModuleDescriptor = analyzedModules.commonizedModules.getValue(sharedTarget)
|
||||
val sharedModuleByCommonizer: ModuleDescriptor =
|
||||
(result.modulesByTargets.getValue(sharedTarget).single() as ModuleResult.Commonized).module!!
|
||||
val sharedModuleAsExpected: SerializedMetadata = analyzedModules.commonizedModules.getValue(sharedTarget)
|
||||
val sharedModuleByCommonizer: SerializedMetadata =
|
||||
(result.modulesByTargets.getValue(sharedTarget).single() as ModuleResult.Commonized).metadata.metadata
|
||||
|
||||
assertValidModule(sharedModuleAsExpected)
|
||||
assertValidModule(sharedModuleByCommonizer)
|
||||
assertModulesAreEqual(sharedModuleAsExpected, sharedModuleByCommonizer, "\"$sharedTarget\" target")
|
||||
assertModulesAreEqual(sharedModuleAsExpected, sharedModuleByCommonizer, sharedTarget)
|
||||
|
||||
val leafTargets: Set<LeafTarget> = analyzedModules.leafTargets
|
||||
assertEquals(leafTargets, result.leafTargets)
|
||||
|
||||
for (leafTarget in leafTargets) {
|
||||
val leafTargetModuleAsExpected: ModuleDescriptor = analyzedModules.commonizedModules.getValue(leafTarget)
|
||||
val leafTargetModuleByCommonizer: ModuleDescriptor =
|
||||
(result.modulesByTargets.getValue(leafTarget).single() as ModuleResult.Commonized).module!!
|
||||
val leafTargetModuleAsExpected: SerializedMetadata = analyzedModules.commonizedModules.getValue(leafTarget)
|
||||
val leafTargetModuleByCommonizer: SerializedMetadata =
|
||||
(result.modulesByTargets.getValue(leafTarget).single() as ModuleResult.Commonized).metadata.metadata
|
||||
|
||||
assertValidModule(leafTargetModuleAsExpected)
|
||||
assertValidModule(leafTargetModuleByCommonizer)
|
||||
assertModulesAreEqual(leafTargetModuleAsExpected, leafTargetModuleByCommonizer, "\"$leafTarget\" target")
|
||||
assertModulesAreEqual(leafTargetModuleAsExpected, leafTargetModuleByCommonizer, leafTarget)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,7 +182,7 @@ private class AnalyzedModuleDependencies(
|
||||
|
||||
private class AnalyzedModules(
|
||||
val originalModules: Map<CommonizerTarget, ModuleDescriptor>,
|
||||
val commonizedModules: Map<CommonizerTarget, ModuleDescriptor>,
|
||||
val commonizedModules: Map<CommonizerTarget, SerializedMetadata>,
|
||||
val dependeeModules: Map<CommonizerTarget, List<ModuleDescriptor>>
|
||||
) {
|
||||
val leafTargets: Set<LeafTarget>
|
||||
@@ -232,11 +232,16 @@ private class AnalyzedModules(
|
||||
parentDisposable: Disposable
|
||||
): AnalyzedModules = with(sourceModuleRoots) {
|
||||
// phase 1: provide the modules that are the dependencies for "original" and "commonized" modules
|
||||
val (dependeeModules, dependencies) = createDependeeModules(sharedTarget, dependeeRoots, parentDisposable)
|
||||
val (dependeeModules: Map<CommonizerTarget, List<ModuleDescriptor>>, dependencies: AnalyzedModuleDependencies) =
|
||||
createDependeeModules(sharedTarget, dependeeRoots, parentDisposable)
|
||||
|
||||
// phase 2: build "original" and "commonized" modules
|
||||
val originalModules = createModules(sharedTarget, originalRoots, dependencies, parentDisposable)
|
||||
val commonizedModules = createModules(sharedTarget, commonizedRoots, dependencies, parentDisposable)
|
||||
val originalModules: Map<CommonizerTarget, ModuleDescriptor> =
|
||||
createModules(sharedTarget, originalRoots, dependencies, parentDisposable)
|
||||
|
||||
val commonizedModules: Map<CommonizerTarget, SerializedMetadata> =
|
||||
createModules(sharedTarget, commonizedRoots, dependencies, parentDisposable)
|
||||
.mapValues { (_, moduleDescriptor) -> serializer.serializeModule(moduleDescriptor) }
|
||||
|
||||
return AnalyzedModules(originalModules, commonizedModules, dependeeModules)
|
||||
}
|
||||
@@ -334,6 +339,13 @@ private class AnalyzedModules(
|
||||
|
||||
return module
|
||||
}
|
||||
|
||||
private val serializer = KlibMetadataMonolithicSerializer(
|
||||
languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
|
||||
metadataVersion = KlibMetadataVersion.INSTANCE,
|
||||
skipExpects = false,
|
||||
project = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-577
@@ -1,577 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.descriptors.commonizer.utils
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.constants.AnnotationValue
|
||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.types.AbbreviatedType
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.reflect.KCallable
|
||||
import kotlin.test.fail
|
||||
|
||||
@ExperimentalContracts
|
||||
internal class ComparingDeclarationsVisitor(
|
||||
val designatorMessage: String
|
||||
) : DeclarationDescriptorVisitor<Unit, ComparingDeclarationsVisitor.Context> {
|
||||
|
||||
inner class Context private constructor(
|
||||
private val actual: DeclarationDescriptor?,
|
||||
private val path: List<String>
|
||||
) {
|
||||
constructor(actual: DeclarationDescriptor?) : this(actual, listOf(actual.toString()))
|
||||
|
||||
fun nextLevel(nextActual: DeclarationDescriptor?) = Context(nextActual, path + nextActual.toString())
|
||||
|
||||
fun nextLevel(customPathElement: String) = Context(actual, path + customPathElement)
|
||||
|
||||
inline fun <reified T> getActualAs() = actual as T
|
||||
|
||||
override fun toString() =
|
||||
"""
|
||||
|Context: ${this@ComparingDeclarationsVisitor.designatorMessage}
|
||||
|Path: ${path.joinToString(separator = " ->\n\t")}"
|
||||
""".trimMargin()
|
||||
}
|
||||
|
||||
override fun visitModuleDeclaration(expected: ModuleDescriptor, context: Context) {
|
||||
val actual = context.getActualAs<ModuleDescriptor>()
|
||||
|
||||
context.assertFieldsEqual(expected::getName, actual::getName)
|
||||
|
||||
fun collectPackageMemberScopes(module: ModuleDescriptor): Map<FqName, MemberScope> = mutableMapOf<FqName, MemberScope>().also {
|
||||
module.collectNonEmptyPackageMemberScopes { packageFqName, memberScope ->
|
||||
if (memberScope.getContributedDescriptors().isNotEmpty())
|
||||
it[packageFqName] = memberScope
|
||||
}
|
||||
}
|
||||
|
||||
val expectedPackageMemberScopes = collectPackageMemberScopes(expected)
|
||||
val actualPackageMemberScopes = collectPackageMemberScopes(actual)
|
||||
|
||||
context.assertSetsEqual(expectedPackageMemberScopes.keys, actualPackageMemberScopes.keys, "sets of packages")
|
||||
|
||||
for (packageFqName in expectedPackageMemberScopes.keys) {
|
||||
val expectedMemberScope = expectedPackageMemberScopes.getValue(packageFqName)
|
||||
val actualMemberScope = actualPackageMemberScopes.getValue(packageFqName)
|
||||
|
||||
visitMemberScopes(expectedMemberScope, actualMemberScope, context.nextLevel("package member scope [$packageFqName]"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitMemberScopes(expected: MemberScope, actual: MemberScope, context: Context) {
|
||||
val expectedProperties = mutableMapOf<PropertyApproximationKey, PropertyDescriptor>()
|
||||
val expectedFunctions = mutableMapOf<FunctionApproximationKey, SimpleFunctionDescriptor>()
|
||||
val expectedClasses = mutableMapOf<FqName, ClassDescriptor>()
|
||||
val expectedTypeAliases = mutableMapOf<FqName, TypeAliasDescriptor>()
|
||||
|
||||
expected.collectMembers(
|
||||
PropertyCollector { expectedProperties[PropertyApproximationKey(it)] = it },
|
||||
FunctionCollector { expectedFunctions[FunctionApproximationKey(it)] = it },
|
||||
ClassCollector { expectedClasses[it.fqNameSafe] = it },
|
||||
TypeAliasCollector { expectedTypeAliases[it.fqNameSafe] = it }
|
||||
)
|
||||
|
||||
val actualProperties = mutableMapOf<PropertyApproximationKey, PropertyDescriptor>()
|
||||
val actualFunctions = mutableMapOf<FunctionApproximationKey, SimpleFunctionDescriptor>()
|
||||
val actualClasses = mutableMapOf<FqName, ClassDescriptor>()
|
||||
val actualTypeAliases = mutableMapOf<FqName, TypeAliasDescriptor>()
|
||||
|
||||
actual.collectMembers(
|
||||
PropertyCollector { actualProperties[PropertyApproximationKey(it)] = it },
|
||||
FunctionCollector { actualFunctions[FunctionApproximationKey(it)] = it },
|
||||
ClassCollector { actualClasses[it.fqNameSafe] = it },
|
||||
TypeAliasCollector { actualTypeAliases[it.fqNameSafe] = it }
|
||||
)
|
||||
|
||||
context.assertSetsEqual(expectedProperties.keys, actualProperties.keys, "sets of properties")
|
||||
|
||||
expectedProperties.forEach { (propertyKey, expectedProperty) ->
|
||||
val actualProperty = actualProperties.getValue(propertyKey)
|
||||
expectedProperty.accept(this, context.nextLevel(actualProperty))
|
||||
}
|
||||
|
||||
context.assertSetsEqual(expectedFunctions.keys, actualFunctions.keys, "sets of functions")
|
||||
|
||||
expectedFunctions.forEach { (functionKey, expectedFunction) ->
|
||||
val actualFunction = actualFunctions.getValue(functionKey)
|
||||
expectedFunction.accept(this, context.nextLevel(actualFunction))
|
||||
}
|
||||
|
||||
context.assertSetsEqual(expectedClasses.keys, actualClasses.keys, "sets of classes")
|
||||
|
||||
expectedClasses.forEach { (classFqName, expectedClass) ->
|
||||
val actualClass = actualClasses.getValue(classFqName)
|
||||
expectedClass.accept(this, context.nextLevel(actualClass))
|
||||
}
|
||||
|
||||
context.assertSetsEqual(expectedTypeAliases.keys, actualTypeAliases.keys, "sets of type aliases")
|
||||
|
||||
expectedTypeAliases.forEach { (typeAliasFqName, expectedTypeAlias) ->
|
||||
val actualTypeAlias = actualTypeAliases.getValue(typeAliasFqName)
|
||||
expectedTypeAlias.accept(this, context.nextLevel(actualTypeAlias))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitFunctionDescriptor(expected: FunctionDescriptor, context: Context) {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val expected = expected as SimpleFunctionDescriptor
|
||||
val actual = context.getActualAs<SimpleFunctionDescriptor>()
|
||||
|
||||
visitAnnotations(expected.annotations, actual.annotations, context.nextLevel("Function annotations"))
|
||||
context.assertFieldsEqual(expected::getName, actual::getName)
|
||||
context.assertFieldsEqual(expected::getVisibility, actual::getVisibility)
|
||||
context.assertFieldsEqual(expected::getModality, actual::getModality)
|
||||
context.assertFieldsEqual(expected::getKind, actual::getKind)
|
||||
context.assertFieldsEqual(expected::isOperator, actual::isOperator)
|
||||
context.assertFieldsEqual(expected::isInfix, actual::isInfix)
|
||||
context.assertFieldsEqual(expected::isInline, actual::isInline)
|
||||
context.assertFieldsEqual(expected::isTailrec, actual::isTailrec)
|
||||
context.assertFieldsEqual(expected::isSuspend, actual::isSuspend)
|
||||
context.assertFieldsEqual(expected::isExternal, actual::isExternal)
|
||||
context.assertFieldsEqual(expected::isExpect, actual::isExpect)
|
||||
context.assertFieldsEqual(expected::hasStableParameterNames, actual::hasStableParameterNames)
|
||||
context.assertFieldsEqual(expected::hasSynthesizedParameterNames, actual::hasSynthesizedParameterNames)
|
||||
|
||||
if (!expected.isActual || actual.kind != CallableMemberDescriptor.Kind.DELEGATION) {
|
||||
context.assertFieldsEqual(expected::isActual, actual::isActual)
|
||||
} /* else {
|
||||
// don't check, because there can be any value in expect.isActual
|
||||
// see org.jetbrains.kotlin.resolve.DelegationResolver
|
||||
} */
|
||||
|
||||
visitType(expected.returnType, actual.returnType, context.nextLevel("Function type"))
|
||||
|
||||
visitValueParameterDescriptorList(expected.valueParameters, actual.valueParameters, context.nextLevel("Function value parameters"))
|
||||
|
||||
visitReceiverParameterDescriptor(expected.extensionReceiverParameter, context.nextLevel(actual.extensionReceiverParameter))
|
||||
visitReceiverParameterDescriptor(expected.dispatchReceiverParameter, context.nextLevel(actual.dispatchReceiverParameter))
|
||||
|
||||
visitTypeParameters(expected.typeParameters, actual.typeParameters, context.nextLevel("Function type parameters"))
|
||||
}
|
||||
|
||||
private fun visitValueParameterDescriptorList(
|
||||
expected: List<ValueParameterDescriptor>,
|
||||
actual: List<ValueParameterDescriptor>,
|
||||
context: Context
|
||||
) {
|
||||
context.assertEquals(expected.size, actual.size, "Size of value parameters list")
|
||||
|
||||
expected.forEachIndexed { index, expectedParam ->
|
||||
val actualParam = actual[index]
|
||||
expectedParam.accept(this, context.nextLevel(actualParam))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitValueParameterDescriptor(expected: ValueParameterDescriptor, context: Context) {
|
||||
val actual = context.getActualAs<ValueParameterDescriptor>()
|
||||
|
||||
visitAnnotations(expected.annotations, actual.annotations, context.nextLevel("Value parameter annotations"))
|
||||
context.assertFieldsEqual(expected::getName, actual::getName)
|
||||
context.assertFieldsEqual(expected::index, actual::index)
|
||||
context.assertFieldsEqual(expected::declaresDefaultValue, actual::declaresDefaultValue)
|
||||
context.assertFieldsEqual(expected::isCrossinline, actual::isCrossinline)
|
||||
context.assertFieldsEqual(expected::isNoinline, actual::isNoinline)
|
||||
visitType(expected.type, actual.type, context.nextLevel("Value parameter type"))
|
||||
visitType(expected.varargElementType, actual.varargElementType, context.nextLevel("Value parameter vararg element type"))
|
||||
}
|
||||
|
||||
private fun visitTypeParameters(expected: List<TypeParameterDescriptor>, actual: List<TypeParameterDescriptor>, context: Context) {
|
||||
context.assertEquals(expected.size, actual.size, "Type parameters list size")
|
||||
|
||||
expected.forEachIndexed { index, expectedParam ->
|
||||
val actualParam = actual[index]
|
||||
visitTypeParameterDescriptor(expectedParam, context.nextLevel(actualParam))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitTypeParameterDescriptor(expected: TypeParameterDescriptor, context: Context) {
|
||||
val actual = context.getActualAs<TypeParameterDescriptor>()
|
||||
|
||||
visitAnnotations(expected.annotations, actual.annotations, context.nextLevel("Type parameter annotations"))
|
||||
context.assertFieldsEqual(expected::getName, actual::getName)
|
||||
context.assertFieldsEqual(expected::getIndex, actual::getIndex)
|
||||
context.assertFieldsEqual(expected::isCapturedFromOuterDeclaration, actual::isCapturedFromOuterDeclaration)
|
||||
context.assertFieldsEqual(expected::isReified, actual::isReified)
|
||||
context.assertFieldsEqual(expected::getVariance, actual::getVariance)
|
||||
|
||||
val expectedUpperBounds = expected.upperBounds
|
||||
val actualUpperBounds = actual.upperBounds
|
||||
|
||||
context.assertEquals(expectedUpperBounds.size, actualUpperBounds.size, "Size of upper bound types")
|
||||
|
||||
expectedUpperBounds.forEachIndexed { index, expectedType ->
|
||||
val actualType = actualUpperBounds[index]
|
||||
visitType(expectedType, actualType, context.nextLevel("Type parameter type"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitClassDescriptor(expected: ClassDescriptor, context: Context) {
|
||||
val actual = context.getActualAs<ClassDescriptor>()
|
||||
|
||||
visitAnnotations(expected.annotations, actual.annotations, context.nextLevel("Class annotations"))
|
||||
context.assertFieldsEqual(expected::getName, actual::getName)
|
||||
context.assertFieldsEqual(expected::getVisibility, actual::getVisibility)
|
||||
context.assertFieldsEqual(expected::getModality, actual::getModality)
|
||||
context.assertFieldsEqual(expected::getKind, actual::getKind)
|
||||
context.assertFieldsEqual(expected::isCompanionObject, actual::isCompanionObject)
|
||||
context.assertFieldsEqual(expected::isData, actual::isData)
|
||||
context.assertFieldsEqual(expected::isInline, actual::isInline)
|
||||
context.assertFieldsEqual(expected::isValue, actual::isValue)
|
||||
context.assertFieldsEqual(expected::isInner, actual::isInner)
|
||||
context.assertFieldsEqual(expected::isExternal, actual::isExternal)
|
||||
context.assertFieldsEqual(expected::isExpect, actual::isExpect)
|
||||
context.assertFieldsEqual(expected::isActual, actual::isActual)
|
||||
|
||||
visitTypeParameters(
|
||||
expected.declaredTypeParameters,
|
||||
actual.declaredTypeParameters,
|
||||
context.nextLevel("Class declared type parameters")
|
||||
)
|
||||
|
||||
if (expected.sealedSubclasses.isNotEmpty() || actual.sealedSubclasses.isNotEmpty()) {
|
||||
val expectedSealedSubclassesFqNames = expected.sealedSubclasses.mapTo(HashSet()) { it.fqNameSafe }
|
||||
val actualSealedSubclassesFqNames = actual.sealedSubclasses.mapTo(HashSet()) { it.fqNameSafe }
|
||||
|
||||
context.assertSetsEqual(expectedSealedSubclassesFqNames, actualSealedSubclassesFqNames, "Sealed subclasses FQ names")
|
||||
}
|
||||
|
||||
val expectedSupertypeSignatures = expected.typeConstructor.supertypes.mapTo(HashSet()) { it.signature }
|
||||
val actualSupertypeSignatures = actual.typeConstructor.supertypes.mapTo(HashSet()) { it.signature }
|
||||
|
||||
context.assertSetsEqual(expectedSupertypeSignatures, actualSupertypeSignatures, "Supertypes signatures")
|
||||
|
||||
if (expected.constructors.isNotEmpty() || actual.constructors.isNotEmpty()) {
|
||||
val expectedConstructors = expected.constructors.associateBy { ConstructorApproximationKey(it) }
|
||||
val actualConstructors = actual.constructors.associateBy { ConstructorApproximationKey(it) }
|
||||
|
||||
context.assertSetsEqual(expectedConstructors.keys, actualConstructors.keys, "sets of class constructors")
|
||||
|
||||
for (key in expectedConstructors.keys) {
|
||||
val expectedConstructor = expectedConstructors.getValue(key)
|
||||
val actualConstructor = actualConstructors.getValue(key)
|
||||
|
||||
visitConstructorDescriptor(expectedConstructor, context.nextLevel(actualConstructor))
|
||||
}
|
||||
}
|
||||
|
||||
visitMemberScopes(
|
||||
expected.unsubstitutedMemberScope,
|
||||
actual.unsubstitutedMemberScope,
|
||||
context.nextLevel("class member scope [${expected.fqNameSafe}]")
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitConstructorDescriptor(expected: ConstructorDescriptor, context: Context) {
|
||||
val actual = context.getActualAs<ConstructorDescriptor>()
|
||||
|
||||
visitAnnotations(expected.annotations, actual.annotations, context.nextLevel("Constructor annotations"))
|
||||
context.assertFieldsEqual(expected::getVisibility, actual::getVisibility)
|
||||
context.assertFieldsEqual(expected::isPrimary, actual::isPrimary)
|
||||
context.assertFieldsEqual(expected::getKind, actual::getKind)
|
||||
context.assertFieldsEqual(expected::hasStableParameterNames, actual::hasStableParameterNames)
|
||||
context.assertFieldsEqual(expected::hasSynthesizedParameterNames, actual::hasSynthesizedParameterNames)
|
||||
context.assertFieldsEqual(expected::isExpect, actual::isExpect)
|
||||
context.assertFieldsEqual(expected::isActual, actual::isActual)
|
||||
|
||||
visitValueParameterDescriptorList(
|
||||
expected.valueParameters,
|
||||
actual.valueParameters,
|
||||
context.nextLevel("Constructor value parameters")
|
||||
)
|
||||
|
||||
visitTypeParameters(expected.typeParameters, actual.typeParameters, context.nextLevel("Constructor type parameters"))
|
||||
}
|
||||
|
||||
override fun visitTypeAliasDescriptor(expected: TypeAliasDescriptor, context: Context) {
|
||||
val actual = context.getActualAs<TypeAliasDescriptor>()
|
||||
|
||||
visitAnnotations(expected.annotations, actual.annotations, context.nextLevel("Type alias annotations"))
|
||||
context.assertFieldsEqual(expected::getName, actual::getName)
|
||||
context.assertFieldsEqual(expected::getVisibility, actual::getVisibility)
|
||||
context.assertFieldsEqual(expected::isActual, actual::isActual)
|
||||
|
||||
visitTypeParameters(
|
||||
expected.declaredTypeParameters,
|
||||
actual.declaredTypeParameters,
|
||||
context.nextLevel("Type alias declared type parameters")
|
||||
)
|
||||
|
||||
visitType(expected.underlyingType, actual.underlyingType, context.nextLevel("Type alias underlying type"))
|
||||
visitType(expected.expandedType, actual.expandedType, context.nextLevel("Type alias expanded type"))
|
||||
}
|
||||
|
||||
override fun visitPropertyDescriptor(expected: PropertyDescriptor, context: Context) {
|
||||
val actual = context.getActualAs<PropertyDescriptor>()
|
||||
|
||||
visitAnnotations(expected.annotations, actual.annotations, context.nextLevel("Property annotations"))
|
||||
context.assertFieldsEqual(expected::getName, actual::getName)
|
||||
context.assertFieldsEqual(expected::getVisibility, actual::getVisibility)
|
||||
context.assertFieldsEqual(expected::getModality, actual::getModality)
|
||||
context.assertFieldsEqual(expected::isVar, actual::isVar)
|
||||
context.assertFieldsEqual(expected::getKind, actual::getKind)
|
||||
context.assertFieldsEqual(expected::isLateInit, actual::isLateInit)
|
||||
context.assertFieldsEqual(expected::isConst, actual::isConst)
|
||||
context.assertFieldsEqual(expected::isExternal, actual::isExternal)
|
||||
context.assertFieldsEqual(expected::isExpect, actual::isExpect)
|
||||
|
||||
if (!expected.isActual || actual.kind != CallableMemberDescriptor.Kind.DELEGATION) {
|
||||
context.assertFieldsEqual(expected::isActual, actual::isActual)
|
||||
} /* else {
|
||||
// don't check, because there can be any value in expect.isActual
|
||||
// see org.jetbrains.kotlin.resolve.DelegationResolver
|
||||
} */
|
||||
|
||||
context.assertFieldsEqual(expected::isDelegated, actual::isDelegated)
|
||||
|
||||
visitAnnotations(
|
||||
expected.delegateField?.annotations,
|
||||
actual.delegateField?.annotations,
|
||||
context.nextLevel("Property delegate field annotations")
|
||||
)
|
||||
visitAnnotations(
|
||||
expected.backingField?.annotations,
|
||||
actual.backingField?.annotations,
|
||||
context.nextLevel("Property backing field annotations")
|
||||
)
|
||||
|
||||
context.assertEquals(expected.compileTimeInitializer.isNull(), actual.compileTimeInitializer.isNull(), "compile-time initializers")
|
||||
visitType(expected.type, actual.type, context.nextLevel("Property type"))
|
||||
|
||||
visitPropertyGetterDescriptor(expected.getter, context.nextLevel(actual.getter))
|
||||
visitPropertySetterDescriptor(expected.setter, context.nextLevel(actual.setter))
|
||||
|
||||
visitReceiverParameterDescriptor(expected.extensionReceiverParameter, context.nextLevel(actual.extensionReceiverParameter))
|
||||
visitReceiverParameterDescriptor(expected.dispatchReceiverParameter, context.nextLevel(actual.dispatchReceiverParameter))
|
||||
|
||||
visitTypeParameters(expected.typeParameters, actual.typeParameters, context.nextLevel("Property type parameters"))
|
||||
}
|
||||
|
||||
override fun visitPropertyGetterDescriptor(expected: PropertyGetterDescriptor?, context: Context) {
|
||||
val actual = context.getActualAs<PropertyGetterDescriptor?>()
|
||||
if (expected === actual) return
|
||||
|
||||
check(actual != null && expected != null)
|
||||
|
||||
visitAnnotations(expected.annotations, actual.annotations, context.nextLevel("Property getter annotations"))
|
||||
context.assertFieldsEqual(expected::isDefault, actual::isDefault)
|
||||
context.assertFieldsEqual(expected::isExternal, actual::isExternal)
|
||||
context.assertFieldsEqual(expected::isInline, actual::isInline)
|
||||
}
|
||||
|
||||
override fun visitPropertySetterDescriptor(expected: PropertySetterDescriptor?, context: Context) {
|
||||
val actual = context.getActualAs<PropertySetterDescriptor?>()
|
||||
if (expected === actual) return
|
||||
|
||||
check(actual != null && expected != null)
|
||||
|
||||
visitAnnotations(expected.annotations, actual.annotations, context.nextLevel("Property setter annotations"))
|
||||
context.assertFieldsEqual(expected::isDefault, actual::isDefault)
|
||||
context.assertFieldsEqual(expected::isExternal, actual::isExternal)
|
||||
context.assertFieldsEqual(expected::isInline, actual::isInline)
|
||||
context.assertFieldsEqual(expected::getVisibility, actual::getVisibility)
|
||||
visitAnnotations(
|
||||
expected.valueParameters.single().annotations,
|
||||
actual.valueParameters.single().annotations,
|
||||
context.nextLevel("Property setter value parameter annotations")
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitReceiverParameterDescriptor(expected: ReceiverParameterDescriptor?, context: Context) {
|
||||
val actual = context.getActualAs<ReceiverParameterDescriptor?>()
|
||||
if (expected === actual) return
|
||||
|
||||
check(actual != null && expected != null)
|
||||
|
||||
visitType(expected.type, actual.type, context.nextLevel("Receiver parameter type"))
|
||||
visitAnnotations(expected.annotations, actual.annotations, context.nextLevel("Receiver parameter annotations"))
|
||||
}
|
||||
|
||||
private fun visitAnnotations(expected: Annotations?, actual: Annotations?, context: Context) {
|
||||
if (expected === actual || (expected?.isEmpty() != false && actual?.isEmpty() != false)) return
|
||||
|
||||
fun AnnotationDescriptor.getMandatoryFqName(): FqName = fqName ?: context.fail("No FQ name for annotation $this")
|
||||
|
||||
val expectedAnnotations: Map<FqName, AnnotationDescriptor> = expected?.associateBy { it.getMandatoryFqName() }.orEmpty()
|
||||
val actualAnnotations: Map<FqName, AnnotationDescriptor> = actual?.associateBy { it.getMandatoryFqName() }.orEmpty()
|
||||
|
||||
context.assertSetsEqual(expectedAnnotations.keys, actualAnnotations.keys, "annotation FQ names")
|
||||
|
||||
for (annotationFqName in expectedAnnotations.keys) {
|
||||
val expectedAnnotation = expectedAnnotations.getValue(annotationFqName)
|
||||
val actualAnnotation = actualAnnotations.getValue(annotationFqName)
|
||||
|
||||
visitAnnotation(expectedAnnotation, actualAnnotation, context.nextLevel("Annotation $annotationFqName"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitAnnotation(expected: AnnotationDescriptor, actual: AnnotationDescriptor, context: Context) {
|
||||
visitType(expected.type, actual.type, context.nextLevel("annotation type"))
|
||||
|
||||
val expectedValueArguments: Map<Name, ConstantValue<*>> = expected.allValueArguments
|
||||
val actualValueArguments: Map<Name, ConstantValue<*>> = actual.allValueArguments
|
||||
|
||||
context.assertSetsEqual(expectedValueArguments.keys, actualValueArguments.keys, "annotation value argument names")
|
||||
|
||||
for (name in expectedValueArguments.keys) {
|
||||
val expectedValueArgument = expectedValueArguments.getValue(name)
|
||||
checkConstantSupportedInCommonization(
|
||||
constantValue = expectedValueArgument,
|
||||
constantName = name,
|
||||
owner = expected,
|
||||
allowAnnotationValues = true,
|
||||
onError = { context.fail(it) }
|
||||
)
|
||||
|
||||
val actualValueArgument = actualValueArguments.getValue(name)
|
||||
checkConstantSupportedInCommonization(
|
||||
constantValue = actualValueArgument,
|
||||
constantName = name,
|
||||
owner = actual,
|
||||
allowAnnotationValues = true,
|
||||
onError = { context.fail(it) }
|
||||
)
|
||||
|
||||
context.assertEquals(expectedValueArgument::class, actualValueArgument::class, "annotation value argument value")
|
||||
if (expectedValueArgument is AnnotationValue && actualValueArgument is AnnotationValue) {
|
||||
context.assertEquals(
|
||||
expectedValueArgument.value.fqName,
|
||||
actualValueArgument.value.fqName,
|
||||
"nested annotation FQ name"
|
||||
)
|
||||
|
||||
visitAnnotation(
|
||||
expectedValueArgument.value,
|
||||
actualValueArgument.value,
|
||||
context.nextLevel("Annotation ${expectedValueArgument.value.fqName}")
|
||||
)
|
||||
} else {
|
||||
context.assertEquals(expectedValueArgument.value, actualValueArgument.value, "annotation value argument value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitType(expected: KotlinType?, actual: KotlinType?, context: Context) {
|
||||
if (expected === actual) return
|
||||
|
||||
check(actual != null && expected != null)
|
||||
|
||||
val expectedUnwrapped = expected.unwrap()
|
||||
val actualUnwrapped = actual.unwrap()
|
||||
|
||||
if (expectedUnwrapped === actualUnwrapped) return
|
||||
|
||||
val expectedAbbreviated = expectedUnwrapped as? AbbreviatedType
|
||||
val actualAbbreviated = actualUnwrapped as? AbbreviatedType
|
||||
|
||||
context.assertEquals(!expectedAbbreviated.isNull(), !actualAbbreviated.isNull(), "type is abbreviated")
|
||||
|
||||
if (expectedAbbreviated != null && actualAbbreviated != null) {
|
||||
visitType(
|
||||
expectedAbbreviated.abbreviation,
|
||||
actualAbbreviated.abbreviation,
|
||||
context.nextLevel("Abbreviation type")
|
||||
)
|
||||
visitType(
|
||||
extractExpandedType(expectedAbbreviated),
|
||||
extractExpandedType(actualAbbreviated),
|
||||
context.nextLevel("Expanded type")
|
||||
)
|
||||
} else {
|
||||
visitAnnotations(
|
||||
expectedUnwrapped.annotations,
|
||||
actualUnwrapped.annotations,
|
||||
context.nextLevel("Type annotations")
|
||||
)
|
||||
|
||||
val expectedId = expectedUnwrapped.declarationDescriptor.run { classId?.asString() ?: name.asString() }
|
||||
val actualId = actualUnwrapped.declarationDescriptor.run { classId?.asString() ?: name.asString() }
|
||||
|
||||
context.assertEquals(expectedId, actualId, "type class ID / name")
|
||||
|
||||
val expectedArguments = expectedUnwrapped.arguments
|
||||
val actualArguments = actualUnwrapped.arguments
|
||||
|
||||
context.assertEquals(expectedArguments.size, actualArguments.size, "size of type arguments list")
|
||||
|
||||
expectedArguments.forEachIndexed { index, expectedArgument ->
|
||||
val actualArgument = actualArguments[index]
|
||||
|
||||
context.assertFieldsEqual(expectedArgument::isStarProjection, actualArgument::isStarProjection)
|
||||
if (!expectedArgument.isStarProjection) {
|
||||
context.assertFieldsEqual(expectedArgument::getProjectionKind, actualArgument::getProjectionKind)
|
||||
visitType(expectedArgument.type, actualArgument.type, context.nextLevel("Type argument type"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> Context.assertEquals(expected: T?, actual: T?, subject: String) {
|
||||
if (expected != actual)
|
||||
fail(
|
||||
buildString {
|
||||
append("Comparing <$subject>:\n")
|
||||
append("\"$expected\" is not equal to \"$actual\"\n")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun <T> Context.assertFieldsEqual(expected: KCallable<T>, actual: KCallable<T>) {
|
||||
val expectedValue = expected.call()
|
||||
val actualValue = actual.call()
|
||||
|
||||
assertEquals(expectedValue, actualValue, "fields \"$expected\"")
|
||||
}
|
||||
|
||||
private fun <T> Context.assertSetsEqual(expected: Set<T>, actual: Set<T>, subject: String) {
|
||||
val expectedMinusActual = expected.subtract(actual)
|
||||
val actualMinusExpected = actual.subtract(expected)
|
||||
|
||||
if (expectedMinusActual.isNotEmpty() || actualMinusExpected.isNotEmpty())
|
||||
fail(
|
||||
buildString {
|
||||
appendLine("Comparing $subject:")
|
||||
appendLine("$expected is not equal to $actual")
|
||||
appendLine("Expected size: ${expected.size}")
|
||||
appendLine("Actual size: ${actual.size}")
|
||||
appendLine("Expected minus actual: $expectedMinusActual")
|
||||
appendLine("Actual minus expected: $actualMinusExpected")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun Context.fail(message: String): Nothing {
|
||||
kotlin.test.fail(
|
||||
buildString {
|
||||
if (message.isNotEmpty()) {
|
||||
if (message.last() != '\n') appendLine(message) else append(message)
|
||||
}
|
||||
append(this@fail.toString())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitPackageViewDescriptor(expected: PackageViewDescriptor, context: Context) =
|
||||
fail("Comparison of package views not supported")
|
||||
|
||||
override fun visitPackageFragmentDescriptor(expected: PackageFragmentDescriptor, context: Context) =
|
||||
fail("Comparison of package fragments not supported")
|
||||
|
||||
override fun visitScriptDescriptor(expected: ScriptDescriptor, context: Context) =
|
||||
fail("Comparison of script descriptors not supported")
|
||||
|
||||
override fun visitVariableDescriptor(expected: VariableDescriptor, context: Context) =
|
||||
fail("Comparison of variables not supported")
|
||||
}
|
||||
+25
-47
@@ -5,15 +5,16 @@
|
||||
|
||||
package org.jetbrains.kotlin.descriptors.commonizer.utils
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import kotlinx.metadata.klib.KlibModuleMetadata
|
||||
import org.jetbrains.kotlin.descriptors.commonizer.CommonizerResult
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.test.util.DescriptorValidator.*
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.descriptors.commonizer.CommonizerTarget
|
||||
import org.jetbrains.kotlin.descriptors.commonizer.metadata.utils.MetadataDeclarationsComparator
|
||||
import org.jetbrains.kotlin.descriptors.commonizer.metadata.utils.MetadataDeclarationsComparator.Result
|
||||
import org.jetbrains.kotlin.descriptors.commonizer.metadata.utils.SerializedMetadataLibraryProvider
|
||||
import org.jetbrains.kotlin.library.SerializedMetadata
|
||||
import java.io.File
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.fail
|
||||
|
||||
fun assertIsDirectory(file: File) {
|
||||
@@ -32,47 +33,24 @@ fun assertCommonizationPerformed(result: CommonizerResult) {
|
||||
}
|
||||
|
||||
@ExperimentalContracts
|
||||
fun assertModulesAreEqual(expected: ModuleDescriptor, actual: ModuleDescriptor, designatorMessage: String) {
|
||||
val visitor = ComparingDeclarationsVisitor(designatorMessage)
|
||||
val context = visitor.Context(actual)
|
||||
fun assertModulesAreEqual(expected: SerializedMetadata, actual: SerializedMetadata, target: CommonizerTarget) {
|
||||
val expectedModule = with(expected) { KlibModuleMetadata.read(SerializedMetadataLibraryProvider(module, fragments, fragmentNames)) }
|
||||
val actualModule = with(actual) { KlibModuleMetadata.read(SerializedMetadataLibraryProvider(module, fragments, fragmentNames)) }
|
||||
|
||||
expected.accept(visitor, context)
|
||||
}
|
||||
|
||||
fun assertValidModule(module: ModuleDescriptor) = validate(
|
||||
object : ValidationVisitor() {
|
||||
override fun validateScope(scopeOwner: DeclarationDescriptor?, scope: MemberScope, collector: DiagnosticCollector) = Unit
|
||||
|
||||
override fun visitModuleDeclaration(descriptor: ModuleDescriptor, collector: DiagnosticCollector): Boolean {
|
||||
assertValid(descriptor)
|
||||
return super.visitModuleDeclaration(descriptor, collector)
|
||||
}
|
||||
|
||||
override fun visitClassDescriptor(descriptor: ClassDescriptor, collector: DiagnosticCollector): Boolean {
|
||||
assertValid(descriptor)
|
||||
return super.visitClassDescriptor(descriptor, collector)
|
||||
}
|
||||
|
||||
override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, collector: DiagnosticCollector): Boolean {
|
||||
assertValid(descriptor)
|
||||
return super.visitFunctionDescriptor(descriptor, collector)
|
||||
}
|
||||
|
||||
override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, collector: DiagnosticCollector): Boolean {
|
||||
assertValid(descriptor)
|
||||
return super.visitPropertyDescriptor(descriptor, collector)
|
||||
}
|
||||
|
||||
override fun visitConstructorDescriptor(constructorDescriptor: ConstructorDescriptor, collector: DiagnosticCollector): Boolean {
|
||||
assertValid(constructorDescriptor)
|
||||
return super.visitConstructorDescriptor(constructorDescriptor, collector)
|
||||
}
|
||||
},
|
||||
module
|
||||
)
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
private inline fun assertValid(descriptor: DeclarationDescriptor) = when (descriptor) {
|
||||
is ModuleDescriptor -> descriptor.assertValid()
|
||||
else -> assertFalse(ErrorUtils.isError(descriptor), "$descriptor is error")
|
||||
when (val result = MetadataDeclarationsComparator().compare(expectedModule, actualModule)) {
|
||||
is Result.Success -> Unit
|
||||
is Result.Failure -> {
|
||||
val mismatches = result.mismatches.sortedBy { it::class.java.simpleName + "_" + it.kind }
|
||||
val digitCount = mismatches.size.toString().length
|
||||
|
||||
val failureMessage = buildString {
|
||||
appendLine("${mismatches.size} mismatches found while comparing module ${expectedModule.name} and ${actualModule.name} for target ${target.prettyName}:")
|
||||
mismatches.forEachIndexed { index, mismatch ->
|
||||
appendLine((index + 1).toString().padStart(digitCount, ' ') + ". " + mismatch)
|
||||
}
|
||||
}
|
||||
|
||||
fail(failureMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user