Files
openide/java/java-tests/testSrc/com/intellij/openapi/roots/impl/OrderEntryGraphTest.kt
2023-08-30 12:53:14 +00:00

292 lines
14 KiB
Kotlin

// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.roots.impl
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.*
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.testFramework.PsiTestUtil
import com.intellij.testFramework.UsefulTestCase
import com.intellij.testFramework.UsefulTestCase.assertEmpty
import com.intellij.testFramework.UsefulTestCase.assertOneElement
import com.intellij.testFramework.junit5.RunInEdt
import com.intellij.testFramework.junit5.TestApplication
import com.intellij.testFramework.rules.ProjectModelExtension
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.EnumSource
@TestApplication
@RunInEdt(writeIntent = true)
class OrderEntryGraphTest {
@JvmField
@RegisterExtension
val projectModel: ProjectModelExtension = ProjectModelExtension()
private val fileIndex
get() = ProjectFileIndex.getInstance(projectModel.project)
@Test
fun `module content`() {
val module = projectModel.createModule()
val moduleDir = projectModel.baseProjectDir.newVirtualDirectory("module")
val srcDir = projectModel.baseProjectDir.newVirtualDirectory("module/src")
val excludedDir = projectModel.baseProjectDir.newVirtualDirectory("module/excluded")
val excludedUnderSrcDir = projectModel.baseProjectDir.newVirtualDirectory("module/src/excluded")
PsiTestUtil.addContentRoot(module, moduleDir)
PsiTestUtil.addSourceRoot(module, srcDir)
PsiTestUtil.addExcludedRoot(module, excludedDir)
PsiTestUtil.addExcludedRoot(module, excludedUnderSrcDir)
assertNoOrderEntries(moduleDir)
assertModuleSourceEntryOnly(srcDir, module)
assertNoOrderEntries(excludedDir)
assertNoOrderEntries(excludedUnderSrcDir)
PsiTestUtil.removeExcludedRoot(module, excludedUnderSrcDir)
assertModuleSourceEntryOnly(excludedUnderSrcDir, module)
PsiTestUtil.removeSourceRoot(module, srcDir)
assertNoOrderEntries(srcDir)
}
@Test
fun `dependency on module`() {
val utilModule = projectModel.createModule("util")
val utilSrcDir = projectModel.baseProjectDir.newVirtualDirectory("util/src")
PsiTestUtil.addSourceContentToRoots(utilModule, utilSrcDir)
val app1Module = projectModel.createModule("app1")
ModuleRootModificationUtil.addDependency(app1Module, utilModule)
val app2Module = projectModel.createModule("app2")
ModuleRootModificationUtil.addDependency(app2Module, utilModule, DependencyScope.COMPILE, true)
val testModule = projectModel.createModule("test")
ModuleRootModificationUtil.addDependency(testModule, utilModule, DependencyScope.TEST, false)
val runtimeModule = projectModel.createModule("runtime")
ModuleRootModificationUtil.addDependency(runtimeModule, utilModule, DependencyScope.RUNTIME, false)
val ext1Module = projectModel.createModule("ext1")
ModuleRootModificationUtil.addDependency(ext1Module, app1Module)
val ext2Module = projectModel.createModule("ext2")
ModuleRootModificationUtil.addDependency(ext2Module, app2Module)
val orderEntries = assertIncludesModuleSourceEntry(utilSrcDir, utilModule)
assertEmpty(orderEntries.filterNot { it is ModuleOrderEntry })
assertFromModules(orderEntries, app1Module, app2Module, testModule, runtimeModule, ext2Module)
}
enum class LibraryLevel { PROJECT, APPLICATION }
@ParameterizedTest
@EnumSource(LibraryLevel::class)
fun `dependency on library`(level: LibraryLevel) {
val classesDir = projectModel.baseProjectDir.newVirtualDirectory("lib/classes")
val sourceDir = projectModel.baseProjectDir.newVirtualDirectory("lib/src")
val docDir = projectModel.baseProjectDir.newVirtualDirectory("lib/doc")
val excludedDir = projectModel.baseProjectDir.newVirtualDirectory("lib/classes/excluded")
fun setupLibrary(model: LibraryEx.ModifiableModelEx) {
model.addRoot(classesDir, OrderRootType.CLASSES)
model.addRoot(sourceDir, OrderRootType.SOURCES)
model.addRoot(docDir, OrderRootType.DOCUMENTATION)
model.addExcludedRoot(excludedDir.url)
}
val library = when (level) {
LibraryLevel.PROJECT -> projectModel.addProjectLevelLibrary("lib", ::setupLibrary)
LibraryLevel.APPLICATION -> projectModel.addApplicationLevelLibrary("lib", ::setupLibrary)
}
val app1Module = projectModel.createModule("app1")
ModuleRootModificationUtil.addDependency(app1Module, library)
val app2Module = projectModel.createModule("app2")
ModuleRootModificationUtil.addDependency(app2Module, library, DependencyScope.COMPILE, true)
val testModule = projectModel.createModule("test")
ModuleRootModificationUtil.addDependency(testModule, library, DependencyScope.TEST, false)
val runtimeModule = projectModel.createModule("runtime")
ModuleRootModificationUtil.addDependency(runtimeModule, library, DependencyScope.RUNTIME, false)
val ext1Module = projectModel.createModule("ext1")
ModuleRootModificationUtil.addDependency(ext1Module, app1Module)
val ext2Module = projectModel.createModule("ext2")
ModuleRootModificationUtil.addDependency(ext2Module, app2Module)
assertNoOrderEntries(excludedDir)
assertNoOrderEntries(docDir)
val entriesForClasses = fileIndex.getOrderEntriesForFile(classesDir)
assertEquals(entriesForClasses, fileIndex.getOrderEntriesForFile(sourceDir))
assertEquals(ext2Module, (entriesForClasses.filterNot { it is LibraryOrderEntry }.single() as ModuleOrderEntry).ownerModule)
assertFromModules(entriesForClasses, app1Module, app2Module, testModule, runtimeModule, ext2Module)
}
@Test
fun `dependency on module-level library`() {
val module = projectModel.createModule("module")
val classesDir = projectModel.baseProjectDir.newVirtualDirectory("lib/classes")
val sourceDir = projectModel.baseProjectDir.newVirtualDirectory("lib/src")
val docDir = projectModel.baseProjectDir.newVirtualDirectory("lib/doc")
val excludedDir = projectModel.baseProjectDir.newVirtualDirectory("lib/classes/excluded")
val library = projectModel.addModuleLevelLibrary(module, "lib") { model ->
model.addRoot(classesDir, OrderRootType.CLASSES)
model.addRoot(sourceDir, OrderRootType.SOURCES)
model.addRoot(docDir, OrderRootType.DOCUMENTATION)
model.addExcludedRoot(excludedDir.url)
}
val testLibraryRoot = projectModel.baseProjectDir.newVirtualDirectory("test-lib")
val testLibrary = projectModel.addModuleLevelLibrary(module, "test-lib") {
it.addRoot(testLibraryRoot, OrderRootType.CLASSES)
}
val runtimeLibraryRoot = projectModel.baseProjectDir.newVirtualDirectory("runtime-lib")
val runtimeLibrary = projectModel.addModuleLevelLibrary(module, "runtime-lib") {
it.addRoot(runtimeLibraryRoot, OrderRootType.CLASSES)
}
val exportedLibraryRoot = projectModel.baseProjectDir.newVirtualDirectory("exported-lib")
val exportedLibrary = projectModel.addModuleLevelLibrary(module, "exported-lib") {
it.addRoot(exportedLibraryRoot, OrderRootType.CLASSES)
}
ModuleRootModificationUtil.modifyModel(module) { model ->
model.findLibraryOrderEntry(testLibrary)!!.scope = DependencyScope.TEST
model.findLibraryOrderEntry(runtimeLibrary)!!.scope = DependencyScope.RUNTIME
model.findLibraryOrderEntry(exportedLibrary)!!.isExported = true
true
}
val depModule = projectModel.createModule("dep")
ModuleRootModificationUtil.addDependency(depModule, module)
assertNoOrderEntries(excludedDir)
assertNoOrderEntries(docDir)
val entriesForClasses = fileIndex.getOrderEntriesForFile(classesDir)
assertEquals(library, (entriesForClasses.single() as LibraryOrderEntry).library)
assertEquals(entriesForClasses, fileIndex.getOrderEntriesForFile(sourceDir))
val entriesForTest = fileIndex.getOrderEntriesForFile(testLibraryRoot)
assertEquals("test-lib", (entriesForTest.single() as LibraryOrderEntry).libraryName)
val entriesForRuntime = fileIndex.getOrderEntriesForFile(runtimeLibraryRoot)
assertEquals("runtime-lib", (entriesForRuntime.single() as LibraryOrderEntry).libraryName)
val entriesForExported = fileIndex.getOrderEntriesForFile(exportedLibraryRoot).sortedBy(OrderEntry::toString)
UsefulTestCase.assertSize(2, entriesForExported)
val (libraryEntry, moduleEntry) = entriesForExported
assertEquals(depModule, (moduleEntry as ModuleOrderEntry).ownerModule)
assertEquals("exported-lib", (libraryEntry as LibraryOrderEntry).libraryName)
}
@Test
fun `dependency on sdk`() {
val classesDir = projectModel.baseProjectDir.newVirtualDirectory("sdk/classes")
val sourceDir = projectModel.baseProjectDir.newVirtualDirectory("sdk/src")
val docDir = projectModel.baseProjectDir.newVirtualDirectory("sdk/doc")
val sdk = projectModel.addSdk {
it.addRoot(classesDir, OrderRootType.CLASSES)
it.addRoot(sourceDir, OrderRootType.SOURCES)
it.addRoot(docDir, OrderRootType.DOCUMENTATION)
}
val module1 = projectModel.createModule("module1")
ModuleRootModificationUtil.setModuleSdk(module1, sdk)
val module2 = projectModel.createModule("module2")
ModuleRootModificationUtil.setModuleSdk(module2, sdk)
assertNoOrderEntries(docDir)
val entriesForClasses = fileIndex.getOrderEntriesForFile(classesDir)
assertEquals(entriesForClasses, fileIndex.getOrderEntriesForFile(sourceDir))
assertEmpty(entriesForClasses.filterNot { it is JdkOrderEntry })
assertFromModules(entriesForClasses, module1, module2)
}
@Test
fun `inherited sdk`() {
val classesDir = projectModel.baseProjectDir.newVirtualDirectory("sdk/classes")
val sdk = projectModel.addSdk {
it.addRoot(classesDir, OrderRootType.CLASSES)
}
runWriteAction { ProjectRootManager.getInstance(projectModel.project).projectSdk = sdk }
assertNoOrderEntries(classesDir)
val module = projectModel.createModule("module")
ModuleRootModificationUtil.setSdkInherited(module)
val entriesForClasses = fileIndex.getOrderEntriesForFile(classesDir)
assertEmpty(entriesForClasses.filterNot { it is JdkOrderEntry })
assertFromModules(entriesForClasses, module)
}
@Test
fun `library root under module root`() {
val module = projectModel.createModule()
val moduleRoot = projectModel.baseProjectDir.newVirtualDirectory("module")
PsiTestUtil.addSourceContentToRoots(module, moduleRoot)
val libraryClasses = projectModel.baseProjectDir.newVirtualDirectory("module/lib/classes")
val librarySource = projectModel.baseProjectDir.newVirtualDirectory("module/lib/source")
val library = projectModel.addModuleLevelLibrary(module, "lib") {
it.addRoot(libraryClasses, OrderRootType.CLASSES)
it.addRoot(librarySource, OrderRootType.SOURCES)
}
assertModuleSourceEntryOnly(moduleRoot, module)
assertEquals(library, (assertIncludesModuleSourceEntry(librarySource, module).single() as LibraryOrderEntry).library)
assertEquals(library, (fileIndex.getOrderEntriesForFile(libraryClasses).single() as LibraryOrderEntry).library)
}
@Test
fun `module root under library root`() {
val libraryClasses = projectModel.baseProjectDir.newVirtualDirectory("lib/classes")
val librarySource = projectModel.baseProjectDir.newVirtualDirectory("lib/source")
val library = projectModel.addModuleLevelLibrary(projectModel.createModule("lib-module"), "lib") {
it.addRoot(libraryClasses, OrderRootType.CLASSES)
it.addRoot(librarySource, OrderRootType.SOURCES)
}
val module = projectModel.createModule()
val moduleRoot1 = projectModel.baseProjectDir.newVirtualDirectory("lib/classes/module")
val moduleRoot2 = projectModel.baseProjectDir.newVirtualDirectory("lib/source/module")
PsiTestUtil.addSourceContentToRoots(module, moduleRoot1)
PsiTestUtil.addSourceContentToRoots(module, moduleRoot2)
assertModuleSourceEntryOnly(moduleRoot1, module)
val orderEntries = assertIncludesModuleSourceEntry(moduleRoot2, module)
assertEquals(library, (orderEntries.single() as LibraryOrderEntry).library)
assertEquals(library, (fileIndex.getOrderEntriesForFile(librarySource).single() as LibraryOrderEntry).library)
assertEquals(library, (fileIndex.getOrderEntriesForFile(libraryClasses).single() as LibraryOrderEntry).library)
}
@Test
fun `same directory as module root and library root`() {
val libraryClasses = projectModel.baseProjectDir.newVirtualDirectory("classes")
val librarySource = projectModel.baseProjectDir.newVirtualDirectory("source")
val library = projectModel.addModuleLevelLibrary(projectModel.createModule("lib-module"), "lib") {
it.addRoot(libraryClasses, OrderRootType.CLASSES)
it.addRoot(librarySource, OrderRootType.SOURCES)
}
val module = projectModel.createModule()
PsiTestUtil.addSourceContentToRoots(module, libraryClasses)
PsiTestUtil.addSourceContentToRoots(module, librarySource)
assertEquals(library, (assertIncludesModuleSourceEntry(librarySource, module).single() as LibraryOrderEntry).library)
assertModuleSourceEntryOnly(libraryClasses, module)
}
private fun assertModuleSourceEntryOnly(file: VirtualFile, module: Module) {
val otherEntries = assertIncludesModuleSourceEntry(file, module)
assertEmpty(otherEntries)
}
private fun assertIncludesModuleSourceEntry(file: VirtualFile, module: Module): List<OrderEntry> {
val orderEntries = fileIndex.getOrderEntriesForFile(file)
assertEquals(module, assertOneElement(orderEntries.filterIsInstance<ModuleSourceOrderEntry>()).rootModel.module)
return orderEntries.filterNot { it is ModuleSourceOrderEntry }
}
private fun assertNoOrderEntries(file: VirtualFile) {
assertEmpty(fileIndex.getOrderEntriesForFile(file))
}
private fun assertFromModules(entries: List<OrderEntry>, vararg modules: Module) {
UsefulTestCase.assertSameElements(entries.map { it.ownerModule }, *modules)
}
}