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