View Javadoc
1   package com.atlassian.plugin.osgi.factory;
2   
3   import com.atlassian.plugin.Application;
4   import com.atlassian.plugin.JarPluginArtifact;
5   import com.atlassian.plugin.event.PluginEventManager;
6   import com.atlassian.plugin.factories.PluginFactory;
7   import com.atlassian.plugin.osgi.container.OsgiContainerManager;
8   import com.atlassian.plugin.osgi.container.OsgiPersistentCache;
9   import com.atlassian.plugin.test.PluginJarBuilder;
10  import org.junit.Test;
11  import org.junit.runner.RunWith;
12  import org.junit.runners.Parameterized;
13  import org.junit.runners.Parameterized.Parameter;
14  import org.junit.runners.Parameterized.Parameters;
15  import org.osgi.framework.Constants;
16  
17  import javax.annotation.Nonnull;
18  import java.io.File;
19  import java.io.IOException;
20  import java.util.Arrays;
21  import java.util.Collection;
22  import java.util.Collections;
23  import java.util.HashMap;
24  import java.util.Map;
25  import java.util.concurrent.TimeUnit;
26  
27  import static org.hamcrest.MatcherAssert.assertThat;
28  import static org.hamcrest.Matchers.notNullValue;
29  import static org.hamcrest.Matchers.nullValue;
30  import static org.mockito.Mockito.mock;
31  
32  /**
33   * Verifies which of OSGiBundleFactory or OSGiPluginFactory processes a given plugin.
34   * <p>
35   * The products just walk a list of {@link PluginFactory} instances and select the first one for which
36   * {@link PluginFactory#canCreate} is true. We don't mandate the order of this list, and it may vary from product to
37   * product.
38   * <p>
39   * This test suite verifies that {@link OsgiBundleFactory} and {@link OsgiPluginFactory} select the correct plugins
40   * regardless of the order of the calls, by verifying there are no intersections in the accepted plugins for each.
41   */
42  @RunWith(Parameterized.class)
43  public class TestPluginFactorySelection {
44      private static final String OSGI_BUNDLE = "OsgiBundleFactory";
45      private static final String OSGI_PLUGIN = "OsgiPluginFactory";
46  
47      /*
48       * Decision space can be described by three boolean variables that represent the different characteristics of
49       * each plugin:
50       * 1. Spring               - the plugin has a Spring context,
51       * 2. Transformerless      - the plugin does not require transformation,
52       * 3. atlassian-plugin.xml - the artifact has an atlassian plugin descriptor.
53       * 
54       * Here is a table describing decision space:
55       * +--------+------------+-----------------+-------------------+
56       * | Spring | plugin.xml | transformerless | result type       |
57       * +--------+------------+-----------------+-------------------+
58       * |   0    |     0      |       0         | OsgiBundlePlugin  | if OSGi bundle, i.e has SymbolicName
59       * +--------+------------+-----------------+-------------------+
60       * |   1    |     0      |       0         | OsgiBundlePlugin  | if OSGi bundle, i.e has SymbolicName
61       * +--------+------------+-----------------+-------------------+
62       * |   0    |     1      |       0         | OsgiPlugin        | Transformation adds a Spring context
63       * +--------+------------+-----------------+-------------------+
64       * |   1    |     1      |       0         | OsgiPlugin        |
65       * +--------+------------+-----------------+-------------------+
66       * |   0    |     0      |       1         | OsgiBundlePlugin  |
67       * +--------+------------+-----------------+-------------------+
68       * |   1    |     0      |       1         | OsgiPlugin        |
69       * +--------+------------+-----------------+-------------------+
70       * |   0    |     1      |       1         | OsgiBundlePlugin  |
71       * +--------+------------+-----------------+-------------------+
72       * |   1    |     1      |       1         | OsgiPlugin        |
73       * +--------+------------+-----------------+-------------------+
74       *
75       * Note that to make table complete it is necessary to introduce one more variable - isOsgiBundle but
76       * for two factories considered below, JAR implies to be a proper OSGi Bundle or a proper Atlassian Plugin,
77       * so isBundle variable has no influence. There are only two cases when it is important (null expectation in
78       * tests table below), all the rest should give same results no matter if it is an OSGi Bundle or no 
79       */
80      @Parameters(name = "test({0},{1},{2},{3})={4}")
81      public static Collection<Object[]> data() {
82          return Arrays.asList(new Object[][]{
83                  // hasSpring | isPlugin | isOsgiBundle | isTransformless | expected |
84                  // ---------------------- Is a bundle
85                  {false, false, true, false, OSGI_BUNDLE},
86                  {true, false, true, false, OSGI_BUNDLE},
87                  {false, true, true, false, OSGI_PLUGIN},
88                  {true, true, true, false, OSGI_PLUGIN},
89                  {false, false, true, true, OSGI_BUNDLE},
90                  {true, false, true, true, OSGI_PLUGIN},
91                  {false, true, true, true, OSGI_BUNDLE},
92                  {true, true, true, true, OSGI_PLUGIN},
93                  // ---------------------- Not a bundle
94                  {false, false, false, false, null},
95                  {true, false, false, false, null},
96                  {false, true, false, false, OSGI_PLUGIN},
97                  {true, true, false, false, OSGI_PLUGIN},
98                  {false, false, false, true, OSGI_BUNDLE},
99                  {true, false, false, true, OSGI_PLUGIN},
100                 {false, true, false, true, OSGI_BUNDLE},
101                 {true, true, false, true, OSGI_PLUGIN},
102         });
103     }
104 
105     private final
106     @Nonnull
107     OsgiPluginFactory opFactory;
108     private final
109     @Nonnull
110     OsgiBundleFactory obFactory;
111 
112     @Parameter(value = 0)
113     public boolean hasSpring;
114 
115     @Parameter(value = 1)
116     public boolean isPlugin;
117 
118     @Parameter(value = 2)
119     public boolean isOsgiBundle;
120 
121     @Parameter(value = 3)
122     public boolean isTransformless;
123 
124     @Parameter(value = 4)
125     public String expected;
126 
127     public TestPluginFactorySelection() {
128         OsgiContainerManager osgi = mock(OsgiContainerManager.class);
129         this.opFactory = new OsgiPluginFactory(
130                 "atlassian-plugin.xml",
131                 Collections.<Application>emptySet(),
132                 mock(OsgiPersistentCache.class),
133                 osgi,
134                 mock(PluginEventManager.class)
135         );
136         this.obFactory = new OsgiBundleFactory(osgi);
137     }
138 
139     @Test
140     public void verificator() throws IOException {
141         // Create artifact based on test parameters
142         Map<String, String> manifest = new HashMap<>();
143         manifest.put(Constants.BUNDLE_NAME, "TestPlugin");
144         manifest.put(Constants.BUNDLE_VENDOR, "Cool Tests Inc.");
145         manifest.put(Constants.BUNDLE_DESCRIPTION, "Test plugin");
146 
147         if (isOsgiBundle || isTransformless) {
148             manifest.put(Constants.BUNDLE_VERSION, "1.0.0");
149             manifest.put(Constants.BUNDLE_SYMBOLICNAME, "test-plugin");
150         }
151 
152         if (isTransformless) {
153             manifest.put("Atlassian-Plugin-Key", "test-plugin");
154         }
155 
156         if (hasSpring) {
157             manifest.put("Spring-Context", "*");
158         }
159 
160         // Create plugin itself: most of the code in factories uses getResourceAsStream call
161         // to calculate various characteristics of plugin. Because of that  it is hard to
162         // create mock object
163         PluginJarBuilder builder = new PluginJarBuilder("test-plugin.jar").manifest(manifest);
164         if (isPlugin) {
165             builder.addPluginInformation("test-plugin", "Test Plugin", "1.0.0");
166         }
167 
168         // Backdate the jar so we can meaningfully test getDateInstalled vs getDateLoaded
169         File bundleJar = builder.build();
170         if (!bundleJar.setLastModified(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1))) {
171             throw new IOException("Test broken, cannot backdate bundleJar '" + bundleJar + "'");
172         }
173 
174         // Verify
175         JarPluginArtifact ar = new JarPluginArtifact(bundleJar);
176         if (expected == null) {
177             assertThat("OsgiPlugin", opFactory.canCreate(ar), nullValue());
178             assertThat("OsgiBundle", obFactory.canCreate(ar), nullValue());
179         } else if (expected == OSGI_PLUGIN) {
180             assertThat("OsgiPlugin", opFactory.canCreate(ar), notNullValue());
181             assertThat("OsgiBundle", obFactory.canCreate(ar), nullValue());
182         } else if (expected == OSGI_BUNDLE) {
183             assertThat("OsgiPlugin", opFactory.canCreate(ar), nullValue());
184             assertThat("OsgiBundle", obFactory.canCreate(ar), notNullValue());
185         }
186     }
187 }