From af4666ea5bb86b676582689bc1bf95975eff3491 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Tue, 12 Nov 2019 08:49:28 +0100 Subject: [PATCH] add test that class loader is properly configured if one plugin depends on another one GitOrigin-RevId: ef32f6f4272d0c3683eed3cc4c2ae398fb3b43cd --- .../ide/plugins/PluginManagerCore.java | 19 +++-- .../ide/plugins/cl/PluginClassLoader.java | 14 +++- .../ide/plugins/PluginDescriptorTest.kt | 73 +++++++++++++++++-- .../ide/plugins/PluginManagerTest.java | 2 +- 4 files changed, 91 insertions(+), 17 deletions(-) diff --git a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java index e66c0602af68..e3ae87d44972 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java @@ -1133,8 +1133,10 @@ public final class PluginManagerCore { public static List testLoadDescriptorsFromDir(@NotNull Path dir) throws ExecutionException, InterruptedException { PluginLoadingResult result = new PluginLoadingResult(Collections.emptyMap()); - loadDescriptorsFromDir(dir, result, true, new DescriptorListLoadingContext(0, Collections.emptySet(), result)); + loadDescriptorsFromDir(dir, result, true, DescriptorListLoadingContext.createSingleDescriptorContext(Collections.emptySet())); result.finishLoading(); + + initializePlugins(result, UrlClassLoader.build().get(), false, true); return result.plugins; } @@ -1620,7 +1622,7 @@ public final class PluginManagerCore { } @ApiStatus.Internal - static void initializePlugins(@NotNull PluginLoadingResult loadResult, @NotNull ClassLoader coreLoader, boolean checkEssentialPlugins) { + static void initializePlugins(@NotNull PluginLoadingResult loadResult, @NotNull ClassLoader coreLoader, boolean checkEssentialPlugins, boolean usePluginClassLoader) { List errors = new ArrayList<>(loadResult.errors); Map idMap = loadResult.idMap; @@ -1631,7 +1633,7 @@ public final class PluginManagerCore { } IdeaPluginDescriptorImpl coreDescriptor = idMap.get(CORE_ID); - if (coreDescriptor == null) { + if (checkEssentialPlugins && coreDescriptor == null) { throw new EssentialPluginMissingException(Collections.singletonList(CORE_ID + " (platform prefix: " + System.getProperty(PlatformUtils.PLATFORM_PREFIX_KEY) + ")")); } @@ -1674,7 +1676,7 @@ public final class PluginManagerCore { } mergeOptionalConfigs(enabledPlugins, idMap); - configureClassLoaders(coreLoader, graph, coreDescriptor, enabledPlugins); + configureClassLoaders(coreLoader, graph, coreDescriptor, enabledPlugins, usePluginClassLoader); if (checkEssentialPlugins) { checkEssentialPluginsAreAvailable(idMap); @@ -1685,8 +1687,9 @@ public final class PluginManagerCore { private static void configureClassLoaders(@NotNull ClassLoader coreLoader, @NotNull CachingSemiGraph graph, - @NotNull IdeaPluginDescriptor coreDescriptor, - @NotNull List enabledPlugins) { + @Nullable IdeaPluginDescriptor coreDescriptor, + @NotNull List enabledPlugins, + boolean usePluginClassLoader) { ArrayList loaders = new ArrayList<>(); ClassLoader[] emptyClassLoaderArray = new ClassLoader[0]; for (IdeaPluginDescriptorImpl rootDescriptor : enabledPlugins) { @@ -1695,7 +1698,7 @@ public final class PluginManagerCore { continue; } - if (isUnitTestMode && !ourUnitTestWithBundledPlugins) { + if (!usePluginClassLoader) { rootDescriptor.setLoader(null); continue; } @@ -1876,7 +1879,7 @@ public final class PluginManagerCore { try { Activity loadPluginsActivity = StartUpMeasurer.startActivity("plugin initialization"); result = loadDescriptors(isRunningFromSources()); - initializePlugins(result, coreLoader, !isUnitTestMode); + initializePlugins(result, coreLoader, !isUnitTestMode, !isUnitTestMode || ourUnitTestWithBundledPlugins); ourPlugins = result.getSortedPlugins(); if (!result.incompletePlugins.isEmpty()) { diff --git a/platform/core-impl/src/com/intellij/ide/plugins/cl/PluginClassLoader.java b/platform/core-impl/src/com/intellij/ide/plugins/cl/PluginClassLoader.java index 4301e2f69b69..2d3ba42ce94a 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/cl/PluginClassLoader.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/cl/PluginClassLoader.java @@ -13,8 +13,10 @@ import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.lang.UrlClassLoader; import gnu.trove.THashSet; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; import java.awt.*; import java.io.File; @@ -33,7 +35,9 @@ import java.util.concurrent.atomic.AtomicLong; */ public final class PluginClassLoader extends UrlClassLoader { static { - if (registerAsParallelCapable()) markParallelCapable(PluginClassLoader.class); + if (registerAsParallelCapable()) { + markParallelCapable(PluginClassLoader.class); + } } private final ClassLoader[] myParents; @@ -422,4 +426,12 @@ public final class PluginClassLoader extends UrlClassLoader { return myEnumerations[myIndex].nextElement(); } } + + @NotNull + @TestOnly + @ApiStatus.Internal + public List _getParents() { + //noinspection SSBasedInspection + return Collections.unmodifiableList(Arrays.asList(myParents)); + } } \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/ide/plugins/PluginDescriptorTest.kt b/platform/platform-tests/testSrc/com/intellij/ide/plugins/PluginDescriptorTest.kt index d040bf53f3fb..f1a20d7f1b92 100644 --- a/platform/platform-tests/testSrc/com/intellij/ide/plugins/PluginDescriptorTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/ide/plugins/PluginDescriptorTest.kt @@ -3,6 +3,7 @@ package com.intellij.ide.plugins +import com.intellij.ide.plugins.cl.PluginClassLoader import com.intellij.openapi.extensions.PluginId import com.intellij.openapi.util.SystemInfo import com.intellij.testFramework.PlatformTestUtil @@ -15,11 +16,11 @@ import com.intellij.util.io.write import com.intellij.util.lang.UrlClassLoader import junit.framework.TestCase import org.assertj.core.api.Assertions.assertThatThrownBy +import org.intellij.lang.annotations.Language import org.junit.Assume.assumeTrue import org.junit.Rule import org.junit.Test import java.io.File -import java.io.IOException import java.net.URL import java.net.URLClassLoader import java.nio.file.Files @@ -125,8 +126,7 @@ class PluginDescriptorTest { urls.add(path.toUri().toURL()) } } - val descriptors = PluginManagerCore.testLoadDescriptorsFromClassPath( - URLClassLoader(urls.toTypedArray(), null)) + val descriptors = PluginManagerCore.testLoadDescriptorsFromClassPath(URLClassLoader(urls.toTypedArray(), null)) // core and com.intellij.workspace assertThat(descriptors).hasSize(1) } @@ -135,8 +135,7 @@ class PluginDescriptorTest { @Throws(Exception::class) fun testProduction2() { assumeTrue(SystemInfo.isMac && !UsefulTestCase.IS_UNDER_TEAMCITY) - val descriptors = PluginManagerCore.testLoadDescriptorsFromDir( - Paths.get("/Volumes/data/plugins")) + val descriptors = PluginManagerCore.testLoadDescriptorsFromDir(Paths.get("/Volumes/data/plugins")) assertThat(descriptors).isNotEmpty() } @@ -158,7 +157,6 @@ class PluginDescriptorTest { } @Test - @Throws(IOException::class) fun releaseDate() { val pluginFile = inMemoryFs.fs.getPath("plugin/META-INF/plugin.xml") pluginFile.write(""" @@ -173,6 +171,63 @@ class PluginDescriptorTest { assertThat(SimpleDateFormat("yyyyMMdd", Locale.US).format(descriptor.releaseDate)).isEqualTo("20190811") } + @Test + fun classLoader() { + val pluginDir = inMemoryFs.fs.getPath("/plugins") + writeDescriptor("foo", pluginDir, """ + + foo + bar + JetBrains + """) + writeDescriptor("bar", pluginDir, """ + + bar + JetBrains + """) + + checkClassLoader(pluginDir) + } + + @Test + fun `classLoader - optional dependency`() { + val pluginDir = inMemoryFs.fs.getPath("/plugins") + writeDescriptor("foo", pluginDir, """ + + foo + bar + JetBrains + """) + + pluginDir.resolve("foo/META-INF/stream-debugger.xml").write(""" + + + + + """.trimIndent()) + + writeDescriptor("bar", pluginDir, """ + + bar + JetBrains + """) + + checkClassLoader(pluginDir) + } + + private fun checkClassLoader(pluginDir: Path) { + val list = PluginManagerCore.testLoadDescriptorsFromDir(pluginDir) + assertThat(list).hasSize(2) + + val bar = list[0] + assertThat(bar.pluginId.idString).isEqualTo("bar") + + val foo = list[1] + assertThat(foo.pluginId.idString).isEqualTo("foo") + val fooClassLoader = foo.pluginClassLoader as PluginClassLoader + assertThat(fooClassLoader._getParents()).containsExactly(bar.pluginClassLoader) + } + @Test fun componentConfig() { val pluginFile = inMemoryFs.fs.getPath("/plugin/META-INF/plugin.xml") @@ -243,4 +298,8 @@ class PluginDescriptorTest { TestCase.assertEquals(impl1.hashCode(), impl2.hashCode()) TestCase.assertNotSame(impl1.name, impl2.name) } -} \ No newline at end of file +} + +private fun writeDescriptor(id: String, pluginDir: Path, @Language("xml") data: String) { + pluginDir.resolve("$id/META-INF/plugin.xml").write(data.trimIndent()) +} diff --git a/platform/platform-tests/testSrc/com/intellij/ide/plugins/PluginManagerTest.java b/platform/platform-tests/testSrc/com/intellij/ide/plugins/PluginManagerTest.java index 3d42ced35a05..6de7c56d29bb 100644 --- a/platform/platform-tests/testSrc/com/intellij/ide/plugins/PluginManagerTest.java +++ b/platform/platform-tests/testSrc/com/intellij/ide/plugins/PluginManagerTest.java @@ -122,7 +122,7 @@ public class PluginManagerTest { private static void doPluginSortTest(@NotNull String testDataName, boolean isBundled) throws IOException, JDOMException { PluginManagerCore.ourPluginError = null; PluginLoadingResult loadPluginResult = loadDescriptors(testDataName + ".xml", isBundled); - PluginManagerCore.initializePlugins(loadPluginResult, PluginManagerTest.class.getClassLoader(), /* checkEssentialPlugins = */ false); + PluginManagerCore.initializePlugins(loadPluginResult, PluginManagerTest.class.getClassLoader(), /* checkEssentialPlugins = */ false, false); String actual = StringUtil.join(loadPluginResult.getSortedPlugins(), o -> (o.isEnabled() ? "+ " : " ") + o.getPluginId().getIdString(), "\n") + "\n\n" + StringUtil.notNullize(PluginManagerCore.ourPluginError).replace("

", "\n"); PluginManagerCore.ourPluginError = null;