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 new file mode 100644 index 00000000000..cbed4ea14a6 --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/AbstractCirTreeDeserializerTest.kt @@ -0,0 +1,163 @@ +/* + * 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.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.psi.KtFile +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.resolve.CompilerEnvironment +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) + + @ModuleBuilderDsl + class ModuleBuilder { + var name: String = "test-module" + var sourceFiles: List = emptyList() + + fun source(name: String = "test.kt", @Language("kotlin") content: String) { + sourceFiles = sourceFiles + SourceFile(name, content) + } + + fun build() = Module(name, sourceFiles.toList()) + } + + @ModuleBuilderDsl + fun module(builder: ModuleBuilder.() -> Unit): Module { + return ModuleBuilder().also(builder).build() + } + + fun deserializeModule(builder: ModuleBuilder.() -> Unit): CirTreeModule { + val module = module(builder) + val moduleFolder = FileUtil.createTempDirectory(module.name, null) + module.sourceFiles.forEach { sourceFile -> + moduleFolder.resolve(sourceFile.name).writeText(sourceFile.content) + } + + val moduleDescriptor = createModuleDescriptor(moduleFolder, module) + val metadata = MockModulesProvider.SERIALIZER.serializeModule(moduleDescriptor) + val typeResolver = CirTypeResolver.create( + CirProvidedClassifiers.of( + CirFictitiousFunctionClassifiers, + CirProvidedClassifiers.by(MockModulesProvider.create(moduleDescriptor)), + CirProvidedClassifiers.by(MockModulesProvider.create(DefaultBuiltIns.Instance.builtInsModule)) + ) + ) + return defaultCirTreeModuleDeserializer(metadata, typeResolver) + } + + fun deserializeSourceFile(@Language("kotlin") sourceFileContent: String): CirTreeModule { + return deserializeModule { + source("test.kt", content = sourceFileContent) + } + } + + + 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(), + ) { content -> + environment.createPackagePartProvider(content.moduleContentScope) + }.moduleDescriptor + } + + private class DependenciesContainerImpl : CommonDependenciesContainer { + 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 + } + + override val moduleInfos: List get() = listOf(DefaultBuiltInsModuleInfo) + 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 { + check(moduleInfo == DefaultBuiltInsModuleInfo) { "Unknown module info $moduleInfo" } + return DefaultBuiltIns.Instance.builtInsModule + } + } + + protected fun CirTreeModule.assertSinglePackage(): CirTreePackage { + return packages.singleOrNull() + ?: kotlin.test.fail("Expected single package. Found ${packages.map { it.pkg.packageName }}") + } + + protected fun CirTreeModule.assertSingleProperty(): CirTreeProperty { + return assertSinglePackage().properties.singleOrNull() + ?: kotlin.test.fail("Expected single property. Found ${assertSinglePackage().properties.map { it.property.name }}") + } + + protected fun CirTreeModule.assertSingleFunction(): CirTreeFunction { + return assertSinglePackage().functions.singleOrNull() + ?: kotlin.test.fail("Expected single property. Found ${assertSinglePackage().functions.map { it.function.name }}") + } + + protected fun CirTreeModule.assertSingleClass(): CirTreeClass { + return assertSinglePackage().classes.singleOrNull() + ?: kotlin.test.fail("Expected single class. Found ${assertSinglePackage().classes.map { it.clazz.name }}") + } + + protected fun CirTreeModule.assertSingleTypeAlias(): CirTreeTypeAlias { + return assertSinglePackage().typeAliases.singleOrNull() + ?: kotlin.test.fail("Expected single type alias. Found ${assertSinglePackage().typeAliases.map { it.typeAlias.name }}") + } +} diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/CirTreeClassDeserializerTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/CirTreeClassDeserializerTest.kt new file mode 100644 index 00000000000..82db282edd8 --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/CirTreeClassDeserializerTest.kt @@ -0,0 +1,176 @@ +/* + * 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.deserializer + +import org.jetbrains.kotlin.commonizer.cir.CirClassType +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibilities +import kotlin.test.* + +class CirTreeClassDeserializerTest : AbstractCirTreeDeserializerTest() { + + fun `test simple class`() { + val module = deserializeSourceFile("class X") + val clazz = module.assertSingleClass().clazz + assertEquals(Visibilities.Public, clazz.visibility, "Expected class to be public") + assertNull(clazz.companion, "Expected class *not* having a companion") + assertFalse(clazz.isInner, "Expected class *not* being inner") + assertFalse(clazz.isCompanion, "Expected class *not* being companion") + assertFalse(clazz.isExternal, "Expected class *not* being external") + assertFalse(clazz.isValue, "Expected class *not* being value") + assertFalse(clazz.isData, "Expected class *not* being data class") + assertTrue(clazz.supertypes.isEmpty(), "Expected class not having any explicit supertypes") + assertEquals(Modality.FINAL, clazz.modality, "Expected class to be final") + assertEquals(ClassKind.CLASS, clazz.kind) + } + + fun `test nested class`() { + val module = deserializeSourceFile( + """ + class Outer { + class Inner + } + """ + ) + + val pkg = module.assertSinglePackage() + val outerClass = pkg.classes.singleOrNull() ?: kotlin.test.fail("Expected single class in package") + assertEquals("Outer", outerClass.clazz.name.toStrippedString()) + + val innerClass = outerClass.classes.singleOrNull() ?: kotlin.test.fail("Expected single nested class 'Inner'") + assertFalse(innerClass.clazz.isInner, "Expected nested class to *not* be inner") + } + + fun `test inner class`() { + val module = deserializeSourceFile( + """ + class Outer { + inner class Inner + } + """ + ) + + val pkg = module.assertSinglePackage() + val outerClass = pkg.classes.singleOrNull() ?: kotlin.test.fail("Expected single class in package") + assertEquals("Outer", outerClass.clazz.name.toStrippedString()) + + val innerClass = outerClass.classes.singleOrNull() ?: kotlin.test.fail("Expected single nested class 'Inner'") + assertTrue(innerClass.clazz.isInner, "Expected nested class to be inner class") + } + + + fun `test data class`() { + val module = deserializeSourceFile("data class X(val x: String)") + val clazz = module.assertSingleClass() + assertTrue(clazz.clazz.isData, "Expected is data class") + assertEquals(1, clazz.constructors.size, "Expected single constructor") + assertEquals(1, clazz.properties.size, "Expected single property") + } + + fun `test companion`() { + val module = deserializeSourceFile( + """ + class Outer { + companion object { + val x: Int = 42 + } + } + """.trimIndent() + ) + val clazz = module.assertSingleClass() + val companion = clazz.classes.singleOrNull() ?: kotlin.test.fail("Expected single class in 'Outer'") + assertTrue(companion.clazz.isCompanion, "Expected companion being marked as companion") + assertEquals(1, companion.properties.size, "Expected exactly one property in companion") + } + + fun `test object`() { + val module = deserializeSourceFile("object X") + val clazz = module.assertSingleClass() + assertEquals(ClassKind.OBJECT, clazz.clazz.kind, "Expected object class kind") + } + + fun `test interface`() { + val module = deserializeSourceFile("interface X") + val clazz = module.assertSingleClass() + assertEquals(ClassKind.INTERFACE, clazz.clazz.kind) + } + + fun `test supertypes`() { + val module = deserializeSourceFile( + """ + interface A + interface B: A + interface C: B + class X: C + """.trimIndent() + ) + + val pkg = module.assertSinglePackage() + val xClass = pkg.classes.singleOrNull { it.clazz.name.toStrippedString() == "X" } ?: kotlin.test.fail("Missing class 'X'") + val xSuperType = xClass.clazz.supertypes.singleOrNull() ?: kotlin.test.fail("Expected single supertype for 'X'") + val xClassSuperType = assertIs(xSuperType, "Expected xSuperType to be class type") + assertEquals("/C", xClassSuperType.classifierId.toString()) + + } + + fun `test abstract class`() { + val module = deserializeSourceFile("abstract class X") + val clazz = module.assertSingleClass() + assertEquals(Modality.ABSTRACT, clazz.clazz.modality) + } + + fun `test open class`() { + val module = deserializeSourceFile("open class X") + val clazz = module.assertSingleClass() + assertEquals(Modality.OPEN, clazz.clazz.modality) + } + + fun `test class with properties functions and nested classes`() { + val module = deserializeSourceFile( + """ + class X { + val myInt: Int = 42 + val myFloat: Float = 42f + val myDouble: Double = 42.0 + val myString = "hello" + + fun myIntFunction(int: Int) = int + fun myStringFunction(string: String) = string + + class MyInnerClass1 + class MyInnerClass2 + } + """.trimIndent() + ) + + val clazz = module.assertSingleClass() + + fun assertContainsProperty(name: String) { + clazz.properties.singleOrNull { it.property.name.toStrippedString() == name } + ?: kotlin.test.fail("Missing property '$name'") + } + + fun assertContainsFunction(name: String) { + clazz.functions.singleOrNull { it.function.name.toStrippedString() == name } + ?: kotlin.test.fail("Missing function '$name'") + } + + fun assertContainsClass(name: String) { + clazz.classes.singleOrNull { it.clazz.name.toStrippedString() == name } + ?: kotlin.test.fail("Missing class '$name'") + } + + assertContainsProperty("myInt") + assertContainsProperty("myFloat") + assertContainsProperty("myDouble") + assertContainsProperty("myString") + assertContainsFunction("myIntFunction") + assertContainsFunction("myStringFunction") + assertContainsClass("MyInnerClass1") + assertContainsClass("MyInnerClass2") + } +} diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/CirTreeFunctionDeserializerTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/CirTreeFunctionDeserializerTest.kt new file mode 100644 index 00000000000..9c4a6aabceb --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/CirTreeFunctionDeserializerTest.kt @@ -0,0 +1,70 @@ +/* + * 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.deserializer + +import org.jetbrains.kotlin.commonizer.cir.CirClass +import org.jetbrains.kotlin.commonizer.cir.CirClassType +import org.jetbrains.kotlin.commonizer.cir.CirTypeParameterType +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibilities +import kotlin.test.* + +class CirTreeFunctionDeserializerTest : AbstractCirTreeDeserializerTest() { + + fun `test simple function`() { + val module = deserializeSourceFile("fun x() = Unit") + val (_, function) = module.assertSingleFunction() + + assertNull(function.containingClass, "Expected function to *not* have containing class") + assertEquals(Visibilities.Public, function.visibility, "Expected function to be public") + val returnType = function.returnType as? CirClassType ?: kotlin.test.fail("Expected return type being class") + assertFalse(returnType.isMarkedNullable, "Expected return type *not* being marked nullable") + assertEquals("kotlin/Unit", returnType.classifierId.toString()) + assertEquals(Modality.FINAL, function.modality, "Expected function to be final") + assertNull(function.extensionReceiver, "Expected *no* extension receiver") + } + + fun `test generic function`() { + val module = deserializeSourceFile("""fun T.isHappy(): Boolean = true""") + val (_, function) = module.assertSingleFunction() + + val extensionReceiver = assertNotNull(function.extensionReceiver, "Expected function being extension receiver") + val extensionReceiverType = extensionReceiver.type as? CirTypeParameterType + ?: kotlin.test.fail("Expected receiver type ${CirTypeParameterType::class.simpleName}") + assertFalse(extensionReceiverType.isMarkedNullable, "Expected extensionReceiverType *not* being marked nullable") + + val typeParameter = function.typeParameters.singleOrNull() + ?: kotlin.test.fail("Expected a single type parameter. Found ${function.typeParameters}") + assertFalse(typeParameter.isReified, "Expected type parameter *not* being reified") + + val upperBound = assertIs( + typeParameter.upperBounds.singleOrNull() + ?: kotlin.test.fail("Expected a single upper bound. Found ${typeParameter.upperBounds}") + ) + assertFalse(upperBound.isMarkedNullable, "Expected upper bound *not* being marked nullable") + assertEquals("kotlin/Any", upperBound.toString()) + } + + fun `test function with outer object`() { + val module = deserializeSourceFile( + """ + object Parent { + fun x(): String = "Hello, you're reading a test" + } + """ + ) + + val parent = module.assertSingleClass() + val function = parent.functions.singleOrNull()?.function + ?: kotlin.test.fail("Expected single function in parent. Found ${parent.functions.map { it.function.name }}") + + val containingClass = assertIs(kotlin.test.assertNotNull(function.containingClass)) + assertEquals(ClassKind.OBJECT, containingClass.kind, "Expected containing class being object") + assertEquals("Parent", containingClass.name.toStrippedString()) + } + +} diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/CirTreePackageDeserializerTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/CirTreePackageDeserializerTest.kt new file mode 100644 index 00000000000..bf10b9dc388 --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/CirTreePackageDeserializerTest.kt @@ -0,0 +1,118 @@ +/* + * 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.deserializer + +class CirTreePackageDeserializerTest : AbstractCirTreeDeserializerTest() { + + fun `test package with multiple classes properties and functions`() { + val module = deserializeModule { + source( + "root.kt", """ + typealias rootTypeAlias = Int + val rootProperty: String = "hello," + fun rootFunction(): String = "it is me" + class RootClass + """.trimIndent() + ) + + source( + "pkg1.kt", """ + package test.pkg1 + typealias pkg1TypeAlias = Int + val pkg1Property: Int = 42 + fun pkg1Function(): String = "answer" + class Pkg1 + """.trimIndent() + ) + + source( + "pkg2.kt", """ + package test.pkg2 + typealias pkg2TypeAlias = Int + val pkg2Property: Int = 42 + fun pkg2Function(): String = "answer" + class Pkg2 + """.trimIndent() + ) + } + + val rootPackage = module.packages.singleOrNull { it.pkg.packageName.isRoot() } + ?: kotlin.test.fail("Missing root package") + + val pkg1 = module.packages.singleOrNull { it.pkg.packageName.toMetadataString() == "test/pkg1" } + ?: kotlin.test.fail("Missing pkg1") + + val pkg2 = module.packages.singleOrNull { it.pkg.packageName.toMetadataString() == "test/pkg2" } + ?: kotlin.test.fail("Missing pkg2") + + + rootPackage.run { + kotlin.test.assertTrue( + typeAliases.any { it.typeAlias.name.toStrippedString() == "rootTypeAlias" }, + "Expected 'rootTypeAlias'" + ) + + kotlin.test.assertTrue( + properties.any { it.property.name.toStrippedString() == "rootProperty" }, + "Expected 'rootProperty'" + ) + + kotlin.test.assertTrue( + functions.any { it.function.name.toStrippedString() == "rootFunction" }, + "Expected 'rootFunction'" + ) + + kotlin.test.assertTrue( + classes.any { it.clazz.name.toStrippedString() == "RootClass" }, + "Expected 'RootClass'" + ) + } + + pkg1.run { + kotlin.test.assertTrue( + typeAliases.any { it.typeAlias.name.toStrippedString() == "pkg1TypeAlias" }, + "Expected 'pkg1TypeAlias'" + ) + + kotlin.test.assertTrue( + properties.any { it.property.name.toStrippedString() == "pkg1Property" }, + "Expected 'pkg1Property'" + ) + + kotlin.test.assertTrue( + functions.any { it.function.name.toStrippedString() == "pkg1Function" }, + "Expected 'pkg1Function'" + ) + + kotlin.test.assertTrue( + classes.any { it.clazz.name.toStrippedString() == "Pkg1" }, + "Expected 'Pkg1'" + ) + } + + pkg2.run { + kotlin.test.assertTrue( + typeAliases.any { it.typeAlias.name.toStrippedString() == "pkg2TypeAlias" }, + "Expected 'pkg2TypeAlias'" + ) + + kotlin.test.assertTrue( + properties.any { it.property.name.toStrippedString() == "pkg2Property" }, + "Expected 'pkg2Property'" + ) + + kotlin.test.assertTrue( + functions.any { it.function.name.toStrippedString() == "pkg2Function" }, + "Expected 'pkg2Function'" + ) + + kotlin.test.assertTrue( + classes.any { it.clazz.name.toStrippedString() == "Pkg2" }, + "Expected 'Pkg2'" + ) + } + } +} diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/CirTreePropertyDeserializerTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/CirTreePropertyDeserializerTest.kt new file mode 100644 index 00000000000..eb3fae2efd2 --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/CirTreePropertyDeserializerTest.kt @@ -0,0 +1,83 @@ +/* + * 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.deserializer + +import org.jetbrains.kotlin.commonizer.cir.CirClassType +import org.jetbrains.kotlin.commonizer.cir.CirName +import org.jetbrains.kotlin.commonizer.cir.CirTypeParameterType +import org.jetbrains.kotlin.commonizer.mergedtree.PropertyApproximationKey +import org.jetbrains.kotlin.descriptors.Visibilities +import kotlin.test.* + + +class CirTreePropertyDeserializerTest : AbstractCirTreeDeserializerTest() { + + fun `test simple val property`() { + val module = deserializeSourceFile("val x: Int = 42") + + val (key, property) = module.assertSingleProperty() + assertEquals(PropertyApproximationKey(CirName.Companion.create("x"), null), key) + + assertEquals("x", property.name.toStrippedString()) + assertFalse(property.isConst, "Expected property to is *no* const") + assertNotNull(property.getter, "Expected property to have getter") + assertNull(property.setter, "Expected property to have *no* setter") + assertFalse(property.isLateInit, "Expected property to be *not* lateinit") + assertFalse(property.isVar, "Expected property to be *not* var") + assertEquals(Visibilities.Public, property.visibility, "Expected property to be public") + assertNull(property.extensionReceiver, "Expected property to *not* have extension receiver") + } + + fun `test simple var property`() { + val module = deserializeSourceFile("var x: Int = 42") + val (_, property) = module.assertSingleProperty() + assertNotNull(property.getter, "Expected property has getter") + assertNotNull(property.setter, "Expected property has setter") + assertFalse(property.isLateInit, "Expected property to be not lateinit") + assertTrue(property.isVar, "Expected property to be var") + } + + fun `test lateinit var property`() { + val module = deserializeSourceFile("lateinit var x: Int") + val (_, property) = module.assertSingleProperty() + + assertNotNull(property.getter, "Expected property has getter") + assertNotNull(property.setter, "Expected property has setter") + assertTrue(property.isLateInit, "Expected property to be lateinit") + assertTrue(property.isVar, "Expected property to be var") + } + + fun `test generic var property`() { + val module = deserializeSourceFile("var T.x: T get() = this") + val (_, property) = module.assertSingleProperty() + + assertNotNull(property.extensionReceiver, "Expected property has extension receiver") + assertTrue( + property.extensionReceiver?.type is CirTypeParameterType, + "Expected extension receiver being type of ${CirTypeParameterType::class.simpleName}" + ) + } + + fun `test multiple properties`() { + val module = deserializeSourceFile( + """ + val x: Int = 42 + val y: Float = 42f + var z: String = "42" + var Any?.answer get() = if(this != null) 42 else null + """.trimIndent() + ) + + val pkg = module.assertSinglePackage() + assertEquals(4, pkg.properties.size, "Expected exactly 4 properties in package") + + val answerProperty = pkg.properties.single { it.property.name.toStrippedString() == "answer" } + val answerReturnType = answerProperty.property.returnType as? CirClassType + ?: kotlin.test.fail("Expected answer return type is class") + assertEquals("kotlin/Int", answerReturnType.classifierId.toString()) + assertTrue(answerReturnType.isMarkedNullable, "Expected answer return type being marked nullable") + } +} diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/CirTreeTypeAliasDeserializerTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/CirTreeTypeAliasDeserializerTest.kt new file mode 100644 index 00000000000..016ffff6f37 --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/deserializer/CirTreeTypeAliasDeserializerTest.kt @@ -0,0 +1,62 @@ +/* + * 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.deserializer + +import org.jetbrains.kotlin.commonizer.cir.CirClassType +import org.jetbrains.kotlin.commonizer.cir.CirName +import org.jetbrains.kotlin.commonizer.cir.CirRegularTypeProjection +import org.jetbrains.kotlin.commonizer.cir.CirTypeAliasType +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertIs + +class CirTreeTypeAliasDeserializerTest : AbstractCirTreeDeserializerTest() { + + fun `test simple type alias`() { + val module = deserializeSourceFile("typealias x = Int") + val typeAlias = module.assertSingleTypeAlias().typeAlias + assertEquals(CirName.create("x"), typeAlias.name, "Expected correct type alias name") + assertEquals("kotlin/Int", typeAlias.expandedType.toString()) + assertEquals("kotlin/Int", typeAlias.underlyingType.toString()) + } + + fun `test transitive type alias`() { + val module = deserializeSourceFile( + """ + interface Map + typealias IntMap = Map + typealias IntStringMap = IntMap + """.trimIndent() + ) + val pkg = module.assertSinglePackage() + assertEquals(2, pkg.typeAliases.size, "Expected exactly 2 type aliases in package") + + val intStringMapTypeAlias = pkg.typeAliases.singleOrNull { it.typeAlias.name.toStrippedString() == "IntStringMap" } + ?: kotlin.test.fail("Missing 'IntStringMap' type alias") + + val underlyingType = assertIs( + intStringMapTypeAlias.typeAlias.underlyingType, "Expected underlying type to be type alias" + ) + + assertEquals("/IntMap", underlyingType.classifierId.toString()) + val underlyingTypeArgument = underlyingType.arguments.singleOrNull() + ?: kotlin.test.fail("Expected single argument on underlying type. Found ${underlyingType.arguments}}") + + assertIs(underlyingTypeArgument, "Expected regular type projection for underlying type") + assertEquals("kotlin/String", underlyingTypeArgument.type.toString()) + + + val expandedType = assertIs(intStringMapTypeAlias.typeAlias.expandedType) + assertFalse(expandedType.isMarkedNullable, "Expected expanded type to be *not* marked nullable") + + kotlin.test.assertEquals("/Map", expandedType.classifierId.toString(), "Expected correct expanded type classifier") + kotlin.test.assertEquals(2, expandedType.arguments.size, "Expected two type arguments") + kotlin.test.assertEquals("kotlin/Int", expandedType.arguments[0].toString(), "Expected correct first type argument") + kotlin.test.assertEquals("kotlin/String", expandedType.arguments[1].toString(), "Expected correct second type argument") + } +}