[Commonizer] Implement basic tests for CirTree merging
This commit is contained in:
@@ -11,20 +11,20 @@ import org.jetbrains.kotlin.commonizer.mergedtree.FunctionApproximationKey
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.PropertyApproximationKey
|
||||
|
||||
data class CirTreeRoot(
|
||||
val modules: List<CirTreeModule>
|
||||
val modules: List<CirTreeModule> = emptyList()
|
||||
)
|
||||
|
||||
data class CirTreeModule(
|
||||
val module: CirModule,
|
||||
val packages: List<CirTreePackage>
|
||||
val packages: List<CirTreePackage> = emptyList()
|
||||
)
|
||||
|
||||
data class CirTreePackage(
|
||||
val pkg: CirPackage,
|
||||
val properties: List<CirTreeProperty>,
|
||||
val functions: List<CirTreeFunction>,
|
||||
val classes: List<CirTreeClass>,
|
||||
val typeAliases: List<CirTreeTypeAlias>
|
||||
val properties: List<CirTreeProperty> = emptyList(),
|
||||
val functions: List<CirTreeFunction> = emptyList(),
|
||||
val classes: List<CirTreeClass> = emptyList(),
|
||||
val typeAliases: List<CirTreeTypeAlias> = emptyList()
|
||||
)
|
||||
|
||||
data class CirTreeProperty(
|
||||
@@ -49,10 +49,10 @@ data class CirTreeTypeAlias(
|
||||
data class CirTreeClass(
|
||||
override val id: CirEntityId,
|
||||
val clazz: CirClass,
|
||||
val properties: List<CirTreeProperty>,
|
||||
val functions: List<CirTreeFunction>,
|
||||
val constructors: List<CirTreeClassConstructor>,
|
||||
val classes: List<CirTreeClass>,
|
||||
val properties: List<CirTreeProperty> = emptyList(),
|
||||
val functions: List<CirTreeFunction> = emptyList(),
|
||||
val constructors: List<CirTreeClassConstructor> = emptyList(),
|
||||
val classes: List<CirTreeClass> = emptyList(),
|
||||
) : CirTreeClassifier
|
||||
|
||||
class CirTreeClassConstructor(
|
||||
|
||||
+4
-166
@@ -5,174 +5,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.tree.deserializer
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.intellij.lang.annotations.Language
|
||||
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.builtins.DefaultBuiltIns
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirFictitiousFunctionClassifiers
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirProvidedClassifiers
|
||||
import org.jetbrains.kotlin.commonizer.metadata.CirTypeResolver
|
||||
import org.jetbrains.kotlin.commonizer.tree.*
|
||||
import org.jetbrains.kotlin.commonizer.utils.MockModulesProvider
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.platform.CommonPlatforms
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.resolve.CompilerEnvironment
|
||||
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractCirTreeDeserializerTest : KtUsefulTestCase() {
|
||||
annotation class ModuleBuilderDsl
|
||||
|
||||
data class SourceFile(val name: String, @Language("kotlin") val content: String)
|
||||
|
||||
data class Module(
|
||||
val name: String, val sourceFiles: List<SourceFile>, val dependencies: List<Module>
|
||||
)
|
||||
|
||||
@ModuleBuilderDsl
|
||||
class ModuleBuilder {
|
||||
var name: String = "test-module"
|
||||
var sourceFiles: List<SourceFile> = emptyList()
|
||||
var dependencies: List<Module> = emptyList()
|
||||
|
||||
@ModuleBuilderDsl
|
||||
fun source(name: String = "test.kt", @Language("kotlin") content: String) {
|
||||
sourceFiles = sourceFiles + SourceFile(name, content)
|
||||
}
|
||||
|
||||
@ModuleBuilderDsl
|
||||
fun dependency(builder: ModuleBuilder.() -> Unit) {
|
||||
val dependency = ModuleBuilder().also(builder).build().run {
|
||||
copy(name = "$name-dependency-${dependencies.size}")
|
||||
}
|
||||
|
||||
dependencies = dependencies + dependency
|
||||
}
|
||||
|
||||
fun build() = Module(name, sourceFiles.toList(), dependencies.toList())
|
||||
}
|
||||
|
||||
@ModuleBuilderDsl
|
||||
fun module(builder: ModuleBuilder.() -> Unit): Module {
|
||||
return ModuleBuilder().also(builder).build()
|
||||
}
|
||||
|
||||
fun deserializeModule(builder: ModuleBuilder.() -> Unit): CirTreeModule {
|
||||
val module = module(builder)
|
||||
val moduleDescriptor = createModuleDescriptor(module)
|
||||
val metadata = MockModulesProvider.SERIALIZER.serializeModule(moduleDescriptor)
|
||||
|
||||
val classifiers = listOf(
|
||||
CirFictitiousFunctionClassifiers,
|
||||
CirProvidedClassifiers.by(MockModulesProvider.create(moduleDescriptor)),
|
||||
CirProvidedClassifiers.by(MockModulesProvider.create(DefaultBuiltIns.Instance.builtInsModule))
|
||||
) + module.dependencies.map { CirProvidedClassifiers.by(MockModulesProvider.create(createModuleDescriptor(it))) }
|
||||
|
||||
val typeResolver = CirTypeResolver.create(
|
||||
CirProvidedClassifiers.of(*classifiers.toTypedArray())
|
||||
)
|
||||
|
||||
return defaultCirTreeModuleDeserializer(metadata, typeResolver)
|
||||
}
|
||||
|
||||
fun deserializeSourceFile(@Language("kotlin") sourceFileContent: String): CirTreeModule {
|
||||
return deserializeModule {
|
||||
source("test.kt", content = sourceFileContent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createModuleDescriptor(module: Module): ModuleDescriptor {
|
||||
val moduleRoot = FileUtil.createTempDirectory(module.name, null)
|
||||
module.sourceFiles.forEach { sourceFile ->
|
||||
moduleRoot.resolve(sourceFile.name).writeText(sourceFile.content)
|
||||
}
|
||||
return createModuleDescriptor(moduleRoot, module)
|
||||
}
|
||||
|
||||
private fun createModuleDescriptor(moduleRoot: File, module: Module): ModuleDescriptor {
|
||||
check(Name.isValidIdentifier(module.name))
|
||||
val configuration = KotlinTestUtils.newConfiguration()
|
||||
configuration.put(CommonConfigurationKeys.MODULE_NAME, module.name)
|
||||
|
||||
val environment: KotlinCoreEnvironment = KotlinCoreEnvironment.createForTests(
|
||||
parentDisposable = testRootDisposable,
|
||||
initialConfiguration = configuration,
|
||||
extensionConfigs = EnvironmentConfigFiles.METADATA_CONFIG_FILES
|
||||
)
|
||||
|
||||
val psiFactory = KtPsiFactory(environment.project)
|
||||
|
||||
val psiFiles: List<KtFile> = moduleRoot.walkTopDown()
|
||||
.filter { it.isFile }
|
||||
.map { psiFactory.createFile(it.name, KtTestUtil.doLoadFile(it)) }
|
||||
.toList()
|
||||
|
||||
return CommonResolverForModuleFactory.analyzeFiles(
|
||||
files = psiFiles,
|
||||
moduleName = Name.special("<${module.name}>"),
|
||||
dependOnBuiltIns = true,
|
||||
languageVersionSettings = environment.configuration.languageVersionSettings,
|
||||
targetPlatform = CommonPlatforms.defaultCommonPlatform,
|
||||
targetEnvironment = CompilerEnvironment,
|
||||
dependenciesContainer = DependenciesContainerImpl(module.dependencies),
|
||||
) { content ->
|
||||
environment.createPackagePartProvider(content.moduleContentScope)
|
||||
}.moduleDescriptor
|
||||
}
|
||||
|
||||
private inner class DependenciesContainerImpl(
|
||||
dependencies: List<Module>
|
||||
) : CommonDependenciesContainer {
|
||||
|
||||
private val dependenciesByModuleInfos = dependencies.associate { module ->
|
||||
ModuleInfoImpl(module) to createModuleDescriptor(module)
|
||||
}
|
||||
|
||||
private inner class ModuleInfoImpl(module: Module) : ModuleInfo {
|
||||
private val dependencyModules = module.dependencies.associateBy(::ModuleInfoImpl)
|
||||
override val name: Name = Name.special("<${module.name}>")
|
||||
override fun dependencies(): List<ModuleInfo> = listOf(this) + dependencyModules.keys
|
||||
override val platform: TargetPlatform get() = CommonPlatforms.defaultCommonPlatform
|
||||
override val analyzerServices: PlatformDependentAnalyzerServices get() = CommonPlatformAnalyzerServices
|
||||
}
|
||||
|
||||
override val moduleInfos: List<ModuleInfo> get() = listOf(DefaultBuiltInsModuleInfo) + dependenciesByModuleInfos.keys
|
||||
override val friendModuleInfos: List<ModuleInfo> get() = emptyList()
|
||||
override val refinesModuleInfos: List<ModuleInfo> get() = emptyList()
|
||||
override fun registerDependencyForAllModules(moduleInfo: ModuleInfo, descriptorForModule: ModuleDescriptorImpl) = Unit
|
||||
override fun packageFragmentProviderForModuleInfo(moduleInfo: ModuleInfo): PackageFragmentProvider? = null
|
||||
|
||||
override fun moduleDescriptorForModuleInfo(moduleInfo: ModuleInfo): ModuleDescriptor {
|
||||
dependenciesByModuleInfos[moduleInfo]?.let { return it }
|
||||
check(moduleInfo == DefaultBuiltInsModuleInfo) { "Unknown module info $moduleInfo" }
|
||||
return DefaultBuiltIns.Instance.builtInsModule
|
||||
}
|
||||
}
|
||||
|
||||
private object DefaultBuiltInsModuleInfo : ModuleInfo {
|
||||
override val name get() = DefaultBuiltIns.Instance.builtInsModule.name
|
||||
override fun dependencies() = listOf(this)
|
||||
override fun dependencyOnBuiltIns() = ModuleInfo.DependencyOnBuiltIns.LAST
|
||||
override val platform get() = CommonPlatforms.defaultCommonPlatform
|
||||
override val analyzerServices get() = CommonPlatformAnalyzerServices
|
||||
}
|
||||
import org.jetbrains.kotlin.commonizer.utils.InlineSourceTest
|
||||
import org.jetbrains.kotlin.commonizer.utils.InlineSourceTestImpl
|
||||
import org.jetbrains.kotlin.commonizer.utils.KtInlineSourceCommonizerTestCase
|
||||
|
||||
abstract class AbstractCirTreeDeserializerTest : KtInlineSourceCommonizerTestCase() {
|
||||
protected fun CirTreeModule.assertSinglePackage(): CirTreePackage {
|
||||
return packages.singleOrNull()
|
||||
?: kotlin.test.fail("Expected single package. Found ${packages.map { it.pkg.packageName }}")
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("TestFunctionName")
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.tree.merge
|
||||
|
||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||
import org.jetbrains.kotlin.commonizer.LeafCommonizerTarget
|
||||
import org.jetbrains.kotlin.commonizer.TargetDependent
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.*
|
||||
import org.jetbrains.kotlin.commonizer.toTargetDependent
|
||||
import org.jetbrains.kotlin.commonizer.tree.CirTreeModule
|
||||
import org.jetbrains.kotlin.commonizer.tree.CirTreeRoot
|
||||
import org.jetbrains.kotlin.commonizer.utils.KtInlineSourceCommonizerTestCase
|
||||
import org.jetbrains.kotlin.commonizer.utils.MockModulesProvider
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
|
||||
abstract class AbstractMergeCirTreeTest : KtInlineSourceCommonizerTestCase() {
|
||||
private val storageManager = LockBasedStorageManager(this::class.simpleName)
|
||||
|
||||
fun mergeCirTree(vararg modules: Pair<String, CirTreeModule>): CirRootNode {
|
||||
return org.jetbrains.kotlin.commonizer.tree.mergeCirTree(storageManager, createDefaultKnownClassifiers(), TargetDependent(*modules))
|
||||
}
|
||||
|
||||
fun CirRootNode.assertSingleModule(): CirModuleNode {
|
||||
kotlin.test.assertEquals(1, modules.size, "Expected exactly one merged module. Found ${modules.map { it.key }}")
|
||||
return modules.values.single()
|
||||
}
|
||||
|
||||
fun CirModuleNode.assertSinglePackage(): CirPackageNode {
|
||||
kotlin.test.assertEquals(1, packages.size, "Expected exactly one package. Found ${packages.map { it.key }}")
|
||||
return packages.values.single()
|
||||
}
|
||||
|
||||
fun CirPackageNode.assertSingleProperty(): CirPropertyNode {
|
||||
kotlin.test.assertEquals(1, properties.size, "Expected exactly one property. Found ${properties.map { it.key }}")
|
||||
return properties.values.single()
|
||||
}
|
||||
|
||||
fun CirPackageNode.assertSingleFunction(): CirFunctionNode {
|
||||
kotlin.test.assertEquals(1, functions.size, "Expected exactly one function. Found ${functions.map { it.key }}")
|
||||
return functions.values.single()
|
||||
}
|
||||
|
||||
fun CirPackageNode.assertSingleTypeAlias(): CirTypeAliasNode {
|
||||
kotlin.test.assertEquals(1, typeAliases.size, "Expected exactly one type alias. Found ${typeAliases.map { it.key }}")
|
||||
return typeAliases.values.single()
|
||||
}
|
||||
|
||||
fun CirPackageNode.assertSingleClass(): CirClassNode {
|
||||
kotlin.test.assertEquals(1, classes.size, "Expected exactly one class. Found ${classes.map { it.key }}")
|
||||
return classes.values.single()
|
||||
}
|
||||
|
||||
fun CirClassNode.assertSingleConstructor(): CirClassConstructorNode {
|
||||
kotlin.test.assertEquals(1, constructors.size, "Expected exactly one function. Found ${constructors.map { it.key }}")
|
||||
return constructors.values.single()
|
||||
}
|
||||
|
||||
fun CirNode<*, *>.assertNoMissingTargetDeclaration() {
|
||||
this.targetDeclarations.forEachIndexed { index, target ->
|
||||
kotlin.test.assertNotNull(target, "Missing target declaration at index $index")
|
||||
}
|
||||
}
|
||||
|
||||
fun CirNode<*, *>.assertOnlyTargetDeclarationAtIndex(vararg indices: Int) {
|
||||
this.targetDeclarations.forEachIndexed { index, target ->
|
||||
if (index in indices) {
|
||||
kotlin.test.assertNotNull(target, "Expected target declaration at index $index")
|
||||
} else {
|
||||
kotlin.test.assertNull(target, "Expected *no* target declaration at index $index")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun TargetDependent(vararg modules: Pair<String, CirTreeModule>): TargetDependent<CirTreeRoot> {
|
||||
return TargetDependent(modules.toList())
|
||||
}
|
||||
|
||||
private fun TargetDependent(modules: Iterable<Pair<String, CirTreeModule>>): TargetDependent<CirTreeRoot> {
|
||||
return modules.toMap().mapKeys { (targetName, _) -> LeafCommonizerTarget(targetName) }
|
||||
.mapValues { (_, module) -> CirTreeRoot(listOf(module)) }
|
||||
.toTargetDependent()
|
||||
}
|
||||
|
||||
private fun createDefaultKnownClassifiers(): CirKnownClassifiers {
|
||||
return CirKnownClassifiers(
|
||||
CirCommonizedClassifierNodes.default(),
|
||||
CirProvidedClassifiers.of(
|
||||
CirFictitiousFunctionClassifiers,
|
||||
CirProvidedClassifiers.by(MockModulesProvider.create(DefaultBuiltIns.Instance.builtInsModule))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.tree.merge
|
||||
|
||||
class MergeCirTreeClassConstructorTest : AbstractMergeCirTreeTest() {
|
||||
|
||||
fun `test simple constructors`() {
|
||||
val aTree = deserializeSourceFile("class X(val x: Int)")
|
||||
val bTree = deserializeSourceFile("class X(val x: Int)")
|
||||
val merged = mergeCirTree("a" to aTree, "b" to bTree)
|
||||
val constructor = merged.assertSingleModule().assertSinglePackage().assertSingleClass().assertSingleConstructor()
|
||||
constructor.assertNoMissingTargetDeclaration()
|
||||
}
|
||||
|
||||
fun `test missing target declaration`() {
|
||||
val aTree = deserializeSourceFile("class X(val a: Int)")
|
||||
val bTree = deserializeSourceFile("class X(val b: Short)")
|
||||
val merged = mergeCirTree("a" to aTree, "b" to bTree)
|
||||
val clazz = merged.assertSingleModule().assertSinglePackage().assertSingleClass()
|
||||
kotlin.test.assertEquals(2, clazz.constructors.size, "Expected two constructors")
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.tree.merge
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirName
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.PropertyApproximationKey
|
||||
|
||||
class MergeCirTreeClassTest : AbstractMergeCirTreeTest() {
|
||||
fun `test simple class`() {
|
||||
val aTree = deserializeSourceFile("class X")
|
||||
val bTree = deserializeSourceFile("class X")
|
||||
val merged = mergeCirTree("a" to aTree, "b" to bTree)
|
||||
val clazz = merged.assertSingleModule().assertSinglePackage().assertSingleClass()
|
||||
clazz.assertNoMissingTargetDeclaration()
|
||||
}
|
||||
|
||||
fun `test missing target declarations`() {
|
||||
val aTree = deserializeSourceFile("class A")
|
||||
val bTree = deserializeSourceFile("class B")
|
||||
val merged = mergeCirTree("a" to aTree, "b" to bTree)
|
||||
val pkg = merged.assertSingleModule().assertSinglePackage()
|
||||
kotlin.test.assertEquals(2, pkg.classes.size, "Expected two classes (A, B)")
|
||||
val a = pkg.classes[CirName.create("A")] ?: kotlin.test.fail("Missing class 'A'")
|
||||
val b = pkg.classes[CirName.create("B")] ?: kotlin.test.fail("Missing class 'B'")
|
||||
|
||||
a.assertOnlyTargetDeclarationAtIndex(0)
|
||||
b.assertOnlyTargetDeclarationAtIndex(1)
|
||||
}
|
||||
|
||||
fun `test with children`() {
|
||||
val aTree = deserializeSourceFile(
|
||||
"""
|
||||
class X {
|
||||
val x: Int = 42
|
||||
val a: Int = 42
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val bTree = deserializeSourceFile(
|
||||
"""
|
||||
class X {
|
||||
val x: Int = 42
|
||||
val b: Int = 42
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val merged = mergeCirTree("a" to aTree, "b" to bTree)
|
||||
val clazz = merged.assertSingleModule().assertSinglePackage().assertSingleClass()
|
||||
kotlin.test.assertEquals(3, clazz.properties.size, "Expected three properties (x, a, b)")
|
||||
val x = clazz.properties[PropertyApproximationKey(CirName.create("x"), null)] ?: kotlin.test.fail("Missing property 'x'")
|
||||
val a = clazz.properties[PropertyApproximationKey(CirName.create("a"), null)] ?: kotlin.test.fail("Missing property 'a'")
|
||||
val b = clazz.properties[PropertyApproximationKey(CirName.create("b"), null)] ?: kotlin.test.fail("Missing property 'b'")
|
||||
|
||||
x.assertNoMissingTargetDeclaration()
|
||||
a.assertOnlyTargetDeclarationAtIndex(0)
|
||||
b.assertOnlyTargetDeclarationAtIndex(1)
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.tree.merge
|
||||
|
||||
class MergeCirTreeFunctionTest : AbstractMergeCirTreeTest() {
|
||||
|
||||
fun `test simple function`() {
|
||||
val aTree = deserializeSourceFile("fun x(): Int = 42")
|
||||
val bTree = deserializeSourceFile("fun x(): Int = 42")
|
||||
val merged = mergeCirTree("a" to aTree, "b" to bTree)
|
||||
val function = merged.assertSingleModule().assertSinglePackage().assertSingleFunction()
|
||||
|
||||
function.assertNoMissingTargetDeclaration()
|
||||
function.targetDeclarations.forEachIndexed { index, cirFunction ->
|
||||
kotlin.test.assertNotNull(cirFunction)
|
||||
kotlin.test.assertEquals("x", cirFunction.name.toStrippedString(), "Expected function name 'x' at index $index")
|
||||
kotlin.test.assertEquals("kotlin/Int", cirFunction.returnType.toString(), "Expected return type 'kotlin/Int' at index $index")
|
||||
}
|
||||
}
|
||||
|
||||
fun `test missing target declarations`() {
|
||||
val aTree = deserializeSourceFile("fun a(): Int = 42")
|
||||
val bTree = deserializeSourceFile("fun b(): Int = 42")
|
||||
val merged = mergeCirTree("a" to aTree, "b" to bTree)
|
||||
val pkg = merged.assertSingleModule().assertSinglePackage()
|
||||
|
||||
val aKey = pkg.functions.keys.singleOrNull { it.name.toStrippedString() == "a" } ?: kotlin.test.fail("Missing function key 'a'")
|
||||
val bKey = pkg.functions.keys.singleOrNull { it.name.toStrippedString() == "b" } ?: kotlin.test.fail("Missing function key 'b'")
|
||||
|
||||
val aFunction = pkg.functions.getValue(aKey)
|
||||
val bFunction = pkg.functions.getValue(bKey)
|
||||
|
||||
kotlin.test.assertNotNull(aFunction.targetDeclarations[0], "Expected target declaration for 'a' at index 0")
|
||||
kotlin.test.assertNotNull(bFunction.targetDeclarations[1], "Expected target for declaration 'b' at index 1")
|
||||
kotlin.test.assertNull(aFunction.targetDeclarations[1], "Expected *no* target declaration for 'a' at index 1")
|
||||
kotlin.test.assertNull(bFunction.targetDeclarations[0], "Expected *no* target declaration for 'b' at index 0")
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.tree.merge
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirPackageName
|
||||
|
||||
|
||||
class MergeCirTreePackageTest : AbstractMergeCirTreeTest() {
|
||||
|
||||
fun `test simple package`() {
|
||||
val aTree = deserializeSourceFile("package test.pkg")
|
||||
val bTree = deserializeSourceFile("package test.pkg")
|
||||
val merged = mergeCirTree("a" to aTree, "b" to bTree)
|
||||
val module = merged.assertSingleModule()
|
||||
kotlin.test.assertEquals(3, module.packages.size, "Expected 3 packages (root, test, test.pkg)")
|
||||
module.packages[CirPackageName.ROOT] ?: kotlin.test.fail("Missing root package")
|
||||
module.packages[CirPackageName.create("test")] ?: kotlin.test.fail("Missing test package")
|
||||
val pkg = module.packages[CirPackageName.create("test.pkg")] ?: kotlin.test.fail("Missing test.pkg package")
|
||||
|
||||
pkg.assertNoMissingTargetDeclaration()
|
||||
pkg.targetDeclarations.forEachIndexed { index, cirPackage ->
|
||||
kotlin.test.assertNotNull(cirPackage)
|
||||
kotlin.test.assertEquals("test/pkg", cirPackage.packageName.toMetadataString(), "Expected correct packageName at index $index")
|
||||
}
|
||||
}
|
||||
|
||||
fun `test missing target declarations`() {
|
||||
val aTree = deserializeSourceFile("package a")
|
||||
val bTree = deserializeSourceFile("package b")
|
||||
val merged = mergeCirTree("a" to aTree, "b" to bTree)
|
||||
val module = merged.assertSingleModule()
|
||||
kotlin.test.assertEquals(3, module.packages.size, "Expected 3 packages (root, a, b)")
|
||||
|
||||
val a = module.packages[CirPackageName.create("a")] ?: kotlin.test.fail("Missing a package")
|
||||
val b = module.packages[CirPackageName.create("b")] ?: kotlin.test.fail("Missing b package")
|
||||
|
||||
kotlin.test.assertNotNull(a.targetDeclarations[0], "Expected target declaration for 'a' at index 0")
|
||||
kotlin.test.assertNotNull(b.targetDeclarations[1], "Expected target declaration for 'b' at index 1")
|
||||
kotlin.test.assertNull(a.targetDeclarations[1], "Expected *no* target declaration for 'a' at index 1")
|
||||
kotlin.test.assertNull(b.targetDeclarations[0], "Expected *no* target declaration for 'b' at index 0")
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("RemoveRedundantQualifierName")
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.tree.merge
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirName.Companion.create
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.PropertyApproximationKey
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class MergeCirTreePropertyTest : AbstractMergeCirTreeTest() {
|
||||
|
||||
fun `test simple property`() {
|
||||
val aTree = deserializeSourceFile("""val a: Int = 42""")
|
||||
val bTree = deserializeSourceFile("""val a: Int = 42""")
|
||||
val merged = mergeCirTree("a" to aTree, "b" to bTree)
|
||||
|
||||
val property = merged.assertSingleModule().assertSinglePackage().assertSingleProperty()
|
||||
property.targetDeclarations.forEachIndexed { index, cirProperty ->
|
||||
assertNotNull(cirProperty, "Expected not-null property at index $index")
|
||||
kotlin.test.assertEquals("a", cirProperty.name.toStrippedString(), "Expected correct property name at index $index")
|
||||
kotlin.test.assertEquals("kotlin/Int", cirProperty.returnType.toString(), "Expected correct return type at index $index")
|
||||
}
|
||||
}
|
||||
|
||||
fun `test multiple properties`() {
|
||||
val aTree = deserializeSourceFile(
|
||||
"""
|
||||
val a: Int = 42
|
||||
val b: Int = 42
|
||||
val c: String = "hello"
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val bTree = deserializeSourceFile(
|
||||
"""
|
||||
val a: Int = 42
|
||||
val b: Int = 42
|
||||
val c: String = "hello"
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val merged = mergeCirTree("a" to aTree, "b" to bTree)
|
||||
val pkg = merged.assertSingleModule().assertSinglePackage()
|
||||
kotlin.test.assertEquals(3, pkg.properties.size, "Expected exactly three properties")
|
||||
|
||||
val a = pkg.properties[PropertyApproximationKey(create("a"), null)] ?: kotlin.test.fail("Missing a property")
|
||||
val b = pkg.properties[PropertyApproximationKey(create("b"), null)] ?: kotlin.test.fail("Missing a property")
|
||||
val c = pkg.properties[PropertyApproximationKey(create("c"), null)] ?: kotlin.test.fail("Missing a property")
|
||||
|
||||
a.assertNoMissingTargetDeclaration()
|
||||
b.assertNoMissingTargetDeclaration()
|
||||
c.assertNoMissingTargetDeclaration()
|
||||
}
|
||||
|
||||
fun `test missing target declarations`() {
|
||||
val aTree = deserializeSourceFile("val a: Int = 42")
|
||||
val bTree = deserializeSourceFile("val b: Int = 42")
|
||||
val merged = mergeCirTree("a" to aTree, "b" to bTree)
|
||||
val pkg = merged.assertSingleModule().assertSinglePackage()
|
||||
|
||||
val a = pkg.properties[PropertyApproximationKey(create("a"), null)] ?: kotlin.test.fail("Missing a property")
|
||||
val b = pkg.properties[PropertyApproximationKey(create("b"), null)] ?: kotlin.test.fail("Missing a property")
|
||||
|
||||
kotlin.test.assertNotNull(a.targetDeclarations[0], "Expected *non* missing target declaration at index 0")
|
||||
kotlin.test.assertNull(a.targetDeclarations[1], "Expected missing target declaration for a at index 1")
|
||||
|
||||
kotlin.test.assertNull(b.targetDeclarations[0], "Expected missing target declaration at index 0")
|
||||
kotlin.test.assertNotNull(b.targetDeclarations[1], "Expected *non* missing target declaration for a at index 1")
|
||||
}
|
||||
}
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.tree.merge
|
||||
|
||||
import org.jetbrains.kotlin.commonizer.cir.CirName
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class MergeCirTreeTypeAliasTest : AbstractMergeCirTreeTest() {
|
||||
|
||||
fun `test simple type alias`() {
|
||||
val aTree = deserializeSourceFile("typealias a = Int")
|
||||
val bTree = deserializeSourceFile("typealias a = Int")
|
||||
val merged = mergeCirTree("a" to aTree, "b" to bTree)
|
||||
val typeAlias = merged.assertSingleModule().assertSinglePackage().assertSingleTypeAlias()
|
||||
typeAlias.assertNoMissingTargetDeclaration()
|
||||
}
|
||||
|
||||
fun `test missing target declarations`() {
|
||||
val aTree = deserializeSourceFile("typealias a = Int")
|
||||
val bTree = deserializeSourceFile("typealias b = Int")
|
||||
val merged = mergeCirTree("a" to aTree, "b" to bTree)
|
||||
val pkg = merged.assertSingleModule().assertSinglePackage()
|
||||
|
||||
assertEquals(2, pkg.typeAliases.size, "Expected 2 type aliases (a, b)")
|
||||
val a = pkg.typeAliases[CirName.create("a")] ?: kotlin.test.fail("Missing type alias 'a'")
|
||||
val b = pkg.typeAliases[CirName.create("b")] ?: kotlin.test.fail("Missing type alias 'b'")
|
||||
|
||||
kotlin.test.assertNotNull(a.targetDeclarations[0], "Expected target declaration for 'a' at index 0")
|
||||
kotlin.test.assertNotNull(b.targetDeclarations[1], "Expected target declaration for 'b' at index 1")
|
||||
kotlin.test.assertNull(a.targetDeclarations[1], "Expected *no* target declaration for 'a' at index 1")
|
||||
kotlin.test.assertNull(b.targetDeclarations[0], "Expected *no* target declaration for 'b' at index 0")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.utils
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.intellij.lang.annotations.Language
|
||||
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.builtins.DefaultBuiltIns
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirFictitiousFunctionClassifiers
|
||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirProvidedClassifiers
|
||||
import org.jetbrains.kotlin.commonizer.metadata.CirTypeResolver
|
||||
import org.jetbrains.kotlin.commonizer.tree.CirTreeModule
|
||||
import org.jetbrains.kotlin.commonizer.tree.defaultCirTreeModuleDeserializer
|
||||
import org.jetbrains.kotlin.commonizer.utils.InlineSourceTest.Module
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.platform.CommonPlatforms
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.resolve.CompilerEnvironment
|
||||
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil
|
||||
import java.io.File
|
||||
|
||||
interface InlineSourceTest {
|
||||
annotation class ModuleBuilderDsl
|
||||
|
||||
data class SourceFile(val name: String, @Language("kotlin") val content: String)
|
||||
|
||||
data class Module(
|
||||
val name: String, val sourceFiles: List<SourceFile>, val dependencies: List<Module>
|
||||
)
|
||||
|
||||
@ModuleBuilderDsl
|
||||
class ModuleBuilder {
|
||||
var name: String = "test-module"
|
||||
private var sourceFiles: List<SourceFile> = emptyList()
|
||||
private var dependencies: List<Module> = emptyList()
|
||||
|
||||
@ModuleBuilderDsl
|
||||
fun source(name: String = "test.kt", @Language("kotlin") content: String) {
|
||||
sourceFiles = sourceFiles + SourceFile(name, content)
|
||||
}
|
||||
|
||||
@ModuleBuilderDsl
|
||||
fun dependency(builder: ModuleBuilder.() -> Unit) {
|
||||
val dependency = ModuleBuilder().also(builder).build().run {
|
||||
copy(name = "$name-dependency-${dependencies.size}")
|
||||
}
|
||||
|
||||
dependencies = dependencies + dependency
|
||||
}
|
||||
|
||||
fun build(): Module = Module(name, sourceFiles.toList(), dependencies.toList())
|
||||
}
|
||||
|
||||
@ModuleBuilderDsl
|
||||
fun module(builder: ModuleBuilder.() -> Unit): Module {
|
||||
return ModuleBuilder().also(builder).build()
|
||||
}
|
||||
|
||||
fun deserializeSourceFile(@Language("kotlin") sourceFileContent: String): CirTreeModule {
|
||||
return deserializeModule {
|
||||
source("test.kt", content = sourceFileContent)
|
||||
}
|
||||
}
|
||||
|
||||
fun deserializeModule(builder: ModuleBuilder.() -> Unit): CirTreeModule {
|
||||
return deserializeModule(module(builder))
|
||||
}
|
||||
|
||||
fun deserializeModule(module: Module): CirTreeModule
|
||||
}
|
||||
|
||||
internal interface InlineSourceTestDelegate : InlineSourceTest {
|
||||
val inlineSourceTest: InlineSourceTest
|
||||
override fun deserializeModule(module: Module): CirTreeModule {
|
||||
return inlineSourceTest.deserializeModule(module)
|
||||
}
|
||||
}
|
||||
|
||||
class InlineSourceTestImpl(private val disposable: Disposable) : InlineSourceTest {
|
||||
|
||||
override fun deserializeModule(module: Module): CirTreeModule {
|
||||
val moduleDescriptor = createModuleDescriptor(module)
|
||||
val metadata = MockModulesProvider.SERIALIZER.serializeModule(moduleDescriptor)
|
||||
|
||||
val classifiers = listOf(
|
||||
CirFictitiousFunctionClassifiers,
|
||||
CirProvidedClassifiers.by(MockModulesProvider.create(moduleDescriptor)),
|
||||
CirProvidedClassifiers.by(MockModulesProvider.create(DefaultBuiltIns.Instance.builtInsModule))
|
||||
) + module.dependencies.map { CirProvidedClassifiers.by(MockModulesProvider.create(createModuleDescriptor(it))) }
|
||||
|
||||
val typeResolver = CirTypeResolver.create(
|
||||
CirProvidedClassifiers.of(*classifiers.toTypedArray())
|
||||
)
|
||||
|
||||
return defaultCirTreeModuleDeserializer(metadata, typeResolver)
|
||||
}
|
||||
|
||||
private fun createModuleDescriptor(module: Module): ModuleDescriptor {
|
||||
val moduleRoot = FileUtil.createTempDirectory(module.name, null)
|
||||
module.sourceFiles.forEach { sourceFile ->
|
||||
moduleRoot.resolve(sourceFile.name).writeText(sourceFile.content)
|
||||
}
|
||||
return createModuleDescriptor(moduleRoot, module)
|
||||
}
|
||||
|
||||
private fun createModuleDescriptor(moduleRoot: File, module: Module): ModuleDescriptor {
|
||||
check(Name.isValidIdentifier(module.name))
|
||||
val configuration = KotlinTestUtils.newConfiguration()
|
||||
configuration.put(CommonConfigurationKeys.MODULE_NAME, module.name)
|
||||
|
||||
val environment: KotlinCoreEnvironment = KotlinCoreEnvironment.createForTests(
|
||||
parentDisposable = disposable,
|
||||
initialConfiguration = configuration,
|
||||
extensionConfigs = EnvironmentConfigFiles.METADATA_CONFIG_FILES
|
||||
)
|
||||
|
||||
val psiFactory = KtPsiFactory(environment.project)
|
||||
|
||||
val psiFiles: List<KtFile> = moduleRoot.walkTopDown()
|
||||
.filter { it.isFile }
|
||||
.map { psiFactory.createFile(it.name, KtTestUtil.doLoadFile(it)) }
|
||||
.toList()
|
||||
|
||||
return CommonResolverForModuleFactory.analyzeFiles(
|
||||
files = psiFiles,
|
||||
moduleName = Name.special("<${module.name}>"),
|
||||
dependOnBuiltIns = true,
|
||||
languageVersionSettings = environment.configuration.languageVersionSettings,
|
||||
targetPlatform = CommonPlatforms.defaultCommonPlatform,
|
||||
targetEnvironment = CompilerEnvironment,
|
||||
dependenciesContainer = DependenciesContainerImpl(module.dependencies),
|
||||
) { content ->
|
||||
environment.createPackagePartProvider(content.moduleContentScope)
|
||||
}.moduleDescriptor
|
||||
}
|
||||
|
||||
private inner class DependenciesContainerImpl(
|
||||
dependencies: List<Module>
|
||||
) : CommonDependenciesContainer {
|
||||
|
||||
private val dependenciesByModuleInfos = dependencies.associate { module ->
|
||||
ModuleInfoImpl(module) to createModuleDescriptor(module)
|
||||
}
|
||||
|
||||
private inner class ModuleInfoImpl(module: Module) : ModuleInfo {
|
||||
private val dependencyModules = module.dependencies.associateBy(::ModuleInfoImpl)
|
||||
override val name: Name = Name.special("<${module.name}>")
|
||||
override fun dependencies(): List<ModuleInfo> = listOf(this) + dependencyModules.keys
|
||||
override val platform: TargetPlatform get() = CommonPlatforms.defaultCommonPlatform
|
||||
override val analyzerServices: PlatformDependentAnalyzerServices get() = CommonPlatformAnalyzerServices
|
||||
}
|
||||
|
||||
override val moduleInfos: List<ModuleInfo> get() = listOf(DefaultBuiltInsModuleInfo) + dependenciesByModuleInfos.keys
|
||||
override val friendModuleInfos: List<ModuleInfo> get() = emptyList()
|
||||
override val refinesModuleInfos: List<ModuleInfo> get() = emptyList()
|
||||
override fun registerDependencyForAllModules(moduleInfo: ModuleInfo, descriptorForModule: ModuleDescriptorImpl) = Unit
|
||||
override fun packageFragmentProviderForModuleInfo(moduleInfo: ModuleInfo): PackageFragmentProvider? = null
|
||||
|
||||
override fun moduleDescriptorForModuleInfo(moduleInfo: ModuleInfo): ModuleDescriptor {
|
||||
dependenciesByModuleInfos[moduleInfo]?.let { return it }
|
||||
check(moduleInfo == DefaultBuiltInsModuleInfo) { "Unknown module info $moduleInfo" }
|
||||
return DefaultBuiltIns.Instance.builtInsModule
|
||||
}
|
||||
}
|
||||
|
||||
private object DefaultBuiltInsModuleInfo : ModuleInfo {
|
||||
override val name get() = DefaultBuiltIns.Instance.builtInsModule.name
|
||||
override fun dependencies() = listOf(this)
|
||||
override fun dependencyOnBuiltIns() = ModuleInfo.DependencyOnBuiltIns.LAST
|
||||
override val platform get() = CommonPlatforms.defaultCommonPlatform
|
||||
override val analyzerServices get() = CommonPlatformAnalyzerServices
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.commonizer.utils
|
||||
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
||||
|
||||
abstract class KtInlineSourceCommonizerTestCase : KtUsefulTestCase(), InlineSourceTestDelegate {
|
||||
override val inlineSourceTest: InlineSourceTest = InlineSourceTestImpl(testRootDisposable)
|
||||
}
|
||||
Reference in New Issue
Block a user