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   * @author pandronov
43   */
44  @RunWith(Parameterized.class)
45  public class TestPluginFactorySelection {
46      private static final String OSGI_BUNDLE = "OsgiBundleFactory";
47      private static final String OSGI_PLUGIN = "OsgiPluginFactory";
48  
49      /*
50       * Decision space can be described by three boolean variables that represent the different characteristics of
51       * each plugin:
52       * 1. Spring               - the plugin has a Spring context,
53       * 2. Transformerless      - the plugin does not require transformation,
54       * 3. atlassian-plugin.xml - the artifact has an atlassian plugin descriptor.
55       * 
56       * Here is a table describing decision space:
57       * +--------+------------+-----------------+-------------------+
58       * | Spring | plugin.xml | transformerless | result type       |
59       * +--------+------------+-----------------+-------------------+
60       * |   0    |     0      |       0         | OsgiBundlePlugin  | if OSGi bundle, i.e has SymbolicName
61       * +--------+------------+-----------------+-------------------+
62       * |   1    |     0      |       0         | OsgiBundlePlugin  | if OSGi bundle, i.e has SymbolicName
63       * +--------+------------+-----------------+-------------------+
64       * |   0    |     1      |       0         | OsgiPlugin        | Transformation adds a Spring context
65       * +--------+------------+-----------------+-------------------+
66       * |   1    |     1      |       0         | OsgiPlugin        |
67       * +--------+------------+-----------------+-------------------+
68       * |   0    |     0      |       1         | OsgiBundlePlugin  |
69       * +--------+------------+-----------------+-------------------+
70       * |   1    |     0      |       1         | OsgiPlugin        |
71       * +--------+------------+-----------------+-------------------+
72       * |   0    |     1      |       1         | OsgiBundlePlugin  |
73       * +--------+------------+-----------------+-------------------+
74       * |   1    |     1      |       1         | OsgiPlugin        |
75       * +--------+------------+-----------------+-------------------+
76       *
77       * Note that to make table complete it is necessary to introduce one more variable - isOsgiBundle but
78       * for two factories considered below, JAR implies to be a proper OSGi Bundle or a proper Atlassian Plugin,
79       * so isBundle variable has no influence. There are only two cases when it is important (null expectation in
80       * tests table below), all the rest should give same results no matter if it is an OSGi Bundle or no 
81       */
82      @Parameters(name = "test({0},{1},{2},{3})={4}")
83      public static Collection<Object[]> data() {
84          return Arrays.asList(new Object[][]{
85                  // hasSpring | isPlugin | isOsgiBundle | isTransformless | expected |
86                  // ---------------------- Is a bundle
87                  {false, false, true, false, OSGI_BUNDLE},
88                  {true, false, true, false, OSGI_BUNDLE},
89                  {false, true, true, false, OSGI_PLUGIN},
90                  {true, true, true, false, OSGI_PLUGIN},
91                  {false, false, true, true, OSGI_BUNDLE},
92                  {true, false, true, true, OSGI_PLUGIN},
93                  {false, true, true, true, OSGI_BUNDLE},
94                  {true, true, true, true, OSGI_PLUGIN},
95                  // ---------------------- Not a bundle
96                  {false, false, false, false, null},
97                  {true, false, false, false, null},
98                  {false, true, false, false, OSGI_PLUGIN},
99                  {true, true, false, false, OSGI_PLUGIN},
100                 {false, false, false, true, OSGI_BUNDLE},
101                 {true, false, false, true, OSGI_PLUGIN},
102                 {false, true, false, true, OSGI_BUNDLE},
103                 {true, true, false, true, OSGI_PLUGIN},
104         });
105     }
106 
107     private final
108     @Nonnull
109     OsgiPluginFactory opFactory;
110     private final
111     @Nonnull
112     OsgiBundleFactory obFactory;
113 
114     @Parameter(value = 0)
115     public boolean hasSpring;
116 
117     @Parameter(value = 1)
118     public boolean isPlugin;
119 
120     @Parameter(value = 2)
121     public boolean isOsgiBundle;
122 
123     @Parameter(value = 3)
124     public boolean isTransformless;
125 
126     @Parameter(value = 4)
127     public String expected;
128 
129     public TestPluginFactorySelection() {
130         OsgiContainerManager osgi = mock(OsgiContainerManager.class);
131         this.opFactory = new OsgiPluginFactory(
132                 "atlassian-plugin.xml",
133                 Collections.<Application>emptySet(),
134                 mock(OsgiPersistentCache.class),
135                 osgi,
136                 mock(PluginEventManager.class)
137         );
138         this.obFactory = new OsgiBundleFactory(osgi);
139     }
140 
141     @Test
142     public void verificator() throws IOException {
143         // Create artifact based on test parameters
144         Map<String, String> manifest = new HashMap<>();
145         manifest.put(Constants.BUNDLE_NAME, "TestPlugin");
146         manifest.put(Constants.BUNDLE_VENDOR, "Cool Tests Inc.");
147         manifest.put(Constants.BUNDLE_DESCRIPTION, "Test plugin");
148 
149         if (isOsgiBundle || isTransformless) {
150             manifest.put(Constants.BUNDLE_VERSION, "1.0.0");
151             manifest.put(Constants.BUNDLE_SYMBOLICNAME, "test-plugin");
152         }
153 
154         if (isTransformless) {
155             manifest.put("Atlassian-Plugin-Key", "test-plugin");
156         }
157 
158         if (hasSpring) {
159             manifest.put("Spring-Context", "*");
160         }
161 
162         // Create plugin itself: most of the code in factories uses getResourceAsStream call
163         // to calculate various characteristics of plugin. Because of that  it is hard to
164         // create mock object
165         PluginJarBuilder builder = new PluginJarBuilder("test-plugin.jar").manifest(manifest);
166         if (isPlugin) {
167             builder.addPluginInformation("test-plugin", "Test Plugin", "1.0.0");
168         }
169 
170         // Backdate the jar so we can meaningfully test getDateInstalled vs getDateLoaded
171         File bundleJar = builder.build();
172         if (!bundleJar.setLastModified(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1))) {
173             throw new IOException("Test broken, cannot backdate bundleJar '" + bundleJar + "'");
174         }
175 
176         // Verify
177         JarPluginArtifact ar = new JarPluginArtifact(bundleJar);
178         if (expected == null) {
179             assertThat("OsgiPlugin", opFactory.canCreate(ar), nullValue());
180             assertThat("OsgiBundle", obFactory.canCreate(ar), nullValue());
181         } else if (expected == OSGI_PLUGIN) {
182             assertThat("OsgiPlugin", opFactory.canCreate(ar), notNullValue());
183             assertThat("OsgiBundle", obFactory.canCreate(ar), nullValue());
184         } else if (expected == OSGI_BUNDLE) {
185             assertThat("OsgiPlugin", opFactory.canCreate(ar), nullValue());
186             assertThat("OsgiBundle", obFactory.canCreate(ar), notNullValue());
187         }
188     }
189 }