From 82ee0cc6937372ea0c8f578c152251138f86e6f7 Mon Sep 17 00:00:00 2001 From: Nikolay Chashnikov Date: Tue, 26 May 2020 12:14:15 +0300 Subject: [PATCH] [project model tests] rewrite test on loading/unloading custom source root types The old test ModuleRootsExternalizationTest::testChangeRootType called method which change root types directly, and making this test to work in the new project model won't be easy, because it stores real data in internal entities and produce implementation of the project model interfaces lazily. The new CustomSourceRootTypeTest works on higher level, it checks loading and unloading source root types by registering and unregistering corresponding extension. GitOrigin-RevId: 2071d30372aac1a9d1c83955afa1a6e2701f7a1b --- .../roots/ModuleRootsExternalizationTest.java | 76 +----------------- .../openapi/extensions/ExtensionPoint.java | 3 + .../extensions/impl/ExtensionPointImpl.java | 17 ++-- .../openapi/roots/CustomSourceRootTypeTest.kt | 80 +++++++++++++++++++ .../openapi/roots}/TestCustomSourceRoot.kt | 37 +++++++-- .../jps/impl/JpsIdePluginManagerImpl.java | 4 +- .../testFramework/rules/ProjectModelRule.kt | 11 ++- ...llij.platform.workspaceModel.ide.tests.iml | 1 + ....serialization.JpsModelSerializerExtension | 1 - .../workspace/jps/LegacyBridgeModulesTest.kt | 11 ++- 10 files changed, 149 insertions(+), 92 deletions(-) create mode 100644 platform/lang-impl/testSources/com/intellij/openapi/roots/CustomSourceRootTypeTest.kt rename platform/{workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps => lang-impl/testSources/com/intellij/openapi/roots}/TestCustomSourceRoot.kt (62%) delete mode 100644 platform/workspaceModel-ide-tests/testResources/META-INF/services/org.jetbrains.jps.model.serialization.JpsModelSerializerExtension 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