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