[LL FIR] Test resolve extension disposal after modification events
- These tests simply ensure that a resolve extension is disposed after any kind of modification event is raised. It's not meant to test complex scenarios, but rather to detect when resolve extension disposal isn't invoked *at all*. - I tried implementing a similar test for resolve extension disposal after soft reference garbage collection, but the only way to force the GC to collect soft references is to allocate memory until an out-of-memory error occurs. That is bad for the test infrastructure, because it might allocate A LOT of memory (depending on the max heap), which is problematic for running tests locally. Also, our Kotlin tests stop on an OOM error altogether (so additional configuration would be required) and the heap is dumped into a 20GB file (on my machine), which is again problematic for local test runs. ^KT-61222
This commit is contained in:
committed by
Space Team
parent
040b7472a9
commit
5819f4eaa2
Vendored
+1
@@ -0,0 +1 @@
|
||||
// MODIFICATION_EVENT: GLOBAL_MODULE_STATE_MODIFICATION
|
||||
+1
@@ -0,0 +1 @@
|
||||
// MODIFICATION_EVENT: GLOBAL_SOURCE_MODULE_STATE_MODIFICATION
|
||||
+1
@@ -0,0 +1 @@
|
||||
// MODIFICATION_EVENT: GLOBAL_SOURCE_OUT_OF_BLOCK_MODIFICATION
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
// MODIFICATION_EVENT: MODULE_OUT_OF_BLOCK_MODIFICATION
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
// MODIFICATION_EVENT: MODULE_STATE_MODIFICATION
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.analysis.low.level.api.fir.resolve.extensions
|
||||
|
||||
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.api.resolve.extensions.KtResolveExtension
|
||||
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionFile
|
||||
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionProvider
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionCache
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.configurators.AnalysisApiFirSourceTestConfigurator
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtModule
|
||||
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
|
||||
import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiBasedTest
|
||||
import org.jetbrains.kotlin.analysis.test.framework.directives.ModificationEventDirectives
|
||||
import org.jetbrains.kotlin.analysis.test.framework.directives.publishModificationEventByDirective
|
||||
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator
|
||||
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestServiceRegistrar
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
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 org.jetbrains.kotlin.test.services.assertions
|
||||
|
||||
/**
|
||||
* A simple test which detects when resolve extension disposal after modification events/session invalidation doesn't work *at all*.
|
||||
*/
|
||||
abstract class AbstractResolveExtensionDisposalAfterModificationEventTest : AbstractAnalysisApiBasedTest() {
|
||||
override fun doTestByMainFile(
|
||||
mainFile: KtFile,
|
||||
mainModule: TestModule,
|
||||
testServices: TestServices,
|
||||
) {
|
||||
val project = mainFile.project
|
||||
val module = ProjectStructureProvider.getModule(project, mainFile, contextualModule = null)
|
||||
val session = LLFirSessionCache.getInstance(project).getSession(module)
|
||||
val resolveExtension = session.llResolveExtensionTool!!.extensions.single() as KtResolveExtensionWithDisposalTracker
|
||||
|
||||
testServices.assertions.assertFalse(resolveExtension.isDisposed) {
|
||||
"The resolve extension should not be disposed before the modification event is published."
|
||||
}
|
||||
|
||||
mainModule.publishModificationEventByDirective(project, module)
|
||||
|
||||
testServices.assertions.assertTrue(resolveExtension.isDisposed) {
|
||||
"The resolve extension should be disposed after the modification event has been published."
|
||||
}
|
||||
}
|
||||
|
||||
override val configurator: AnalysisApiTestConfigurator
|
||||
get() = ResolveExtensionDisposalTestConfigurator
|
||||
}
|
||||
|
||||
class KtResolveExtensionWithDisposalTracker() : KtResolveExtension() {
|
||||
override fun getKtFiles(): List<KtResolveExtensionFile> = emptyList()
|
||||
override fun getContainedPackages(): Set<FqName> = emptySet()
|
||||
override fun getShadowedScope(): GlobalSearchScope = GlobalSearchScope.EMPTY_SCOPE
|
||||
|
||||
var isDisposed: Boolean = false
|
||||
|
||||
override fun dispose() {
|
||||
isDisposed = true
|
||||
}
|
||||
}
|
||||
|
||||
class KtResolveExtensionWithDisposalTrackerProvider() : KtResolveExtensionProvider() {
|
||||
override fun provideExtensionsFor(module: KtModule): List<KtResolveExtension> = listOf(KtResolveExtensionWithDisposalTracker())
|
||||
}
|
||||
|
||||
object ResolveExtensionDisposalTestConfigurator : AnalysisApiFirSourceTestConfigurator(analyseInDependentSession = false) {
|
||||
override fun configureTest(builder: TestConfigurationBuilder, disposable: Disposable) {
|
||||
super.configureTest(builder, disposable)
|
||||
|
||||
builder.useDirectives(ModificationEventDirectives)
|
||||
}
|
||||
|
||||
override val serviceRegistrars: List<AnalysisApiTestServiceRegistrar>
|
||||
get() = buildList {
|
||||
addAll(super.serviceRegistrars)
|
||||
add(ResolveExtensionDisposalTestServiceRegistrar)
|
||||
}
|
||||
}
|
||||
|
||||
object ResolveExtensionDisposalTestServiceRegistrar : AnalysisApiTestServiceRegistrar() {
|
||||
override fun registerApplicationServices(application: MockApplication, testServices: TestServices) {}
|
||||
override fun registerProjectExtensionPoints(project: MockProject, testServices: TestServices) {}
|
||||
override fun registerProjectModelServices(project: MockProject, testServices: TestServices) {}
|
||||
|
||||
override fun registerProjectServices(
|
||||
project: MockProject,
|
||||
testServices: TestServices,
|
||||
) {
|
||||
val extensionPoint = project.extensionArea.getExtensionPoint(KtResolveExtensionProvider.EP_NAME)
|
||||
extensionPoint.registerExtension(KtResolveExtensionWithDisposalTrackerProvider(), project)
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2010-2024 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.low.level.api.fir.resolve.extensions;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.analysis.api.GenerateAnalysisApiTestsKt}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("analysis/low-level-api-fir/testData/resolveExtensionDisposal")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class ResolveExtensionDisposalAfterModificationEventTestGenerated extends AbstractResolveExtensionDisposalAfterModificationEventTest {
|
||||
@Test
|
||||
@TestMetadata("afterGlobalModuleStateModification.kt")
|
||||
public void testAfterGlobalModuleStateModification() {
|
||||
runTest("analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterGlobalModuleStateModification.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("afterGlobalSourceModuleStateModification.kt")
|
||||
public void testAfterGlobalSourceModuleStateModification() {
|
||||
runTest("analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterGlobalSourceModuleStateModification.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("afterGlobalSourceOutOfBlockModification.kt")
|
||||
public void testAfterGlobalSourceOutOfBlockModification() {
|
||||
runTest("analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterGlobalSourceOutOfBlockModification.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("afterModuleOutOfBlockModification.kt")
|
||||
public void testAfterModuleOutOfBlockModification() {
|
||||
runTest("analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterModuleOutOfBlockModification.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("afterModuleStateModification.kt")
|
||||
public void testAfterModuleStateModification() {
|
||||
runTest("analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterModuleStateModification.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllFilesPresentInResolveExtensionDisposal() {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/low-level-api-fir/testData/resolveExtensionDisposal"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
}
|
||||
+5
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.AbstractScriptLaz
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.AbstractScriptWholeFileResolvePhaseTest
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.AbstractSourceLazyDeclarationResolveScopeBasedTest
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.AbstractSourceWholeFileResolvePhaseTest
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.extensions.AbstractResolveExtensionDisposalAfterModificationEventTest
|
||||
import org.jetbrains.kotlin.generators.TestGroup
|
||||
import org.jetbrains.kotlin.generators.TestGroupSuite
|
||||
import org.jetbrains.kotlin.generators.util.TestGeneratorUtil
|
||||
@@ -281,6 +282,10 @@ internal fun TestGroupSuite.generateFirLowLevelApiTests() {
|
||||
testClass<AbstractContextCollectorScriptTest> {
|
||||
model("contextCollector", pattern = TestGeneratorUtil.KTS)
|
||||
}
|
||||
|
||||
testClass<AbstractResolveExtensionDisposalAfterModificationEventTest> {
|
||||
model("resolveExtensionDisposal")
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("analysis/low-level-api-fir/tests", "analysis/analysis-api/testData") {
|
||||
|
||||
Reference in New Issue
Block a user