FIR IDE: introduce ProjectWideOutOfBlockKotlinModificationTrackerTest

This commit is contained in:
Ilya Kirillov
2020-11-03 23:06:45 +03:00
parent 880e76b203
commit da7b12f7e1
6 changed files with 129 additions and 86 deletions
@@ -84,6 +84,7 @@ import org.jetbrains.kotlin.idea.fir.AbstractKtDeclarationAndFirDeclarationEqual
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirLazyDeclarationResolveTest 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.AbstractFirMultiModuleLazyResolveTest
import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.AbstractFileStructureTest import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.AbstractFileStructureTest
import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.AbstractFileStructureAndOutOfBlockModificationTrackerConsistencyTest
import org.jetbrains.kotlin.idea.folding.AbstractKotlinFoldingTest import org.jetbrains.kotlin.idea.folding.AbstractKotlinFoldingTest
import org.jetbrains.kotlin.idea.frontend.api.fir.AbstractResolveCallTest import org.jetbrains.kotlin.idea.frontend.api.fir.AbstractResolveCallTest
import org.jetbrains.kotlin.idea.fir.low.level.api.trackers.AbstractProjectWideOutOfBlockKotlinModificationTrackerTest import org.jetbrains.kotlin.idea.fir.low.level.api.trackers.AbstractProjectWideOutOfBlockKotlinModificationTrackerTest
@@ -1041,6 +1042,9 @@ fun main(args: Array<String>) {
testClass<AbstractProjectWideOutOfBlockKotlinModificationTrackerTest> { testClass<AbstractProjectWideOutOfBlockKotlinModificationTrackerTest> {
model("outOfBlockProjectWide") model("outOfBlockProjectWide")
} }
testClass<AbstractFileStructureAndOutOfBlockModificationTrackerConsistencyTest> {
model("outOfBlockProjectWide")
}
testClass<AbstractFileStructureTest> { testClass<AbstractFileStructureTest> {
model("fileStructure") model("fileStructure")
} }
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.idea.fir.low.level.api.element.builder package org.jetbrains.kotlin.idea.fir.low.level.api.element.builder
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver
@@ -13,9 +14,11 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.annotations.ThreadSafe
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.FileStructureCache import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.FileStructureCache
import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.FileStructureElement
import org.jetbrains.kotlin.idea.util.getElementTextInContext import org.jetbrains.kotlin.idea.util.getElementTextInContext
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.psi2ir.deparenthesize import org.jetbrains.kotlin.psi2ir.deparenthesize
/** /**
@@ -53,6 +56,16 @@ internal class FirElementBuilder {
return psi.getFirOfClosestParent(mappings)?.second return psi.getFirOfClosestParent(mappings)?.second
?: error("FirElement is not found for:\n${element.getElementTextInContext()}") ?: error("FirElement is not found for:\n${element.getElementTextInContext()}")
} }
@TestOnly
fun getStructureElementFor(
element: KtElement,
moduleFileCache: ModuleFileCache,
fileStructureCache: FileStructureCache,
): FileStructureElement {
val fileStructure = fileStructureCache.getFileStructure(element.containingKtFile, moduleFileCache)
return fileStructure.getStructureElementFor(element)
}
} }
private fun KtElement.getFirOfClosestParent(cache: Map<KtElement, FirElement>): Pair<KtElement, FirElement>? { private fun KtElement.getFirOfClosestParent(cache: Map<KtElement, FirElement>): Pair<KtElement, FirElement>? {
@@ -12,63 +12,49 @@ import junit.framework.Assert
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveStateImpl import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveStateImpl
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacade import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacade
import org.jetbrains.kotlin.idea.search.getKotlinFqName import org.jetbrains.kotlin.idea.fir.low.level.api.trackers.AbstractProjectWideOutOfBlockKotlinModificationTrackerTest
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.util.getElementTextInContext
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.InTextDirectivesUtils import org.jetbrains.kotlin.test.InTextDirectivesUtils
import java.io.File import java.io.File
abstract class AbstractReanalyzableFileStructureElementCreationTest : KotlinLightCodeInsightFixtureTestCase() { abstract class AbstractFileStructureAndOutOfBlockModificationTrackerConsistencyTest : KotlinLightCodeInsightFixtureTestCase() {
override fun isFirPlugin(): Boolean = true override fun isFirPlugin(): Boolean = true
fun doTest(path: String) { fun doTest(path: String) {
val testDataFile = File(path) val testDataFile = File(path)
val initialFileText = FileUtil.loadFile(testDataFile) val fileText = FileUtil.loadFile(testDataFile)
val ktFile = myFixture.configureByText(testDataFile.name, initialFileText) as KtFile val ktFile = myFixture.configureByText(testDataFile.name, fileText) as KtFile
val expectedFqName = val textToType = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// TYPE:")
InTextDirectivesUtils.findStringWithPrefixes(initialFileText, STRUCTURE_ELEMENT_FQ_NAME_DIRECTIVE) ?: AbstractProjectWideOutOfBlockKotlinModificationTrackerTest.DEFAULT_TEXT_TO_TYPE
?: error("Please specify // STRUCTURE_ELEMENT directive") val outOfBlock = InTextDirectivesUtils.getPrefixedBoolean(fileText, "// OUT_OF_BLOCK:")
val shouldStructureElementBeRecreated = ?: error("Please, specify should out of block change happen or not by `// OUT_OF_BLOCK:` directive")
InTextDirectivesUtils.getPrefixedBoolean(initialFileText, SHOULD_ELEMENT_BE_RECREATED)
?: error("Please specify // SHOULD_ELEMENT_BE_RECREATED directive")
val elementAtCaret = ktFile.findElementAtCaret() val elementAtCaret = ktFile.findElementAtCaret()
val (initialStructureElement, initialFileStructure, initialModuleResolveState) = getStructureElementForKtElement(elementAtCaret) val (initialStructureElement, initialFileStructure, initialModuleResolveState) = getStructureElementForKtElement(elementAtCaret)
Assert.assertEquals(expectedFqName, initialStructureElement.psi.getKotlinFqName()?.asString())
myFixture.type("hello") myFixture.type(textToType)
PsiDocumentManager.getInstance(project).commitAllDocuments() PsiDocumentManager.getInstance(project).commitAllDocuments()
val newElementAtCaret = ktFile.findElementAtCaret() val newElementAtCaret = ktFile.findElementAtCaret()
val (newStructureElement, newFileStructure, newModuleResolveState) = getStructureElementForKtElement(newElementAtCaret) val (newStructureElement, newFileStructure, newModuleResolveState) = getStructureElementForKtElement(newElementAtCaret)
Assert.assertEquals(
"Structure elements should be build by the same KtDeclaration's",
expectedFqName,
newStructureElement.psi.getKotlinFqName()?.asString()
)
Assert.assertTrue("Structure elements should be different after typing", newStructureElement !== initialStructureElement) Assert.assertTrue("Structure elements should be different after typing", newStructureElement !== initialStructureElement)
Assert.assertEquals( Assert.assertEquals(
"FirModuleResolveState should change only of out of block modification", "FirModuleResolveState should change only on out of block modification",
shouldStructureElementBeRecreated, outOfBlock,
newModuleResolveState !== initialModuleResolveState newModuleResolveState !== initialModuleResolveState
) )
Assert.assertEquals( Assert.assertEquals(
"FileStructure state should change only of out of block modification", "FileStructure state should change only on out of block modification",
shouldStructureElementBeRecreated, outOfBlock,
initialFileStructure !== newFileStructure initialFileStructure !== newFileStructure
) )
} }
private fun KtFile.findElementAtCaret(): KtElement { private fun KtFile.findElementAtCaret(): KtElement =
val elementAtCaret = findElementAt(myFixture.caretOffset)!!.parentOfType<KtElement>()!! findElementAt(myFixture.caretOffset)!!.parentOfType()!!
if (elementAtCaret is KtDeclaration) {
error("Expected element inside declaration but was\n${elementAtCaret.getElementTextInContext()}")
}
return elementAtCaret
}
private fun getStructureElementForKtElement(element: KtElement): Triple<FileStructureElement, FileStructure, FirModuleResolveState> { private fun getStructureElementForKtElement(element: KtElement): Triple<FileStructureElement, FileStructure, FirModuleResolveState> {
val moduleResolveState = LowLevelFirApiFacade.getResolveStateFor(element) as FirModuleResolveStateImpl val moduleResolveState = LowLevelFirApiFacade.getResolveStateFor(element) as FirModuleResolveStateImpl
@@ -77,9 +63,4 @@ abstract class AbstractReanalyzableFileStructureElementCreationTest : KotlinLigh
val fileStructureElement = fileStructure.getStructureElementFor(element) val fileStructureElement = fileStructure.getStructureElementFor(element)
return Triple(fileStructureElement, fileStructure, moduleResolveState) return Triple(fileStructureElement, fileStructure, moduleResolveState)
} }
companion object {
private const val STRUCTURE_ELEMENT_FQ_NAME_DIRECTIVE = "// STRUCTURE_ELEMENT:"
private const val SHOULD_ELEMENT_BE_RECREATED = "// SHOULD_ELEMENT_BE_RECREATED:"
}
} }
@@ -0,0 +1,95 @@
/*
* 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/outOfBlockProjectWide")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class FileStructureAndOutOfBlockModificationTrackerConsistencyTestGenerated extends AbstractFileStructureAndOutOfBlockModificationTrackerConsistencyTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInOutOfBlockProjectWide() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("localFun.kt")
public void testLocalFun() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/localFun.kt");
}
@TestMetadata("topLevelExpressionBodyFunWithType.kt")
public void testTopLevelExpressionBodyFunWithType() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/topLevelExpressionBodyFunWithType.kt");
}
@TestMetadata("topLevelExpressionBodyFunWithoutType.kt")
public void testTopLevelExpressionBodyFunWithoutType() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/topLevelExpressionBodyFunWithoutType.kt");
}
@TestMetadata("topLevelFunWithType.kt")
public void testTopLevelFunWithType() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/topLevelFunWithType.kt");
}
@TestMetadata("topLevelUnitFun.kt")
public void testTopLevelUnitFun() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/topLevelUnitFun.kt");
}
@TestMetadata("typeInFunctionAnnotation.kt")
public void testTypeInFunctionAnnotation() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/typeInFunctionAnnotation.kt");
}
@TestMetadata("typeInFunctionAnnotationParameter.kt")
public void testTypeInFunctionAnnotationParameter() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/typeInFunctionAnnotationParameter.kt");
}
@TestMetadata("typeInFunctionModifiers.kt")
public void testTypeInFunctionModifiers() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/typeInFunctionModifiers.kt");
}
@TestMetadata("typeInFunctionName.kt")
public void testTypeInFunctionName() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/typeInFunctionName.kt");
}
@TestMetadata("typeInFunctionParams.kt")
public void testTypeInFunctionParams() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/typeInFunctionParams.kt");
}
@TestMetadata("typeInFunctionParamsType.kt")
public void testTypeInFunctionParamsType() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/typeInFunctionParamsType.kt");
}
@TestMetadata("typeInFunctionReturnType.kt")
public void testTypeInFunctionReturnType() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/typeInFunctionReturnType.kt");
}
@TestMetadata("typeInFunctionTypeParams.kt")
public void testTypeInFunctionTypeParams() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/typeInFunctionTypeParams.kt");
}
}
@@ -1,50 +0,0 @@
/*
* 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/elementBuilder")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class ReanalyzableFileStructureElementCreationTestGenerated extends AbstractReanalyzableFileStructureElementCreationTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInElementBuilder() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/elementBuilder"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("topLevelExpressionBodyFunWithType.kt")
public void testTopLevelExpressionBodyFunWithType() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/elementBuilder/topLevelExpressionBodyFunWithType.kt");
}
@TestMetadata("topLevelExpressionBodyFunWithoutType.kt")
public void testTopLevelExpressionBodyFunWithoutType() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/elementBuilder/topLevelExpressionBodyFunWithoutType.kt");
}
@TestMetadata("topLevelFunWithType.kt")
public void testTopLevelFunWithType() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/elementBuilder/topLevelFunWithType.kt");
}
@TestMetadata("topLevelUnitFun.kt")
public void testTopLevelUnitFun() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/elementBuilder/topLevelUnitFun.kt");
}
}
@@ -32,6 +32,6 @@ abstract class AbstractProjectWideOutOfBlockKotlinModificationTrackerTest : Kotl
} }
companion object { companion object {
private const val DEFAULT_TEXT_TO_TYPE = "hello" const val DEFAULT_TEXT_TO_TYPE = "hello"
} }
} }