[LL API] Add tests for 'out of content root' analysis mode

This commit is contained in:
Yan Zhulanow
2022-12-14 20:37:14 +09:00
committed by Space Team
parent e4c2902e81
commit f1edbc1a14
58 changed files with 2698 additions and 280 deletions
@@ -21,8 +21,6 @@ abstract class AnalysisApiKtModuleProvider : TestService {
abstract fun registerProjectStructure(modules: KtModuleProjectStructure)
protected abstract fun getModuleName(ktModule: KtModule): String
abstract fun getModuleStructure(): KtModuleProjectStructure
}
@@ -43,20 +41,24 @@ class AnalysisApiKtModuleProviderImpl(
require(!this::modulesByName.isInitialized)
this.modulesStructure = modules
this.modulesByName = modulesStructure.mainModules.associateBy { getModuleName(it.ktModule) }
this.modulesByName = modulesStructure.mainModules.associateByName()
}
override fun getModuleName(ktModule: KtModule): String = when (ktModule) {
is KtLibraryModule -> ktModule.libraryName
is KtSdkModule -> ktModule.sdkName
is KtLibrarySourceModule -> ktModule.libraryName
is KtSourceModule -> ktModule.moduleName
is KtNotUnderContentRootModule -> TODO()
is KtBuiltinsModule -> "Builtins for ${ktModule.platform}"
}
override fun getModuleStructure(): KtModuleProjectStructure = modulesStructure
}
val TestServices.ktModuleProvider: AnalysisApiKtModuleProvider by TestServices.testServiceAccessor()
fun List<KtModuleWithFiles>.associateByName(): Map<String, KtModuleWithFiles> {
return associateBy { (ktModule, _) ->
when (ktModule) {
is KtSourceModule -> ktModule.moduleName
is KtLibraryModule -> ktModule.libraryName
is KtLibrarySourceModule -> ktModule.libraryName
is KtSdkModule -> ktModule.sdkName
is KtBuiltinsModule -> "Builtins for ${ktModule.platform}"
is KtNotUnderContentRootModuleForTest -> ktModule.moduleName
else -> error("Unsupported module type: " + ktModule.javaClass.name)
}
}
}
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2022 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.project.structure
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleWithFiles
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestService
import org.jetbrains.kotlin.test.services.TestServices
fun interface KtModuleFactory : TestService {
fun createModule(testModule: TestModule, testServices: TestServices, project: Project): KtModuleWithFiles
}
val TestServices.ktModuleFactory: KtModuleFactory by TestServices.testServiceAccessor()
@@ -0,0 +1,53 @@
/*
* Copyright 2010-2022 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.project.structure
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleWithFiles
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.KtNotUnderContentRootModule
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import org.jetbrains.kotlin.test.getAnalyzerServices
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
class KtOutOfContentRootModuleFactory : KtModuleFactory {
override fun createModule(testModule: TestModule, testServices: TestServices, project: Project): KtModuleWithFiles {
val psiFiles = TestModuleStructureFactory.createSourcePsiFiles(testModule, testServices, project)
val module = KtNotUnderContentRootModuleForTest(testModule.name, psiFiles.single(), testModule.targetPlatform)
return KtModuleWithFiles(module, psiFiles)
}
}
internal class KtNotUnderContentRootModuleForTest(
val moduleName: String,
override val file: PsiFile,
override val platform: TargetPlatform
) : KtNotUnderContentRootModule {
override val directRegularDependencies: List<KtModule>
get() = emptyList()
override val directRefinementDependencies: List<KtModule>
get() = emptyList()
override val directFriendDependencies: List<KtModule>
get() = emptyList()
override val contentScope: GlobalSearchScope
get() = GlobalSearchScope.fileScope(file)
override val analyzerServices: PlatformDependentAnalyzerServices
get() = platform.getAnalyzerServices()
override val project: Project
get() = file.project
override val moduleDescription: String
get() = "Not under content root for ${file.virtualFile.path}"
}
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2022 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.project.structure
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleWithFiles
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
class KtSourceModuleFactory : KtModuleFactory {
override fun createModule(testModule: TestModule, testServices: TestServices, project: Project): KtModuleWithFiles {
val psiFiles = TestModuleStructureFactory.createSourcePsiFiles(testModule, testServices, project)
val module = KtSourceModuleImpl(
testModule.name,
testModule.targetPlatform,
testModule.languageVersionSettings,
project,
GlobalSearchScope.filesScope(project, psiFiles.mapTo(mutableSetOf()) { it.virtualFile }),
)
return KtModuleWithFiles(module, psiFiles)
}
}
@@ -16,9 +16,8 @@ import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.Stand
import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule
import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.allDirectDependenciesOfType
import org.jetbrains.kotlin.analysis.project.structure.KtNotUnderContentRootModule
import org.jetbrains.kotlin.analysis.test.framework.services.environmentManager
import org.jetbrains.kotlin.analysis.utils.errors.checkIsInstance
import org.jetbrains.kotlin.analysis.utils.errors.requireIsInstance
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
@@ -37,66 +36,42 @@ import kotlin.io.path.exists
import kotlin.io.path.extension
import kotlin.io.path.nameWithoutExtension
fun interface KtMainModuleFactory {
fun createMainModule(
testModule: TestModule,
testServices: TestServices,
project: Project,
): KtModuleWithFiles
}
object KtMainModuleFactoryForSourceModules : KtMainModuleFactory {
override fun createMainModule(
testModule: TestModule,
testServices: TestServices,
project: Project,
): KtModuleWithFiles {
val psiFiles = TestModuleStructureFactory.createSourcePsiFiles(testModule, testServices, project)
return KtModuleWithFiles(
KtSourceModuleImpl(
testModule.name,
testModule.targetPlatform,
testModule.languageVersionSettings,
project,
GlobalSearchScope.filesScope(project, psiFiles.mapTo(mutableSetOf()) { it.virtualFile }),
),
psiFiles
)
}
}
object TestModuleStructureFactory {
fun createProjectStructureByTestStructure(
moduleStructure: TestModuleStructure,
testServices: TestServices,
project: Project,
mainModuleFactory: KtMainModuleFactory
project: Project
): KtModuleProjectStructure {
val modulesFromTestServices = moduleStructure.modules.map { testModule ->
testModule.toKtModule(testServices, project, mainModuleFactory)
}
val moduleByName = modulesFromTestServices.associateBy { getModuleName(it.ktModule) }
val moduleEntries = moduleStructure.modules
.map { testModule -> testServices.ktModuleFactory.createModule(testModule, testServices, project) }
val moduleEntriesByName = moduleEntries.associateByName()
val binaryModulesBySourceRoots = mutableMapOf<Set<Path>, KtBinaryModule>()
moduleStructure.modules.forEach { testModule ->
val ktModule = moduleByName.getValue(testModule.name).ktModule
checkIsInstance<KtModuleWithModifiableDependencies>(ktModule)
for (testModule in moduleStructure.modules) {
when (val ktModule = moduleEntriesByName.getValue(testModule.name).ktModule) {
is KtNotUnderContentRootModule -> {
// Not-under-content-root modules have no external dependencies on purpose
}
is KtModuleWithModifiableDependencies -> {
addModuleDependencies(testModule, moduleEntriesByName, ktModule)
addModuleDependencies(testModule, moduleByName, ktModule)
buildList {
addIfNotNull(getJdkModule(testModule, project, testServices))
addAll(getStdlibModules(testModule, project, testServices))
addAll(getLibraryModules(testServices, testModule, project))
addAll(createLibrariesByCompilerConfigurators(testModule, testServices, project))
}.forEach { library ->
val cachedLibrary = binaryModulesBySourceRoots.getOrPut(library.getBinaryRoots().toSet()) { library }
ktModule.directRegularDependencies.add(cachedLibrary)
buildList {
addIfNotNull(getJdkModule(testModule, project, testServices))
addAll(getStdlibModules(testModule, project, testServices))
addAll(getLibraryModules(testServices, testModule, project))
addAll(createLibrariesByCompilerConfigurators(testModule, testServices, project))
}.forEach { library ->
val cachedLibrary = binaryModulesBySourceRoots.getOrPut(library.getBinaryRoots().toSet()) { library }
ktModule.directRegularDependencies.add(cachedLibrary)
}
}
else -> error("Unexpected module type: " + ktModule.javaClass.name)
}
}
return KtModuleProjectStructure(modulesFromTestServices, binaryModulesBySourceRoots.values)
return KtModuleProjectStructure(moduleEntries, binaryModulesBySourceRoots.values)
}
@OptIn(TestInfrastructureInternals::class)
@@ -112,19 +87,7 @@ object TestModuleStructureFactory {
.map { root -> createKtLibraryModuleByJar(root.file.toPath(), testServices, project) }
}
private fun getModuleName(ktModule: KtModule) = when (ktModule) {
is KtSourceModuleImpl -> ktModule.moduleName
is KtLibraryModuleImpl -> ktModule.libraryName
is KtLibrarySourceModuleImpl -> ktModule.libraryName
else -> error("Unknown module ${ktModule::class}")
}
private fun addModuleDependencies(
testModule: TestModule,
moduleByName: Map<String, KtModuleWithFiles>,
ktModule: KtModule
) {
private fun addModuleDependencies(testModule: TestModule, moduleByName: Map<String, KtModuleWithFiles>, ktModule: KtModule) {
requireIsInstance<KtModuleWithModifiableDependencies>(ktModule)
testModule.allDependencies.forEach { dependency ->
val dependencyKtModule = moduleByName.getValue(dependency.moduleName).ktModule
@@ -224,14 +187,6 @@ object TestModuleStructureFactory {
)
}
private fun TestModule.toKtModule(
testServices: TestServices,
project: Project,
mainModuleFactory: KtMainModuleFactory,
): KtModuleWithFiles {
return mainModuleFactory.createMainModule(this, testServices, project)
}
fun createSourcePsiFiles(
testModule: TestModule,
testServices: TestServices,
@@ -9,10 +9,12 @@ import com.intellij.mock.MockApplication
import com.intellij.mock.MockProject
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.Application
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleProjectStructure
import org.jetbrains.kotlin.analysis.providers.KotlinModificationTrackerFactory
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
@@ -38,7 +40,10 @@ abstract class AnalysisApiTestConfigurator {
open fun prepareFilesInModule(files: List<PsiFile>, module: TestModule, testServices: TestServices) {}
abstract fun doOutOfBlockModification(file: KtFile)
open fun doOutOfBlockModification(file: KtFile) {
ServiceManager.getService(file.project, KotlinModificationTrackerFactory::class.java)
.incrementModificationsCount()
}
open fun preprocessTestDataPath(path: Path): Path = path