diff --git a/analysis/analysis-test-framework/tests/org/jetbrains/kotlin/analysis/test/framework/TestWithMockProject.kt b/analysis/analysis-test-framework/tests/org/jetbrains/kotlin/analysis/test/framework/TestWithMockProject.kt new file mode 100644 index 00000000000..9c1113a95e9 --- /dev/null +++ b/analysis/analysis-test-framework/tests/org/jetbrains/kotlin/analysis/test/framework/TestWithMockProject.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2023 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.analysis.test.framework + +import com.intellij.mock.MockProject +import com.intellij.openapi.project.Project +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach + +/** + * Note that the project created by this class is a completely non-functional stub. + * It is only useful in tests that don't depend on Project's functionality. + */ +abstract class TestWithMockProject : TestWithDisposable() { + private var _project: Project? = null + protected val project: Project get() = _project!! + + @BeforeEach + fun initProject() { + _project = MockProject(null, disposable) + } + + @AfterEach + fun cleanup() { + _project = null + } +} diff --git a/analysis/analysis-test-framework/tests/org/jetbrains/kotlin/analysis/test/framework/project/structure/KtSourceModuleImpl.kt b/analysis/analysis-test-framework/tests/org/jetbrains/kotlin/analysis/test/framework/project/structure/KtSourceModuleImpl.kt index a487a192a41..e4ed9f96e98 100644 --- a/analysis/analysis-test-framework/tests/org/jetbrains/kotlin/analysis/test/framework/project/structure/KtSourceModuleImpl.kt +++ b/analysis/analysis-test-framework/tests/org/jetbrains/kotlin/analysis/test/framework/project/structure/KtSourceModuleImpl.kt @@ -25,4 +25,8 @@ class KtSourceModuleImpl( override val directRegularDependencies: MutableList = mutableListOf() override val directDependsOnDependencies: MutableList = mutableListOf() override val directFriendDependencies: MutableList = mutableListOf() + + override fun toString(): String { + return moduleName + } } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirAbstractSessionFactory.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirAbstractSessionFactory.kt index 28a077f486a..ba3ef5c5fc4 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirAbstractSessionFactory.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirAbstractSessionFactory.kt @@ -609,6 +609,7 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje } } + // Please update KmpModuleSorterTest#buildDependenciesToTest if the logic of collecting dependencies changes val dependencyModules = buildSet { addAll(module.directRegularDependencies) diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/KmpModuleSorterTest.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/KmpModuleSorterTest.kt new file mode 100644 index 00000000000..cf7b7eae4df --- /dev/null +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/KmpModuleSorterTest.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2010-2023 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.analysis.low.level.api.fir + +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.KmpModuleSorter +import org.jetbrains.kotlin.analysis.project.structure.KtModule +import org.jetbrains.kotlin.analysis.test.framework.TestWithMockProject +import org.jetbrains.kotlin.analysis.test.framework.project.structure.KtSourceModuleImpl +import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl +import org.jetbrains.kotlin.platform.CommonPlatforms +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class KmpModuleSorterTest : TestWithMockProject() { + @Test + fun testNonKmpDependencies() { + val a = createKtModule("A") + val b = createKtModule("B") + val c = createKtModule("C") + val d = createKtModule("D", directRegularDependencies = listOf(a, b, c)) + assertEquals(listOf(a, b, c), buildDependenciesToTest(d)) + } + + @Test + fun testOnlyKmpDependencies() { + val p1CommonMain = createKtModule("p1.commonMain") + val p1NativeMain = createKtModule("p1.nativeMain", directDependsOnDependencies = listOf(p1CommonMain)) + val p1IosMain = createKtModule("p1.iosMain", directDependsOnDependencies = listOf(p1NativeMain)) + val p2IosMain = createKtModule("p2.iosMain", directRegularDependencies = listOf(p1CommonMain, p1NativeMain, p1IosMain)) + + assertEquals(listOf(p1IosMain, p1NativeMain, p1CommonMain), buildDependenciesToTest(p2IosMain)) + } + + @Test + fun testMixedKmpAndUsualDependenciesShuffled() { + val a = createKtModule("A") + val b1 = createKtModule("B1") + val b2 = createKtModule("B2", directDependsOnDependencies = listOf(b1)) + val b3 = createKtModule("B3", directDependsOnDependencies = listOf(b2)) + val c = createKtModule("C") + val d1 = createKtModule("D1") + val d2 = createKtModule("D2", directDependsOnDependencies = listOf(d1)) + + val p2IosMain = createKtModule( + "p2.iosMain", directRegularDependencies = listOf( + a, b2, c, b1, d2, b3, d1 + ) + ) + + assertEquals(listOf(a, b3, c, b2, d2, b1, d1), this.buildDependenciesToTest(p2IosMain)) + } + + @Test + fun testDependsOnDependenciesFromSelfAndOtherProject() { + val p1Common = createKtModule("p1.common") + val p1Intermediate = createKtModule("p1.intermediate", directDependsOnDependencies = listOf(p1Common)) + val p1Platform = createKtModule("p1.platform", directDependsOnDependencies = listOf(p1Intermediate)) + val p2Common = createKtModule("p2.common", directRegularDependencies = listOf(p1Common)) + val p2Intermediate = createKtModule( + "p2.intermediate", + directDependsOnDependencies = listOf(p2Common), + directRegularDependencies = listOf(p1Common, p1Intermediate), + ) + val p2Platform = createKtModule( + "p2.platform", + directDependsOnDependencies = listOf(p2Intermediate), + directRegularDependencies = listOf(p1Common, p1Intermediate, p1Platform) + ) + + assertEquals(listOf(p1Common), this.buildDependenciesToTest(p2Common)) + assertEquals(listOf(p1Intermediate, p1Common, p2Common), this.buildDependenciesToTest(p2Intermediate)) + assertEquals(listOf(p1Platform, p1Intermediate, p1Common, p2Intermediate, p2Common), this.buildDependenciesToTest(p2Platform)) + } + + // See [org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirAbstractSessionFactory#collectDependencySymbolProviders] + private fun buildDependenciesToTest(module: KtModule): List { + val dependenciesToSort = buildSet { + addAll(module.directRegularDependencies) + addAll(module.transitiveDependsOnDependencies) + } + return KmpModuleSorter.order(dependenciesToSort.toList()) + } + + private fun createKtModule( + name: String, + directRegularDependencies: List = emptyList(), + directDependsOnDependencies: List = emptyList(), + directFriendDependencies: List = emptyList(), + ): KtModule { + return KtSourceModuleImpl( + name, CommonPlatforms.defaultCommonPlatform, LanguageVersionSettingsImpl.DEFAULT, project, GlobalSearchScope.EMPTY_SCOPE + ).apply { + this.directRegularDependencies.addAll(directRegularDependencies) + this.directFriendDependencies.addAll(directFriendDependencies) + this.directDependsOnDependencies.addAll(directDependsOnDependencies) + } + } +}