PageRenderTime 24ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/atlassian-plugins-osgi/src/test/java/com/atlassian/plugin/osgi/factory/TestOsgiBundlePlugin.java

https://bitbucket.org/purewind/atlassian-plugins
Java | 335 lines | 270 code | 40 blank | 25 comment | 1 complexity | 2e7cb7d55509432d6977b4ba68cfe583 MD5 | raw file
  1. package com.atlassian.plugin.osgi.factory;
  2. import com.atlassian.plugin.IllegalPluginStateException;
  3. import com.atlassian.plugin.JarPluginArtifact;
  4. import com.atlassian.plugin.Plugin;
  5. import com.atlassian.plugin.PluginArtifact;
  6. import com.atlassian.plugin.osgi.container.OsgiContainerException;
  7. import com.atlassian.plugin.osgi.container.OsgiContainerManager;
  8. import com.atlassian.plugin.test.PluginJarBuilder;
  9. import com.google.common.collect.ImmutableMap;
  10. import org.junit.Before;
  11. import org.junit.Rule;
  12. import org.junit.Test;
  13. import org.junit.rules.ExpectedException;
  14. import org.junit.runner.RunWith;
  15. import org.mockito.Mock;
  16. import org.mockito.runners.MockitoJUnitRunner;
  17. import org.osgi.framework.Bundle;
  18. import org.osgi.framework.BundleContext;
  19. import org.osgi.framework.Constants;
  20. import org.osgi.util.tracker.ServiceTracker;
  21. import java.io.File;
  22. import java.io.IOException;
  23. import java.io.InputStream;
  24. import java.net.URL;
  25. import java.net.URLConnection;
  26. import java.net.URLStreamHandler;
  27. import java.util.Hashtable;
  28. import java.util.concurrent.TimeUnit;
  29. import static com.atlassian.plugin.ReferenceMode.FORBID_REFERENCE;
  30. import static com.atlassian.plugin.ReferenceMode.PERMIT_REFERENCE;
  31. import static org.hamcrest.MatcherAssert.assertThat;
  32. import static org.hamcrest.Matchers.greaterThanOrEqualTo;
  33. import static org.hamcrest.Matchers.is;
  34. import static org.hamcrest.Matchers.lessThanOrEqualTo;
  35. import static org.hamcrest.Matchers.nullValue;
  36. import static org.hamcrest.Matchers.sameInstance;
  37. import static org.mockito.Mockito.any;
  38. import static org.mockito.Mockito.mock;
  39. import static org.mockito.Mockito.verify;
  40. import static org.mockito.Mockito.when;
  41. // An IntelliJ pattern to run both tests in this class
  42. // com.atlassian.plugin.osgi.factory.TestOsgiBundlePlugin$Modern||com.atlassian.plugin.osgi.factory.TestOsgiBundlePlugin$Legacy
  43. /**
  44. * Tests for OsgiBundlePlugin.
  45. * <p/>
  46. * This is an abstract base class for the common functionality between instances created with the preferred constructor {@link
  47. * OsgiBundlePlugin#OsgiBundlePlugin(OsgiContainerManager, String, PluginArtifact)} and the legacy constructor {@link
  48. * OsgiBundlePlugin#OsgiBundlePlugin(Bundle, String, PluginArtifact)}. Static inner classes subclass it to configure for the
  49. * actual tests, and to add additional tests.
  50. */
  51. @RunWith(MockitoJUnitRunner.class)
  52. public abstract class TestOsgiBundlePlugin {
  53. public static final String DESCRIPTION = "A Bundle for Testing";
  54. public static final String NAME = "Test Bundle";
  55. public static final String SYMBOLIC_NAME = TestOsgiBundlePlugin.class.getName() + ".testBundle";
  56. public static final String VENDOR = "Some Bundle Vendor";
  57. public static final String VERSION = "1.2";
  58. public static final String PLUGIN_KEY = SYMBOLIC_NAME + "-" + VERSION;
  59. private long testStarted;
  60. @Mock
  61. Bundle bundle;
  62. PluginArtifact pluginArtifact;
  63. OsgiBundlePlugin osgiBundlePlugin;
  64. @Before
  65. public void setUp() throws Exception {
  66. testStarted = System.currentTimeMillis();
  67. when(bundle.getHeaders()).thenReturn(new Hashtable<>(getBundleManifest()));
  68. when(bundle.getState()).thenReturn(Bundle.INSTALLED);
  69. pluginArtifact = getBundleJarPluginArtifact();
  70. osgiBundlePlugin = getOsgiBundlePlugin();
  71. }
  72. protected abstract OsgiBundlePlugin getOsgiBundlePlugin();
  73. @Test
  74. public void constructorConfiguresPluginCorrectly() {
  75. assertThatPluginIsConfigured(osgiBundlePlugin);
  76. }
  77. @Test
  78. public void getDateLoadedIsRecent() {
  79. final long timeLoaded = osgiBundlePlugin.getDateLoaded().getTime();
  80. final long now = System.currentTimeMillis();
  81. assertThat(timeLoaded, greaterThanOrEqualTo(testStarted));
  82. assertThat(timeLoaded, lessThanOrEqualTo(now));
  83. }
  84. @Test
  85. public void getDateInstalledMatchesFileTimestamp() {
  86. final long timeInstalled = osgiBundlePlugin.getDateInstalled().getTime();
  87. assertThat(timeInstalled, is(pluginArtifact.toFile().lastModified()));
  88. }
  89. @Test
  90. public void isUninstallableIsTrue() {
  91. assertThat(osgiBundlePlugin.isUninstallable(), is(true));
  92. }
  93. @Test
  94. public void isDeleteableIsTrue() {
  95. assertThat(osgiBundlePlugin.isDeleteable(), is(true));
  96. }
  97. @Test
  98. public void isDynamicallyLoadedIsTrue() {
  99. assertThat(osgiBundlePlugin.isDynamicallyLoaded(), is(true));
  100. }
  101. @Test
  102. public void loadClassForwardsToBundle() throws Exception {
  103. osgiBundlePlugin.install();
  104. // We're not stressing the class loader here, just checking the api works.
  105. final String className = "java.lang.String";
  106. final Class expected = String.class;
  107. // Cast here is to avoid warnings about Class<?> vs Class
  108. when((Class) bundle.loadClass(className)).thenReturn(expected);
  109. final Class actual = (Class) osgiBundlePlugin.loadClass(className, getClass());
  110. verify(bundle).loadClass(className);
  111. assertThat(actual, sameInstance(expected));
  112. }
  113. @Test
  114. public void getResourceForwardsToBundle() throws Exception {
  115. osgiBundlePlugin.install();
  116. final String resourceName = "someResource";
  117. final URL expected = new URL("mock", null, 0, "some/resource", mock(URLStreamHandler.class));
  118. when(bundle.getResource(resourceName)).thenReturn(expected);
  119. final URL actual = osgiBundlePlugin.getResource(resourceName);
  120. verify(bundle).getResource(resourceName);
  121. assertThat(actual, sameInstance(expected));
  122. }
  123. @Test
  124. public void getResourceAsStreamForwardsToBundle() throws Exception {
  125. osgiBundlePlugin.install();
  126. final String resourceName = "someResource";
  127. final InputStream expected = mock(InputStream.class);
  128. final URLConnection urlConnection = mock(URLConnection.class);
  129. when(urlConnection.getInputStream()).thenReturn(expected);
  130. final URLStreamHandler urlStreamHandler = new URLStreamHandler() {
  131. @Override
  132. protected URLConnection openConnection(final URL u) throws IOException {
  133. return urlConnection;
  134. }
  135. };
  136. final URL resourceUrl = new URL("mock", null, 0, "some/resource", urlStreamHandler);
  137. when(bundle.getResource(resourceName)).thenReturn(resourceUrl);
  138. final InputStream actual = osgiBundlePlugin.getResourceAsStream(resourceName);
  139. verify(bundle).getResource(resourceName);
  140. assertThat(actual, sameInstance(expected));
  141. }
  142. @Test
  143. public void getClassLoaderReturnsClassLoaderThatUsesBundle() throws Exception {
  144. osgiBundlePlugin.install();
  145. // Need a class name which won't be owned by a parent class loader
  146. final String className = "com.atlassian.plugin.test.some.fake.class.name";
  147. // Doesn't really matter what class we use, but we can't mock a final like Class
  148. final Class expected = String.class;
  149. // Cast here is to avoid warnings about Class<?> vs Class
  150. when((Class) bundle.loadClass(className)).thenReturn(expected);
  151. final Class actual = osgiBundlePlugin.getClassLoader().loadClass(className);
  152. verify(bundle).loadClass(className);
  153. assertThat(actual, sameInstance(expected));
  154. }
  155. @Test
  156. public void getPluginArtifactReturnsPluginArtifact() {
  157. assertThat(osgiBundlePlugin.getPluginArtifact(), is(pluginArtifact));
  158. }
  159. @Test
  160. public void executeBasicLifeCycle() throws Exception {
  161. final BundleContext bundleContext = mock(BundleContext.class);
  162. when(bundle.getBundleContext()).thenReturn(bundleContext);
  163. // Just a smoke test that we can drive the plugin through the typical states
  164. osgiBundlePlugin.install();
  165. osgiBundlePlugin.enable();
  166. verify(bundle).start();
  167. when(bundle.getState()).thenReturn(Bundle.ACTIVE);
  168. osgiBundlePlugin.disable();
  169. verify(bundle).stop();
  170. osgiBundlePlugin.uninstall();
  171. }
  172. static void assertThatPluginIsConfigured(final Plugin plugin) {
  173. assertThat(plugin.getKey(), is(PLUGIN_KEY));
  174. assertThat(plugin.getPluginInformation().getDescription(), is(DESCRIPTION));
  175. assertThat(plugin.getName(), is(NAME));
  176. assertThat(plugin.getPluginInformation().getVendorName(), is(VENDOR));
  177. assertThat(plugin.getPluginInformation().getVersion(), is(VERSION));
  178. assertThat(plugin.getI18nNameKey(), nullValue());
  179. }
  180. static PluginArtifact getBundleJarPluginArtifact() throws IOException {
  181. // For historic and convenience reasons, we just make a test jar rather than mocking out PluginArtifact
  182. final File bundleJar = new PluginJarBuilder("somebundle")
  183. .manifest(getBundleManifest())
  184. .build();
  185. // Backdate the jar so we can meaningfully test getDateInstalled vs getDateLoaded
  186. if (!bundleJar.setLastModified(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1))) {
  187. throw new IOException("Test broken, cannot backdate bundleJar '" + bundleJar + "'");
  188. }
  189. return new JarPluginArtifact(bundleJar);
  190. }
  191. static ImmutableMap<String, String> getBundleManifest() {
  192. return ImmutableMap.<String, String>builder()
  193. .put(Constants.BUNDLE_DESCRIPTION, DESCRIPTION)
  194. .put(Constants.BUNDLE_NAME, NAME)
  195. .put(Constants.BUNDLE_SYMBOLICNAME, SYMBOLIC_NAME)
  196. .put(Constants.BUNDLE_VENDOR, VENDOR)
  197. .put(Constants.BUNDLE_VERSION, VERSION)
  198. .build();
  199. }
  200. public static class Modern extends TestOsgiBundlePlugin {
  201. @Rule
  202. public ExpectedException expectedException = ExpectedException.none();
  203. @Mock
  204. private OsgiContainerManager osgiContainerManager;
  205. @Override
  206. public void setUp() throws Exception {
  207. super.setUp();
  208. // Ensure that we can install the plugin
  209. when(osgiContainerManager.installBundle(pluginArtifact.toFile(), FORBID_REFERENCE)).thenReturn(bundle);
  210. when(osgiContainerManager.getServiceTracker(any(String.class))).thenReturn(mock(ServiceTracker.class));
  211. }
  212. @Override
  213. protected OsgiBundlePlugin getOsgiBundlePlugin() {
  214. return new OsgiBundlePlugin(osgiContainerManager, PLUGIN_KEY, pluginArtifact);
  215. }
  216. @Test
  217. public void loadClassThrowsBeforeInstall() throws Exception {
  218. expectedException.expect(IllegalPluginStateException.class);
  219. osgiBundlePlugin.loadClass("java.lang.String", getClass());
  220. }
  221. @Test
  222. public void getResourceThrowsBeforeInstall() throws Exception {
  223. expectedException.expect(IllegalPluginStateException.class);
  224. osgiBundlePlugin.getResource("someResource");
  225. }
  226. @Test
  227. public void getResourceAsStreamThrowsBeforeInstall() throws Exception {
  228. expectedException.expect(IllegalPluginStateException.class);
  229. osgiBundlePlugin.getResourceAsStream("someResource");
  230. }
  231. @Test
  232. public void getClassLoaderThrowsBeforeInstall() throws Exception {
  233. expectedException.expect(IllegalPluginStateException.class);
  234. osgiBundlePlugin.getClassLoader();
  235. }
  236. @Test
  237. public void loadClassThrowsAfterUninstall() throws Exception {
  238. osgiBundlePlugin.install();
  239. osgiBundlePlugin.uninstall();
  240. expectedException.expect(IllegalPluginStateException.class);
  241. osgiBundlePlugin.loadClass("java.lang.String", getClass());
  242. }
  243. @Test
  244. public void getResourceThrowsAfterUninstall() throws Exception {
  245. osgiBundlePlugin.install();
  246. osgiBundlePlugin.uninstall();
  247. expectedException.expect(IllegalPluginStateException.class);
  248. osgiBundlePlugin.getResource("someResource");
  249. }
  250. @Test
  251. public void getResourceAsStreamThrowsAfterUninstall() throws Exception {
  252. osgiBundlePlugin.install();
  253. osgiBundlePlugin.uninstall();
  254. expectedException.expect(IllegalPluginStateException.class);
  255. osgiBundlePlugin.getResourceAsStream("someResource");
  256. }
  257. @Test
  258. public void getClassLoaderThrowsAfterUninstall() throws Exception {
  259. osgiBundlePlugin.install();
  260. osgiBundlePlugin.uninstall();
  261. expectedException.expect(IllegalPluginStateException.class);
  262. osgiBundlePlugin.getClassLoader();
  263. }
  264. @Test
  265. public void installFailsIfOsgiContainerManagerInstallBundleFails() {
  266. final OsgiContainerException osgiContainerException = new OsgiContainerException("Intentional fail for test");
  267. when(osgiContainerManager.installBundle(pluginArtifact.toFile(), FORBID_REFERENCE)).thenThrow(osgiContainerException);
  268. // For now expect the exception to be directly propagated. Given that the type matches what our container throws,
  269. // and what OsgiPlugin (rethrows), this type should be ok, we can rethink if we need to wrap / adjust the exception.
  270. expectedException.expect(is(osgiContainerException));
  271. osgiBundlePlugin.install();
  272. }
  273. @Test
  274. public void pluginArtifactThatAllowsReferenceIsInstalledByReference() throws Exception {
  275. final File bundleJar = pluginArtifact.toFile();
  276. pluginArtifact = new JarPluginArtifact(bundleJar, PERMIT_REFERENCE);
  277. osgiContainerManager = mock(OsgiContainerManager.class);
  278. // Have our mock OsgiContainerManager respond only to a reference install (the PERMIT_REFERENCE in the next line)
  279. when(osgiContainerManager.installBundle(bundleJar, PERMIT_REFERENCE)).thenReturn(bundle);
  280. osgiBundlePlugin = new OsgiBundlePlugin(osgiContainerManager, PLUGIN_KEY, pluginArtifact);
  281. osgiBundlePlugin.install();
  282. verify(osgiContainerManager).installBundle(bundleJar, PERMIT_REFERENCE);
  283. // We know, and tested above, that getClassLoader only works once installed, but in fact the previous call with have
  284. // thrown if we don't perform a reference install, since our mock can't respond.
  285. osgiBundlePlugin.getClassLoader();
  286. }
  287. }
  288. public static class Legacy extends TestOsgiBundlePlugin {
  289. @Override
  290. protected OsgiBundlePlugin getOsgiBundlePlugin() {
  291. //noinspection deprecation
  292. return new OsgiBundlePlugin(bundle, PLUGIN_KEY, pluginArtifact);
  293. }
  294. }
  295. }