PageRenderTime 42ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/atlassian-plugins-core/src/test/java/com/atlassian/plugin/loaders/TestBundledPluginLoader.java

https://bitbucket.org/purewind/atlassian-plugins
Java | 283 lines | 222 code | 41 blank | 20 comment | 4 complexity | fbc7f22b5c53eb3dba5b469da235f83c MD5 | raw file
  1. package com.atlassian.plugin.loaders;
  2. import com.atlassian.plugin.ModuleDescriptorFactory;
  3. import com.atlassian.plugin.Plugin;
  4. import com.atlassian.plugin.PluginArtifact;
  5. import com.atlassian.plugin.PluginException;
  6. import com.atlassian.plugin.PluginInternal;
  7. import com.atlassian.plugin.ReferenceMode;
  8. import com.atlassian.plugin.event.PluginEventManager;
  9. import com.atlassian.plugin.factories.PluginFactory;
  10. import com.atlassian.plugin.loaders.classloading.DeploymentUnit;
  11. import com.atlassian.plugin.loaders.classloading.Scanner;
  12. import com.atlassian.plugin.test.CapturedLogging;
  13. import com.atlassian.plugin.test.PluginJarBuilder;
  14. import com.atlassian.plugin.test.PluginTestUtils;
  15. import com.google.common.base.Function;
  16. import com.google.common.collect.Iterables;
  17. import com.google.common.collect.Lists;
  18. import org.apache.commons.io.FileUtils;
  19. import org.junit.After;
  20. import org.junit.Before;
  21. import org.junit.Rule;
  22. import org.junit.Test;
  23. import org.junit.rules.ExpectedException;
  24. import org.mockito.ArgumentCaptor;
  25. import java.io.File;
  26. import java.io.IOException;
  27. import java.net.URISyntaxException;
  28. import java.net.URL;
  29. import java.util.Arrays;
  30. import java.util.Collection;
  31. import java.util.List;
  32. import static com.atlassian.plugin.test.CapturedLogging.didLogWarn;
  33. import static org.hamcrest.Matchers.containsInAnyOrder;
  34. import static org.hamcrest.Matchers.instanceOf;
  35. import static org.hamcrest.Matchers.is;
  36. import static org.junit.Assert.assertEquals;
  37. import static org.junit.Assert.assertThat;
  38. import static org.junit.Assert.assertTrue;
  39. import static org.mockito.Matchers.isA;
  40. import static org.mockito.Matchers.same;
  41. import static org.mockito.Mockito.mock;
  42. import static org.mockito.Mockito.verify;
  43. import static org.mockito.Mockito.when;
  44. public class TestBundledPluginLoader {
  45. @Rule
  46. public ExpectedException expectedException = ExpectedException.none();
  47. @Rule
  48. public CapturedLogging capturedLogging = new CapturedLogging(BundledPluginLoader.class);
  49. private File pluginDir;
  50. private static final String fooXml = "foo.xml";
  51. @Before
  52. public void createTemporaryDirectory() throws IOException, URISyntaxException {
  53. pluginDir = PluginTestUtils.createTempDirectory(TestBundledPluginLoader.class);
  54. }
  55. @After
  56. public void deleteTemporaryDirectory() throws Exception {
  57. FileUtils.deleteDirectory(pluginDir);
  58. pluginDir = null;
  59. }
  60. @Test
  61. public void loaderFromZipWithUrlConstructorContainsExpectedFiles() throws IOException {
  62. final File bundledZip = buildBundledZip();
  63. final BundledPluginLoader loader = buildBundledPluginLoader(bundledZip, true);
  64. assertLoaderContains(loader, fooXml);
  65. }
  66. @Test
  67. public void loaderFromZipWithNonUrlConstructorContainsExpectedFiles() throws IOException {
  68. final File bundledZip = buildBundledZip();
  69. final BundledPluginLoader loader = buildBundledPluginLoader(bundledZip, false);
  70. // Yes, this will be empty. For a zip, we must use the constructor that can specify a
  71. // directory. However, the code logs and returns and empty loader rather than throwing,
  72. // because this is behaviour that will make the jira change most compatible.
  73. assertLoaderContains(loader);
  74. }
  75. @Test
  76. public void loaderFromDirectoryWithUrlConstructorContainsExpectedFiles() throws IOException {
  77. validateLoaderFromDirectoryContainsExpectedFiles(true);
  78. }
  79. @Test
  80. public void loaderFromDirectoryWithNonUrlConstructorContainsExpectedFiles() throws IOException {
  81. validateLoaderFromDirectoryContainsExpectedFiles(false);
  82. }
  83. private void validateLoaderFromDirectoryContainsExpectedFiles(final boolean useUrlConstructor) throws IOException {
  84. final File dir = PluginTestUtils.createTempDirectory(TestBundledPluginLoader.class);
  85. FileUtils.writeStringToFile(new File(dir, "foo.txt"), "hello");
  86. final BundledPluginLoader loader = buildBundledPluginLoader(dir, useUrlConstructor);
  87. assertLoaderContains(loader, "foo.txt");
  88. }
  89. @Test
  90. public void loaderFromFileListWithUrlConstructorContainsExpectedFiles() throws IOException {
  91. validateLoaderFromFileListContainsExpectedFiles(true);
  92. }
  93. @Test
  94. public void loaderFromFileListWithNonUrlConstructorContainsExpectedFiles() throws IOException {
  95. validateLoaderFromFileListContainsExpectedFiles(false);
  96. }
  97. private void validateLoaderFromFileListContainsExpectedFiles(final boolean useUrlConstructor) throws IOException {
  98. FileUtils.writeStringToFile(new File(pluginDir, "foo.txt"), "hello");
  99. FileUtils.writeStringToFile(new File(pluginDir, "bar.txt"), "world");
  100. final File listFile = new File(pluginDir, "bundled-plugins" + BundledPluginLoader.getListSuffix());
  101. FileUtils.writeStringToFile(listFile, "foo.txt\nbar.txt");
  102. final BundledPluginLoader loader = buildBundledPluginLoader(listFile, useUrlConstructor);
  103. assertLoaderContains(loader, "foo.txt", "bar.txt");
  104. }
  105. @Test
  106. public void loaderFromUnsupportedFileWithUrlConstructorContainsExpectedFiles() throws IOException {
  107. validateLoaderFromUnsupportedFileContainsExpectedFiles(true);
  108. }
  109. @Test
  110. public void loaderFromUnsupportedFileWithNonUrlConstructorContainsExpectedFiles() throws IOException {
  111. validateLoaderFromUnsupportedFileContainsExpectedFiles(false);
  112. }
  113. private void validateLoaderFromUnsupportedFileContainsExpectedFiles(final boolean useUrlConstructor) throws IOException {
  114. final File unsupportedFile = new File(pluginDir, "notASuitableFile.unknown-suffix");
  115. FileUtils.writeStringToFile(unsupportedFile, "Some\nRandom\nContent\n");
  116. final BundledPluginLoader loader = buildBundledPluginLoader(unsupportedFile, useUrlConstructor);
  117. assertLoaderContains(loader);
  118. }
  119. @Test
  120. public void loaderScannerDoesNotRemoveUnderlyingFiles() throws IOException {
  121. final File bundledZip = buildBundledZip();
  122. // Since it's actually a zip, we must use the urlConstructor (hence true)
  123. final BundledPluginLoader loader = buildBundledPluginLoader(bundledZip, true);
  124. final Scanner scanner = loader.scanner;
  125. assertLoaderContains(loader, fooXml);
  126. final Collection<DeploymentUnit> units = scanner.getDeploymentUnits();
  127. final int countBefore = units.size();
  128. int found = 0;
  129. for (final DeploymentUnit unit : units) {
  130. final File file = unit.getPath();
  131. if (fooXml.equals(file.getName())) {
  132. found += 1;
  133. assertTrue(file.exists());
  134. scanner.remove(unit);
  135. // Yes, it should still exist
  136. assertTrue(file.exists());
  137. }
  138. }
  139. // We should have found a foo.txt
  140. assertEquals(1, found);
  141. // We should not have actually removed it from the scanner
  142. assertEquals(countBefore, scanner.getDeploymentUnits().size());
  143. }
  144. @Test
  145. public void loaderRemoveDoesUninstallPlugin() throws IOException {
  146. final File bundledZip = buildBundledZip();
  147. final PluginInternal mockPlugin = mock(PluginInternal.class);
  148. final PluginFactory pluginFactory = mock(PluginFactory.class);
  149. final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
  150. final String key = "plugin.key.foo";
  151. final ArgumentCaptor<PluginArtifact> pluginArtifact = ArgumentCaptor.forClass(PluginArtifact.class);
  152. when(mockPlugin.getKey()).thenReturn(key);
  153. when(mockPlugin.isDeleteable()).thenReturn(true);
  154. when(mockPlugin.isUninstallable()).thenReturn(true);
  155. when(pluginFactory.canCreate(isA(PluginArtifact.class))).thenReturn(key);
  156. when(pluginFactory.create(pluginArtifact.capture(), same(moduleDescriptorFactory))).thenReturn(mockPlugin);
  157. // Since it's actually a zip, we must use the urlConstructor (hence true)
  158. final BundledPluginLoader loader = buildBundledPluginLoader(bundledZip, true, pluginFactory);
  159. final Collection<Plugin> plugins = Lists.newArrayList(loader.loadAllPlugins(moduleDescriptorFactory));
  160. assertEquals(1, plugins.size());
  161. // Note that the plugin we get back is actually wrapped, so we don't test equality
  162. final Plugin bundledPlugin = Iterables.getOnlyElement(plugins);
  163. assertEquals(key, bundledPlugin.getKey());
  164. assertTrue(bundledPlugin.isDeleteable());
  165. verify(mockPlugin).isDeleteable();
  166. verify(mockPlugin).setBundledPlugin(true);
  167. // Remove it, and check Plugin was uninstalled
  168. loader.removePlugin(bundledPlugin);
  169. verify(mockPlugin).isUninstallable();
  170. verify(mockPlugin).uninstall();
  171. // Check file on disk didn't get removed
  172. assertTrue(pluginArtifact.getValue().toFile().exists());
  173. // There's no way to query a loader for its plugins, so we try to unload again and look for
  174. // the exception. This currently throws because the Plugin is gone. This is a little
  175. // brittle, because code evolution in the test subject means it might throw because the
  176. // Plugin state changed, but since the Plugin is a mock, it's state doesn't change without
  177. // us asking. So it's likely to be good. We could check the exception text, but i reckon
  178. // this is even more brittle.
  179. expectedException.expect(PluginException.class);
  180. loader.removePlugin(bundledPlugin);
  181. }
  182. @Test
  183. public void pluginArtifactsAllowReference() throws IOException {
  184. // Use a list file for the bundled plugins to make it easy to get a jar filename in there
  185. final File dir = PluginTestUtils.createTempDirectory(TestBundledPluginLoader.class);
  186. final File bundleList = new File(dir, "bundled-plugins" + BundledPluginLoader.getListSuffix());
  187. FileUtils.writeStringToFile(bundleList, "some.jar\n");
  188. final Plugin mockPlugin = mock(Plugin.class);
  189. final PluginFactory pluginFactory = mock(PluginFactory.class);
  190. final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
  191. final String key = "plugin.key.foo";
  192. final ArgumentCaptor<PluginArtifact> pluginArtifactCaptor = ArgumentCaptor.forClass(PluginArtifact.class);
  193. when(mockPlugin.getKey()).thenReturn(key);
  194. when(pluginFactory.canCreate(isA(PluginArtifact.class))).thenReturn(key);
  195. when(pluginFactory.create(pluginArtifactCaptor.capture(), same(moduleDescriptorFactory))).thenReturn(mockPlugin);
  196. // We could use either constructor, but non url is recommended for non zip source.
  197. final BundledPluginLoader loader = buildBundledPluginLoader(bundleList, false, pluginFactory);
  198. loader.loadAllPlugins(moduleDescriptorFactory);
  199. final PluginArtifact pluginArtifact = pluginArtifactCaptor.getValue();
  200. assertThat(pluginArtifact.getReferenceMode(), is(ReferenceMode.PERMIT_REFERENCE));
  201. // Legacy Interfaces
  202. assertThat(pluginArtifact, instanceOf(PluginArtifact.AllowsReference.class));
  203. assertTrue(((PluginArtifact.AllowsReference) pluginArtifact).allowsReference());
  204. }
  205. @Test
  206. public void postProcessWarnsAboutInvalidPlugin() throws IOException {
  207. final Plugin plugin = mock(Plugin.class);
  208. when(plugin.toString()).thenReturn("blargh");
  209. final BundledPluginLoader bundledPluginLoader = buildBundledPluginLoader(buildBundledZip(), true);
  210. bundledPluginLoader.postProcess(plugin);
  211. assertThat(capturedLogging, didLogWarn("blargh", Plugin.class.getCanonicalName()));
  212. }
  213. private File buildBundledZip() throws IOException {
  214. return new PluginJarBuilder("bundledPlugins")
  215. .addResource(fooXml, "<foo/>")
  216. .buildWithNoManifest();
  217. }
  218. private BundledPluginLoader buildBundledPluginLoader(
  219. final File bundledPlugins,
  220. final boolean useUrlConstructor,
  221. final PluginFactory... pluginFactories)
  222. throws IOException {
  223. final List<PluginFactory> pluginFactoryList = Arrays.asList(pluginFactories);
  224. final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
  225. if (useUrlConstructor) {
  226. final URL bundledPluginsUrl = bundledPlugins.toURI().toURL();
  227. return new BundledPluginLoader(bundledPluginsUrl, pluginDir, pluginFactoryList, pluginEventManager);
  228. } else {
  229. return new BundledPluginLoader(bundledPlugins, pluginFactoryList, pluginEventManager);
  230. }
  231. }
  232. private void assertLoaderContains(final BundledPluginLoader loader, final String... expectedEntries) {
  233. final Collection<DeploymentUnit> scanned = loader.scanner.scan();
  234. final Iterable<String> actualEntries = Iterables.transform(scanned, new Function<DeploymentUnit, String>() {
  235. @Override
  236. public String apply(final DeploymentUnit unit) {
  237. return unit.getPath().getName();
  238. }
  239. });
  240. assertThat(actualEntries, containsInAnyOrder(expectedEntries));
  241. }
  242. }