[analysis api] move some test framework parts to a separate module
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testApi(kotlinStdlib())
|
||||
testApi(intellijCore())
|
||||
testApiJUnit5()
|
||||
|
||||
testApi(project(":kotlin-test:kotlin-test-junit"))
|
||||
testApi(project(":compiler:psi"))
|
||||
testApi(project(":analysis:kt-references"))
|
||||
testApi(projectTests(":compiler:tests-common-new"))
|
||||
testApi(project(":analysis:analysis-api-providers"))
|
||||
testApi(project(":analysis:analysis-api"))
|
||||
testApi(project(":analysis:analysis-api-impl-barebone"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { none() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
projectTest(jUnitMode = JUnitMode.JUnit5) {
|
||||
dependsOn(":dist")
|
||||
workingDir = rootDir
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
testsJar()
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.intellij.mock.MockApplication
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import java.nio.file.Path
|
||||
|
||||
interface FrontendApiTestConfiguratorService {
|
||||
val testPrefix: String? get() = null
|
||||
val allowDependedAnalysisSession: Boolean get() = true
|
||||
|
||||
fun TestConfigurationBuilder.configureTest(disposable: Disposable)
|
||||
fun processTestFiles(files: List<KtFile>): List<KtFile> = files
|
||||
|
||||
fun getOriginalFile(file: KtFile): KtFile = file
|
||||
fun registerProjectServices(
|
||||
project: MockProject,
|
||||
compilerConfig: CompilerConfiguration,
|
||||
files: List<KtFile>,
|
||||
packagePartProvider: (GlobalSearchScope) -> PackagePartProvider,
|
||||
projectStructureProvider: ProjectStructureProvider
|
||||
)
|
||||
|
||||
fun registerApplicationServices(application: MockApplication)
|
||||
|
||||
fun prepareTestFiles(files: List<KtFile>, module: TestModule, testServices: TestServices) {}
|
||||
|
||||
fun doOutOfBlockModification(file: KtFile)
|
||||
|
||||
fun preprocessTestDataPath(path: Path): Path = path
|
||||
}
|
||||
+28
@@ -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
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.TestInfo
|
||||
|
||||
abstract class TestWithDisposable {
|
||||
private var _disposable: Disposable? = null
|
||||
protected val disposable: Disposable get() = _disposable!!
|
||||
|
||||
@BeforeEach
|
||||
private fun initDisposable(testInfo: TestInfo) {
|
||||
_disposable = Disposer.newDisposable("disposable for ${testInfo.displayName}")
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
private fun disposeDisposable() {
|
||||
_disposable?.let { Disposer.dispose(it) }
|
||||
_disposable = null
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.base
|
||||
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
abstract class AbstractHLApiSingleFileTest : AbstractHLApiSingleModuleTest() {
|
||||
final override fun doTestByFileStructure(ktFiles: List<KtFile>, module: TestModule, testServices: TestServices) {
|
||||
val singleFile = ktFiles.singleOrNull() ?: ktFiles.first { it.name == "main.kt" }
|
||||
doTestByFileStructure(singleFile, module, testServices)
|
||||
}
|
||||
|
||||
protected abstract fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices)
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.analysis.test.framework.base
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.util.Computable
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.analyse
|
||||
import org.jetbrains.kotlin.analysis.api.analyseInDependedAnalysisSession
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestModuleStructure
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
abstract class AbstractHLApiSingleModuleTest : AnalysisApiBasedTest() {
|
||||
final override fun doTestByFileStructure(ktFiles: List<KtFile>, moduleStructure: TestModuleStructure, testServices: TestServices) {
|
||||
val singleModule = moduleStructure.modules.single()
|
||||
doTestByFileStructure(ktFiles, singleModule, testServices)
|
||||
}
|
||||
|
||||
protected abstract fun doTestByFileStructure(ktFiles: List<KtFile>, module: TestModule, testServices: TestServices)
|
||||
|
||||
protected inline fun <R> analyseOnPooledThreadInReadAction(context: KtElement, crossinline action: KtAnalysisSession.() -> R): R =
|
||||
executeOnPooledThreadInReadAction {
|
||||
analyseForTest(context) { action() }
|
||||
}
|
||||
|
||||
protected inline fun <T> runReadAction(crossinline runnable: () -> T): T {
|
||||
return ApplicationManager.getApplication().runReadAction(Computable { runnable() })
|
||||
}
|
||||
|
||||
protected inline fun <R> executeOnPooledThreadInReadAction(crossinline action: () -> R): R =
|
||||
ApplicationManager.getApplication().executeOnPooledThread<R> { runReadAction(action) }.get()
|
||||
|
||||
protected fun <R> analyseForTest(contextElement: KtElement, action: KtAnalysisSession.() -> R): R {
|
||||
return if (useDependedAnalysisSession) {
|
||||
// Depended mode does not support analysing a KtFile.
|
||||
// See org.jetbrains.kotlin.analysis.low.level.api.fir.api.LowLevelFirApiFacadeForResolveOnAir#getResolveStateForDependentCopy
|
||||
if (contextElement is KtFile) {
|
||||
throw SkipDependedModeException()
|
||||
}
|
||||
|
||||
require(!contextElement.isPhysical)
|
||||
analyseInDependedAnalysisSession(configurator.getOriginalFile(contextElement.containingKtFile), contextElement, action)
|
||||
} else {
|
||||
analyse(contextElement, action)
|
||||
}
|
||||
}
|
||||
}
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* 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.base
|
||||
|
||||
import com.intellij.mock.MockApplication
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.testFramework.TestDataFile
|
||||
import junit.framework.ComparisonFailure
|
||||
import org.jetbrains.kotlin.analysis.test.framework.FrontendApiTestConfiguratorService
|
||||
import org.jetbrains.kotlin.analysis.test.framework.TestWithDisposable
|
||||
import org.jetbrains.kotlin.analysis.test.framework.project.structure.KotlinProjectStructureProviderTestImpl
|
||||
import org.jetbrains.kotlin.analysis.test.framework.project.structure.TestKtModuleProvider
|
||||
import org.jetbrains.kotlin.analysis.test.framework.services.ExpressionMarkerProvider
|
||||
import org.jetbrains.kotlin.analysis.test.framework.services.ExpressionMarkersSourceFilePreprocessor
|
||||
import org.jetbrains.kotlin.analysis.test.framework.utils.SkipTestException
|
||||
import org.jetbrains.kotlin.analysis.test.framework.project.structure.getKtFilesFromModule
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoots
|
||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.ExecutionListenerBasedDisposableProvider
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.TestInfrastructureInternals
|
||||
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
|
||||
import org.jetbrains.kotlin.test.builders.testConfiguration
|
||||
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
|
||||
import org.jetbrains.kotlin.test.model.DependencyKind
|
||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||
import org.jetbrains.kotlin.test.model.ResultingArtifact
|
||||
import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerTest
|
||||
import org.jetbrains.kotlin.test.services.*
|
||||
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.impl.TemporaryDirectoryManagerImpl
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.TestInfo
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.concurrent.ExecutionException
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.nameWithoutExtension
|
||||
|
||||
abstract class AnalysisApiBasedTest : TestWithDisposable() {
|
||||
abstract val configurator: FrontendApiTestConfiguratorService
|
||||
|
||||
protected open val enableTestInDependedMode: Boolean
|
||||
get() = configurator.allowDependedAnalysisSession
|
||||
|
||||
protected lateinit var testInfo: KotlinTestInfo
|
||||
private set
|
||||
|
||||
protected var useDependedAnalysisSession: Boolean = false
|
||||
|
||||
protected lateinit var testDataPath: Path
|
||||
private set
|
||||
|
||||
protected open fun configureTest(builder: TestConfigurationBuilder) {
|
||||
with(configurator) {
|
||||
builder.configureTest(disposable)
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun doTestByFileStructure(ktFiles: List<KtFile>, moduleStructure: TestModuleStructure, testServices: TestServices)
|
||||
|
||||
protected fun AssertionsService.assertEqualsToTestDataFileSibling(actual: String, extension: String = ".txt") {
|
||||
val testPrefix = configurator.testPrefix
|
||||
|
||||
val expectedFile = getTestDataFileSiblingPath(extension, testPrefix = testPrefix)
|
||||
assertEqualsToFile(expectedFile, actual)
|
||||
|
||||
if (testPrefix != null) {
|
||||
val expectedFileWithoutPrefix = getTestDataFileSiblingPath(extension, testPrefix = null)
|
||||
if (expectedFile != expectedFileWithoutPrefix) {
|
||||
try {
|
||||
assertEqualsToFile(expectedFileWithoutPrefix, actual)
|
||||
} catch (ignored: ComparisonFailure) {
|
||||
return
|
||||
}
|
||||
|
||||
throw AssertionError("\"$expectedFile\" has the same content as \"$expectedFileWithoutPrefix\". Delete the prefixed file.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun getTestDataFileSiblingPath(extension: String, testPrefix: String?): Path {
|
||||
val extensionWithDot = "." + extension.removePrefix(".")
|
||||
val baseName = testDataPath.nameWithoutExtension
|
||||
|
||||
if (testPrefix != null) {
|
||||
val prefixedFile = testDataPath.resolveSibling("$baseName.$testPrefix$extensionWithDot")
|
||||
if (prefixedFile.exists()) {
|
||||
return prefixedFile
|
||||
}
|
||||
}
|
||||
|
||||
return testDataPath.resolveSibling(baseName + extensionWithDot)
|
||||
}
|
||||
|
||||
@OptIn(TestInfrastructureInternals::class)
|
||||
private val configure: TestConfigurationBuilder.() -> Unit = {
|
||||
globalDefaults {
|
||||
frontend = FrontendKinds.FIR
|
||||
targetPlatform = JvmPlatforms.defaultJvmPlatform
|
||||
dependencyKind = DependencyKind.Source
|
||||
}
|
||||
useConfigurators(
|
||||
::CommonEnvironmentConfigurator,
|
||||
::JvmEnvironmentConfigurator,
|
||||
)
|
||||
assertions = JUnit5Assertions
|
||||
useAdditionalService<TemporaryDirectoryManager>(::TemporaryDirectoryManagerImpl)
|
||||
|
||||
useDirectives(*AbstractKotlinCompilerTest.defaultDirectiveContainers.toTypedArray())
|
||||
useDirectives(JvmEnvironmentConfigurationDirectives)
|
||||
|
||||
useSourcePreprocessor(::ExpressionMarkersSourceFilePreprocessor)
|
||||
useAdditionalService { ExpressionMarkerProvider() }
|
||||
useAdditionalService(::TestKtModuleProvider)
|
||||
useAdditionalService<ApplicationDisposableProvider> { ExecutionListenerBasedDisposableProvider() }
|
||||
useAdditionalService<KotlinStandardLibrariesPathProvider> { StandardLibrariesPathProviderForKotlinProject }
|
||||
configureTest(this)
|
||||
|
||||
startingArtifactFactory = { ResultingArtifact.Source() }
|
||||
this.testInfo = this@AnalysisApiBasedTest.testInfo
|
||||
}
|
||||
|
||||
protected open fun handleInitializationError(exception: Throwable, moduleStructure: TestModuleStructure): InitializationErrorAction =
|
||||
InitializationErrorAction.THROW
|
||||
|
||||
enum class InitializationErrorAction {
|
||||
IGNORE, THROW
|
||||
}
|
||||
|
||||
protected fun runTest(@TestDataFile path: String) {
|
||||
testDataPath = configurator.preprocessTestDataPath(Paths.get(path))
|
||||
val testConfiguration = testConfiguration(path, configure)
|
||||
Disposer.register(disposable, testConfiguration.rootDisposable)
|
||||
val testServices = testConfiguration.testServices
|
||||
val moduleStructure = testConfiguration.moduleStructureExtractor.splitTestDataByModules(
|
||||
path,
|
||||
testConfiguration.directives,
|
||||
)
|
||||
val singleModule = moduleStructure.modules.single()
|
||||
val project = try {
|
||||
testServices.compilerConfigurationProvider.getProject(singleModule)
|
||||
} catch (_: SkipTestException) {
|
||||
return
|
||||
}
|
||||
|
||||
registerApplicationServices()
|
||||
testConfiguration.testServices.register(TestModuleStructure::class, moduleStructure)
|
||||
testConfiguration.preAnalysisHandlers.forEach { preprocessor ->
|
||||
try {
|
||||
preprocessor.preprocessModuleStructure(moduleStructure)
|
||||
} catch (exception: Throwable) {
|
||||
when (handleInitializationError(exception, moduleStructure)) {
|
||||
InitializationErrorAction.IGNORE -> {}
|
||||
InitializationErrorAction.THROW -> throw exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val ktFiles = getKtFilesFromModule(testServices, singleModule)
|
||||
with(project as MockProject) {
|
||||
val compilerConfiguration = testServices.compilerConfigurationProvider.getCompilerConfiguration(singleModule)
|
||||
compilerConfiguration.addJavaSourceRoots(ktFiles.map { File(it.virtualFilePath) })
|
||||
configurator.registerProjectServices(
|
||||
this,
|
||||
compilerConfiguration,
|
||||
ktFiles,
|
||||
testServices.compilerConfigurationProvider.getPackagePartProviderFactory(singleModule),
|
||||
KotlinProjectStructureProviderTestImpl(testServices)
|
||||
)
|
||||
}
|
||||
|
||||
testConfiguration.preAnalysisHandlers.forEach { preprocessor ->
|
||||
try {
|
||||
preprocessor.prepareSealedClassInheritors(moduleStructure)
|
||||
} catch (exception: Throwable) {
|
||||
when (handleInitializationError(exception, moduleStructure)) {
|
||||
InitializationErrorAction.IGNORE -> {}
|
||||
InitializationErrorAction.THROW -> throw exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurator.prepareTestFiles(ktFiles, singleModule, testServices)
|
||||
doTestByFileStructure(ktFiles, moduleStructure, testServices)
|
||||
if (!enableTestInDependedMode || ktFiles.any {
|
||||
InTextDirectivesUtils.isDirectiveDefined(it.text, DISABLE_DEPENDED_MODE_DIRECTIVE)
|
||||
}) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
useDependedAnalysisSession = true
|
||||
doTestByFileStructure(configurator.processTestFiles(ktFiles), moduleStructure, testServices)
|
||||
} catch (e: SkipDependedModeException) {
|
||||
// Skip the test if needed
|
||||
} catch (e: ExecutionException) {
|
||||
if (e.cause !is SkipDependedModeException)
|
||||
throw Exception("Test succeeded in normal analysis mode but failed in depended analysis mode.", e)
|
||||
} catch (e: Exception) {
|
||||
throw Exception("Test succeeded in normal analysis mode but failed in depended analysis mode.", e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerApplicationServices() {
|
||||
val application = ApplicationManager.getApplication() as MockApplication
|
||||
KotlinCoreEnvironment.underApplicationLock {
|
||||
configurator.registerApplicationServices(application)
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
fun initTestInfo(testInfo: TestInfo) {
|
||||
this.testInfo = KotlinTestInfo(
|
||||
className = testInfo.testClass.orElseGet(null)?.name ?: "_undefined_",
|
||||
methodName = testInfo.testMethod.orElseGet(null)?.name ?: "_testUndefined_",
|
||||
tags = testInfo.tags
|
||||
)
|
||||
}
|
||||
|
||||
protected class SkipDependedModeException : Exception()
|
||||
|
||||
companion object {
|
||||
val DISABLE_DEPENDED_MODE_DIRECTIVE = "DISABLE_DEPENDED_MODE"
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.psi.PsiElement
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtModule
|
||||
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
class KotlinProjectStructureProviderTestImpl(private val testServices: TestServices) : ProjectStructureProvider() {
|
||||
private val moduleInfoProvider = testServices.projectModuleProvider
|
||||
override fun getKtModuleForKtElement(element: PsiElement): KtModule {
|
||||
val containingFile = element.containingFile as KtFile
|
||||
return moduleInfoProvider.getModuleInfoByKtFile(containingFile) as KtModule
|
||||
}
|
||||
|
||||
override fun getKtLibraryModules(): Collection<TestKtLibraryModule> {
|
||||
return moduleInfoProvider.getLibraryModules()
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestService
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
class TestKtModuleProvider(
|
||||
private val testServices: TestServices
|
||||
) : TestService {
|
||||
private val cache = mutableMapOf<String, TestKtModule>()
|
||||
|
||||
fun registerModuleInfo(testModule: TestModule, ktModule: TestKtModule) {
|
||||
cache[testModule.name] = ktModule
|
||||
}
|
||||
|
||||
fun getModuleInfoByKtFile(ktFile: KtFile): TestKtModule =
|
||||
cache.values.first { moduleSourceInfo ->
|
||||
(if (ktFile.isPhysical) ktFile else ktFile.originalFile) in moduleSourceInfo.ktFiles
|
||||
}
|
||||
|
||||
fun getModule(moduleName: String): TestKtModule =
|
||||
cache.getValue(moduleName)
|
||||
|
||||
fun getLibraryModules(): Collection<TestKtLibraryModule> {
|
||||
return cache.values.filterIsInstance<TestKtLibraryModule>()
|
||||
}
|
||||
}
|
||||
|
||||
val TestServices.projectModuleProvider: TestKtModuleProvider by TestServices.testServiceAccessor()
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.analysis.test.framework.project.structure
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.ProjectScope
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtLibrarySourceModule
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtModule
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmModularRoots
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
|
||||
import org.jetbrains.kotlin.test.frontend.fir.getAnalyzerServices
|
||||
import org.jetbrains.kotlin.test.model.TestFile
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
abstract class TestKtModule(
|
||||
val project: Project,
|
||||
val testModule: TestModule,
|
||||
val ktFiles: Set<KtFile>,
|
||||
testServices: TestServices,
|
||||
) {
|
||||
private val moduleProvider = testServices.projectModuleProvider
|
||||
private val compilerConfigurationProvider = testServices.compilerConfigurationProvider
|
||||
private val configuration = compilerConfigurationProvider.getCompilerConfiguration(testModule)
|
||||
|
||||
|
||||
val moduleName: String
|
||||
get() = testModule.name
|
||||
|
||||
val directRegularDependencies: List<KtModule> by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||
buildList {
|
||||
testModule.allDependencies.mapTo(this) { moduleProvider.getModule(it.moduleName) as KtModule }
|
||||
addIfNotNull(
|
||||
libraryByRoots(
|
||||
(configuration.jvmModularRoots + configuration.jvmClasspathRoots).map(File::toPath)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val directRefinementDependencies: List<KtModule> by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||
testModule.dependsOnDependencies
|
||||
.map { moduleProvider.getModule(it.moduleName) as KtModule }
|
||||
}
|
||||
|
||||
val directFriendDependencies: List<KtModule> by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||
buildList {
|
||||
testModule.friendDependencies.mapTo(this) { moduleProvider.getModule(it.moduleName) as KtModule }
|
||||
addIfNotNull(
|
||||
libraryByRoots(configuration[JVMConfigurationKeys.FRIEND_PATHS].orEmpty().map(Paths::get))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract val ktModule: KtModule
|
||||
|
||||
private fun libraryByRoots(roots: List<Path>): LibraryByRoots? {
|
||||
if (roots.isEmpty()) return null
|
||||
return LibraryByRoots(
|
||||
roots,
|
||||
ktModule,
|
||||
project,
|
||||
)
|
||||
}
|
||||
|
||||
val languageVersionSettings: LanguageVersionSettings
|
||||
get() = testModule.languageVersionSettings
|
||||
|
||||
val platform: TargetPlatform
|
||||
get() = testModule.targetPlatform
|
||||
|
||||
val analyzerServices: PlatformDependentAnalyzerServices
|
||||
get() = testModule.targetPlatform.getAnalyzerServices()
|
||||
}
|
||||
|
||||
class TestKtSourceModule(
|
||||
project: Project,
|
||||
testModule: TestModule,
|
||||
val testFilesToKtFiles: Map<TestFile, KtFile>,
|
||||
testServices: TestServices
|
||||
) : TestKtModule(project, testModule, testFilesToKtFiles.values.toSet(), testServices), KtSourceModule {
|
||||
override val ktModule: KtModule get() = this
|
||||
|
||||
override val contentScope: GlobalSearchScope =
|
||||
TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, testFilesToKtFiles.values)
|
||||
}
|
||||
|
||||
class TestKtLibraryModule(
|
||||
project: Project,
|
||||
testModule: TestModule,
|
||||
ktFiles: Collection<KtFile>,
|
||||
testServices: TestServices
|
||||
) : TestKtModule(project, testModule, ktFiles.toSet(), testServices), KtLibraryModule {
|
||||
override val ktModule: KtModule get() = this
|
||||
override val libraryName: String get() = testModule.name
|
||||
override val librarySources: KtLibrarySourceModule? get() = null
|
||||
|
||||
override fun getBinaryRoots(): Collection<Path> = ktFiles.map { it.virtualFile.toNioPath() }
|
||||
|
||||
override val contentScope: GlobalSearchScope =
|
||||
GlobalSearchScope.filesScope(project, ktFiles.map { it.virtualFile })
|
||||
}
|
||||
|
||||
class TestKtLibrarySourceModule(
|
||||
project: Project,
|
||||
testModule: TestModule,
|
||||
ktFilesFromSourceJar: Set<KtFile>,
|
||||
testServices: TestServices,
|
||||
override val binaryLibrary: KtLibraryModule,
|
||||
) : TestKtModule(project, testModule, ktFilesFromSourceJar, testServices), KtLibrarySourceModule {
|
||||
override val ktModule: KtModule get() = this
|
||||
override val contentScope: GlobalSearchScope get() = GlobalSearchScope.filesScope(project, ktFiles.map { it.virtualFile })
|
||||
|
||||
override val libraryName: String get() = testModule.name
|
||||
}
|
||||
|
||||
private class LibraryByRoots(
|
||||
private val roots: List<Path>,
|
||||
private val module: KtModule,
|
||||
override val project: Project,
|
||||
) : KtLibraryModule {
|
||||
override val libraryName: String get() = "Test Library"
|
||||
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() = ProjectScope.getLibrariesScope(project)
|
||||
override val platform: TargetPlatform get() = module.platform
|
||||
override val analyzerServices: PlatformDependentAnalyzerServices get() = module.analyzerServices
|
||||
override fun getBinaryRoots(): Collection<Path> = roots
|
||||
override val librarySources: KtLibrarySourceModule? get() = null
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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 org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
fun getKtFilesFromModule(testServices: TestServices, testModule: TestModule): List<KtFile> {
|
||||
val moduleInfoProvider = testServices.projectModuleProvider
|
||||
return when (val moduleInfo = moduleInfoProvider.getModule(testModule.name)) {
|
||||
is TestKtSourceModule -> moduleInfo.testFilesToKtFiles.filterKeys { testFile -> !testFile.isAdditional }.values.toList()
|
||||
is TestKtLibraryModule -> moduleInfo.ktFiles.toList()
|
||||
is TestKtLibrarySourceModule -> moduleInfo.ktFiles.toList()
|
||||
else -> error("Unexpected $moduleInfo")
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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.analysis.test.framework.services
|
||||
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import org.jetbrains.kotlin.analysis.api.impl.barebone.parentOfType
|
||||
import org.jetbrains.kotlin.fir.PrivateForInline
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
|
||||
import org.jetbrains.kotlin.test.model.TestFile
|
||||
import org.jetbrains.kotlin.test.services.SourceFilePreprocessor
|
||||
import org.jetbrains.kotlin.test.services.TestService
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
internal class ExpressionMarkersSourceFilePreprocessor(testServices: TestServices) : SourceFilePreprocessor(testServices) {
|
||||
override fun process(file: TestFile, content: String): String {
|
||||
val withSelectedProcessed = processSelectedExpression(file, content)
|
||||
return processCaretExpression(file, withSelectedProcessed)
|
||||
}
|
||||
|
||||
private fun processSelectedExpression(file: TestFile, content: String): String {
|
||||
val startCaretPosition = content.indexOfOrNull(TAGS.OPENING_EXPRESSION_TAG) ?: return content
|
||||
|
||||
val endCaretPosition = content.indexOfOrNull(TAGS.CLOSING_EXPRESSION_TAG)
|
||||
?: error("${TAGS.CLOSING_EXPRESSION_TAG} was not found in the file")
|
||||
|
||||
check(startCaretPosition < endCaretPosition)
|
||||
testServices.expressionMarkerProvider.addSelectedExpression(
|
||||
file,
|
||||
TextRange.create(startCaretPosition, endCaretPosition - TAGS.OPENING_EXPRESSION_TAG.length)
|
||||
)
|
||||
return content
|
||||
.replace(TAGS.OPENING_EXPRESSION_TAG, "")
|
||||
.replace(TAGS.CLOSING_EXPRESSION_TAG, "")
|
||||
}
|
||||
|
||||
private fun processCaretExpression(file: TestFile, content: String): String {
|
||||
var result = content
|
||||
var match = TAGS.CARET_REGEXP.find(result)
|
||||
while (match != null) {
|
||||
val startCaretPosition = match.range.first
|
||||
val tag = match.groups[2]?.value
|
||||
testServices.expressionMarkerProvider.addCaret(file, tag, startCaretPosition)
|
||||
result = result.removeRange(match.range)
|
||||
match = TAGS.CARET_REGEXP.find(result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
object TAGS {
|
||||
const val OPENING_EXPRESSION_TAG = "<expr>"
|
||||
const val CLOSING_EXPRESSION_TAG = "</expr>"
|
||||
val CARET_REGEXP = "<caret(_(\\w+))?>".toRegex()
|
||||
}
|
||||
}
|
||||
|
||||
class ExpressionMarkerProvider : TestService {
|
||||
private val selected = mutableMapOf<String, TextRange>()
|
||||
|
||||
@PrivateForInline
|
||||
val carets = CaretProvider()
|
||||
|
||||
fun addSelectedExpression(file: TestFile, range: TextRange) {
|
||||
selected[file.relativePath] = range
|
||||
}
|
||||
|
||||
@OptIn(PrivateForInline::class)
|
||||
fun addCaret(file: TestFile, caretTag: String?, caretOffset: Int) {
|
||||
carets.addCaret(file.name, caretTag, caretOffset)
|
||||
}
|
||||
|
||||
@OptIn(PrivateForInline::class)
|
||||
fun getCaretPosition(file: KtFile, caretTag: String? = null): Int {
|
||||
return carets.getCaretOffset(file.name, caretTag)
|
||||
?: run {
|
||||
val caretName = "caret${caretTag?.let { "_$it" }.orEmpty()}"
|
||||
error("No <$caretName> found in file")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline fun <reified P : KtElement> getElementOfTypAtCaret(file: KtFile, caretName: String? = null): P {
|
||||
val offset = getCaretPosition(file, caretName)
|
||||
return file.findElementAt(offset)
|
||||
?.parentOfType<P>()
|
||||
?: error("No expression found at caret")
|
||||
}
|
||||
|
||||
fun getSelectedElement(file: KtFile): KtElement {
|
||||
val range = selected[file.name]
|
||||
?: error("No selected expression found in file")
|
||||
val elements = file.elementsInRange(range).trimWhitespaces()
|
||||
if (elements.size != 1) {
|
||||
error("Expected one element at rage but found ${elements.size} [${elements.joinToString { it::class.simpleName + ": " + it.text }}]")
|
||||
}
|
||||
return elements.single() as KtElement
|
||||
}
|
||||
|
||||
private fun List<PsiElement>.trimWhitespaces(): List<PsiElement> =
|
||||
dropWhile { it is PsiWhiteSpace }
|
||||
.dropLastWhile { it is PsiWhiteSpace }
|
||||
}
|
||||
|
||||
@PrivateForInline
|
||||
class CaretProvider {
|
||||
private val caretToFile = mutableMapOf<String, CaretsInFile>()
|
||||
|
||||
fun getCaretOffset(filename: String, caretTag: String?): Int? {
|
||||
val cartsInFile = caretToFile[filename] ?: return null
|
||||
return cartsInFile.getCaretOffsetByTag(caretTag)
|
||||
}
|
||||
|
||||
fun addCaret(filename: String, caretTag: String?, caretOffset: Int) {
|
||||
val cartsInFile = caretToFile.getOrPut(filename) { CaretsInFile() }
|
||||
cartsInFile.addCaret(caretTag, caretOffset)
|
||||
}
|
||||
|
||||
private class CaretsInFile {
|
||||
private val carets = mutableMapOf<String, Int>()
|
||||
|
||||
fun getCaretOffsetByTag(tag: String?): Int? {
|
||||
return carets[tag.orEmpty()]
|
||||
}
|
||||
|
||||
fun addCaret(caretTag: String?, caretOffset: Int) {
|
||||
carets[caretTag.orEmpty()] = caretOffset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val TestServices.expressionMarkerProvider: ExpressionMarkerProvider by TestServices.testServiceAccessor()
|
||||
|
||||
fun String.indexOfOrNull(substring: String) =
|
||||
indexOf(substring).takeIf { it >= 0 }
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.services.libraries
|
||||
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestService
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import java.nio.file.Path
|
||||
|
||||
class CompiledLibraryProvider(private val testServices: TestServices) : TestService {
|
||||
private val libraries = mutableMapOf<String, CompiledLibrary>()
|
||||
|
||||
fun compileToLibrary(module: TestModule): CompiledLibrary {
|
||||
if (module.name in libraries) {
|
||||
error("Library for module ${module.name} is already compiled")
|
||||
}
|
||||
val libraryJar = TestModuleCompiler.compileTestModuleToLibrary(module, testServices)
|
||||
val librarySourcesJar = TestModuleCompiler.compileTestModuleToLibrarySources(module, testServices)
|
||||
return CompiledLibrary(libraryJar, librarySourcesJar).also { libraries[module.name] = it }
|
||||
}
|
||||
|
||||
fun getCompiledLibrary(module: TestModule): CompiledLibrary =
|
||||
libraries.getValue(module.name)
|
||||
}
|
||||
|
||||
val TestServices.compiledLibraryProvider: CompiledLibraryProvider by TestServices.testServiceAccessor()
|
||||
|
||||
data class CompiledLibrary(
|
||||
val jar: Path,
|
||||
val sourcesJar: Path,
|
||||
)
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.services.libraries
|
||||
|
||||
import org.jetbrains.kotlin.analysis.test.framework.utils.SkipTestException
|
||||
import org.jetbrains.kotlin.test.MockLibraryUtil
|
||||
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
|
||||
import org.jetbrains.kotlin.test.directives.LanguageSettingsDirectives
|
||||
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.absolutePathString
|
||||
import kotlin.io.path.div
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.notExists
|
||||
|
||||
object CompilerExecutor {
|
||||
fun compileLibrary(sourcesPath: Path, options: List<String>, compilationErrorExpected: Boolean): Path {
|
||||
val library = sourcesPath / "library.jar"
|
||||
val sourceFiles = sourcesPath.toFile().walkBottomUp()
|
||||
val commands = buildList {
|
||||
sourceFiles.mapTo(this) { it.absolutePath }
|
||||
addAll(options)
|
||||
add("-d")
|
||||
add(library.absolutePathString())
|
||||
}
|
||||
try {
|
||||
MockLibraryUtil.runJvmCompiler(commands)
|
||||
} catch (e: Throwable) {
|
||||
if (!compilationErrorExpected) {
|
||||
throw IllegalStateException("Unexpected compilation error while compiling library", e)
|
||||
}
|
||||
}
|
||||
|
||||
if (library.exists() && compilationErrorExpected) {
|
||||
error("Compilation error expected but, code was compiled successfully")
|
||||
}
|
||||
if (library.notExists()) {
|
||||
throw LibraryWasNotCompiledDueToExpectedCompilationError()
|
||||
}
|
||||
return library
|
||||
}
|
||||
|
||||
fun parseCompilerOptionsFromTestdata(module: TestModule): List<String> = buildList {
|
||||
module.directives[LanguageSettingsDirectives.API_VERSION].firstOrNull()?.let { apiVersion ->
|
||||
addAll(listOf("-api-version", apiVersion.versionString))
|
||||
}
|
||||
|
||||
module.directives[JvmEnvironmentConfigurationDirectives.JVM_TARGET].firstOrNull()?.let { jvmTarget ->
|
||||
addAll(listOf("-jvm-target", jvmTarget.description))
|
||||
}
|
||||
|
||||
addAll(module.directives[Directives.COMPILER_ARGUMENTS])
|
||||
}
|
||||
|
||||
object Directives : SimpleDirectivesContainer() {
|
||||
val COMPILER_ARGUMENTS by stringDirective("List of additional compiler arguments")
|
||||
val COMPILATION_ERRORS by directive("Is compilation errors expected in the file")
|
||||
}
|
||||
}
|
||||
|
||||
class LibraryWasNotCompiledDueToExpectedCompilationError : SkipTestException()
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.services.libraries
|
||||
|
||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
class LibraryEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) {
|
||||
override fun configureCompilerConfiguration(configuration: CompilerConfiguration, module: TestModule) {
|
||||
val library = testServices.compiledLibraryProvider.compileToLibrary(module).jar
|
||||
configuration.addJvmClasspathRoot(library.toFile())
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.services.libraries
|
||||
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.sourceFileProvider
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.nio.file.Path
|
||||
import java.util.jar.Attributes
|
||||
import java.util.jar.JarEntry
|
||||
import java.util.jar.JarOutputStream
|
||||
import java.util.jar.Manifest
|
||||
import kotlin.io.path.createFile
|
||||
import kotlin.io.path.div
|
||||
import kotlin.io.path.outputStream
|
||||
import kotlin.io.path.writeText
|
||||
|
||||
|
||||
internal object TestModuleCompiler {
|
||||
fun compileTestModuleToLibrary(module: TestModule, testServices: TestServices): Path {
|
||||
val tmpDir = KtTestUtil.tmpDir("testSourcesToCompile").toPath()
|
||||
for (testFile in module.files) {
|
||||
val text = testServices.sourceFileProvider.getContentOfSourceFile(testFile)
|
||||
val tmpSourceFile = (tmpDir / testFile.name).createFile()
|
||||
tmpSourceFile.writeText(text)
|
||||
}
|
||||
return CompilerExecutor.compileLibrary(
|
||||
tmpDir,
|
||||
CompilerExecutor.parseCompilerOptionsFromTestdata(module),
|
||||
compilationErrorExpected = CompilerExecutor.Directives.COMPILATION_ERRORS in module.directives
|
||||
)
|
||||
}
|
||||
|
||||
fun compileTestModuleToLibrarySources(module: TestModule, testServices: TestServices): Path {
|
||||
val tmpDir = KtTestUtil.tmpDir("testSourcesToCompile").toPath()
|
||||
val librarySourcesPath = tmpDir / "library-sources.jar"
|
||||
val manifest = Manifest().apply { mainAttributes[Attributes.Name.MANIFEST_VERSION] = "1.0" }
|
||||
JarOutputStream(librarySourcesPath.outputStream(), manifest).use { jarOutputStream ->
|
||||
for (testFile in module.files) {
|
||||
val text = testServices.sourceFileProvider.getContentOfSourceFile(testFile)
|
||||
addFileToJar(testFile.name, text, jarOutputStream)
|
||||
}
|
||||
}
|
||||
return librarySourcesPath
|
||||
}
|
||||
|
||||
private fun addFileToJar(path: String, text: String, jarOutputStream: JarOutputStream) {
|
||||
jarOutputStream.putNextEntry(JarEntry(path))
|
||||
ByteArrayInputStream(text.toByteArray()).copyTo(jarOutputStream)
|
||||
jarOutputStream.closeEntry()
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* 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.utils
|
||||
|
||||
abstract class SkipTestException : RuntimeException()
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.utils
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.util.Computable
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.analyse
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||
import org.jetbrains.kotlin.diagnostics.PsiDiagnosticUtils.offsetToLineAndColumn
|
||||
import org.jetbrains.kotlin.idea.references.KtReference
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
inline fun <T> runReadAction(crossinline runnable: () -> T): T {
|
||||
return ApplicationManager.getApplication().runReadAction(Computable { runnable() })
|
||||
}
|
||||
|
||||
fun <R> executeOnPooledThreadInReadAction(action: () -> R): R =
|
||||
ApplicationManager.getApplication().executeOnPooledThread<R> { runReadAction(action) }.get()
|
||||
|
||||
inline fun <R> analyseOnPooledThreadInReadAction(context: KtElement, crossinline action: KtAnalysisSession.() -> R): R =
|
||||
executeOnPooledThreadInReadAction {
|
||||
analyse(context) { action() }
|
||||
}
|
||||
|
||||
fun PsiElement?.position(): String {
|
||||
if (this == null) return "(unknown)"
|
||||
return offsetToLineAndColumn(containingFile.viewProvider.document, textRange.startOffset).toString()
|
||||
}
|
||||
|
||||
fun KtSymbol.getNameWithPositionString(): String {
|
||||
return when (val psi = this.psi) {
|
||||
is KtDeclarationWithBody -> psi.name
|
||||
is KtNamedDeclaration -> psi.name
|
||||
null -> "null"
|
||||
else -> psi::class.simpleName
|
||||
} + "@" + psi.position()
|
||||
}
|
||||
|
||||
fun String.indented(indent: Int): String {
|
||||
val indentString = " ".repeat(indent)
|
||||
return indentString + replace("\n", "\n$indentString")
|
||||
}
|
||||
|
||||
fun KtDeclaration.getNameWithPositionString(): String {
|
||||
return (presentation?.presentableText ?: name ?: this::class.simpleName) + "@" + position()
|
||||
}
|
||||
|
||||
fun findReferencesAtCaret(mainKtFile: KtFile, caretPosition: Int): List<KtReference> =
|
||||
mainKtFile.findReferenceAt(caretPosition)?.unwrapMultiReferences().orEmpty().filterIsInstance<KtReference>()
|
||||
|
||||
fun PsiReference.unwrapMultiReferences(): List<PsiReference> = when (this) {
|
||||
is KtReference -> listOf(this)
|
||||
is PsiMultiReference -> references.flatMap { it.unwrapMultiReferences() }
|
||||
else -> error("Unexpected reference $this")
|
||||
}
|
||||
Reference in New Issue
Block a user