diff --git a/java/java-tests/testSrc/com/intellij/roots/ModuleRootsExternalizationTest.java b/java/java-tests/testSrc/com/intellij/roots/ModuleRootsExternalizationTest.java index 63a2ea9db5ae..c9f9cc46bd7c 100644 --- a/java/java-tests/testSrc/com/intellij/roots/ModuleRootsExternalizationTest.java +++ b/java/java-tests/testSrc/com/intellij/roots/ModuleRootsExternalizationTest.java @@ -4,7 +4,6 @@ package com.intellij.roots; import com.intellij.application.options.ReplacePathToMacroMap; import com.intellij.configurationStore.StoreUtil; import com.intellij.ide.highlighter.ModuleFileType; -import com.intellij.jps.impl.JpsIdePluginManagerImpl; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ExpandMacroToPathMap; import com.intellij.openapi.module.Module; @@ -21,19 +20,16 @@ import com.intellij.testFramework.JavaModuleTestCase; import com.intellij.testFramework.PsiTestUtil; import org.jdom.Element; import org.jdom.JDOMException; -import org.jetbrains.jps.model.JpsElement; -import org.jetbrains.jps.model.java.JavaSourceRootProperties; -import org.jetbrains.jps.model.java.JavaSourceRootType; -import org.jetbrains.jps.model.java.JpsJavaExtensionService; import org.jetbrains.jps.model.module.JpsModuleSourceRootType; -import org.jetbrains.jps.model.module.UnknownSourceRootType; -import org.jetbrains.jps.model.module.UnknownSourceRootTypeProperties; import org.jetbrains.jps.model.serialization.JpsModelSerializerExtension; import org.jetbrains.jps.model.serialization.module.JpsModuleSourceRootPropertiesSerializer; import java.io.File; import java.io.IOException; -import java.util.*; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; import java.util.stream.Collectors; import static com.intellij.testFramework.assertions.Assertions.assertThat; @@ -151,70 +147,6 @@ public class ModuleRootsExternalizationTest extends JavaModuleTestCase { ); } - public void testChangeRootType() throws JDOMException, IOException { - File content = new File(getProject().getBasePath()); - File source = new File(content, "source"); - File testSource = new File(content, "testSource"); - FileUtil.createDirectory(source); - FileUtil.createDirectory(testSource); - final VirtualFile contentFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(content); - assertNotNull(contentFile); - refreshRecursively(contentFile); - final VirtualFile sourceFile = LocalFileSystem.getInstance().findFileByIoFile(source); - assertNotNull(sourceFile); - final VirtualFile testSourceFile = LocalFileSystem.getInstance().findFileByIoFile(testSource); - assertNotNull(testSourceFile); - - final File moduleFile = new File(content, "test.iml"); - final Module module = createModule(moduleFile); - - PsiTestUtil.addContentRoot(module, contentFile); - PsiTestUtil.addSourceRoot( - module, sourceFile, JavaSourceRootType.SOURCE, JpsJavaExtensionService.getInstance().createSourceRootProperties("org.jetbrains", true) - ); - PsiTestUtil.addSourceRoot( - module, testSourceFile, JavaSourceRootType.TEST_SOURCE, JpsJavaExtensionService.getInstance().createSourceRootProperties("org.jetbrains", false) - ); - - StoreUtil.saveDocumentsAndProjectSettings(myProject); - - String expectedXml = "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - ""; - - assertEquals(expectedXml, JDOMUtil.writeElement(JDOMUtil.load(moduleFile).getChild("component"))); - - JpsIdePluginManagerImpl.replaceWithUnknownRootType(myProject, findSerializers(Arrays.asList(JavaSourceRootType.SOURCE, JavaSourceRootType.TEST_SOURCE))); - for (ContentEntry entry : ModuleRootManager.getInstance(module).getContentEntries()) { - for (SourceFolder folder : entry.getSourceFolders()) { - assertTrue("Root type expected to be 'Unknown' for " + folder.getUrl(), folder.getRootType() instanceof UnknownSourceRootType); - JpsElement properties = folder.getJpsElement().getProperties(folder.getRootType()); - assertTrue(properties instanceof UnknownSourceRootTypeProperties); - assertTrue(((UnknownSourceRootTypeProperties)properties).getPropertiesData() instanceof Element); - } - } - StoreUtil.saveSettings(myProject, true); - assertEquals(expectedXml, JDOMUtil.writeElement(JDOMUtil.load(moduleFile).getChild("component"))); - - JpsIdePluginManagerImpl.updateCustomRootTypes(myProject, findSerializers(Arrays.asList(JavaSourceRootType.SOURCE, JavaSourceRootType.TEST_SOURCE))); - for (ContentEntry entry : ModuleRootManager.getInstance(module).getContentEntries()) { - for (SourceFolder folder : entry.getSourceFolders()) { - assertFalse("'Unknown' root type is not expected: " + folder.getUrl(), folder.getRootType() instanceof UnknownSourceRootType); - JpsElement properties = folder.getJpsElement().getProperties(folder.getRootType()); - assertTrue(properties instanceof JavaSourceRootProperties); - assertEquals("org.jetbrains", ((JavaSourceRootProperties)properties).getPackagePrefix()); - } - } - StoreUtil.saveSettings(myProject, true); - assertEquals(expectedXml, JDOMUtil.writeElement(JDOMUtil.load(moduleFile).getChild("component"))); - } - - private static Collection> findSerializers(Collection> rootTypes) { final Set> typesSet = rootTypes instanceof Set ? (Set>)rootTypes : new HashSet<>(rootTypes); Set> result = new HashSet<>(); diff --git a/platform/extensions/src/com/intellij/openapi/extensions/ExtensionPoint.java b/platform/extensions/src/com/intellij/openapi/extensions/ExtensionPoint.java index 770f249ea072..48dc95ab3a04 100644 --- a/platform/extensions/src/com/intellij/openapi/extensions/ExtensionPoint.java +++ b/platform/extensions/src/com/intellij/openapi/extensions/ExtensionPoint.java @@ -35,6 +35,9 @@ public interface ExtensionPoint<@NotNull T> { @TestOnly void registerExtension(@NotNull T extension, @NotNull Disposable parentDisposable); + @TestOnly + void registerExtension(@NotNull T extension, @NotNull PluginDescriptor pluginDescriptor, @NotNull Disposable parentDisposable); + /** * Use {@link com.intellij.testFramework.PlatformTestUtil#maskExtensions} * to register extension as first or to completely replace existing extensions in tests. diff --git a/platform/extensions/src/com/intellij/openapi/extensions/impl/ExtensionPointImpl.java b/platform/extensions/src/com/intellij/openapi/extensions/impl/ExtensionPointImpl.java index 76e663a7d9e8..b2f205f05b87 100644 --- a/platform/extensions/src/com/intellij/openapi/extensions/impl/ExtensionPointImpl.java +++ b/platform/extensions/src/com/intellij/openapi/extensions/impl/ExtensionPointImpl.java @@ -118,12 +118,18 @@ public abstract class ExtensionPointImpl<@NotNull T> implements ExtensionPoint implements ExtensionPoint implements ExtensionPoint adapter = new ObjectComponentAdapter<>(extension, getPluginDescriptor(), order); + ObjectComponentAdapter adapter = new ObjectComponentAdapter<>(extension, pluginDescriptor, order); addExtensionAdapter(adapter); notifyListeners(false, Collections.singletonList(adapter), myListeners); diff --git a/platform/lang-impl/testSources/com/intellij/openapi/roots/CustomSourceRootTypeTest.kt b/platform/lang-impl/testSources/com/intellij/openapi/roots/CustomSourceRootTypeTest.kt new file mode 100644 index 000000000000..4ab6a35e7ef6 --- /dev/null +++ b/platform/lang-impl/testSources/com/intellij/openapi/roots/CustomSourceRootTypeTest.kt @@ -0,0 +1,80 @@ +// 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.openapi.roots + +import com.intellij.openapi.module.Module +import com.intellij.openapi.util.Disposer +import com.intellij.testFramework.ApplicationRule +import com.intellij.testFramework.DisposableRule +import com.intellij.testFramework.rules.ProjectModelRule +import com.intellij.testFramework.rules.TempDirectory +import org.assertj.core.api.Assertions.assertThat +import org.jetbrains.jps.model.module.UnknownSourceRootType +import org.jetbrains.jps.model.serialization.JpsModelSerializerExtension +import org.junit.Before +import org.junit.ClassRule +import org.junit.Rule +import org.junit.Test + +class CustomSourceRootTypeTest { + companion object { + @JvmField + @ClassRule + val appRule = ApplicationRule() + } + + @Rule + @JvmField + val projectModel = ProjectModelRule() + + @Rule + @JvmField + val tempDirectory = TempDirectory() + + @Rule + @JvmField + val disposable = DisposableRule() + + lateinit var module: Module + + @Before + fun setUp() { + module = projectModel.createModule() + JpsModelSerializerExtension.getExtensions() + } + + @Test + fun `load unload custom source root`() { + val srcDir = projectModel.baseProjectDir.newVirtualDirectory("src") + runWithRegisteredExtension { + val model = createModifiableModel(module) + model.addContentEntry(srcDir).addSourceFolder(srcDir, TestCustomSourceRootType.INSTANCE, TestCustomSourceRootProperties("hello")) + val committed = commitModifiableRootModel(model) + val sourceFolder = committed.contentEntries.single().sourceFolders.single() + assertThat(sourceFolder.file).isEqualTo(srcDir) + assertThat(sourceFolder.rootType).isEqualTo(TestCustomSourceRootType.INSTANCE) + assertThat((sourceFolder.jpsElement.properties as TestCustomSourceRootProperties).testString).isEqualTo("hello") + } + + val sourceFolderWithUnknownType = ModuleRootManager.getInstance(module).contentEntries.single().sourceFolders.single() + assertThat(sourceFolderWithUnknownType.file).isEqualTo(srcDir) + assertThat(sourceFolderWithUnknownType.rootType).isInstanceOf(UnknownSourceRootType::class.java) + + TestCustomRootModelSerializerExtension.registerTestCustomSourceRootType(tempDirectory.newDirectory(), disposable.disposable) + val sourceFolder = ModuleRootManager.getInstance(module).contentEntries.single().sourceFolders.single() + assertThat(sourceFolder.file).isEqualTo(srcDir) + assertThat(sourceFolder.rootType).isEqualTo(TestCustomSourceRootType.INSTANCE) + assertThat((sourceFolder.jpsElement.properties as TestCustomSourceRootProperties).testString).isEqualTo("hello") + } + + private fun runWithRegisteredExtension(action: () -> Unit) { + val disposable = Disposer.newDisposable() + TestCustomRootModelSerializerExtension.registerTestCustomSourceRootType(tempDirectory.newDirectory(), disposable) + try { + action() + } + finally { + Disposer.dispose(disposable) + } + } + +} \ No newline at end of file diff --git a/platform/workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps/TestCustomSourceRoot.kt b/platform/lang-impl/testSources/com/intellij/openapi/roots/TestCustomSourceRoot.kt similarity index 62% rename from platform/workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps/TestCustomSourceRoot.kt rename to platform/lang-impl/testSources/com/intellij/openapi/roots/TestCustomSourceRoot.kt index d0647d1bc7aa..d93bb7dc43a6 100644 --- a/platform/workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps/TestCustomSourceRoot.kt +++ b/platform/lang-impl/testSources/com/intellij/openapi/roots/TestCustomSourceRoot.kt @@ -1,17 +1,45 @@ -// Copyright 2000-2019 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.workspace.jps +// 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. +@file:Suppress("unused") -import com.intellij.openapi.util.JDOMUtil +package com.intellij.openapi.roots + +import com.intellij.jps.impl.JpsPluginBean +import com.intellij.openapi.Disposable +import com.intellij.openapi.application.runWriteActionAndWait +import com.intellij.openapi.extensions.DefaultPluginDescriptor +import com.intellij.openapi.extensions.PluginId +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.io.FileUtil +import com.intellij.util.lang.UrlClassLoader import org.jdom.Element import org.jetbrains.jps.model.ex.JpsElementBase import org.jetbrains.jps.model.ex.JpsElementTypeBase import org.jetbrains.jps.model.module.JpsModuleSourceRootType import org.jetbrains.jps.model.serialization.JpsModelSerializerExtension import org.jetbrains.jps.model.serialization.module.JpsModuleSourceRootPropertiesSerializer +import java.io.File class TestCustomRootModelSerializerExtension : JpsModelSerializerExtension() { override fun getModuleSourceRootPropertiesSerializers(): List> = listOf(TestCustomSourceRootPropertiesSerializer(TestCustomSourceRootType.INSTANCE, TestCustomSourceRootType.TYPE_ID)) + + companion object { + fun registerTestCustomSourceRootType(tempPluginRoot: File, disposable: Disposable) { + val jpsPluginDisposable = Disposer.newDisposable() + Disposer.register(disposable, Disposable { + runWriteActionAndWait { + Disposer.dispose(jpsPluginDisposable) + } + }) + + FileUtil.writeToFile(File(tempPluginRoot, "META-INF/services/${JpsModelSerializerExtension::class.java.name}"), + TestCustomRootModelSerializerExtension::class.java.name) + val pluginClassLoader = UrlClassLoader.build().parent(TestCustomRootModelSerializerExtension::class.java.classLoader).urls(tempPluginRoot.toURI().toURL()).get() + val pluginDescriptor = DefaultPluginDescriptor(PluginId.getId("com.intellij.custom.source.root.test"), pluginClassLoader) + JpsPluginBean.EP_NAME.point.registerExtension(JpsPluginBean(), pluginDescriptor, jpsPluginDisposable) + } + + } } class TestCustomSourceRootType private constructor() : JpsElementTypeBase(), JpsModuleSourceRootType { @@ -48,9 +76,6 @@ class TestCustomSourceRootPropertiesSerializer( : JpsModuleSourceRootPropertiesSerializer(type, typeId) { override fun loadProperties(sourceRootTag: Element): TestCustomSourceRootProperties { - if (sourceRootTag.getAttributeValue("url") == null) error("url is missing in '${JDOMUtil.writeElement(sourceRootTag)}'") - if (sourceRootTag.getAttributeValue("type") != typeId) error("expected type '$typeId' in '${JDOMUtil.writeElement(sourceRootTag)}'") - val testString = sourceRootTag.getAttributeValue("testString") return TestCustomSourceRootProperties(testString) } diff --git a/platform/platform-impl/src/com/intellij/jps/impl/JpsIdePluginManagerImpl.java b/platform/platform-impl/src/com/intellij/jps/impl/JpsIdePluginManagerImpl.java index 9765ac75bf34..659fad9edb7a 100644 --- a/platform/platform-impl/src/com/intellij/jps/impl/JpsIdePluginManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/jps/impl/JpsIdePluginManagerImpl.java @@ -145,7 +145,7 @@ public final class JpsIdePluginManagerImpl extends JpsPluginManager { } } - public static void replaceWithUnknownRootType(Project project, Collection> unregisteredSerializers) { + private static void replaceWithUnknownRootType(Project project, Collection> unregisteredSerializers) { if (unregisteredSerializers.isEmpty()) { return; } @@ -174,7 +174,7 @@ public final class JpsIdePluginManagerImpl extends JpsPluginManager { } } - public static void updateCustomRootTypes(Project project, Collection> registeredSerializers) { + private static void updateCustomRootTypes(Project project, Collection> registeredSerializers) { if (registeredSerializers.isEmpty()) { return; } diff --git a/platform/testFramework/src/com/intellij/testFramework/rules/ProjectModelRule.kt b/platform/testFramework/src/com/intellij/testFramework/rules/ProjectModelRule.kt index aa959c9ee8cb..d6051deb9654 100644 --- a/platform/testFramework/src/com/intellij/testFramework/rules/ProjectModelRule.kt +++ b/platform/testFramework/src/com/intellij/testFramework/rules/ProjectModelRule.kt @@ -1,6 +1,7 @@ // 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.testFramework.rules +import com.intellij.openapi.application.invokeAndWaitIfNeeded import com.intellij.openapi.application.runReadAction import com.intellij.openapi.application.runWriteActionAndWait import com.intellij.openapi.module.EmptyModuleType @@ -30,7 +31,12 @@ import java.io.File class ProjectModelRule : TestRule { val baseProjectDir = TempDirectory() - private val projectDelegate = lazy { createHeavyProject(baseProjectDir.root.toPath()) } + private val projectDelegate = lazy { val project = createHeavyProject(baseProjectDir.root.toPath()) + invokeAndWaitIfNeeded { + ProjectManagerEx.getInstanceEx().openTestProject(project) + } + project + } private val disposableRule = DisposableRule() val project by projectDelegate private val closeProject = object : ExternalResource() { @@ -50,8 +56,9 @@ class ProjectModelRule : TestRule { fun createModule(name: String = "module"): Module { val imlFile = File(baseProjectDir.root, "$name/$name.iml") + val manager = moduleManager return runWriteActionAndWait { - moduleManager.newModule(imlFile.systemIndependentPath, EmptyModuleType.EMPTY_MODULE) + manager.newModule(imlFile.systemIndependentPath, EmptyModuleType.EMPTY_MODULE) } } diff --git a/platform/workspaceModel-ide-tests/intellij.platform.workspaceModel.ide.tests.iml b/platform/workspaceModel-ide-tests/intellij.platform.workspaceModel.ide.tests.iml index 317670601397..7671295d7c85 100644 --- a/platform/workspaceModel-ide-tests/intellij.platform.workspaceModel.ide.tests.iml +++ b/platform/workspaceModel-ide-tests/intellij.platform.workspaceModel.ide.tests.iml @@ -13,6 +13,7 @@ + \ No newline at end of file diff --git a/platform/workspaceModel-ide-tests/testResources/META-INF/services/org.jetbrains.jps.model.serialization.JpsModelSerializerExtension b/platform/workspaceModel-ide-tests/testResources/META-INF/services/org.jetbrains.jps.model.serialization.JpsModelSerializerExtension deleted file mode 100644 index b9ea079670d7..000000000000 --- a/platform/workspaceModel-ide-tests/testResources/META-INF/services/org.jetbrains.jps.model.serialization.JpsModelSerializerExtension +++ /dev/null @@ -1 +0,0 @@ -com.intellij.workspace.jps.TestCustomRootModelSerializerExtension \ No newline at end of file diff --git a/platform/workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps/LegacyBridgeModulesTest.kt b/platform/workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps/LegacyBridgeModulesTest.kt index 20c93e3eeffb..0ad4f4f69eaf 100644 --- a/platform/workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps/LegacyBridgeModulesTest.kt +++ b/platform/workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps/LegacyBridgeModulesTest.kt @@ -456,6 +456,8 @@ class LegacyBridgeModulesTest { @Test fun `test custom source root loading`() { + TestCustomRootModelSerializerExtension.registerTestCustomSourceRootType(temporaryDirectoryRule.newPath().toFile(), + disposableRule.disposable) val tempDir = temporaryDirectoryRule.newPath().toFile() val moduleImlFile = File(tempDir, "my.iml") Files.createDirectories(moduleImlFile.parentFile.toPath()) @@ -534,6 +536,7 @@ class LegacyBridgeModulesTest { @Test fun `test custom source root saving`() { val tempDir = temporaryDirectoryRule.newPath().toFile() + TestCustomRootModelSerializerExtension.registerTestCustomSourceRootType(temporaryDirectoryRule.newPath().toFile(), disposableRule.disposable) val moduleImlFile = File(tempDir, "my.iml") Files.createDirectories(moduleImlFile.parentFile.toPath()) @@ -581,17 +584,17 @@ class LegacyBridgeModulesTest { val tempDir = temporaryDirectoryRule.newPath().toFile() val url = VfsUtilCore.pathToUrl(FileUtil.toSystemIndependentName(tempDir.path)) val contentEntry = model.addContentEntry(url) - contentEntry.addSourceFolder("$url/$antLibraryFolder", TestCustomSourceRootType.INSTANCE) + contentEntry.addSourceFolder("$url/$antLibraryFolder", false) } StoreUtil.saveDocumentsAndProjectSettings(project) assertTrue(moduleFile.readText().contains(antLibraryFolder)) val entityStore = WorkspaceModel.getInstance(project).entityStore assertEquals(1, entityStore.current.entities(ContentRootEntity::class.java).count()) - assertEquals(1, entityStore.current.entities(CustomSourceRootPropertiesEntity::class.java).count()) + assertEquals(1, entityStore.current.entities(JavaSourceRootEntity::class.java).count()) ModuleManager.getInstance(project).disposeModule(module) assertEmpty(entityStore.current.entities(ContentRootEntity::class.java).toList()) - assertEmpty(entityStore.current.entities(CustomSourceRootPropertiesEntity::class.java).toList()) + assertEmpty(entityStore.current.entities(JavaSourceRootEntity::class.java).toList()) } @Test @@ -610,7 +613,7 @@ class LegacyBridgeModulesTest { val tempDir = temporaryDirectoryRule.newPath().toFile() val url = VfsUtilCore.pathToUrl(FileUtil.toSystemIndependentName(tempDir.path)) val contentEntry = model.addContentEntry(url) - contentEntry.addSourceFolder("$url/$antLibraryFolder", TestCustomSourceRootType.INSTANCE) + contentEntry.addSourceFolder("$url/$antLibraryFolder", false) } val entityStore = WorkspaceModel.getInstance(project).entityStore