[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(commonDep("junit:junit"))
|
||||||
testImplementation(projectTests(":compiler:tests-common"))
|
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) {
|
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.
|
* 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.
|
* 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?
|
// TODO: extract to a separate module (kotlin-native-utils-metadata?) to share with C-interop tool?
|
||||||
class TrivialLibraryProvider(
|
class KotlinMetadataLibraryProvider(private val library: MetadataLibrary) : KlibModuleMetadata.MetadataLibraryProvider {
|
||||||
private val library: MetadataLibrary
|
|
||||||
) : KlibModuleMetadata.MetadataLibraryProvider {
|
|
||||||
|
|
||||||
override val moduleHeaderData: ByteArray
|
override val moduleHeaderData: ByteArray
|
||||||
get() = library.moduleHeaderData
|
get() = library.moduleHeaderData
|
||||||
|
|
||||||
@@ -34,7 +31,7 @@ class TrivialLibraryProvider(
|
|||||||
check(libraryPath.exists()) { "Library does not exist: $libraryPath" }
|
check(libraryPath.exists()) { "Library does not exist: $libraryPath" }
|
||||||
|
|
||||||
val library = resolveSingleFileKlib(KFile(libraryPath.absolutePath), strategy = ToolingSingleFileKlibResolveStrategy)
|
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 java.util.*
|
||||||
import kotlin.reflect.KProperty0
|
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) {
|
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 val mismatches = mutableListOf<Mismatch>()
|
||||||
|
|
||||||
private class Context(pathElement: String, parent: Context? = null) {
|
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.CommonDependenciesContainer
|
||||||
import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices
|
import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices
|
||||||
import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory
|
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.builtins.DefaultBuiltIns
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
|
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
||||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
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.FunctionDescriptorImpl
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||||
|
import org.jetbrains.kotlin.library.SerializedMetadata
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.platform.CommonPlatforms
|
import org.jetbrains.kotlin.platform.CommonPlatforms
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
@@ -74,25 +78,21 @@ abstract class AbstractCommonizationFromSourcesTest : KtUsefulTestCase() {
|
|||||||
val sharedTarget: SharedTarget = analyzedModules.sharedTarget
|
val sharedTarget: SharedTarget = analyzedModules.sharedTarget
|
||||||
assertEquals(sharedTarget, result.sharedTarget)
|
assertEquals(sharedTarget, result.sharedTarget)
|
||||||
|
|
||||||
val sharedModuleAsExpected: ModuleDescriptor = analyzedModules.commonizedModules.getValue(sharedTarget)
|
val sharedModuleAsExpected: SerializedMetadata = analyzedModules.commonizedModules.getValue(sharedTarget)
|
||||||
val sharedModuleByCommonizer: ModuleDescriptor =
|
val sharedModuleByCommonizer: SerializedMetadata =
|
||||||
(result.modulesByTargets.getValue(sharedTarget).single() as ModuleResult.Commonized).module!!
|
(result.modulesByTargets.getValue(sharedTarget).single() as ModuleResult.Commonized).metadata.metadata
|
||||||
|
|
||||||
assertValidModule(sharedModuleAsExpected)
|
assertModulesAreEqual(sharedModuleAsExpected, sharedModuleByCommonizer, sharedTarget)
|
||||||
assertValidModule(sharedModuleByCommonizer)
|
|
||||||
assertModulesAreEqual(sharedModuleAsExpected, sharedModuleByCommonizer, "\"$sharedTarget\" target")
|
|
||||||
|
|
||||||
val leafTargets: Set<LeafTarget> = analyzedModules.leafTargets
|
val leafTargets: Set<LeafTarget> = analyzedModules.leafTargets
|
||||||
assertEquals(leafTargets, result.leafTargets)
|
assertEquals(leafTargets, result.leafTargets)
|
||||||
|
|
||||||
for (leafTarget in leafTargets) {
|
for (leafTarget in leafTargets) {
|
||||||
val leafTargetModuleAsExpected: ModuleDescriptor = analyzedModules.commonizedModules.getValue(leafTarget)
|
val leafTargetModuleAsExpected: SerializedMetadata = analyzedModules.commonizedModules.getValue(leafTarget)
|
||||||
val leafTargetModuleByCommonizer: ModuleDescriptor =
|
val leafTargetModuleByCommonizer: SerializedMetadata =
|
||||||
(result.modulesByTargets.getValue(leafTarget).single() as ModuleResult.Commonized).module!!
|
(result.modulesByTargets.getValue(leafTarget).single() as ModuleResult.Commonized).metadata.metadata
|
||||||
|
|
||||||
assertValidModule(leafTargetModuleAsExpected)
|
assertModulesAreEqual(leafTargetModuleAsExpected, leafTargetModuleByCommonizer, leafTarget)
|
||||||
assertValidModule(leafTargetModuleByCommonizer)
|
|
||||||
assertModulesAreEqual(leafTargetModuleAsExpected, leafTargetModuleByCommonizer, "\"$leafTarget\" target")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,7 +182,7 @@ private class AnalyzedModuleDependencies(
|
|||||||
|
|
||||||
private class AnalyzedModules(
|
private class AnalyzedModules(
|
||||||
val originalModules: Map<CommonizerTarget, ModuleDescriptor>,
|
val originalModules: Map<CommonizerTarget, ModuleDescriptor>,
|
||||||
val commonizedModules: Map<CommonizerTarget, ModuleDescriptor>,
|
val commonizedModules: Map<CommonizerTarget, SerializedMetadata>,
|
||||||
val dependeeModules: Map<CommonizerTarget, List<ModuleDescriptor>>
|
val dependeeModules: Map<CommonizerTarget, List<ModuleDescriptor>>
|
||||||
) {
|
) {
|
||||||
val leafTargets: Set<LeafTarget>
|
val leafTargets: Set<LeafTarget>
|
||||||
@@ -232,11 +232,16 @@ private class AnalyzedModules(
|
|||||||
parentDisposable: Disposable
|
parentDisposable: Disposable
|
||||||
): AnalyzedModules = with(sourceModuleRoots) {
|
): AnalyzedModules = with(sourceModuleRoots) {
|
||||||
// phase 1: provide the modules that are the dependencies for "original" and "commonized" modules
|
// 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
|
// phase 2: build "original" and "commonized" modules
|
||||||
val originalModules = createModules(sharedTarget, originalRoots, dependencies, parentDisposable)
|
val originalModules: Map<CommonizerTarget, ModuleDescriptor> =
|
||||||
val commonizedModules = createModules(sharedTarget, commonizedRoots, dependencies, parentDisposable)
|
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)
|
return AnalyzedModules(originalModules, commonizedModules, dependeeModules)
|
||||||
}
|
}
|
||||||
@@ -334,6 +339,13 @@ private class AnalyzedModules(
|
|||||||
|
|
||||||
return module
|
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
|
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.descriptors.commonizer.CommonizerResult
|
||||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
import org.jetbrains.kotlin.descriptors.commonizer.CommonizerTarget
|
||||||
import org.jetbrains.kotlin.test.util.DescriptorValidator.*
|
import org.jetbrains.kotlin.descriptors.commonizer.metadata.utils.MetadataDeclarationsComparator
|
||||||
import org.jetbrains.kotlin.types.ErrorUtils
|
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 java.io.File
|
||||||
import kotlin.contracts.ExperimentalContracts
|
import kotlin.contracts.ExperimentalContracts
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
import kotlin.test.assertFalse
|
|
||||||
import kotlin.test.fail
|
import kotlin.test.fail
|
||||||
|
|
||||||
fun assertIsDirectory(file: File) {
|
fun assertIsDirectory(file: File) {
|
||||||
@@ -32,47 +33,24 @@ fun assertCommonizationPerformed(result: CommonizerResult) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ExperimentalContracts
|
@ExperimentalContracts
|
||||||
fun assertModulesAreEqual(expected: ModuleDescriptor, actual: ModuleDescriptor, designatorMessage: String) {
|
fun assertModulesAreEqual(expected: SerializedMetadata, actual: SerializedMetadata, target: CommonizerTarget) {
|
||||||
val visitor = ComparingDeclarationsVisitor(designatorMessage)
|
val expectedModule = with(expected) { KlibModuleMetadata.read(SerializedMetadataLibraryProvider(module, fragments, fragmentNames)) }
|
||||||
val context = visitor.Context(actual)
|
val actualModule = with(actual) { KlibModuleMetadata.read(SerializedMetadataLibraryProvider(module, fragments, fragmentNames)) }
|
||||||
|
|
||||||
expected.accept(visitor, context)
|
when (val result = MetadataDeclarationsComparator().compare(expectedModule, actualModule)) {
|
||||||
}
|
is Result.Success -> Unit
|
||||||
|
is Result.Failure -> {
|
||||||
fun assertValidModule(module: ModuleDescriptor) = validate(
|
val mismatches = result.mismatches.sortedBy { it::class.java.simpleName + "_" + it.kind }
|
||||||
object : ValidationVisitor() {
|
val digitCount = mismatches.size.toString().length
|
||||||
override fun validateScope(scopeOwner: DeclarationDescriptor?, scope: MemberScope, collector: DiagnosticCollector) = Unit
|
|
||||||
|
val failureMessage = buildString {
|
||||||
override fun visitModuleDeclaration(descriptor: ModuleDescriptor, collector: DiagnosticCollector): Boolean {
|
appendLine("${mismatches.size} mismatches found while comparing module ${expectedModule.name} and ${actualModule.name} for target ${target.prettyName}:")
|
||||||
assertValid(descriptor)
|
mismatches.forEachIndexed { index, mismatch ->
|
||||||
return super.visitModuleDeclaration(descriptor, collector)
|
appendLine((index + 1).toString().padStart(digitCount, ' ') + ". " + mismatch)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
override fun visitClassDescriptor(descriptor: ClassDescriptor, collector: DiagnosticCollector): Boolean {
|
|
||||||
assertValid(descriptor)
|
fail(failureMessage)
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user