From a2436699ea9089d8d504dd18f2ef0d27c9f57ace Mon Sep 17 00:00:00 2001 From: "sebastian.sellmair" Date: Tue, 30 Mar 2021 12:25:12 +0200 Subject: [PATCH] [Commonizer] Implement basic tests for CirTree merging --- .../kotlin/commonizer/tree/CirTree.kt | 20 +- .../AbstractCirTreeDeserializerTest.kt | 170 +--------------- .../tree/merge/AbstractMergeCirTreeTest.kt | 98 +++++++++ .../merge/MergeCirTreeClassConstructorTest.kt | 25 +++ .../tree/merge/MergeCirTreeClassTest.kt | 63 ++++++ .../tree/merge/MergeCirTreeFunctionTest.kt | 41 ++++ .../tree/merge/MergeCirTreePackageTest.kt | 45 +++++ .../tree/merge/MergeCirTreePropertyTest.kt | 75 +++++++ .../tree/merge/MergeCirTreeTypeAliasTest.kt | 36 ++++ .../commonizer/utils/InlineSourceTest.kt | 191 ++++++++++++++++++ .../utils/KtInlineSourceCommonizerTestCase.kt | 12 ++ 11 files changed, 600 insertions(+), 176 deletions(-) create mode 100644 native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/AbstractMergeCirTreeTest.kt create mode 100644 native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreeClassConstructorTest.kt create mode 100644 native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreeClassTest.kt create mode 100644 native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreeFunctionTest.kt create mode 100644 native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreePackageTest.kt create mode 100644 native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreePropertyTest.kt create mode 100644 native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreeTypeAliasTest.kt create mode 100644 native/commonizer/tests/org/jetbrains/kotlin/commonizer/utils/InlineSourceTest.kt create mode 100644 native/commonizer/tests/org/jetbrains/kotlin/commonizer/utils/KtInlineSourceCommonizerTestCase.kt diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/tree/CirTree.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/tree/CirTree.kt index 19fe6b8ba37..9d6e191c698 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/tree/CirTree.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/tree/CirTree.kt @@ -11,20 +11,20 @@ import org.jetbrains.kotlin.commonizer.mergedtree.FunctionApproximationKey import org.jetbrains.kotlin.commonizer.mergedtree.PropertyApproximationKey data class CirTreeRoot( - val modules: List + val modules: List = emptyList() ) data class CirTreeModule( val module: CirModule, - val packages: List + val packages: List = emptyList() ) data class CirTreePackage( val pkg: CirPackage, - val properties: List, - val functions: List, - val classes: List, - val typeAliases: List + val properties: List = emptyList(), + val functions: List = emptyList(), + val classes: List = emptyList(), + val typeAliases: List = emptyList() ) data class CirTreeProperty( @@ -49,10 +49,10 @@ data class CirTreeTypeAlias( data class CirTreeClass( override val id: CirEntityId, val clazz: CirClass, - val properties: List, - val functions: List, - val constructors: List, - val classes: List, + val properties: List = emptyList(), + val functions: List = emptyList(), + val constructors: List = emptyList(), + val classes: List = emptyList(), ) : CirTreeClassifier class CirTreeClassConstructor( diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/AbstractCirTreeDeserializerTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/AbstractCirTreeDeserializerTest.kt index 757f52fa5d8..599c0431409 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/AbstractCirTreeDeserializerTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/AbstractCirTreeDeserializerTest.kt @@ -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, val dependencies: List - ) - - @ModuleBuilderDsl - class ModuleBuilder { - var name: String = "test-module" - var sourceFiles: List = emptyList() - var dependencies: List = 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 = 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 - ) : 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 = listOf(this) + dependencyModules.keys - override val platform: TargetPlatform get() = CommonPlatforms.defaultCommonPlatform - override val analyzerServices: PlatformDependentAnalyzerServices get() = CommonPlatformAnalyzerServices - } - - override val moduleInfos: List get() = listOf(DefaultBuiltInsModuleInfo) + dependenciesByModuleInfos.keys - override val friendModuleInfos: List get() = emptyList() - override val refinesModuleInfos: List 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 }}") diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/AbstractMergeCirTreeTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/AbstractMergeCirTreeTest.kt new file mode 100644 index 00000000000..aeb7f431ec9 --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/AbstractMergeCirTreeTest.kt @@ -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): 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): TargetDependent { + return TargetDependent(modules.toList()) + } + + private fun TargetDependent(modules: Iterable>): TargetDependent { + 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)) + ) + ) + } +} diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreeClassConstructorTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreeClassConstructorTest.kt new file mode 100644 index 00000000000..5f440e3e290 --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreeClassConstructorTest.kt @@ -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") + } +} diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreeClassTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreeClassTest.kt new file mode 100644 index 00000000000..0de2bb4d659 --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreeClassTest.kt @@ -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) + } +} diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreeFunctionTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreeFunctionTest.kt new file mode 100644 index 00000000000..a2b4a16c96e --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreeFunctionTest.kt @@ -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") + } +} diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreePackageTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreePackageTest.kt new file mode 100644 index 00000000000..33e359b0762 --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreePackageTest.kt @@ -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") + } +} diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreePropertyTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreePropertyTest.kt new file mode 100644 index 00000000000..75760973b5f --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreePropertyTest.kt @@ -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") + } +} + diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreeTypeAliasTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreeTypeAliasTest.kt new file mode 100644 index 00000000000..e543f04a4f3 --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/MergeCirTreeTypeAliasTest.kt @@ -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") + } +} diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/utils/InlineSourceTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/utils/InlineSourceTest.kt new file mode 100644 index 00000000000..6e612988f9a --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/utils/InlineSourceTest.kt @@ -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, val dependencies: List + ) + + @ModuleBuilderDsl + class ModuleBuilder { + var name: String = "test-module" + private var sourceFiles: List = emptyList() + private var dependencies: List = 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 = 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 + ) : 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 = listOf(this) + dependencyModules.keys + override val platform: TargetPlatform get() = CommonPlatforms.defaultCommonPlatform + override val analyzerServices: PlatformDependentAnalyzerServices get() = CommonPlatformAnalyzerServices + } + + override val moduleInfos: List get() = listOf(DefaultBuiltInsModuleInfo) + dependenciesByModuleInfos.keys + override val friendModuleInfos: List get() = emptyList() + override val refinesModuleInfos: List 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 + } +} diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/utils/KtInlineSourceCommonizerTestCase.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/utils/KtInlineSourceCommonizerTestCase.kt new file mode 100644 index 00000000000..9723f3fb018 --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/utils/KtInlineSourceCommonizerTestCase.kt @@ -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) +}