/atlassian-plugins-core/src/test/java/com/atlassian/plugin/loaders/TestBundledPluginLoader.java
Java | 283 lines | 222 code | 41 blank | 20 comment | 4 complexity | fbc7f22b5c53eb3dba5b469da235f83c MD5 | raw file
- package com.atlassian.plugin.loaders;
- import com.atlassian.plugin.ModuleDescriptorFactory;
- import com.atlassian.plugin.Plugin;
- import com.atlassian.plugin.PluginArtifact;
- import com.atlassian.plugin.PluginException;
- import com.atlassian.plugin.PluginInternal;
- import com.atlassian.plugin.ReferenceMode;
- import com.atlassian.plugin.event.PluginEventManager;
- import com.atlassian.plugin.factories.PluginFactory;
- import com.atlassian.plugin.loaders.classloading.DeploymentUnit;
- import com.atlassian.plugin.loaders.classloading.Scanner;
- import com.atlassian.plugin.test.CapturedLogging;
- import com.atlassian.plugin.test.PluginJarBuilder;
- import com.atlassian.plugin.test.PluginTestUtils;
- import com.google.common.base.Function;
- import com.google.common.collect.Iterables;
- import com.google.common.collect.Lists;
- import org.apache.commons.io.FileUtils;
- import org.junit.After;
- import org.junit.Before;
- import org.junit.Rule;
- import org.junit.Test;
- import org.junit.rules.ExpectedException;
- import org.mockito.ArgumentCaptor;
- import java.io.File;
- import java.io.IOException;
- import java.net.URISyntaxException;
- import java.net.URL;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.List;
- import static com.atlassian.plugin.test.CapturedLogging.didLogWarn;
- import static org.hamcrest.Matchers.containsInAnyOrder;
- import static org.hamcrest.Matchers.instanceOf;
- import static org.hamcrest.Matchers.is;
- import static org.junit.Assert.assertEquals;
- import static org.junit.Assert.assertThat;
- import static org.junit.Assert.assertTrue;
- import static org.mockito.Matchers.isA;
- import static org.mockito.Matchers.same;
- import static org.mockito.Mockito.mock;
- import static org.mockito.Mockito.verify;
- import static org.mockito.Mockito.when;
- public class TestBundledPluginLoader {
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
- @Rule
- public CapturedLogging capturedLogging = new CapturedLogging(BundledPluginLoader.class);
- private File pluginDir;
- private static final String fooXml = "foo.xml";
- @Before
- public void createTemporaryDirectory() throws IOException, URISyntaxException {
- pluginDir = PluginTestUtils.createTempDirectory(TestBundledPluginLoader.class);
- }
- @After
- public void deleteTemporaryDirectory() throws Exception {
- FileUtils.deleteDirectory(pluginDir);
- pluginDir = null;
- }
- @Test
- public void loaderFromZipWithUrlConstructorContainsExpectedFiles() throws IOException {
- final File bundledZip = buildBundledZip();
- final BundledPluginLoader loader = buildBundledPluginLoader(bundledZip, true);
- assertLoaderContains(loader, fooXml);
- }
- @Test
- public void loaderFromZipWithNonUrlConstructorContainsExpectedFiles() throws IOException {
- final File bundledZip = buildBundledZip();
- final BundledPluginLoader loader = buildBundledPluginLoader(bundledZip, false);
- // Yes, this will be empty. For a zip, we must use the constructor that can specify a
- // directory. However, the code logs and returns and empty loader rather than throwing,
- // because this is behaviour that will make the jira change most compatible.
- assertLoaderContains(loader);
- }
- @Test
- public void loaderFromDirectoryWithUrlConstructorContainsExpectedFiles() throws IOException {
- validateLoaderFromDirectoryContainsExpectedFiles(true);
- }
- @Test
- public void loaderFromDirectoryWithNonUrlConstructorContainsExpectedFiles() throws IOException {
- validateLoaderFromDirectoryContainsExpectedFiles(false);
- }
- private void validateLoaderFromDirectoryContainsExpectedFiles(final boolean useUrlConstructor) throws IOException {
- final File dir = PluginTestUtils.createTempDirectory(TestBundledPluginLoader.class);
- FileUtils.writeStringToFile(new File(dir, "foo.txt"), "hello");
- final BundledPluginLoader loader = buildBundledPluginLoader(dir, useUrlConstructor);
- assertLoaderContains(loader, "foo.txt");
- }
- @Test
- public void loaderFromFileListWithUrlConstructorContainsExpectedFiles() throws IOException {
- validateLoaderFromFileListContainsExpectedFiles(true);
- }
- @Test
- public void loaderFromFileListWithNonUrlConstructorContainsExpectedFiles() throws IOException {
- validateLoaderFromFileListContainsExpectedFiles(false);
- }
- private void validateLoaderFromFileListContainsExpectedFiles(final boolean useUrlConstructor) throws IOException {
- FileUtils.writeStringToFile(new File(pluginDir, "foo.txt"), "hello");
- FileUtils.writeStringToFile(new File(pluginDir, "bar.txt"), "world");
- final File listFile = new File(pluginDir, "bundled-plugins" + BundledPluginLoader.getListSuffix());
- FileUtils.writeStringToFile(listFile, "foo.txt\nbar.txt");
- final BundledPluginLoader loader = buildBundledPluginLoader(listFile, useUrlConstructor);
- assertLoaderContains(loader, "foo.txt", "bar.txt");
- }
- @Test
- public void loaderFromUnsupportedFileWithUrlConstructorContainsExpectedFiles() throws IOException {
- validateLoaderFromUnsupportedFileContainsExpectedFiles(true);
- }
- @Test
- public void loaderFromUnsupportedFileWithNonUrlConstructorContainsExpectedFiles() throws IOException {
- validateLoaderFromUnsupportedFileContainsExpectedFiles(false);
- }
- private void validateLoaderFromUnsupportedFileContainsExpectedFiles(final boolean useUrlConstructor) throws IOException {
- final File unsupportedFile = new File(pluginDir, "notASuitableFile.unknown-suffix");
- FileUtils.writeStringToFile(unsupportedFile, "Some\nRandom\nContent\n");
- final BundledPluginLoader loader = buildBundledPluginLoader(unsupportedFile, useUrlConstructor);
- assertLoaderContains(loader);
- }
- @Test
- public void loaderScannerDoesNotRemoveUnderlyingFiles() throws IOException {
- final File bundledZip = buildBundledZip();
- // Since it's actually a zip, we must use the urlConstructor (hence true)
- final BundledPluginLoader loader = buildBundledPluginLoader(bundledZip, true);
- final Scanner scanner = loader.scanner;
- assertLoaderContains(loader, fooXml);
- final Collection<DeploymentUnit> units = scanner.getDeploymentUnits();
- final int countBefore = units.size();
- int found = 0;
- for (final DeploymentUnit unit : units) {
- final File file = unit.getPath();
- if (fooXml.equals(file.getName())) {
- found += 1;
- assertTrue(file.exists());
- scanner.remove(unit);
- // Yes, it should still exist
- assertTrue(file.exists());
- }
- }
- // We should have found a foo.txt
- assertEquals(1, found);
- // We should not have actually removed it from the scanner
- assertEquals(countBefore, scanner.getDeploymentUnits().size());
- }
- @Test
- public void loaderRemoveDoesUninstallPlugin() throws IOException {
- final File bundledZip = buildBundledZip();
- final PluginInternal mockPlugin = mock(PluginInternal.class);
- final PluginFactory pluginFactory = mock(PluginFactory.class);
- final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
- final String key = "plugin.key.foo";
- final ArgumentCaptor<PluginArtifact> pluginArtifact = ArgumentCaptor.forClass(PluginArtifact.class);
- when(mockPlugin.getKey()).thenReturn(key);
- when(mockPlugin.isDeleteable()).thenReturn(true);
- when(mockPlugin.isUninstallable()).thenReturn(true);
- when(pluginFactory.canCreate(isA(PluginArtifact.class))).thenReturn(key);
- when(pluginFactory.create(pluginArtifact.capture(), same(moduleDescriptorFactory))).thenReturn(mockPlugin);
- // Since it's actually a zip, we must use the urlConstructor (hence true)
- final BundledPluginLoader loader = buildBundledPluginLoader(bundledZip, true, pluginFactory);
- final Collection<Plugin> plugins = Lists.newArrayList(loader.loadAllPlugins(moduleDescriptorFactory));
- assertEquals(1, plugins.size());
- // Note that the plugin we get back is actually wrapped, so we don't test equality
- final Plugin bundledPlugin = Iterables.getOnlyElement(plugins);
- assertEquals(key, bundledPlugin.getKey());
- assertTrue(bundledPlugin.isDeleteable());
- verify(mockPlugin).isDeleteable();
- verify(mockPlugin).setBundledPlugin(true);
- // Remove it, and check Plugin was uninstalled
- loader.removePlugin(bundledPlugin);
- verify(mockPlugin).isUninstallable();
- verify(mockPlugin).uninstall();
- // Check file on disk didn't get removed
- assertTrue(pluginArtifact.getValue().toFile().exists());
- // There's no way to query a loader for its plugins, so we try to unload again and look for
- // the exception. This currently throws because the Plugin is gone. This is a little
- // brittle, because code evolution in the test subject means it might throw because the
- // Plugin state changed, but since the Plugin is a mock, it's state doesn't change without
- // us asking. So it's likely to be good. We could check the exception text, but i reckon
- // this is even more brittle.
- expectedException.expect(PluginException.class);
- loader.removePlugin(bundledPlugin);
- }
- @Test
- public void pluginArtifactsAllowReference() throws IOException {
- // Use a list file for the bundled plugins to make it easy to get a jar filename in there
- final File dir = PluginTestUtils.createTempDirectory(TestBundledPluginLoader.class);
- final File bundleList = new File(dir, "bundled-plugins" + BundledPluginLoader.getListSuffix());
- FileUtils.writeStringToFile(bundleList, "some.jar\n");
- final Plugin mockPlugin = mock(Plugin.class);
- final PluginFactory pluginFactory = mock(PluginFactory.class);
- final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
- final String key = "plugin.key.foo";
- final ArgumentCaptor<PluginArtifact> pluginArtifactCaptor = ArgumentCaptor.forClass(PluginArtifact.class);
- when(mockPlugin.getKey()).thenReturn(key);
- when(pluginFactory.canCreate(isA(PluginArtifact.class))).thenReturn(key);
- when(pluginFactory.create(pluginArtifactCaptor.capture(), same(moduleDescriptorFactory))).thenReturn(mockPlugin);
- // We could use either constructor, but non url is recommended for non zip source.
- final BundledPluginLoader loader = buildBundledPluginLoader(bundleList, false, pluginFactory);
- loader.loadAllPlugins(moduleDescriptorFactory);
- final PluginArtifact pluginArtifact = pluginArtifactCaptor.getValue();
- assertThat(pluginArtifact.getReferenceMode(), is(ReferenceMode.PERMIT_REFERENCE));
- // Legacy Interfaces
- assertThat(pluginArtifact, instanceOf(PluginArtifact.AllowsReference.class));
- assertTrue(((PluginArtifact.AllowsReference) pluginArtifact).allowsReference());
- }
- @Test
- public void postProcessWarnsAboutInvalidPlugin() throws IOException {
- final Plugin plugin = mock(Plugin.class);
- when(plugin.toString()).thenReturn("blargh");
- final BundledPluginLoader bundledPluginLoader = buildBundledPluginLoader(buildBundledZip(), true);
- bundledPluginLoader.postProcess(plugin);
- assertThat(capturedLogging, didLogWarn("blargh", Plugin.class.getCanonicalName()));
- }
- private File buildBundledZip() throws IOException {
- return new PluginJarBuilder("bundledPlugins")
- .addResource(fooXml, "<foo/>")
- .buildWithNoManifest();
- }
- private BundledPluginLoader buildBundledPluginLoader(
- final File bundledPlugins,
- final boolean useUrlConstructor,
- final PluginFactory... pluginFactories)
- throws IOException {
- final List<PluginFactory> pluginFactoryList = Arrays.asList(pluginFactories);
- final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
- if (useUrlConstructor) {
- final URL bundledPluginsUrl = bundledPlugins.toURI().toURL();
- return new BundledPluginLoader(bundledPluginsUrl, pluginDir, pluginFactoryList, pluginEventManager);
- } else {
- return new BundledPluginLoader(bundledPlugins, pluginFactoryList, pluginEventManager);
- }
- }
- private void assertLoaderContains(final BundledPluginLoader loader, final String... expectedEntries) {
- final Collection<DeploymentUnit> scanned = loader.scanner.scan();
- final Iterable<String> actualEntries = Iterables.transform(scanned, new Function<DeploymentUnit, String>() {
- @Override
- public String apply(final DeploymentUnit unit) {
- return unit.getPath().getName();
- }
- });
- assertThat(actualEntries, containsInAnyOrder(expectedEntries));
- }
- }