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.AbstractFirMultiModuleLazyResolveTest
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.frontend.api.fir.AbstractResolveCallTest
import org.jetbrains.kotlin.idea.fir.low.level.api.trackers.AbstractProjectWideOutOfBlockKotlinModificationTrackerTest
@@ -1041,6 +1042,9 @@ fun main(args: Array<String>) {
testClass<AbstractProjectWideOutOfBlockKotlinModificationTrackerTest> {
model("outOfBlockProjectWide")
}
testClass<AbstractFileStructureAndOutOfBlockModificationTrackerConsistencyTest> {
model("outOfBlockProjectWide")
}
testClass<AbstractFileStructureTest> {
model("fileStructure")
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.idea.fir.low.level.api.element.builder
import com.intellij.psi.PsiElement
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
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.ModuleFileCache
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.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.psi2ir.deparenthesize
/**
@@ -53,6 +56,16 @@ internal class FirElementBuilder {
return psi.getFirOfClosestParent(mappings)?.second
?: 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>? {
@@ -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.api.FirModuleResolveState
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.util.getElementTextInContext
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import java.io.File
abstract class AbstractReanalyzableFileStructureElementCreationTest : KotlinLightCodeInsightFixtureTestCase() {
abstract class AbstractFileStructureAndOutOfBlockModificationTrackerConsistencyTest : 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 expectedFqName =
InTextDirectivesUtils.findStringWithPrefixes(initialFileText, STRUCTURE_ELEMENT_FQ_NAME_DIRECTIVE)
?: error("Please specify // STRUCTURE_ELEMENT directive")
val shouldStructureElementBeRecreated =
InTextDirectivesUtils.getPrefixedBoolean(initialFileText, SHOULD_ELEMENT_BE_RECREATED)
?: error("Please specify // SHOULD_ELEMENT_BE_RECREATED directive")
val fileText = FileUtil.loadFile(testDataFile)
val ktFile = myFixture.configureByText(testDataFile.name, fileText) as KtFile
val textToType = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// TYPE:")
?: AbstractProjectWideOutOfBlockKotlinModificationTrackerTest.DEFAULT_TEXT_TO_TYPE
val outOfBlock = InTextDirectivesUtils.getPrefixedBoolean(fileText, "// OUT_OF_BLOCK:")
?: error("Please, specify should out of block change happen or not by `// OUT_OF_BLOCK:` directive")
val elementAtCaret = ktFile.findElementAtCaret()
val (initialStructureElement, initialFileStructure, initialModuleResolveState) = getStructureElementForKtElement(elementAtCaret)
Assert.assertEquals(expectedFqName, initialStructureElement.psi.getKotlinFqName()?.asString())
myFixture.type("hello")
myFixture.type(textToType)
PsiDocumentManager.getInstance(project).commitAllDocuments()
val newElementAtCaret = ktFile.findElementAtCaret()
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.assertEquals(
"FirModuleResolveState should change only of out of block modification",
shouldStructureElementBeRecreated,
"FirModuleResolveState should change only on out of block modification",
outOfBlock,
newModuleResolveState !== initialModuleResolveState
)
Assert.assertEquals(
"FileStructure state should change only of out of block modification",
shouldStructureElementBeRecreated,
"FileStructure state should change only on out of block modification",
outOfBlock,
initialFileStructure !== newFileStructure
)
}
private fun KtFile.findElementAtCaret(): KtElement {
val elementAtCaret = findElementAt(myFixture.caretOffset)!!.parentOfType<KtElement>()!!
if (elementAtCaret is KtDeclaration) {
error("Expected element inside declaration but was\n${elementAtCaret.getElementTextInContext()}")
}
return elementAtCaret
}
private fun KtFile.findElementAtCaret(): KtElement =
findElementAt(myFixture.caretOffset)!!.parentOfType()!!
private fun getStructureElementForKtElement(element: KtElement): Triple<FileStructureElement, FileStructure, FirModuleResolveState> {
val moduleResolveState = LowLevelFirApiFacade.getResolveStateFor(element) as FirModuleResolveStateImpl
@@ -77,9 +63,4 @@ abstract class AbstractReanalyzableFileStructureElementCreationTest : KotlinLigh
val fileStructureElement = fileStructure.getStructureElementFor(element)
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 {
private const val DEFAULT_TEXT_TO_TYPE = "hello"
const val DEFAULT_TEXT_TO_TYPE = "hello"
}
}