[Analysis API] Add tests for foreign values

This commit is contained in:
Yan Zhulanow
2024-02-05 18:30:30 +09:00
committed by Space Team
parent 2dd16e1179
commit 1f39bc9a18
27 changed files with 237 additions and 48 deletions
@@ -12,7 +12,7 @@ import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.TestInfo
abstract class TestWithDisposable {
private var _disposable: Disposable? = null
private var _disposable: Disposable? = Disposer.newDisposable("Disposable for Analysis Api tests")
protected val disposable: Disposable get() = _disposable!!
@BeforeEach
@@ -43,7 +43,7 @@ class AnalysisApiKtModuleProviderImpl(
require(!this::modulesByName.isInitialized)
this.modulesStructure = modules
this.modulesByName = modulesStructure.mainModules.associateByName()
this.modulesByName = modulesStructure.mainModules.associateBy { it.testModule.name }
}
override fun getModuleStructure(): KtTestModuleProjectStructure = modulesStructure
@@ -57,21 +57,4 @@ fun TestServices.allKtFiles(): List<KtFile> = moduleStructure.modules.flatMap(kt
val TestServices.ktModuleProvider: AnalysisApiKtModuleProvider by TestServices.testServiceAccessor()
fun TestModule.getKtModule(testServices: TestServices): KtModule = testServices.ktModuleProvider.getModule(name)
fun List<KtTestModule>.associateByName(): Map<String, KtTestModule> {
return associateBy { ktTestModule ->
when (val ktModule = ktTestModule.ktModule) {
is KtModuleByCompilerConfiguration -> ktModule.moduleName
is KtSourceModule -> ktModule.moduleName
is KtLibraryModule -> ktModule.libraryName
is KtLibrarySourceModule -> ktModule.libraryName
is KtSdkModule -> ktModule.sdkName
is KtBuiltinsModule -> "Builtins for ${ktModule.platform}"
is KtNotUnderContentRootModule -> ktModule.name
is KtScriptModule -> ktModule.file.name
is KtDanglingFileModule -> ktModule.file.name
else -> error("Unsupported module type: " + ktModule.javaClass.name)
}
}
}
fun TestModule.getKtModule(testServices: TestServices): KtModule = testServices.ktModuleProvider.getModule(name)
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.analysis.test.framework.project.structure
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.project.structure.DanglingFileResolutionMode
import org.jetbrains.kotlin.analysis.project.structure.impl.KtDanglingFileModuleImpl
import org.jetbrains.kotlin.analysis.test.framework.services.TestForeignValue
import org.jetbrains.kotlin.analysis.test.framework.services.TestForeignValueProviderService
import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.TestModuleKind
import org.jetbrains.kotlin.psi.KtBlockCodeFragment
@@ -17,12 +19,14 @@ import org.jetbrains.kotlin.psi.KtExpressionCodeFragment
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtTypeCodeFragment
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.isIdentifier
import org.jetbrains.kotlin.test.directives.model.DirectiveApplicability
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
import org.jetbrains.kotlin.test.directives.model.singleOrZeroValue
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.sourceFileProvider
import org.jetbrains.org.objectweb.asm.Type
import java.nio.file.Path
/**
@@ -66,6 +70,9 @@ object KtCodeFragmentTestModuleFactory : KtTestModuleFactory {
CodeFragmentKind.TYPE -> KtTypeCodeFragment(project, fileName, fileText, contextElement)
}
val foreignValues = testFile.directives[AnalysisApiTestCodeFragmentDirectives.CODE_FRAGMENT_FOREIGN_VALUE]
TestForeignValueProviderService.submitForeignValues(codeFragment, foreignValues)
val module = KtDanglingFileModuleImpl(
codeFragment,
contextModule.ktModule,
@@ -91,6 +98,19 @@ object AnalysisApiTestCodeFragmentDirectives : SimpleDirectivesContainer() {
description = "Import local to the code fragment content",
applicability = DirectiveApplicability.File
)
val CODE_FRAGMENT_FOREIGN_VALUE by valueDirective<TestForeignValue>(
description = "Value injected to a code fragment",
applicability = DirectiveApplicability.File,
parser = fun(rawText: String): TestForeignValue? {
val match = CODE_FRAGMENT_FOREIGN_VALUE_REGEX.matchEntire(rawText) ?: return null
val valueName = match.groupValues[1].also { require(it.isIdentifier()) }
val valueType = match.groupValues[2].also { Type.getType(it) } // Check that it is a valid type descriptor
return TestForeignValue(valueName, valueType)
}
)
private val CODE_FRAGMENT_FOREIGN_VALUE_REGEX = Regex("^(.+)\\((.+)\\)$")
}
enum class CodeFragmentKind {
@@ -49,7 +49,7 @@ object TestModuleStructureFactory {
): KtTestModuleProjectStructure {
val modules = createModules(moduleStructure, testServices, project)
val modulesByName = modules.associateByName()
val modulesByName = modules.associateBy { it.testModule.name }
val libraryCache: LibraryCache = mutableMapOf()
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2024 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.services
import com.intellij.openapi.util.Key
import org.jetbrains.kotlin.analysis.providers.ForeignValueProviderService
import org.jetbrains.kotlin.psi.KtCodeFragment
class TestForeignValue(val name: String, val internalType: String)
class TestForeignValueProviderService : ForeignValueProviderService {
override fun getForeignValues(codeFragment: KtCodeFragment): Map<String, String> {
return codeFragment.getUserData(FOREIGN_VALUES_KEY) ?: emptyMap()
}
companion object {
private val FOREIGN_VALUES_KEY = Key<Map<String, String>>("TestForeignValues")
fun submitForeignValues(codeFragment: KtCodeFragment, values: List<TestForeignValue>) {
require(codeFragment.getUserData(FOREIGN_VALUES_KEY) == null)
val map = values.map { Pair(it.name, it.internalType) }.toMap()
codeFragment.putUserData(FOREIGN_VALUES_KEY, map)
}
}
}