[SLC] extract base class from AbstractSymbolLightClassesStructureTest
^KT-56613
This commit is contained in:
committed by
Space Team
parent
5e18ebcd7e
commit
bf600afce0
+1
-1
@@ -20,7 +20,7 @@ abstract class AbstractSymbolLightClassesStructureByFqNameTest(
|
|||||||
configurator: AnalysisApiTestConfigurator,
|
configurator: AnalysisApiTestConfigurator,
|
||||||
testPrefix: String,
|
testPrefix: String,
|
||||||
stopIfCompilationErrorDirectivePresent: Boolean,
|
stopIfCompilationErrorDirectivePresent: Boolean,
|
||||||
) : AbstractSymbolLightClassesStructureTest(configurator, testPrefix, stopIfCompilationErrorDirectivePresent) {
|
) : AbstractSymbolLightClassesStructureTestBase(configurator, testPrefix, stopIfCompilationErrorDirectivePresent) {
|
||||||
override fun configureTest(builder: TestConfigurationBuilder) {
|
override fun configureTest(builder: TestConfigurationBuilder) {
|
||||||
super.configureTest(builder)
|
super.configureTest(builder)
|
||||||
builder.useDirectives(Directives)
|
builder.useDirectives(Directives)
|
||||||
|
|||||||
+3
-153
@@ -5,35 +5,18 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.light.classes.symbol.base
|
package org.jetbrains.kotlin.light.classes.symbol.base
|
||||||
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.psi.JavaPsiFacade
|
|
||||||
import com.intellij.psi.PsiClass
|
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
|
||||||
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator
|
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator
|
||||||
import org.jetbrains.kotlin.analysis.utils.printer.PrettyPrinter
|
|
||||||
import org.jetbrains.kotlin.analysis.utils.printer.prettyPrint
|
import org.jetbrains.kotlin.analysis.utils.printer.prettyPrint
|
||||||
import org.jetbrains.kotlin.asJava.toLightClass
|
|
||||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
|
||||||
import org.jetbrains.kotlin.test.model.TestModule
|
import org.jetbrains.kotlin.test.model.TestModule
|
||||||
import org.jetbrains.kotlin.test.services.TestServices
|
import org.jetbrains.kotlin.test.services.TestServices
|
||||||
import org.jetbrains.kotlin.test.services.assertions
|
import org.jetbrains.kotlin.test.services.assertions
|
||||||
import java.nio.file.Path
|
|
||||||
import kotlin.io.path.forEachLine
|
|
||||||
import kotlin.io.path.notExists
|
|
||||||
|
|
||||||
private const val INHERITORS_EXTENSION = "inheritors.txt"
|
|
||||||
private const val PAIRS_DELIMITER = ", "
|
|
||||||
private const val VALUE_DELIMITER = ": "
|
|
||||||
|
|
||||||
abstract class AbstractSymbolLightClassesStructureTest(
|
abstract class AbstractSymbolLightClassesStructureTest(
|
||||||
configurator: AnalysisApiTestConfigurator,
|
configurator: AnalysisApiTestConfigurator,
|
||||||
protected val testPrefix: String,
|
testPrefix: String,
|
||||||
override val stopIfCompilationErrorDirectivePresent: Boolean,
|
stopIfCompilationErrorDirectivePresent: Boolean,
|
||||||
) : AbstractSymbolLightClassesTestBase(configurator) {
|
) : AbstractSymbolLightClassesStructureTestBase(configurator, testPrefix, stopIfCompilationErrorDirectivePresent) {
|
||||||
override val currentExtension: String get() = throw UnsupportedOperationException()
|
|
||||||
|
|
||||||
override fun doTestByFileStructure(ktFiles: List<KtFile>, module: TestModule, testServices: TestServices) {
|
override fun doTestByFileStructure(ktFiles: List<KtFile>, module: TestModule, testServices: TestServices) {
|
||||||
val result = prettyPrint {
|
val result = prettyPrint {
|
||||||
@@ -50,139 +33,6 @@ abstract class AbstractSymbolLightClassesStructureTest(
|
|||||||
}
|
}
|
||||||
|
|
||||||
testServices.assertions.assertEqualsToTestDataFileSibling(result, testPrefix = testPrefix)
|
testServices.assertions.assertEqualsToTestDataFileSibling(result, testPrefix = testPrefix)
|
||||||
|
|
||||||
doTestInheritors(ktFiles, testServices)
|
doTestInheritors(ktFiles, testServices)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun doTestInheritors(ktFiles: List<KtFile>, testServices: TestServices) {
|
|
||||||
val testData = getTestDataFileSiblingPath(extension = INHERITORS_EXTENSION, testPrefix = testPrefix)
|
|
||||||
if (testData.notExists()) return
|
|
||||||
val project = ktFiles.first().project
|
|
||||||
|
|
||||||
val queries = parseInheritorsFile(testData)
|
|
||||||
val result = buildString {
|
|
||||||
for (query in queries) {
|
|
||||||
append("subClass")
|
|
||||||
append(VALUE_DELIMITER)
|
|
||||||
append(query.fqNameToCheck)
|
|
||||||
append(PAIRS_DELIMITER)
|
|
||||||
|
|
||||||
append("superClass")
|
|
||||||
append(VALUE_DELIMITER)
|
|
||||||
append(query.baseFqName)
|
|
||||||
append(PAIRS_DELIMITER)
|
|
||||||
|
|
||||||
append("deepSearch")
|
|
||||||
append(VALUE_DELIMITER)
|
|
||||||
append(query.deep.toString())
|
|
||||||
|
|
||||||
append(" -> ")
|
|
||||||
appendLine(query.isInheritor(project).toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testServices.assertions.assertEqualsToTestDataFileSibling(
|
|
||||||
actual = result,
|
|
||||||
testPrefix = testPrefix,
|
|
||||||
extension = INHERITORS_EXTENSION,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun InheritorStructure.isInheritor(project: Project): Boolean {
|
|
||||||
val lightClass = findLightClass(fqNameToCheck, project) ?: error("Can't find light class by '$fqNameToCheck' qualifier")
|
|
||||||
val baseClass = JavaPsiFacade.getInstance(project).findClass(baseFqName, GlobalSearchScope.allScope(project))
|
|
||||||
?: error("Can't find class by '$baseFqName' qualifier")
|
|
||||||
|
|
||||||
return lightClass.isInheritor(/* baseClass = */ baseClass, /* checkDeep = */ deep)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseInheritorsFile(path: Path): Collection<InheritorStructure> = buildList {
|
|
||||||
path.forEachLine { line: String ->
|
|
||||||
if (line.isBlank()) return@forEachLine
|
|
||||||
|
|
||||||
val arguments = line.split(PAIRS_DELIMITER)
|
|
||||||
val fqNameToCheck = arguments.getOrNull(0)
|
|
||||||
?.substringAfter(VALUE_DELIMITER)
|
|
||||||
?: wrongInheritorStructure(line)
|
|
||||||
|
|
||||||
val baseFqName = arguments.getOrNull(1)
|
|
||||||
?.substringAfter(VALUE_DELIMITER)
|
|
||||||
?: wrongInheritorStructure(line)
|
|
||||||
|
|
||||||
val deep = arguments.getOrNull(2)
|
|
||||||
?.substringAfter(VALUE_DELIMITER)
|
|
||||||
?.substringBefore(' ')
|
|
||||||
?.toBoolean()
|
|
||||||
?: wrongInheritorStructure(line)
|
|
||||||
|
|
||||||
add(InheritorStructure(fqNameToCheck = fqNameToCheck, baseFqName = baseFqName, deep = deep))
|
|
||||||
}
|
|
||||||
}.toSet()
|
|
||||||
|
|
||||||
private fun wrongInheritorStructure(line: String): Nothing = error("Can't parse '$line' line correctly")
|
|
||||||
|
|
||||||
private fun PrettyPrinter.handleFile(ktFile: KtFile) {
|
|
||||||
val text = ktFile.text
|
|
||||||
ktFile.forEachDescendantOfType<KtClassOrObject> { classOrObject ->
|
|
||||||
handleClassDeclaration(classOrObject, text)
|
|
||||||
appendLine()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun PrettyPrinter.handleClassDeclaration(declaration: KtClassOrObject, fileText: String) {
|
|
||||||
appendLine("${declaration::class.simpleName}:")
|
|
||||||
withIndent {
|
|
||||||
val lineNumber = fileText.subSequence(0, declaration.startOffset).count { it == '\n' } + 1
|
|
||||||
appendLine("line: $lineNumber")
|
|
||||||
appendLine("name: ${declaration.name}")
|
|
||||||
appendLine("qualifier: ${declaration.fqName}")
|
|
||||||
append("light: ")
|
|
||||||
val lightClass = declaration.toLightClass()
|
|
||||||
if (lightClass != null) {
|
|
||||||
handleClass(lightClass)
|
|
||||||
} else {
|
|
||||||
appendLine("null")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun PrettyPrinter.handleClass(psiClass: PsiClass) {
|
|
||||||
appendLine(psiClass::class.simpleName)
|
|
||||||
withIndent {
|
|
||||||
appendLine("name: ${psiClass.name}")
|
|
||||||
appendLine("qualifier: ${psiClass.qualifiedName}")
|
|
||||||
appendCollection("superTypes", psiClass.superTypes.asList()) { it.toString() }
|
|
||||||
appendLine("superClass: ${psiClass.superClass?.render()}")
|
|
||||||
appendCollection("interfaces", psiClass.interfaces.asList()) { it.render() }
|
|
||||||
appendCollection("supers", psiClass.supers.asList()) { it.render() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun PsiClass.render(): String = "${this::class.simpleName}: $name ($qualifiedName)"
|
|
||||||
|
|
||||||
private inline fun <T> PrettyPrinter.appendCollection(
|
|
||||||
name: String,
|
|
||||||
collection: Collection<T>,
|
|
||||||
crossinline renderer: (T) -> CharSequence,
|
|
||||||
) {
|
|
||||||
append("$name: ")
|
|
||||||
if (collection.isEmpty()) {
|
|
||||||
appendLine("[]")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
withIndentInSquareBrackets {
|
|
||||||
printCollection(collection, separator = "\n") {
|
|
||||||
append(renderer(it))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
appendLine()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getRenderResult(ktFile: KtFile, ktFiles: List<KtFile>, testDataFile: Path, module: TestModule, project: Project): String {
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class InheritorStructure(val fqNameToCheck: String, val baseFqName: String, val deep: Boolean)
|
|
||||||
+165
@@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* 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.light.classes.symbol.base
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.JavaPsiFacade
|
||||||
|
import com.intellij.psi.PsiClass
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator
|
||||||
|
import org.jetbrains.kotlin.analysis.utils.printer.PrettyPrinter
|
||||||
|
import org.jetbrains.kotlin.asJava.toLightClass
|
||||||
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
|
import org.jetbrains.kotlin.test.model.TestModule
|
||||||
|
import org.jetbrains.kotlin.test.services.TestServices
|
||||||
|
import org.jetbrains.kotlin.test.services.assertions
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.forEachLine
|
||||||
|
import kotlin.io.path.notExists
|
||||||
|
|
||||||
|
private const val INHERITORS_EXTENSION = "inheritors.txt"
|
||||||
|
private const val PAIRS_DELIMITER = ", "
|
||||||
|
private const val VALUE_DELIMITER = ": "
|
||||||
|
|
||||||
|
open class AbstractSymbolLightClassesStructureTestBase(
|
||||||
|
configurator: AnalysisApiTestConfigurator,
|
||||||
|
protected val testPrefix: String,
|
||||||
|
override val stopIfCompilationErrorDirectivePresent: Boolean
|
||||||
|
) : AbstractSymbolLightClassesTestBase(configurator) {
|
||||||
|
override val currentExtension: String get() = throw UnsupportedOperationException()
|
||||||
|
protected fun doTestInheritors(ktFiles: List<KtFile>, testServices: TestServices) {
|
||||||
|
val testData = getTestDataFileSiblingPath(extension = INHERITORS_EXTENSION, testPrefix = testPrefix)
|
||||||
|
if (testData.notExists()) return
|
||||||
|
val project = ktFiles.first().project
|
||||||
|
|
||||||
|
val queries = parseInheritorsFile(testData)
|
||||||
|
val result = buildString {
|
||||||
|
for (query in queries) {
|
||||||
|
append("subClass")
|
||||||
|
append(VALUE_DELIMITER)
|
||||||
|
append(query.fqNameToCheck)
|
||||||
|
append(PAIRS_DELIMITER)
|
||||||
|
|
||||||
|
append("superClass")
|
||||||
|
append(VALUE_DELIMITER)
|
||||||
|
append(query.baseFqName)
|
||||||
|
append(PAIRS_DELIMITER)
|
||||||
|
|
||||||
|
append("deepSearch")
|
||||||
|
append(VALUE_DELIMITER)
|
||||||
|
append(query.deep.toString())
|
||||||
|
|
||||||
|
append(" -> ")
|
||||||
|
appendLine(query.isInheritor(project).toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testServices.assertions.assertEqualsToTestDataFileSibling(
|
||||||
|
actual = result,
|
||||||
|
testPrefix = testPrefix,
|
||||||
|
extension = INHERITORS_EXTENSION,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun InheritorStructure.isInheritor(project: Project): Boolean {
|
||||||
|
val lightClass = findLightClass(fqNameToCheck, project) ?: error("Can't find light class by '$fqNameToCheck' qualifier")
|
||||||
|
val baseClass = JavaPsiFacade.getInstance(project).findClass(baseFqName, GlobalSearchScope.allScope(project))
|
||||||
|
?: error("Can't find class by '$baseFqName' qualifier")
|
||||||
|
|
||||||
|
return lightClass.isInheritor(/* baseClass = */ baseClass, /* checkDeep = */ deep)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseInheritorsFile(path: Path): Collection<InheritorStructure> = buildList {
|
||||||
|
path.forEachLine { line: String ->
|
||||||
|
if (line.isBlank()) return@forEachLine
|
||||||
|
|
||||||
|
val arguments = line.split(PAIRS_DELIMITER)
|
||||||
|
val fqNameToCheck = arguments.getOrNull(0)
|
||||||
|
?.substringAfter(VALUE_DELIMITER)
|
||||||
|
?: wrongInheritorStructure(line)
|
||||||
|
|
||||||
|
val baseFqName = arguments.getOrNull(1)
|
||||||
|
?.substringAfter(VALUE_DELIMITER)
|
||||||
|
?: wrongInheritorStructure(line)
|
||||||
|
|
||||||
|
val deep = arguments.getOrNull(2)
|
||||||
|
?.substringAfter(VALUE_DELIMITER)
|
||||||
|
?.substringBefore(' ')
|
||||||
|
?.toBoolean()
|
||||||
|
?: wrongInheritorStructure(line)
|
||||||
|
|
||||||
|
add(InheritorStructure(fqNameToCheck = fqNameToCheck, baseFqName = baseFqName, deep = deep))
|
||||||
|
}
|
||||||
|
}.toSet()
|
||||||
|
|
||||||
|
private fun wrongInheritorStructure(line: String): Nothing = error("Can't parse '$line' line correctly")
|
||||||
|
protected fun PrettyPrinter.handleFile(ktFile: KtFile) {
|
||||||
|
val text = ktFile.text
|
||||||
|
ktFile.forEachDescendantOfType<KtClassOrObject> { classOrObject ->
|
||||||
|
handleClassDeclaration(classOrObject, text)
|
||||||
|
appendLine()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PrettyPrinter.handleClassDeclaration(declaration: KtClassOrObject, fileText: String) {
|
||||||
|
appendLine("${declaration::class.simpleName}:")
|
||||||
|
withIndent {
|
||||||
|
val lineNumber = fileText.subSequence(0, declaration.startOffset).count { it == '\n' } + 1
|
||||||
|
appendLine("line: $lineNumber")
|
||||||
|
appendLine("name: ${declaration.name}")
|
||||||
|
appendLine("qualifier: ${declaration.fqName}")
|
||||||
|
append("light: ")
|
||||||
|
val lightClass = declaration.toLightClass()
|
||||||
|
if (lightClass != null) {
|
||||||
|
handleClass(lightClass)
|
||||||
|
} else {
|
||||||
|
appendLine("null")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun PrettyPrinter.handleClass(psiClass: PsiClass) {
|
||||||
|
appendLine(psiClass::class.simpleName)
|
||||||
|
withIndent {
|
||||||
|
appendLine("name: ${psiClass.name}")
|
||||||
|
appendLine("qualifier: ${psiClass.qualifiedName}")
|
||||||
|
appendCollection("superTypes", psiClass.superTypes.asList()) { it.toString() }
|
||||||
|
appendLine("superClass: ${psiClass.superClass?.render()}")
|
||||||
|
appendCollection("interfaces", psiClass.interfaces.asList()) { it.render() }
|
||||||
|
appendCollection("supers", psiClass.supers.asList()) { it.render() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PsiClass.render(): String = "${this::class.simpleName}: $name ($qualifiedName)"
|
||||||
|
private inline fun <T> PrettyPrinter.appendCollection(
|
||||||
|
name: String,
|
||||||
|
collection: Collection<T>,
|
||||||
|
crossinline renderer: (T) -> CharSequence,
|
||||||
|
) {
|
||||||
|
append("$name: ")
|
||||||
|
if (collection.isEmpty()) {
|
||||||
|
appendLine("[]")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
withIndentInSquareBrackets {
|
||||||
|
printCollection(collection, separator = "\n") {
|
||||||
|
append(renderer(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appendLine()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRenderResult(ktFile: KtFile, ktFiles: List<KtFile>, testDataFile: Path, module: TestModule, project: Project): String {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class InheritorStructure(val fqNameToCheck: String, val baseFqName: String, val deep: Boolean)
|
||||||
Reference in New Issue
Block a user