/atlassian-plugins-osgi/src/test/java/com/atlassian/plugin/osgi/factory/TestOsgiBundlePlugin.java
Java | 335 lines | 270 code | 40 blank | 25 comment | 1 complexity | 2e7cb7d55509432d6977b4ba68cfe583 MD5 | raw file
- package com.atlassian.plugin.osgi.factory;
- import com.atlassian.plugin.IllegalPluginStateException;
- import com.atlassian.plugin.JarPluginArtifact;
- import com.atlassian.plugin.Plugin;
- import com.atlassian.plugin.PluginArtifact;
- import com.atlassian.plugin.osgi.container.OsgiContainerException;
- import com.atlassian.plugin.osgi.container.OsgiContainerManager;
- import com.atlassian.plugin.test.PluginJarBuilder;
- import com.google.common.collect.ImmutableMap;
- import org.junit.Before;
- import org.junit.Rule;
- import org.junit.Test;
- import org.junit.rules.ExpectedException;
- import org.junit.runner.RunWith;
- import org.mockito.Mock;
- import org.mockito.runners.MockitoJUnitRunner;
- import org.osgi.framework.Bundle;
- import org.osgi.framework.BundleContext;
- import org.osgi.framework.Constants;
- import org.osgi.util.tracker.ServiceTracker;
- import java.io.File;
- import java.io.IOException;
- import java.io.InputStream;
- import java.net.URL;
- import java.net.URLConnection;
- import java.net.URLStreamHandler;
- import java.util.Hashtable;
- import java.util.concurrent.TimeUnit;
- import static com.atlassian.plugin.ReferenceMode.FORBID_REFERENCE;
- import static com.atlassian.plugin.ReferenceMode.PERMIT_REFERENCE;
- import static org.hamcrest.MatcherAssert.assertThat;
- import static org.hamcrest.Matchers.greaterThanOrEqualTo;
- import static org.hamcrest.Matchers.is;
- import static org.hamcrest.Matchers.lessThanOrEqualTo;
- import static org.hamcrest.Matchers.nullValue;
- import static org.hamcrest.Matchers.sameInstance;
- import static org.mockito.Mockito.any;
- import static org.mockito.Mockito.mock;
- import static org.mockito.Mockito.verify;
- import static org.mockito.Mockito.when;
- // An IntelliJ pattern to run both tests in this class
- // com.atlassian.plugin.osgi.factory.TestOsgiBundlePlugin$Modern||com.atlassian.plugin.osgi.factory.TestOsgiBundlePlugin$Legacy
- /**
- * Tests for OsgiBundlePlugin.
- * <p/>
- * This is an abstract base class for the common functionality between instances created with the preferred constructor {@link
- * OsgiBundlePlugin#OsgiBundlePlugin(OsgiContainerManager, String, PluginArtifact)} and the legacy constructor {@link
- * OsgiBundlePlugin#OsgiBundlePlugin(Bundle, String, PluginArtifact)}. Static inner classes subclass it to configure for the
- * actual tests, and to add additional tests.
- */
- @RunWith(MockitoJUnitRunner.class)
- public abstract class TestOsgiBundlePlugin {
- public static final String DESCRIPTION = "A Bundle for Testing";
- public static final String NAME = "Test Bundle";
- public static final String SYMBOLIC_NAME = TestOsgiBundlePlugin.class.getName() + ".testBundle";
- public static final String VENDOR = "Some Bundle Vendor";
- public static final String VERSION = "1.2";
- public static final String PLUGIN_KEY = SYMBOLIC_NAME + "-" + VERSION;
- private long testStarted;
- @Mock
- Bundle bundle;
- PluginArtifact pluginArtifact;
- OsgiBundlePlugin osgiBundlePlugin;
- @Before
- public void setUp() throws Exception {
- testStarted = System.currentTimeMillis();
- when(bundle.getHeaders()).thenReturn(new Hashtable<>(getBundleManifest()));
- when(bundle.getState()).thenReturn(Bundle.INSTALLED);
- pluginArtifact = getBundleJarPluginArtifact();
- osgiBundlePlugin = getOsgiBundlePlugin();
- }
- protected abstract OsgiBundlePlugin getOsgiBundlePlugin();
- @Test
- public void constructorConfiguresPluginCorrectly() {
- assertThatPluginIsConfigured(osgiBundlePlugin);
- }
- @Test
- public void getDateLoadedIsRecent() {
- final long timeLoaded = osgiBundlePlugin.getDateLoaded().getTime();
- final long now = System.currentTimeMillis();
- assertThat(timeLoaded, greaterThanOrEqualTo(testStarted));
- assertThat(timeLoaded, lessThanOrEqualTo(now));
- }
- @Test
- public void getDateInstalledMatchesFileTimestamp() {
- final long timeInstalled = osgiBundlePlugin.getDateInstalled().getTime();
- assertThat(timeInstalled, is(pluginArtifact.toFile().lastModified()));
- }
- @Test
- public void isUninstallableIsTrue() {
- assertThat(osgiBundlePlugin.isUninstallable(), is(true));
- }
- @Test
- public void isDeleteableIsTrue() {
- assertThat(osgiBundlePlugin.isDeleteable(), is(true));
- }
- @Test
- public void isDynamicallyLoadedIsTrue() {
- assertThat(osgiBundlePlugin.isDynamicallyLoaded(), is(true));
- }
- @Test
- public void loadClassForwardsToBundle() throws Exception {
- osgiBundlePlugin.install();
- // We're not stressing the class loader here, just checking the api works.
- final String className = "java.lang.String";
- final Class expected = String.class;
- // Cast here is to avoid warnings about Class<?> vs Class
- when((Class) bundle.loadClass(className)).thenReturn(expected);
- final Class actual = (Class) osgiBundlePlugin.loadClass(className, getClass());
- verify(bundle).loadClass(className);
- assertThat(actual, sameInstance(expected));
- }
- @Test
- public void getResourceForwardsToBundle() throws Exception {
- osgiBundlePlugin.install();
- final String resourceName = "someResource";
- final URL expected = new URL("mock", null, 0, "some/resource", mock(URLStreamHandler.class));
- when(bundle.getResource(resourceName)).thenReturn(expected);
- final URL actual = osgiBundlePlugin.getResource(resourceName);
- verify(bundle).getResource(resourceName);
- assertThat(actual, sameInstance(expected));
- }
- @Test
- public void getResourceAsStreamForwardsToBundle() throws Exception {
- osgiBundlePlugin.install();
- final String resourceName = "someResource";
- final InputStream expected = mock(InputStream.class);
- final URLConnection urlConnection = mock(URLConnection.class);
- when(urlConnection.getInputStream()).thenReturn(expected);
- final URLStreamHandler urlStreamHandler = new URLStreamHandler() {
- @Override
- protected URLConnection openConnection(final URL u) throws IOException {
- return urlConnection;
- }
- };
- final URL resourceUrl = new URL("mock", null, 0, "some/resource", urlStreamHandler);
- when(bundle.getResource(resourceName)).thenReturn(resourceUrl);
- final InputStream actual = osgiBundlePlugin.getResourceAsStream(resourceName);
- verify(bundle).getResource(resourceName);
- assertThat(actual, sameInstance(expected));
- }
- @Test
- public void getClassLoaderReturnsClassLoaderThatUsesBundle() throws Exception {
- osgiBundlePlugin.install();
- // Need a class name which won't be owned by a parent class loader
- final String className = "com.atlassian.plugin.test.some.fake.class.name";
- // Doesn't really matter what class we use, but we can't mock a final like Class
- final Class expected = String.class;
- // Cast here is to avoid warnings about Class<?> vs Class
- when((Class) bundle.loadClass(className)).thenReturn(expected);
- final Class actual = osgiBundlePlugin.getClassLoader().loadClass(className);
- verify(bundle).loadClass(className);
- assertThat(actual, sameInstance(expected));
- }
- @Test
- public void getPluginArtifactReturnsPluginArtifact() {
- assertThat(osgiBundlePlugin.getPluginArtifact(), is(pluginArtifact));
- }
- @Test
- public void executeBasicLifeCycle() throws Exception {
- final BundleContext bundleContext = mock(BundleContext.class);
- when(bundle.getBundleContext()).thenReturn(bundleContext);
- // Just a smoke test that we can drive the plugin through the typical states
- osgiBundlePlugin.install();
- osgiBundlePlugin.enable();
- verify(bundle).start();
- when(bundle.getState()).thenReturn(Bundle.ACTIVE);
- osgiBundlePlugin.disable();
- verify(bundle).stop();
- osgiBundlePlugin.uninstall();
- }
- static void assertThatPluginIsConfigured(final Plugin plugin) {
- assertThat(plugin.getKey(), is(PLUGIN_KEY));
- assertThat(plugin.getPluginInformation().getDescription(), is(DESCRIPTION));
- assertThat(plugin.getName(), is(NAME));
- assertThat(plugin.getPluginInformation().getVendorName(), is(VENDOR));
- assertThat(plugin.getPluginInformation().getVersion(), is(VERSION));
- assertThat(plugin.getI18nNameKey(), nullValue());
- }
- static PluginArtifact getBundleJarPluginArtifact() throws IOException {
- // For historic and convenience reasons, we just make a test jar rather than mocking out PluginArtifact
- final File bundleJar = new PluginJarBuilder("somebundle")
- .manifest(getBundleManifest())
- .build();
- // Backdate the jar so we can meaningfully test getDateInstalled vs getDateLoaded
- if (!bundleJar.setLastModified(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1))) {
- throw new IOException("Test broken, cannot backdate bundleJar '" + bundleJar + "'");
- }
- return new JarPluginArtifact(bundleJar);
- }
- static ImmutableMap<String, String> getBundleManifest() {
- return ImmutableMap.<String, String>builder()
- .put(Constants.BUNDLE_DESCRIPTION, DESCRIPTION)
- .put(Constants.BUNDLE_NAME, NAME)
- .put(Constants.BUNDLE_SYMBOLICNAME, SYMBOLIC_NAME)
- .put(Constants.BUNDLE_VENDOR, VENDOR)
- .put(Constants.BUNDLE_VERSION, VERSION)
- .build();
- }
- public static class Modern extends TestOsgiBundlePlugin {
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
- @Mock
- private OsgiContainerManager osgiContainerManager;
- @Override
- public void setUp() throws Exception {
- super.setUp();
- // Ensure that we can install the plugin
- when(osgiContainerManager.installBundle(pluginArtifact.toFile(), FORBID_REFERENCE)).thenReturn(bundle);
- when(osgiContainerManager.getServiceTracker(any(String.class))).thenReturn(mock(ServiceTracker.class));
- }
- @Override
- protected OsgiBundlePlugin getOsgiBundlePlugin() {
- return new OsgiBundlePlugin(osgiContainerManager, PLUGIN_KEY, pluginArtifact);
- }
- @Test
- public void loadClassThrowsBeforeInstall() throws Exception {
- expectedException.expect(IllegalPluginStateException.class);
- osgiBundlePlugin.loadClass("java.lang.String", getClass());
- }
- @Test
- public void getResourceThrowsBeforeInstall() throws Exception {
- expectedException.expect(IllegalPluginStateException.class);
- osgiBundlePlugin.getResource("someResource");
- }
- @Test
- public void getResourceAsStreamThrowsBeforeInstall() throws Exception {
- expectedException.expect(IllegalPluginStateException.class);
- osgiBundlePlugin.getResourceAsStream("someResource");
- }
- @Test
- public void getClassLoaderThrowsBeforeInstall() throws Exception {
- expectedException.expect(IllegalPluginStateException.class);
- osgiBundlePlugin.getClassLoader();
- }
- @Test
- public void loadClassThrowsAfterUninstall() throws Exception {
- osgiBundlePlugin.install();
- osgiBundlePlugin.uninstall();
- expectedException.expect(IllegalPluginStateException.class);
- osgiBundlePlugin.loadClass("java.lang.String", getClass());
- }
- @Test
- public void getResourceThrowsAfterUninstall() throws Exception {
- osgiBundlePlugin.install();
- osgiBundlePlugin.uninstall();
- expectedException.expect(IllegalPluginStateException.class);
- osgiBundlePlugin.getResource("someResource");
- }
- @Test
- public void getResourceAsStreamThrowsAfterUninstall() throws Exception {
- osgiBundlePlugin.install();
- osgiBundlePlugin.uninstall();
- expectedException.expect(IllegalPluginStateException.class);
- osgiBundlePlugin.getResourceAsStream("someResource");
- }
- @Test
- public void getClassLoaderThrowsAfterUninstall() throws Exception {
- osgiBundlePlugin.install();
- osgiBundlePlugin.uninstall();
- expectedException.expect(IllegalPluginStateException.class);
- osgiBundlePlugin.getClassLoader();
- }
- @Test
- public void installFailsIfOsgiContainerManagerInstallBundleFails() {
- final OsgiContainerException osgiContainerException = new OsgiContainerException("Intentional fail for test");
- when(osgiContainerManager.installBundle(pluginArtifact.toFile(), FORBID_REFERENCE)).thenThrow(osgiContainerException);
- // For now expect the exception to be directly propagated. Given that the type matches what our container throws,
- // and what OsgiPlugin (rethrows), this type should be ok, we can rethink if we need to wrap / adjust the exception.
- expectedException.expect(is(osgiContainerException));
- osgiBundlePlugin.install();
- }
- @Test
- public void pluginArtifactThatAllowsReferenceIsInstalledByReference() throws Exception {
- final File bundleJar = pluginArtifact.toFile();
- pluginArtifact = new JarPluginArtifact(bundleJar, PERMIT_REFERENCE);
- osgiContainerManager = mock(OsgiContainerManager.class);
- // Have our mock OsgiContainerManager respond only to a reference install (the PERMIT_REFERENCE in the next line)
- when(osgiContainerManager.installBundle(bundleJar, PERMIT_REFERENCE)).thenReturn(bundle);
- osgiBundlePlugin = new OsgiBundlePlugin(osgiContainerManager, PLUGIN_KEY, pluginArtifact);
- osgiBundlePlugin.install();
- verify(osgiContainerManager).installBundle(bundleJar, PERMIT_REFERENCE);
- // We know, and tested above, that getClassLoader only works once installed, but in fact the previous call with have
- // thrown if we don't perform a reference install, since our mock can't respond.
- osgiBundlePlugin.getClassLoader();
- }
- }
- public static class Legacy extends TestOsgiBundlePlugin {
- @Override
- protected OsgiBundlePlugin getOsgiBundlePlugin() {
- //noinspection deprecation
- return new OsgiBundlePlugin(bundle, PLUGIN_KEY, pluginArtifact);
- }
- }
- }