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