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"})
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
47 public void testMissingPluginInfo()
48 {
49
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
275 Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
276 parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
277
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
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
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
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
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 }