1 package com.atlassian.plugin.impl;
2
3 import com.atlassian.plugin.ModuleDescriptor;
4 import com.atlassian.plugin.Plugin;
5 import com.atlassian.plugin.PluginInformation;
6 import com.atlassian.plugin.PluginState;
7 import com.atlassian.plugin.util.VersionStringComparator;
8 import org.hamcrest.Description;
9 import org.hamcrest.Matcher;
10 import org.hamcrest.Matchers;
11 import org.hamcrest.TypeSafeMatcher;
12 import org.junit.Test;
13
14 import java.io.InputStream;
15 import java.net.URL;
16 import java.util.Date;
17 import java.util.Optional;
18
19 import static org.hamcrest.MatcherAssert.assertThat;
20 import static org.hamcrest.Matchers.comparesEqualTo;
21 import static org.hamcrest.Matchers.empty;
22 import static org.hamcrest.Matchers.emptyIterable;
23 import static org.hamcrest.Matchers.equalTo;
24 import static org.hamcrest.Matchers.equalToIgnoringWhiteSpace;
25 import static org.hamcrest.Matchers.greaterThan;
26 import static org.hamcrest.Matchers.is;
27 import static org.hamcrest.Matchers.lessThan;
28 import static org.hamcrest.Matchers.lessThanOrEqualTo;
29 import static org.hamcrest.Matchers.not;
30 import static org.hamcrest.Matchers.notNullValue;
31 import static org.hamcrest.Matchers.nullValue;
32 import static org.junit.Assert.assertEquals;
33 import static org.junit.Assert.assertFalse;
34 import static org.junit.Assert.assertTrue;
35 import static org.mockito.Mockito.mock;
36
37 public class TestAbstractPlugin {
38 @Test
39 public void compareToSortsByKey() {
40 final Plugin alpha = createAbstractPlugin("alpha");
41 final Plugin beta = createAbstractPlugin("beta");
42
43
44 assertThat(alpha, lessThan(beta));
45 assertThat(beta, greaterThan(alpha));
46 }
47
48 @Test
49 public void compareToSortsMilestonesBeforeNumericVersions() {
50 final Plugin milestone = createAbstractPlugin("foo", "1.2.m2");
51 final Plugin numeric = createAbstractPlugin("foo", "1.2.1");
52
53
54 assertThat(milestone, lessThan(numeric));
55 assertThat(numeric, greaterThan(milestone));
56 }
57
58 @Test
59 public void compareToSortsByVersion() {
60 final Plugin lateVersion = createAbstractPlugin("foo", "3.4.1");
61 final Plugin earlyVersion = createAbstractPlugin("foo", "3.1.4");
62
63
64 assertThat(earlyVersion, lessThan(lateVersion));
65 assertThat(lateVersion, greaterThan(earlyVersion));
66 }
67
68 @Test
69 public void compareToEqualWhenKeyAndVersionEqual() {
70 final Plugin firstPlugin = createAbstractPlugin("foo", "3.1.4");
71 final Plugin secondPlugin = createAbstractPlugin("foo", "3.1.4");
72
73
74 assertThat(firstPlugin, comparesEqualTo(secondPlugin));
75 assertThat(secondPlugin, comparesEqualTo(firstPlugin));
76 }
77
78 @Test
79 public void compareToTreatsNullPluginInformationAsVersionZero() {
80 final Plugin nullPluginInformation = createAbstractPlugin("foo");
81 nullPluginInformation.setPluginInformation(null);
82 final Plugin defaultPluginInformation = createAbstractPlugin("foo");
83
84
85 assertThat(defaultPluginInformation.getPluginInformation().getVersion(), is("0.0"));
86
87 assertThat(nullPluginInformation, comparesEqualTo(defaultPluginInformation));
88 assertThat(defaultPluginInformation, comparesEqualTo(nullPluginInformation));
89 }
90
91 @Test
92 public void compareToSortsInvalidVersionBeforeValidVersion() throws Exception {
93 final String invalidVersion = "@$%^#";
94 assertThat(invalidVersion, not(validVersion()));
95 final String validVersion = "3.2";
96 assertThat(validVersion, validVersion());
97
98 final Plugin invalidVersionPlugin = createAbstractPlugin("foo", invalidVersion);
99 final Plugin validVersionPlugin = createAbstractPlugin("foo", validVersion);
100
101
102 assertThat(invalidVersionPlugin, lessThan(validVersionPlugin));
103 assertThat(validVersionPlugin, greaterThan(invalidVersionPlugin));
104 }
105
106 @Test
107 public void compareToEqualWhenBothVersionsInvalid() throws Exception {
108 final Plugin firstInvalidPlugin = createAbstractPlugin("foo", "@$%^#");
109 final Plugin secondInvalidPlugin = createAbstractPlugin("foo", "!!");
110
111 assertThat(firstInvalidPlugin.getPluginInformation().getVersion(), not(validVersion()));
112 assertThat(secondInvalidPlugin.getPluginInformation().getVersion(), not(validVersion()));
113
114
115 assertThat(firstInvalidPlugin, comparesEqualTo(secondInvalidPlugin));
116 assertThat(secondInvalidPlugin, comparesEqualTo(firstInvalidPlugin));
117 }
118
119 @Test
120 public void compareToSortsNullKeyBeforeNonNullKey() {
121 final Plugin nullKey = createAbstractPlugin();
122 final Plugin nonNullKey = createAbstractPlugin("foo");
123
124
125 assertThat(nullKey.getKey(), nullValue());
126 assertThat(nullKey, lessThan(nonNullKey));
127 assertThat(nonNullKey, greaterThan(nullKey));
128 }
129
130 @Test
131 public void compareToEqualWhenBothKeysNull() {
132 final Plugin firstNullPlugin = createAbstractPlugin();
133 final Plugin secondNullPlugin = createAbstractPlugin();
134
135 assertThat(firstNullPlugin.getKey(), nullValue());
136 assertThat(secondNullPlugin.getKey(), nullValue());
137 assertThat(firstNullPlugin, comparesEqualTo(secondNullPlugin));
138 assertThat(secondNullPlugin, comparesEqualTo(firstNullPlugin));
139 }
140
141 @Test
142 public void getNameDefaultsToKey() {
143 final AbstractPlugin plugin = createAbstractPlugin("foo");
144 assertThat(plugin.getName(), is("foo"));
145 }
146
147 @Test
148 public void getNameReturnsSetName() {
149 final Plugin plugin = createAbstractPlugin("key");
150 plugin.setI18nNameKey("i18n");
151 plugin.setName("name");
152 assertThat(plugin.getName(), is("name"));
153 }
154
155 @Test
156 public void getNameReturnsBlankIfI18nNameKeySpecified() {
157 final Plugin plugin = createAbstractPlugin("foo");
158 plugin.setI18nNameKey("i18n");
159 assertThat(plugin.getName(), equalToIgnoringWhiteSpace(""));
160 }
161
162 @Test(timeout = 5000)
163 public void fastAsynchronousEnableIsNotLost() {
164 final boolean[] enableThreadDidSet = new boolean[1];
165
166
167
168 final Plugin plugin = new ConcretePlugin() {
169 final private Thread enableThread = new Thread() {
170 @Override
171 public void run() {
172 synchronized (enableThreadDidSet) {
173
174 enableThreadDidSet[0] = compareAndSetPluginState(PluginState.ENABLING, PluginState.ENABLED);
175 enableThreadDidSet.notify();
176 }
177 }
178 };
179
180 private PluginState slowly(final PluginState state) {
181 try {
182
183
184
185 enableThreadDidSet.wait();
186 } catch (final InterruptedException interruptedException) {
187 throw new RuntimeException(interruptedException);
188 }
189 return state;
190 }
191
192 @Override
193 protected PluginState enableInternal() {
194 final PluginState state = PluginState.ENABLING;
195 setPluginState(state);
196
197
198 synchronized (enableThreadDidSet) {
199 enableThread.start();
200 return slowly(PluginState.PENDING);
201 }
202 }
203 };
204 plugin.enable();
205
206 assertThat(enableThreadDidSet[0], is(true));
207
208 assertThat(plugin.getPluginState(), is(PluginState.ENABLED));
209 }
210
211 @Test(timeout = 5000)
212 public void slowAsynchronousEnableIsNotLost() throws Exception {
213 final boolean[] enableThreadDidSet = new boolean[1];
214
215
216
217 final Plugin plugin = new ConcretePlugin() {
218 final private Thread enableThread = new Thread() {
219 @Override
220 public void run() {
221 synchronized (enableThreadDidSet) {
222
223 enableThreadDidSet[0] = compareAndSetPluginState(PluginState.ENABLING, PluginState.ENABLED);
224 enableThreadDidSet.notify();
225 }
226 }
227 };
228
229 @Override
230 protected PluginState enableInternal() {
231 final PluginState state = PluginState.ENABLING;
232 setPluginState(state);
233 enableThread.start();
234 return PluginState.PENDING;
235 }
236 };
237
238
239
240
241 synchronized (enableThreadDidSet) {
242 plugin.enable();
243
244 assertThat(plugin.getPluginState(), is(PluginState.ENABLING));
245
246
247
248 enableThreadDidSet.wait();
249 }
250
251 assertThat(enableThreadDidSet[0], is(true));
252
253 assertThat(plugin.getPluginState(), is(PluginState.ENABLED));
254 }
255
256
257 @Test
258 public void enableTimesAreInitiallyNull() {
259 final AbstractPlugin plugin = createAbstractPlugin();
260 assertThat(plugin.getDateEnabling(), nullValue());
261 assertThat(plugin.getDateEnabled(), nullValue());
262 }
263
264 @Test
265 public void simpleEnableSetsBothEnableTimes()
266 throws InterruptedException {
267 final AbstractPlugin plugin = createAbstractPlugin();
268
269 final Date before = ensureTimePasses();
270
271 plugin.enable();
272 final Date enabling = plugin.getDateEnabling();
273 final Date enabled = plugin.getDateEnabled();
274
275 final Date after = ensureTimePasses();
276
277
278 assertThat(enabling, notNullValue());
279 assertThat(enabled, notNullValue());
280 assertThat(before, lessThan(enabling));
281 assertThat(enabling, lessThanOrEqualTo(enabled));
282 assertThat(enabled, lessThan(after));
283 }
284
285 @Test
286 public void slowEnableSetsEnableTimesSeparately()
287 throws InterruptedException {
288 final SlowAbstractPlugin plugin = new SlowAbstractPlugin();
289
290 final Date before = ensureTimePasses();
291
292 plugin.enable();
293 assertThat(plugin.getPluginState(), is(PluginState.ENABLING));
294 assertThat(plugin.getDateEnabled(), nullValue());
295 final Date enabling = plugin.getDateEnabling();
296
297 final Date middle = ensureTimePasses();
298
299 final boolean finishedEnable = plugin.finishEnable();
300 assertThat(finishedEnable, is(true));
301 assertThat(plugin.getPluginState(), is(PluginState.ENABLED));
302 assertThat(plugin.getDateEnabling(), equalTo(enabling));
303 final Date enabled = plugin.getDateEnabled();
304
305 final Date after = ensureTimePasses();
306
307 assertThat(enabling, notNullValue());
308 assertThat(enabled, notNullValue());
309 assertThat(before, lessThan(enabling));
310 assertThat(enabling, lessThan(middle));
311 assertThat(middle, lessThan(enabled));
312 assertThat(enabled, lessThan(after));
313 }
314
315 @Test
316 public void secondEnableClearsEnablingTime()
317 throws InterruptedException {
318 final SlowAbstractPlugin plugin = new SlowAbstractPlugin();
319
320 plugin.enable();
321
322 ensureTimePasses();
323
324 assertThat(plugin.getDateEnabled(), nullValue());
325 final Date firstEnabling = plugin.getDateEnabling();
326 plugin.finishEnable();
327
328 ensureTimePasses();
329
330 assertThat(plugin.getDateEnabling(), equalTo(firstEnabling));
331 final Date firstEnabled = plugin.getDateEnabled();
332 plugin.disable();
333
334 ensureTimePasses();
335
336 assertThat(plugin.getDateEnabling(), equalTo(firstEnabling));
337 assertThat(plugin.getDateEnabled(), equalTo(firstEnabled));
338 plugin.enable();
339
340 ensureTimePasses();
341
342 assertThat(plugin.getDateEnabled(), nullValue());
343 final Date secondEnabling = plugin.getDateEnabling();
344 plugin.finishEnable();
345
346 ensureTimePasses();
347
348 assertThat(plugin.getDateEnabling(), equalTo(secondEnabling));
349 final Date secondEnabled = plugin.getDateEnabled();
350
351 assertThat(firstEnabling, notNullValue());
352 assertThat(firstEnabled, notNullValue());
353 assertThat(secondEnabling, notNullValue());
354 assertThat(secondEnabled, notNullValue());
355 assertThat(firstEnabling, lessThan(firstEnabled));
356 assertThat(firstEnabled, lessThan(secondEnabling));
357 assertThat(secondEnabling, lessThan(secondEnabled));
358 }
359
360 @Test
361 public void dynamicModuleDescriptors() {
362 final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class);
363
364 final AbstractPlugin abstractPlugin = createAbstractPlugin();
365
366 assertThat(abstractPlugin.getModuleDescriptors(), empty());
367 assertThat(abstractPlugin.getDynamicModuleDescriptors(), emptyIterable());
368
369 abstractPlugin.addDynamicModuleDescriptor(moduleDescriptor);
370
371 assertThat(abstractPlugin.getModuleDescriptors(), Matchers.contains(moduleDescriptor));
372 assertThat(abstractPlugin.getDynamicModuleDescriptors(), Matchers.contains(moduleDescriptor));
373
374 abstractPlugin.removeDynamicModuleDescriptor(moduleDescriptor);
375
376 assertThat(abstractPlugin.getModuleDescriptors(), empty());
377 assertThat(abstractPlugin.getDynamicModuleDescriptors(), emptyIterable());
378 }
379
380 private AbstractPlugin createAbstractPlugin(final String key, final String version) {
381 final AbstractPlugin plugin = createAbstractPlugin(key);
382 plugin.getPluginInformation().setVersion(version);
383 return plugin;
384 }
385
386 private AbstractPlugin createAbstractPlugin(final String key) {
387 final AbstractPlugin plugin = createAbstractPlugin();
388 plugin.setKey(key);
389 return plugin;
390 }
391
392 private AbstractPlugin createAbstractPlugin() {
393 return new ConcretePlugin();
394 }
395
396
397
398
399 private static class ConcretePlugin extends AbstractPlugin {
400 public ConcretePlugin() {
401 super(null);
402 }
403
404 @Override
405 public boolean isUninstallable() {
406 return false;
407 }
408
409 @Override
410 public boolean isDeleteable() {
411 return false;
412 }
413
414 @Override
415 public boolean isDynamicallyLoaded() {
416 return false;
417 }
418
419 @Override
420 public <T> Class<T> loadClass(final String clazz, final Class<?> callingClass) throws ClassNotFoundException {
421 return null;
422 }
423
424 @Override
425 public ClassLoader getClassLoader() {
426 return null;
427 }
428
429 @Override
430 public URL getResource(final String path) {
431 return null;
432 }
433
434 @Override
435 public InputStream getResourceAsStream(final String name) {
436 return null;
437 }
438 }
439
440
441
442
443 private class SlowAbstractPlugin extends ConcretePlugin {
444 @Override
445 public PluginState enableInternal() {
446 return PluginState.ENABLING;
447 }
448
449 public boolean finishEnable() {
450 return compareAndSetPluginState(PluginState.ENABLING, PluginState.ENABLED);
451 }
452 }
453
454
455
456
457
458
459
460 private Date ensureTimePasses()
461 throws InterruptedException {
462 final long before = System.currentTimeMillis();
463 while (before >= System.currentTimeMillis()) {
464 Thread.sleep(2);
465 }
466 final Date date = new Date();
467 while (date.getTime() >= System.currentTimeMillis()) {
468 Thread.sleep(2);
469 }
470 return date;
471 }
472
473 private static final Matcher<String> VALID_VERSION_MATCHER = new TypeSafeMatcher<String>() {
474 @Override
475 protected boolean matchesSafely(final String version) {
476 return VersionStringComparator.isValidVersionString(version);
477 }
478
479 @Override
480 public void describeTo(final Description description) {
481 description.appendText("valid version string");
482 }
483 };
484
485 private Matcher<String> validVersion() {
486 return VALID_VERSION_MATCHER;
487 }
488
489
490 @Test
491 public void testComparePluginNewer() {
492
493 final Plugin p1 = createPluginWithVersion("1.1");
494 final Plugin p2 = createPluginWithVersion("1.0");
495 assertTrue(p1.compareTo(p2) == 1);
496
497 p1.getPluginInformation().setVersion("1.10");
498 p2.getPluginInformation().setVersion("1.2");
499 assertTrue(p1.compareTo(p2) == 1);
500
501 p1.getPluginInformation().setVersion("1.2");
502 p2.getPluginInformation().setVersion("1.01");
503 assertTrue(p1.compareTo(p2) == 1);
504
505 p1.getPluginInformation().setVersion("1.0.1");
506 p2.getPluginInformation().setVersion("1.0");
507 assertTrue(p1.compareTo(p2) == 1);
508
509 p1.getPluginInformation().setVersion("1.2");
510 p2.getPluginInformation().setVersion("1.1.1");
511 assertTrue(p1.compareTo(p2) == 1);
512 }
513
514 @Test
515 public void testComparePluginOlder() {
516 final Plugin p1 = createPluginWithVersion("1.0");
517 final Plugin p2 = createPluginWithVersion("1.1");
518 assertTrue(p1.compareTo(p2) == -1);
519
520 p1.getPluginInformation().setVersion("1.2");
521 p2.getPluginInformation().setVersion("1.10");
522 assertTrue(p1.compareTo(p2) == -1);
523
524 p1.getPluginInformation().setVersion("1.01");
525 p2.getPluginInformation().setVersion("1.2");
526 assertTrue(p1.compareTo(p2) == -1);
527
528 p1.getPluginInformation().setVersion("1.0");
529 p2.getPluginInformation().setVersion("1.0.1");
530 assertTrue(p1.compareTo(p2) == -1);
531
532 p1.getPluginInformation().setVersion("1.1.1");
533 p2.getPluginInformation().setVersion("1.2");
534 assertTrue(p1.compareTo(p2) == -1);
535 }
536
537 @Test
538 public void testComparePluginEqual() {
539 final Plugin p1 = createPluginWithVersion("1.0");
540 final Plugin p2 = createPluginWithVersion("1.0");
541 assertTrue(p1.compareTo(p2) == 0);
542
543 p1.getPluginInformation().setVersion("1.1.0.0");
544 p2.getPluginInformation().setVersion("1.1");
545 assertTrue(p1.compareTo(p2) == 0);
546
547 p1.getPluginInformation().setVersion(" 1 . 1 ");
548 p2.getPluginInformation().setVersion("1.1");
549 assertTrue(p1.compareTo(p2) == 0);
550 }
551
552
553 @Test
554 public void testComparePluginNoVersion() {
555 final Plugin p1 = createPluginWithVersion("1.0");
556 final Plugin p2 = createPluginWithVersion("#$%");
557 assertEquals(1, p1.compareTo(p2));
558
559 p1.getPluginInformation().setVersion("#$%");
560 p2.getPluginInformation().setVersion("1.0");
561 assertEquals(-1, p1.compareTo(p2));
562 }
563
564 @Test
565 public void testComparePluginBadPlugin() {
566 final Plugin p1 = createPluginWithVersion("1.0");
567 final Plugin p2 = createPluginWithVersion("1.0");
568
569
570 p2.setKey("bad.key");
571 assertTrue(p1.compareTo(p2) != 0);
572 }
573
574 @Test
575 public void testMissingScopeInPluginInformation() {
576 final Plugin p1 = createAbstractPlugin();
577 p1.getPluginInformation().setScopeKey(Optional.<String>empty());
578
579 assertFalse(p1.getScopeKey().isPresent());
580 }
581
582 @Test
583 public void testScopePresentInPluginInformation() {
584 final Plugin p1 = createAbstractPlugin();
585 p1.getPluginInformation().setScopeKey(Optional.of("dark-features/confluence-team-calendars"));
586
587 assertThat(p1.getScopeKey().get(), equalTo("dark-features/confluence-team-calendars"));
588 }
589
590 public static Plugin createPluginWithVersion(final String version) {
591 final Plugin p = new ConcretePlugin();
592 p.setKey("test.default.plugin");
593 final PluginInformation pInfo = p.getPluginInformation();
594 pInfo.setVersion(version);
595 return p;
596 }
597 }