mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 22:51:17 +07:00
Before AutomaticModuleUnloader was executed only during initial project loading, and wasn't executed if project configuration files were changed after the project is opened. Now it is executed in the both cases. Also handling of unloaded modules is simplified: since we load ModuleEntity instance for them anyway, there is no need to load them again in loadStateOfUnloadedModules, and there is no need to create UnloadedModuleDescription instances for AutomaticModuleUnloader. GitOrigin-RevId: e025b344e0e7343a4740544bc47e7beca0c5bd0a
238 lines
8.5 KiB
Kotlin
238 lines
8.5 KiB
Kotlin
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
|
package com.intellij.roots
|
|
|
|
import com.intellij.openapi.application.runWriteAction
|
|
import com.intellij.openapi.module.Module
|
|
import com.intellij.openapi.module.ModuleManager
|
|
import com.intellij.openapi.module.StdModuleTypes
|
|
import com.intellij.openapi.project.Project
|
|
import com.intellij.openapi.project.ex.ProjectManagerEx
|
|
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
|
import com.intellij.openapi.util.JDOMUtil
|
|
import com.intellij.openapi.vfs.VfsUtil
|
|
import com.intellij.project.ProjectStoreOwner
|
|
import com.intellij.testFramework.*
|
|
import com.intellij.testFramework.UsefulTestCase.assertSameElements
|
|
import com.intellij.testFramework.configurationStore.copyFilesAndReloadProject
|
|
import com.intellij.testFramework.rules.TempDirectory
|
|
import com.intellij.util.io.systemIndependentPath
|
|
import kotlinx.coroutines.runBlocking
|
|
import org.jdom.Element
|
|
import org.jetbrains.jps.model.serialization.JDomSerializationUtil
|
|
import org.jetbrains.jps.model.serialization.JpsProjectLoader
|
|
import org.junit.ClassRule
|
|
import org.junit.Rule
|
|
import org.junit.Test
|
|
import org.junit.runner.RunWith
|
|
import org.junit.runners.Parameterized
|
|
import java.io.File
|
|
import java.nio.file.Path
|
|
import java.nio.file.Paths
|
|
|
|
@RunsInEdt
|
|
@RunWith(Parameterized::class)
|
|
class AutomaticModuleUnloaderTest(private val reloadingMode: ReloadingMode) {
|
|
@Rule
|
|
@JvmField
|
|
val tempDir = TempDirectory()
|
|
|
|
@JvmField
|
|
@Rule
|
|
val disposableRule = DisposableRule()
|
|
|
|
@Test
|
|
fun `unload simple module`() {
|
|
val project = createProject()
|
|
createModule(project, "a")
|
|
createModule(project, "b")
|
|
val moduleManager = ModuleManager.getInstance(project)
|
|
moduleManager.setUnloadedModules(listOf("a"))
|
|
createModule(project, "c")
|
|
|
|
val moduleFiles = createNewModuleFiles(listOf("d")) {}
|
|
val newProject = reloadProjectWithNewModules(project, moduleFiles)
|
|
|
|
assertSameElements(ModuleManager.getInstance(newProject).unloadedModuleDescriptions.map { it.name }, "a", "d")
|
|
}
|
|
|
|
@Test
|
|
fun `unload modules with dependencies between them`() {
|
|
val project = createProject()
|
|
createModule(project, "a")
|
|
createModule(project, "b")
|
|
doTest(project, "a", listOf("c", "d"), { modules ->
|
|
ModuleRootModificationUtil.updateModel(modules.getValue("c")) {
|
|
it.addModuleOrderEntry(modules.getValue("d"))
|
|
}
|
|
}, "a", "c", "d")
|
|
}
|
|
|
|
@Test
|
|
fun `do not unload module if loaded module depends on it`() {
|
|
val project = createProject()
|
|
createModule(project, "a")
|
|
val b = createModule(project, "b")
|
|
ModuleRootModificationUtil.updateModel(b) {
|
|
it.addInvalidModuleEntry("d")
|
|
}
|
|
doTest(project, "a", listOf("d"), {}, "a")
|
|
}
|
|
|
|
@Test
|
|
fun `unload module if only unloaded module depends on it`() {
|
|
val project = createProject()
|
|
val a = createModule(project, "a")
|
|
createModule(project, "b")
|
|
ModuleRootModificationUtil.updateModel(a) {
|
|
it.addInvalidModuleEntry("d")
|
|
}
|
|
doTest(project, "a", listOf("d"), {}, "a", "d")
|
|
}
|
|
|
|
@Test
|
|
fun `do not unload modules if loaded module depends on them transitively`() {
|
|
val project = createProject()
|
|
createModule(project, "a")
|
|
val b = createModule(project, "b")
|
|
ModuleRootModificationUtil.updateModel(b) {
|
|
it.addInvalidModuleEntry("d")
|
|
}
|
|
|
|
doTest(project, "a", listOf("c", "d"), { modules ->
|
|
ModuleRootModificationUtil.updateModel(modules.getValue("d")) {
|
|
it.addModuleOrderEntry(modules.getValue("c"))
|
|
}
|
|
}, "a")
|
|
}
|
|
|
|
@Test
|
|
fun `unload module if loaded module transitively depends on it via previously unloaded module`() {
|
|
val project = createProject()
|
|
val a = createModule(project, "a")
|
|
val b = createModule(project, "b")
|
|
ModuleRootModificationUtil.addDependency(a, b)
|
|
ModuleRootModificationUtil.updateModel(b) {
|
|
it.addInvalidModuleEntry("c")
|
|
}
|
|
doTest(project, "b", listOf("c"), {}, "b", "c")
|
|
}
|
|
|
|
@Test
|
|
fun `deleted iml file`() {
|
|
val project = createProject()
|
|
createModule(project, "a")
|
|
createModule(project, "b")
|
|
val deletedIml = createModule(project, "deleted")
|
|
val moduleManager = ModuleManager.getInstance(project)
|
|
moduleManager.setUnloadedModules(listOf("a"))
|
|
createModule(project, "c")
|
|
|
|
val moduleFiles = createNewModuleFiles(listOf("d")) {}
|
|
val deletedImlFile = File(deletedIml.moduleFilePath)
|
|
val newProject = reloadProjectWithNewModules(project, moduleFiles) {
|
|
deletedImlFile.delete()
|
|
}
|
|
|
|
assertSameElements(ModuleManager.getInstance(newProject).unloadedModuleDescriptions.map { it.name }, "a", "d")
|
|
}
|
|
|
|
|
|
private fun doTest(project: Project,
|
|
initiallyUnloaded: String,
|
|
newModulesName: List<String>,
|
|
setup: (Map<String, Module>) -> Unit,
|
|
vararg expectedUnloadedModules: String) {
|
|
val moduleManager = ModuleManager.getInstance(project)
|
|
moduleManager.setUnloadedModules(listOf(initiallyUnloaded))
|
|
|
|
val moduleFiles = createNewModuleFiles(newModulesName, setup)
|
|
val newProject = reloadProjectWithNewModules(project, moduleFiles)
|
|
|
|
assertSameElements(ModuleManager.getInstance(newProject).unloadedModuleDescriptions.map { it.name }, *expectedUnloadedModules)
|
|
}
|
|
|
|
private fun createProject(): Project {
|
|
val project = ProjectManagerEx.getInstanceEx().openProject(tempDir.newDirectory("automaticReloaderTest").toPath(),
|
|
createTestOpenProjectOptions())
|
|
return project!!
|
|
}
|
|
|
|
private fun createModule(project: Project, moduleName: String): Module {
|
|
return runWriteAction { ModuleManager.getInstance(project).newModule("${project.basePath}/$moduleName.iml", "JAVA") }
|
|
}
|
|
|
|
private fun createNewModuleFiles(moduleNames: List<String>, setup: (Map<String, Module>) -> Unit): List<Path> {
|
|
val newModulesProjectDir = tempDir.newDirectory("newModules").toPath()
|
|
val moduleFiles = moduleNames.map { newModulesProjectDir.resolve("$it.iml") }
|
|
val project = ProjectManagerEx.getInstanceEx().newProject(newModulesProjectDir, createTestOpenProjectOptions())!!
|
|
try {
|
|
val moduleManager = ModuleManager.getInstance(project)
|
|
runWriteAction {
|
|
moduleFiles.map {
|
|
moduleManager.newModule(it.toAbsolutePath().toString(), StdModuleTypes.JAVA.id)
|
|
}
|
|
}
|
|
setup(moduleManager.modules.associateBy { it.name })
|
|
}
|
|
finally {
|
|
saveAndCloseProject(project)
|
|
}
|
|
return moduleFiles
|
|
}
|
|
|
|
private fun saveAndCloseProject(project: Project) {
|
|
PlatformTestUtil.saveProject(project, true)
|
|
ProjectManagerEx.getInstanceEx().forceCloseProject(project)
|
|
}
|
|
|
|
private fun reloadProjectWithNewModules(project: Project, moduleFiles: List<Path>, beforeReload: () -> Unit = {}): Project {
|
|
when (reloadingMode) {
|
|
ReloadingMode.ON_THE_FLY -> PlatformTestUtil.saveProject(project, true)
|
|
ReloadingMode.REOPEN -> saveAndCloseProject(project)
|
|
}
|
|
|
|
val modulesXmlFile = (project as ProjectStoreOwner).componentStore.getDirectoryStorePath()!!.resolve("modules.xml")
|
|
val rootElement = JDOMUtil.load(modulesXmlFile)
|
|
val moduleRootComponent = JDomSerializationUtil.findComponent(rootElement, JpsProjectLoader.MODULE_MANAGER_COMPONENT)
|
|
val modulesTag = moduleRootComponent!!.getChild("modules")!!
|
|
moduleFiles.forEach {
|
|
val filePath = it.systemIndependentPath
|
|
val fileUrl = VfsUtil.pathToUrl(filePath)
|
|
modulesTag.addContent(Element("module").setAttribute("fileurl", fileUrl).setAttribute("filepath", filePath))
|
|
}
|
|
|
|
when (reloadingMode) {
|
|
ReloadingMode.ON_THE_FLY -> {
|
|
val modulesXmlCopyDir = tempDir.newDirectory("modules-xml").toPath()
|
|
JDOMUtil.write(rootElement, modulesXmlCopyDir.resolve(".idea/modules.xml"))
|
|
beforeReload()
|
|
runBlocking {
|
|
copyFilesAndReloadProject(project, modulesXmlCopyDir)
|
|
}
|
|
disposableRule.register { PlatformTestUtil.forceCloseProjectWithoutSaving(project) }
|
|
return project
|
|
}
|
|
ReloadingMode.REOPEN -> {
|
|
JDOMUtil.write(rootElement, modulesXmlFile)
|
|
beforeReload()
|
|
return PlatformTestUtil.loadAndOpenProject(Paths.get(project.basePath!!), disposableRule.disposable)
|
|
}
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
@ClassRule
|
|
@JvmField
|
|
val appRule = ApplicationRule()
|
|
|
|
@ClassRule
|
|
@JvmField
|
|
val edtRule = EdtRule()
|
|
|
|
@Parameterized.Parameters(name = "{0}")
|
|
@JvmStatic
|
|
fun modes(): Array<ReloadingMode> = ReloadingMode.values()
|
|
}
|
|
|
|
enum class ReloadingMode { ON_THE_FLY, REOPEN }
|
|
} |