FIR IDE: introduce file structure tests

This commit is contained in:
Ilya Kirillov
2020-11-01 16:37:49 +03:00
parent 7c912cd3e4
commit 6c1faec171
11 changed files with 215 additions and 0 deletions
@@ -83,6 +83,7 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirMultiModuleResolve
import org.jetbrains.kotlin.idea.fir.AbstractKtDeclarationAndFirDeclarationEqualityChecker
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirLazyDeclarationResolveTest
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirMultiModuleLazyResolveTest
import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.AbstractFileStructureTest
import org.jetbrains.kotlin.idea.folding.AbstractKotlinFoldingTest
import org.jetbrains.kotlin.idea.frontend.api.fir.AbstractResolveCallTest
import org.jetbrains.kotlin.idea.frontend.api.scopes.AbstractMemberScopeByFqNameTest
@@ -1036,6 +1037,9 @@ fun main(args: Array<String>) {
testClass<AbstractFirLazyDeclarationResolveTest> {
model("lazyResolve")
}
testClass<AbstractFileStructureTest> {
model("fileStructure")
}
}
testGroup("idea/idea-fir/tests", "idea") {
@@ -0,0 +1,6 @@
class A {/* NonLocalDeclarationFileStructureElement */
fun x() {/* IncrementallyReanalyzableFunction */
}
fun y(): Int = 10/* IncrementallyReanalyzableFunction */
}
@@ -0,0 +1,9 @@
fun a() {/* IncrementallyReanalyzableFunction */
class X
}
class Y {/* NonLocalDeclarationFileStructureElement */
fun b() {/* IncrementallyReanalyzableFunction */
class Z
}
}
@@ -0,0 +1,13 @@
fun x() {/* IncrementallyReanalyzableFunction */
fun y() {
}
}
class A {/* NonLocalDeclarationFileStructureElement */
fun z() {/* IncrementallyReanalyzableFunction */
fun q() {
}
}
}
@@ -0,0 +1,16 @@
class A {/* NonLocalDeclarationFileStructureElement */
class B {/* NonLocalDeclarationFileStructureElement */
fun x() {/* IncrementallyReanalyzableFunction */
}
class C {/* NonLocalDeclarationFileStructureElement */
}
}
class E {/* NonLocalDeclarationFileStructureElement */
}
fun y(): Int = 10/* IncrementallyReanalyzableFunction */
}
@@ -0,0 +1 @@
fun foo(): Int = 42/* IncrementallyReanalyzableFunction */
@@ -0,0 +1 @@
fun foo() = 42/* NonLocalDeclarationFileStructureElement */
@@ -0,0 +1,4 @@
fun foo(): Int {/* IncrementallyReanalyzableFunction */
println("")
return 10
}
@@ -0,0 +1,4 @@
fun foo() {/* IncrementallyReanalyzableFunction */
println("")
}
@@ -0,0 +1,87 @@
/*
* Copyright 2010-2020 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.idea.fir.low.level.api.file.structure
import com.intellij.openapi.application.runUndoTransparentWriteAction
import com.intellij.openapi.util.io.FileUtil
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.util.collectDescendantsOfType
import com.intellij.psi.util.forEachDescendantOfType
import com.intellij.psi.util.parentOfType
import junit.framework.Assert
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveStateImpl
import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacade
import org.jetbrains.kotlin.idea.search.getKotlinFqName
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.util.getElementTextInContext
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.calls.callUtil.isFakeElement
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
abstract class AbstractFileStructureTest : KotlinLightCodeInsightFixtureTestCase() {
override fun isFirPlugin(): Boolean = true
fun doTest(path: String) {
val testDataFile = File(path)
val initialFileText = FileUtil.loadFile(testDataFile)
val ktFile = myFixture.configureByText(testDataFile.name, initialFileText) as KtFile
val fileStructure = ktFile.getFileStructure()
val allStructureElements = fileStructure.getAllStructureElements(ktFile)
val declarationToStructureElement = allStructureElements.associateBy { it.psi }
runUndoTransparentWriteAction {
ktFile.collectDescendantsOfType<PsiComment>().forEach { it.delete() }
ktFile.forEachDescendantOfType<KtDeclaration> { ktDeclaration ->
val structureElement = declarationToStructureElement[ktDeclaration] ?: return@forEachDescendantOfType
val comment = structureElement.createComment()
when (ktDeclaration) {
is KtClassOrObject -> {
val lBrace = ktDeclaration.body?.lBrace
if (lBrace != null) {
ktDeclaration.body!!.addAfter(comment, lBrace)
} else {
ktDeclaration.parent.addAfter(comment, ktDeclaration)
}
}
is KtFunction -> {
val lBrace = ktDeclaration.bodyBlockExpression?.lBrace
if (lBrace != null) {
ktDeclaration.bodyBlockExpression!!.addAfter(comment, lBrace)
} else {
ktDeclaration.parent.addAfter(comment, ktDeclaration)
}
}
else -> error("Unsupported declaration $ktDeclaration")
}
}
}
KotlinTestUtils.assertEqualsToFile(testDataFile, ktFile.text)
}
private fun FileStructureElement.createComment(): PsiComment {
val text = """/* ${this::class.simpleName!!} */"""
return KtPsiFactory(psi.project).createComment(text)
}
private fun KtFile.getFileStructure(): FileStructure {
val moduleResolveState = LowLevelFirApiFacade.getResolveStateFor(this) as FirModuleResolveStateImpl
return moduleResolveState.fileStructureCache.getFileStructure(
ktFile = this,
moduleFileCache = moduleResolveState.rootModuleSession.cache
)
}
@OptIn(ExperimentalStdlibApi::class)
private fun FileStructure.getAllStructureElements(ktFile: KtFile): Collection<FileStructureElement> = buildSet {
ktFile.forEachDescendantOfType<KtElement> { ktElement ->
add(getStructureElementFor(ktElement))
}
}
}
@@ -0,0 +1,70 @@
/*
* Copyright 2010-2020 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.idea.fir.low.level.api.file.structure;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/fileStructure")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class FileStructureTestGenerated extends AbstractFileStructureTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInFileStructure() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/fileStructure"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("class.kt")
public void testClass() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/fileStructure/class.kt");
}
@TestMetadata("localClass.kt")
public void testLocalClass() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/fileStructure/localClass.kt");
}
@TestMetadata("localFun.kt")
public void testLocalFun() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/fileStructure/localFun.kt");
}
@TestMetadata("nestedClasses.kt")
public void testNestedClasses() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/fileStructure/nestedClasses.kt");
}
@TestMetadata("topLevelExpressionBodyFunWithType.kt")
public void testTopLevelExpressionBodyFunWithType() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/fileStructure/topLevelExpressionBodyFunWithType.kt");
}
@TestMetadata("topLevelExpressionBodyFunWithoutType.kt")
public void testTopLevelExpressionBodyFunWithoutType() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/fileStructure/topLevelExpressionBodyFunWithoutType.kt");
}
@TestMetadata("topLevelFunWithType.kt")
public void testTopLevelFunWithType() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/fileStructure/topLevelFunWithType.kt");
}
@TestMetadata("topLevelUnitFun.kt")
public void testTopLevelUnitFun() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/fileStructure/topLevelUnitFun.kt");
}
}