[Commonizer] Implement basic tests for CirTree merging

This commit is contained in:
sebastian.sellmair
2021-03-30 12:25:12 +02:00
parent f252c3e2c1
commit a2436699ea
11 changed files with 600 additions and 176 deletions
@@ -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(
@@ -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 }}")
@@ -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))
)
)
}
}
@@ -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")
}
}
@@ -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)
}
}
@@ -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")
}
}
@@ -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")
}
}
@@ -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")
}
}
@@ -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
}
}
@@ -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)
}