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