View Javadoc

1   package com.atlassian.plugin.parsers;
2   
3   import com.atlassian.fugue.Option;
4   import com.atlassian.fugue.Suppliers;
5   import com.atlassian.plugin.Application;
6   import com.atlassian.plugin.InstallationMode;
7   import com.atlassian.plugin.ModuleDescriptorFactory;
8   import com.atlassian.plugin.Plugin;
9   import com.atlassian.plugin.PluginArtifact;
10  import com.atlassian.plugin.PluginInformation;
11  import com.atlassian.plugin.PluginParseException;
12  import com.atlassian.plugin.PluginPermission;
13  import com.atlassian.plugin.classloader.PluginClassLoader;
14  import com.atlassian.plugin.impl.DefaultDynamicPlugin;
15  import com.atlassian.plugin.mock.MockAnimalModuleDescriptor;
16  import com.atlassian.plugin.util.ClassLoaderUtils;
17  import com.google.common.base.Function;
18  import com.google.common.collect.ImmutableSet;
19  import com.google.common.collect.Iterables;
20  import com.mockobjects.dynamic.C;
21  import com.mockobjects.dynamic.Mock;
22  import junit.framework.TestCase;
23  
24  import java.io.ByteArrayInputStream;
25  import java.io.File;
26  import java.io.FileInputStream;
27  import java.io.FileNotFoundException;
28  import java.io.InputStream;
29  import java.net.URL;
30  import java.util.Set;
31  
32  import static com.atlassian.fugue.Option.option;
33  import static com.atlassian.fugue.Option.some;
34  
35  @SuppressWarnings({"deprecation"}) //suppress deprecation warnings because we still need to test deprecated methods.
36  public class TestXmlDescriptorParser extends TestCase
37  {
38      private static final String MISSING_INFO_TEST_FILE = "test-missing-plugin-info.xml";
39      private static final String DUMMY_PLUGIN_FILE = "pooh-test-plugin.jar";
40  
41      public TestXmlDescriptorParser(String name)
42      {
43          super(name);
44      }
45  
46      // CONF-12680 Test for missing plugin-info
47      public void testMissingPluginInfo()
48      {
49          // mock up some supporting objects
50          PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
51          Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
52          mockFactory.expect("getModuleDescriptorClass", "unknown-plugin");
53  
54          // create a Plugin for testing
55          Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
56  
57          try
58          {
59              XmlDescriptorParser parser = new XmlDescriptorParser(new FileInputStream(getTestFile(MISSING_INFO_TEST_FILE)), ImmutableSet.<Application>of());
60              parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
61  
62              PluginInformation info = testPlugin.getPluginInformation();
63              assertNotNull("Info should not be null", info);
64          }
65          catch (PluginParseException e)
66          {
67              e.printStackTrace();
68              fail("Plugin information parsing should not fail.");
69          }
70          catch (FileNotFoundException e)
71          {
72              e.printStackTrace();
73              // This shouldn't happen
74              fail("Error setting up test");
75          }
76      }
77  
78      public void testPluginsApplicationVersionMinMax()
79      {
80          XmlDescriptorParser parser = parse(null,
81                  "<atlassian-plugin key='foo'>",
82                  "  <plugin-info>",
83                  "    <application-version min='3' max='4' />",
84                  "  </plugin-info>",
85                  "</atlassian-plugin>");
86          assertEquals(3, (int) parser.getPluginInformation().getMinVersion());
87          assertEquals(4, (int) parser.getPluginInformation().getMaxVersion());
88      }
89  
90      public void testPluginsApplicationVersionMinMaxWithOnlyMin()
91      {
92          XmlDescriptorParser parser = parse(null,
93                  "<atlassian-plugin key='foo'>",
94                  "  <plugin-info>",
95                  "    <application-version min='3' />",
96                  "  </plugin-info>",
97                  "</atlassian-plugin>");
98          assertEquals(3, (int) parser.getPluginInformation().getMinVersion());
99          assertEquals(0, (int) parser.getPluginInformation().getMaxVersion());
100     }
101 
102     public void testPluginsApplicationVersionMinMaxWithOnlyMax()
103     {
104         XmlDescriptorParser parser = parse(null,
105                 "<atlassian-plugin key='foo'>",
106                 "  <plugin-info>",
107                 "    <application-version max='3' />",
108                 "  </plugin-info>",
109                 "</atlassian-plugin>");
110         assertEquals(3, (int) parser.getPluginInformation().getMaxVersion());
111         assertEquals(0, (int) parser.getPluginInformation().getMinVersion());
112     }
113 
114     // Also CONF-12680 test for missing "essential metadata"
115 
116     public void testPluginsVersion()
117     {
118         String xml = "<atlassian-plugin key=\"foo\" pluginsVersion=\"2\" />";
119         XmlDescriptorParser parser = new XmlDescriptorParser(new ByteArrayInputStream(xml.getBytes()), ImmutableSet.<Application>of());
120         assertEquals(2, parser.getPluginsVersion());
121     }
122 
123     public void testPluginsVersionAfterConfigure()
124     {
125         XmlDescriptorParser parser = new XmlDescriptorParser(new ByteArrayInputStream("<atlassian-plugin key=\"foo\" plugins-version=\"2\" />".getBytes()), ImmutableSet.<Application>of());
126         // mock up some supporting objects
127         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
128         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
129         mockFactory.expect("getModuleDescriptorClass", "unknown-plugin");
130 
131         // create a Plugin for testing
132         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
133         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
134         assertEquals(2, testPlugin.getPluginsVersion());
135     }
136 
137     public void testPluginWithModules()
138     {
139         XmlDescriptorParser parser = parse(null,
140                 "<atlassian-plugin key='foo'>",
141                 "  <animal key='bear' />",
142                 "</atlassian-plugin>");
143         // mock up some supporting objects
144         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
145         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
146         mockFactory.expectAndReturn("getModuleDescriptorClass", C.args(C.eq("animal")), MockAnimalModuleDescriptor.class);
147 
148         // create a Plugin for testing
149         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
150         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
151         assertNotNull(testPlugin.getModuleDescriptor("bear"));
152     }
153 
154     public void testPluginWithModulesNoApplicationKey()
155     {
156         XmlDescriptorParser parser = parse(null,
157                 "<atlassian-plugin key='foo'>",
158                 "  <animal key='bear' application='foo'/>",
159                 "</atlassian-plugin>");
160         // mock up some supporting objects
161         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
162         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
163         mockFactory.expectAndReturn("getModuleDescriptorClass", C.args(C.eq("animal")), MockAnimalModuleDescriptor.class);
164 
165         // create a Plugin for testing
166         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
167         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
168         assertNull(testPlugin.getModuleDescriptor("bear"));
169     }
170 
171     public void testPluginWithSomeNonApplicationModules()
172     {
173         XmlDescriptorParser parser = parse(newApplication("myapp"),
174                 "<atlassian-plugin key='foo'>",
175                 "  <animal key='bear' application='myapp'/>",
176                 "  <animal key='bear2' application='otherapp'/>",
177                 "</atlassian-plugin>");
178         // mock up some supporting objects
179         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
180         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
181         mockFactory.expectAndReturn("getModuleDescriptorClass", C.args(C.eq("animal")), MockAnimalModuleDescriptor.class);
182 
183         // create a Plugin for testing
184         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
185         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
186         assertNotNull(testPlugin.getModuleDescriptor("bear"));
187         assertNull(testPlugin.getModuleDescriptor("bear2"));
188     }
189 
190     public void testPluginWithSomeRestrictionOnModulesNoVersion()
191     {
192         XmlDescriptorParser parser = parse(newApplication("myapp"),
193                 "<atlassian-plugin key='foo'>",
194                 "  <animal key='bear'>",
195                 "    <restrict application='myapp'/>",
196                 "  </animal>",
197                 "  <animal key='bear2' application='otherapp'/>",
198                 "</atlassian-plugin>");
199         // mock up some supporting objects
200         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
201         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
202         mockFactory.expectAndReturn("getModuleDescriptorClass", C.args(C.eq("animal")), MockAnimalModuleDescriptor.class);
203 
204         // create a Plugin for testing
205         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
206         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
207         assertNotNull(testPlugin.getModuleDescriptor("bear"));
208         assertNull(testPlugin.getModuleDescriptor("bear2"));
209     }
210 
211     public void testPluginWithSomeRestrictionOnModulesVersionAsAttribute()
212     {
213         XmlDescriptorParser parser = parse(newApplication("myapp", "2.0"),
214                 "<atlassian-plugin key='foo'>",
215                 "  <animal key='bear'>",
216                 "    <restrict application='myapp' version='[2.0]' />",
217                 "  </animal>",
218                 "  <animal key='bear2'>",
219                 "    <restrict application='myapp' version='[1.0]' />",
220                 "  </animal>",
221                 "</atlassian-plugin>");
222         // mock up some supporting objects
223         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
224         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
225         mockFactory.matchAndReturn("getModuleDescriptorClass", C.args(C.eq("animal")), MockAnimalModuleDescriptor.class);
226 
227         // create a Plugin for testing
228         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
229         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
230         assertNotNull(testPlugin.getModuleDescriptor("bear"));
231         assertNull(testPlugin.getModuleDescriptor("bear2"));
232     }
233 
234     public void testPluginWithSomeRestrictionOnModulesVersionAsElement()
235     {
236         XmlDescriptorParser parser = parse(newApplication("myapp", "2.0"),
237                 "<atlassian-plugin key='foo'>",
238                 "  <animal key='bear'>",
239                 "    <restrict application='myapp'>",
240                 "      <version>(,3.0)</version>",
241                 "      <version>[5.0,)</version>",
242                 "    </restrict>",
243                 "  </animal>",
244                 "  <animal key='bear2'>",
245                 "    <restrict application='myapp'>",
246                 "      <version>(,2.0)</version>",
247                 "      <version>[5.0,)</version>",
248                 "    </restrict>",
249                 "  </animal>",
250                 "</atlassian-plugin>");
251         // mock up some supporting objects
252         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
253         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
254         mockFactory.matchAndReturn("getModuleDescriptorClass", C.args(C.eq("animal")), MockAnimalModuleDescriptor.class);
255 
256         // create a Plugin for testing
257         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
258         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
259         assertNotNull(testPlugin.getModuleDescriptor("bear"));
260         assertNull(testPlugin.getModuleDescriptor("bear2"));
261     }
262 
263     public void testPluginWithSystemAttribute()
264     {
265         XmlDescriptorParser parser = parse(null,
266                 "<atlassian-plugin key='foo' system='true'>",
267                 "</atlassian-plugin>");
268 
269         // mock up some supporting objects
270         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
271         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
272         mockFactory.expectAndReturn("getModuleDescriptorClass", C.args(C.eq("animal")), MockAnimalModuleDescriptor.class);
273 
274         // create a Plugin for testing
275         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
276         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
277         // PLUG-415 Plugins2 plugins now need to be able to be declared as system.
278         assertEquals("This plugin should be a system plugin - bundled plugins2 plugins are system plugins.", true, testPlugin.isSystemPlugin());
279     }
280 
281     public void testPluginWithoutSystemAttribute()
282     {
283         XmlDescriptorParser parser = parse(null,
284                 "<atlassian-plugin key='foo' >",
285                 "</atlassian-plugin>");
286 
287         // mock up some supporting objects
288         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
289         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
290         mockFactory.expectAndReturn("getModuleDescriptorClass", C.args(C.eq("animal")), MockAnimalModuleDescriptor.class);
291 
292         // create a Plugin for testing
293         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
294         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
295         assertEquals("This plugin should not be a system plugin.", false, testPlugin.isSystemPlugin());
296     }
297 
298     public void testPluginsVersionWithDash()
299     {
300         String xml = "<atlassian-plugin key=\"foo\" plugins-version=\"2\" />";
301         XmlDescriptorParser parser = new XmlDescriptorParser(new ByteArrayInputStream(xml.getBytes()), ImmutableSet.<Application>of());
302         assertEquals(2, parser.getPluginsVersion());
303     }
304 
305     public void testPluginsVersionMissing()
306     {
307         String xml = "<atlassian-plugin key=\"foo\" />";
308         XmlDescriptorParser parser = new XmlDescriptorParser(new ByteArrayInputStream(xml.getBytes()), ImmutableSet.<Application>of());
309         assertEquals(1, parser.getPluginsVersion());
310     }
311 
312     public void testPluginsResourcesAvailableToModuleDescriptors()
313     {
314         XmlDescriptorParser parser = parse(null,
315                 "<atlassian-plugin key='foo'>",
316                 "  <resource type='velocity' name='edit'>Show an input box here.</resource>",
317                 "  <animal key='bear' />",
318                 "</atlassian-plugin>");
319         // mock up some supporting objects
320         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
321         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
322         MockAnimalModuleDescriptor descriptor = new MockAnimalModuleDescriptor("velocity", "edit");
323         mockFactory.expectAndReturn("getModuleDescriptor", C.args(C.eq("animal")), descriptor);
324 
325         // create a Plugin for testing
326         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
327         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
328         assertNotNull(testPlugin.getModuleDescriptor("bear"));
329 
330         mockFactory.verify();
331     }
332 
333     public void testPluginPermissionsIsAllPermissionsForPluginsWithNoVersionByDefault()
334     {
335         XmlDescriptorParser parser = parse(null,
336                 "<atlassian-plugin key='foo'>",
337                 "  <plugin-info>",
338                 "  </plugin-info>",
339                 "</atlassian-plugin>");
340 
341         final Set<PluginPermission> parsedPermissions = parser.getPluginInformation().getPermissions();
342         assertEquals(1, parsedPermissions.size());
343         assertSame(PluginPermission.ALL, Iterables.get(parsedPermissions, 0));
344     }
345 
346     public void testPluginPermissionsIsAllPermissionsForPlugins1ByDefault()
347     {
348         XmlDescriptorParser parser = parse(null,
349                 "<atlassian-plugin key='foo' plugins-version='1'>",
350                 "  <plugin-info>",
351                 "  </plugin-info>",
352                 "</atlassian-plugin>");
353 
354         final Set<PluginPermission> parsedPermissions = parser.getPluginInformation().getPermissions();
355         assertEquals(1, parsedPermissions.size());
356         assertSame(PluginPermission.ALL, Iterables.get(parsedPermissions, 0));
357     }
358 
359     public void testPluginPermissionsIsAllPermissionsForPlugins2ByDefault()
360     {
361         XmlDescriptorParser parser = parse(null,
362                 "<atlassian-plugin key='foo' plugins-version='2'>",
363                 "  <plugin-info>",
364                 "  </plugin-info>",
365                 "</atlassian-plugin>");
366 
367         final Set<PluginPermission> parsedPermissions = parser.getPluginInformation().getPermissions();
368         assertEquals(1, parsedPermissions.size());
369         assertSame(PluginPermission.ALL, Iterables.get(parsedPermissions, 0));
370     }
371 
372     public void testPluginPermissionsIsFilteredByApplications()
373     {
374         XmlDescriptorParser parser = parse(newApplication("my-app"),
375                 "<atlassian-plugin key='foo' plugins-version='2'>",
376                 "  <plugin-info>",
377                 "    <permissions>",
378                 "      <permission application='my-other-app'>some_permission</permission>",
379                 "      <permission application='my-app'>some_other_permission</permission>",
380                 "      <permission application='my-other-app' installation-mode='local'>yet_another_permission</permission>",
381                 "    </permissions>",
382                 "  </plugin-info>",
383                 "</atlassian-plugin>");
384 
385         final Set<PluginPermission> parsedPermissions = parser.getPluginInformation().getPermissions();
386         assertEquals(1, parsedPermissions.size());
387         assertEquals("some_other_permission", Iterables.get(parsedPermissions, 0).getName());
388     }
389 
390     public void testPluginPermissions()
391     {
392         XmlDescriptorParser parser = parse(newApplication("my-other-app"),
393                 "<atlassian-plugin key='foo' plugins-version='2'>",
394                 "  <plugin-info>",
395                 "    <permissions>",
396                 "      <permission>some_permission</permission>",
397                 "      <permission application='my-app'>some_other_permission</permission>",
398                 "      <permission installation-mode='local'>yet_another_permission</permission>",
399                 "    </permissions>",
400                 "  </plugin-info>",
401                 "</atlassian-plugin>");
402 
403         final Set<PluginPermission> parsedPermissions = parser.getPluginInformation().getPermissions();
404         assertEquals(2, parsedPermissions.size());
405 
406         final PluginPermission firstPermission = Iterables.get(parsedPermissions, 0);
407         assertEquals("some_permission", firstPermission.getName());
408         assertEquals(Option.<InstallationMode>none(), firstPermission.getInstallationMode());
409 
410         final PluginPermission secondPermission = Iterables.get(parsedPermissions, 1);
411         assertEquals("yet_another_permission", secondPermission.getName());
412         assertEquals(some(InstallationMode.LOCAL), secondPermission.getInstallationMode());
413     }
414 
415     public void testPluginPermissionsIsEmptyForRemotablePluginByDefault()
416     {
417         XmlDescriptorParser parser = parse(null,
418                 "<atlassian-plugin key='foo' plugins-version='3'>",
419                 "  <plugin-info>",
420                 "  </plugin-info>",
421                 "</atlassian-plugin>");
422 
423         assertTrue(parser.getPluginInformation().getPermissions().isEmpty());
424     }
425 
426     public void testPluginPermissionsIsEmptyForBlankPermissions()
427     {
428         XmlDescriptorParser parser = parse(null,
429                 "<atlassian-plugin key='foo' plugins-version='3'>",
430                 "  <plugin-info>",
431                 "    <permissions>",
432                 "      <permission> \t </permission>",
433                 "      <permission></permission>",
434                 "    </permissions>",
435                 "  </plugin-info>",
436                 "</atlassian-plugin>");
437 
438         assertTrue(parser.getPluginInformation().getPermissions().isEmpty());
439     }
440 
441     private String getTestFile(String filename)
442     {
443         final URL url = ClassLoaderUtils.getResource(filename, this.getClass());
444         return url.getFile();
445     }
446 
447     private static XmlDescriptorParser parse(Application application, String... lines)
448     {
449         StringBuffer sb = new StringBuffer();
450         for (String line : lines)
451         {
452             sb.append(line.replace('\'', '"')).append('\n');
453         }
454         InputStream in = new ByteArrayInputStream(sb.toString().getBytes());
455         return new XmlDescriptorParser(in, option(application).fold(
456                 Suppliers.ofInstance(ImmutableSet.<Application>of()),
457                 new Function<Application, Set<Application>>()
458                 {
459                     @Override
460                     public Set<Application> apply(Application app)
461                     {
462                         return ImmutableSet.of(app);
463                     }
464                 }));
465     }
466 
467     private Application newApplication(final String appKey)
468     {
469         return newApplication(appKey, null);
470     }
471 
472     private Application newApplication(final String appKey, final String version)
473     {
474         return new Application()
475         {
476             @Override
477             public String getKey()
478             {
479                 return appKey;
480             }
481 
482             @Override
483             public String getVersion()
484             {
485                 return version;
486             }
487 
488             @Override
489             public String getBuildNumber()
490             {
491                 return null;
492             }
493         };
494     }
495 }