1   package com.atlassian.plugin.webresource;
2   
3   import com.atlassian.plugin.Plugin;
4   import com.atlassian.plugin.PluginAccessor;
5   import com.atlassian.plugin.elements.ResourceDescriptor;
6   import com.atlassian.plugin.impl.StaticPlugin;
7   import com.atlassian.plugin.servlet.AbstractFileServerServlet;
8   import com.mockobjects.dynamic.C;
9   import com.mockobjects.dynamic.Mock;
10  import junit.framework.TestCase;
11  
12  import java.io.StringWriter;
13  import java.util.Arrays;
14  import java.util.Collection;
15  import java.util.Collections;
16  import java.util.HashMap;
17  import java.util.HashSet;
18  import java.util.List;
19  import java.util.Map;
20  
21  import org.dom4j.DocumentException;
22  
23  public class TestWebResourceManagerImpl extends TestCase
24  {
25  
26      private Mock mockWebResourceIntegration;
27      private Mock mockPluginAccessor;
28      private WebResourceManagerImpl webResourceManager;
29      private PluginResourceLocator pluginResourceLocator;
30      private TestResourceBatchingConfiguration resourceBatchingConfiguration;
31      private Plugin testPlugin;
32  
33      private static final String ANIMAL_PLUGIN_VERSION = "2";
34      private static final String BASEURL = "http://www.foo.com";
35      private static final String SYSTEM_COUNTER = "123";
36      private static final String SYSTEM_BUILD_NUMBER = "650";
37  
38      protected void setUp() throws Exception
39      {
40          super.setUp();
41  
42          mockPluginAccessor = new Mock(PluginAccessor.class);
43  
44          mockWebResourceIntegration = new Mock(WebResourceIntegration.class);
45          mockWebResourceIntegration.matchAndReturn("getPluginAccessor", mockPluginAccessor.proxy());
46  
47          resourceBatchingConfiguration = new TestResourceBatchingConfiguration();
48          pluginResourceLocator = new PluginResourceLocatorImpl((WebResourceIntegration) mockWebResourceIntegration.proxy(), null);
49          webResourceManager = new WebResourceManagerImpl(pluginResourceLocator, (WebResourceIntegration) mockWebResourceIntegration.proxy(), resourceBatchingConfiguration);
50  
51  
52          mockWebResourceIntegration.matchAndReturn("getBaseUrl", BASEURL);
53          mockWebResourceIntegration.matchAndReturn("getBaseUrl", C.args(C.eq(UrlMode.ABSOLUTE)), BASEURL);
54          mockWebResourceIntegration.matchAndReturn("getBaseUrl", C.args(C.eq(UrlMode.RELATIVE)), "");
55          mockWebResourceIntegration.matchAndReturn("getBaseUrl", C.args(C.eq(UrlMode.AUTO)), "");
56          mockWebResourceIntegration.matchAndReturn("getSystemBuildNumber", SYSTEM_BUILD_NUMBER);
57          mockWebResourceIntegration.matchAndReturn("getSystemCounter", SYSTEM_COUNTER);
58          mockWebResourceIntegration.matchAndReturn("getSuperBatchVersion", "12");
59  
60          testPlugin = TestUtils.createTestPlugin();
61      }
62  
63      protected void tearDown() throws Exception
64      {
65          webResourceManager = null;
66          mockPluginAccessor = null;
67          mockWebResourceIntegration = null;
68          testPlugin = null;
69          resourceBatchingConfiguration = null;
70  
71          super.tearDown();
72      }
73  
74      public void testRequireResources()
75      {
76          String resource1 = "test.atlassian:cool-stuff";
77          String resource2 = "test.atlassian:hot-stuff";
78  
79          mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resource1)),
80              TestUtils.createWebResourceModuleDescriptor(resource1, testPlugin));
81          mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resource2)),
82              TestUtils.createWebResourceModuleDescriptor(resource2, testPlugin));
83  
84          Map requestCache = setupRequestCache();
85          webResourceManager.requireResource(resource1);
86          webResourceManager.requireResource(resource2);
87          webResourceManager.requireResource(resource1); // require again to test it only gets included once
88  
89          Collection resources = (Collection) requestCache.get(WebResourceManagerImpl.REQUEST_CACHE_RESOURCE_KEY);
90          assertEquals(2, resources.size());
91          assertTrue(resources.contains(resource1));
92          assertTrue(resources.contains(resource2));
93      }
94  
95      public void testRequireResourcesWithDependencies()
96      {
97          String resource = "test.atlassian:cool-stuff";
98          String dependencyResource = "test.atlassian:hot-stuff";
99  
100         // cool-stuff depends on hot-stuff
101         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resource)),
102             TestUtils.createWebResourceModuleDescriptor(resource, testPlugin, Collections.EMPTY_LIST, Collections.singletonList(dependencyResource)));
103         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(dependencyResource)),
104             TestUtils.createWebResourceModuleDescriptor(dependencyResource, testPlugin));
105 
106         Map requestCache = setupRequestCache();
107         webResourceManager.requireResource(resource);
108 
109         Collection resources = (Collection) requestCache.get(WebResourceManagerImpl.REQUEST_CACHE_RESOURCE_KEY);
110         assertEquals(2, resources.size());
111         Object[] resourceArray = resources.toArray();
112         assertEquals(dependencyResource, resourceArray[0]);
113         assertEquals(resource, resourceArray[1]);
114     }
115 
116     public void testRequireResourcesWithCyclicDependency()
117     {
118         String resource1 = "test.atlassian:cool-stuff";
119         String resource2 = "test.atlassian:hot-stuff";
120 
121         // cool-stuff and hot-stuff depend on each other
122         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resource1)),
123             TestUtils.createWebResourceModuleDescriptor(resource1, testPlugin, Collections.EMPTY_LIST, Collections.singletonList(resource2)));
124         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resource2)),
125             TestUtils.createWebResourceModuleDescriptor(resource2, testPlugin, Collections.EMPTY_LIST, Collections.singletonList(resource1)));
126 
127         Map requestCache = setupRequestCache();
128         webResourceManager.requireResource(resource1);
129 
130         Collection resources = (Collection) requestCache.get(WebResourceManagerImpl.REQUEST_CACHE_RESOURCE_KEY);
131         assertEquals(2, resources.size());
132         Object[] resourceArray = resources.toArray();
133         assertEquals(resource2, resourceArray[0]);
134         assertEquals(resource1, resourceArray[1]);
135     }
136 
137     public void testRequireResourcesWithComplexCyclicDependency()
138     {
139         String resourceA = "test.atlassian:a";
140         String resourceB = "test.atlassian:b";
141         String resourceC = "test.atlassian:c";
142         String resourceD = "test.atlassian:d";
143         String resourceE = "test.atlassian:e";
144 
145         // A depends on B, C
146         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceA)),
147             TestUtils.createWebResourceModuleDescriptor(resourceA, testPlugin, Collections.EMPTY_LIST, Arrays.asList(resourceB, resourceC)));
148         // B depends on D
149         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceB)),
150             TestUtils.createWebResourceModuleDescriptor(resourceB, testPlugin, Collections.EMPTY_LIST, Collections.singletonList(resourceD)));
151         // C has no dependencies
152         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceC)),
153             TestUtils.createWebResourceModuleDescriptor(resourceC, testPlugin));
154         // D depends on E, A (cyclic dependency)
155         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceD)),
156             TestUtils.createWebResourceModuleDescriptor(resourceD, testPlugin, Collections.EMPTY_LIST, Arrays.asList(resourceE, resourceA)));
157         // E has no dependencies
158         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceE)),
159             TestUtils.createWebResourceModuleDescriptor(resourceE, testPlugin));
160 
161         Map requestCache = setupRequestCache();
162         webResourceManager.requireResource(resourceA);
163         // requiring a resource already included by A's dependencies shouldn't change the order
164         webResourceManager.requireResource(resourceD);
165 
166         Collection resources = (Collection) requestCache.get(WebResourceManagerImpl.REQUEST_CACHE_RESOURCE_KEY);
167         assertEquals(5, resources.size());
168         Object[] resourceArray = resources.toArray();
169         assertEquals(resourceE, resourceArray[0]);
170         assertEquals(resourceD, resourceArray[1]);
171         assertEquals(resourceB, resourceArray[2]);
172         assertEquals(resourceC, resourceArray[3]);
173         assertEquals(resourceA, resourceArray[4]);
174     }
175 
176     public void testGetResourceContext() throws Exception
177     {
178         final String resourceA = "test.atlassian:a";
179         final String resourceB = "test.atlassian:b";
180         final String resourceC = "test.atlassian:c";
181 
182         final WebResourceModuleDescriptor descriptor1 = TestUtils.createWebResourceModuleDescriptor(
183                 resourceA, testPlugin, TestUtils.createResourceDescriptors("resourceA.css"), Collections.EMPTY_LIST, Collections.EMPTY_SET);
184         final WebResourceModuleDescriptor descriptor2 = TestUtils.createWebResourceModuleDescriptor(
185                 resourceB, testPlugin, TestUtils.createResourceDescriptors("resourceB.css"), Collections.EMPTY_LIST, new HashSet<String>() {{
186                     add("foo");
187                 }});
188         final WebResourceModuleDescriptor descriptor3 = TestUtils.createWebResourceModuleDescriptor(
189                 resourceC, testPlugin, TestUtils.createResourceDescriptors("resourceC.css"), Collections.EMPTY_LIST, new HashSet<String>() {{
190                     add("foo");
191                     add("bar");
192                 }});
193 
194         final List<WebResourceModuleDescriptor> descriptors = Arrays.asList(descriptor1, descriptor2, descriptor3);
195 
196         mockPluginAccessor.matchAndReturn("getEnabledModuleDescriptorsByClass", C.args(C.eq(WebResourceModuleDescriptor.class)), descriptors);
197         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceA)), descriptor1);
198         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceB)), descriptor2);
199         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceC)), descriptor3);
200 
201         setupRequestCache();
202 
203         // write includes for all resources for "foo":
204         webResourceManager.requireResourcesForContext("foo");
205         StringWriter writer = new StringWriter();
206         webResourceManager.includeResources(writer);
207         String resources = writer.toString();
208         assertFalse(resources.contains(resourceA + ".css"));
209         assertTrue(resources.contains(resourceB + ".css"));
210         assertTrue(resources.contains(resourceC + ".css"));
211 
212         // write includes for all resources for "bar":
213         webResourceManager.requireResourcesForContext("bar");
214         writer = new StringWriter();
215         webResourceManager.includeResources(writer);
216         resources = writer.toString();
217         assertFalse(resources.contains(resourceA + ".css"));
218         assertFalse(resources.contains(resourceB + ".css"));
219         assertTrue(resources.contains(resourceC + ".css"));
220     }
221 
222     private Map setupRequestCache()
223     {
224         Map requestCache = new HashMap();
225         mockWebResourceIntegration.matchAndReturn("getRequestCache", requestCache);
226         return requestCache;
227     }
228 
229     public void testRequireResourceWithDuplicateDependencies()
230     {
231         String resourceA = "test.atlassian:a";
232         String resourceB = "test.atlassian:b";
233         String resourceC = "test.atlassian:c";
234         String resourceD = "test.atlassian:d";
235         String resourceE = "test.atlassian:e";
236 
237         // A depends on B, C
238         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceA)),
239             TestUtils.createWebResourceModuleDescriptor(resourceA, testPlugin, Collections.EMPTY_LIST, Arrays.asList(resourceB, resourceC)));
240         // B depends on D
241         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceB)),
242             TestUtils.createWebResourceModuleDescriptor(resourceB, testPlugin, Collections.EMPTY_LIST, Collections.singletonList(resourceD)));
243         // C depends on E
244         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceC)),
245             TestUtils.createWebResourceModuleDescriptor(resourceC, testPlugin, Collections.EMPTY_LIST, Collections.singletonList(resourceE)));
246         // D depends on C
247         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceD)),
248             TestUtils.createWebResourceModuleDescriptor(resourceD, testPlugin, Collections.EMPTY_LIST, Collections.singletonList(resourceC)));
249         // E has no dependencies
250         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceE)),
251             TestUtils.createWebResourceModuleDescriptor(resourceE, testPlugin));
252 
253         Map requestCache = setupRequestCache();
254         webResourceManager.requireResource(resourceA);
255 
256         Collection resources = (Collection) requestCache.get(WebResourceManagerImpl.REQUEST_CACHE_RESOURCE_KEY);
257         assertEquals(5, resources.size());
258         Object[] resourceArray = resources.toArray();
259         assertEquals(resourceE, resourceArray[0]);
260         assertEquals(resourceC, resourceArray[1]);
261         assertEquals(resourceD, resourceArray[2]);
262         assertEquals(resourceB, resourceArray[3]);
263         assertEquals(resourceA, resourceArray[4]);
264     }
265 
266     public void testRequireSingleResourceGetsDeps() throws Exception
267     {
268         String resourceA = "test.atlassian:a";
269         String resourceB = "test.atlassian:b";
270 
271         final List<ResourceDescriptor> resourceDescriptorsA = TestUtils.createResourceDescriptors("resourceA.css");
272         final List<ResourceDescriptor> resourceDescriptorsB = TestUtils.createResourceDescriptors("resourceB.css", "resourceB-more.css");
273 
274         // A depends on B
275         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceA)),
276             TestUtils.createWebResourceModuleDescriptor(resourceA, testPlugin, resourceDescriptorsA, Collections.singletonList(resourceB)));
277         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceB)),
278             TestUtils.createWebResourceModuleDescriptor(resourceB, testPlugin, resourceDescriptorsB, Collections.EMPTY_LIST));
279 
280         String s = webResourceManager.getResourceTags(resourceA);
281         int indexA = s.indexOf(resourceA);
282         int indexB = s.indexOf(resourceB);
283 
284         assertNotSame(-1, indexA);
285         assertNotSame(-1, indexB);
286         assertTrue(indexB < indexA);
287     }
288 
289     public void testIncludeResourcesWithResourceList() throws Exception
290     {
291         String resourceA = "test.atlassian:a";
292 
293         final List<ResourceDescriptor> resourceDescriptorsA = TestUtils.createResourceDescriptors("resourceA.css");
294         final List<ResourceDescriptor> resourceDescriptorsB = TestUtils.createResourceDescriptors("resourceB.css", "resourceB-more.css");
295 
296         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceA)),
297             TestUtils.createWebResourceModuleDescriptor(resourceA, testPlugin, resourceDescriptorsA, Collections.<String>emptyList()));
298 
299         StringWriter requiredResourceWriter = new StringWriter();
300         webResourceManager.includeResources(Arrays.<String>asList(resourceA), requiredResourceWriter, UrlMode.ABSOLUTE);
301         String result = requiredResourceWriter.toString();
302         assertTrue(result.contains(resourceA));
303     }
304 
305     public void testIncludeResourcesWithResourceListIgnoresRequireResource() throws Exception
306     {
307         String resourceA = "test.atlassian:a";
308         String resourceB = "test.atlassian:b";
309 
310         final List<ResourceDescriptor> resourceDescriptorsA = TestUtils.createResourceDescriptors("resourceA.css");
311         final List<ResourceDescriptor> resourceDescriptorsB = TestUtils.createResourceDescriptors("resourceB.css", "resourceB-more.css");
312 
313         final Map requestCache = new HashMap();
314         mockWebResourceIntegration.matchAndReturn("getRequestCache", requestCache);
315         
316 
317         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceA)),
318             TestUtils.createWebResourceModuleDescriptor(resourceA, testPlugin, resourceDescriptorsA, Collections.<String>emptyList()));
319 
320         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceB)),
321             TestUtils.createWebResourceModuleDescriptor(resourceB, testPlugin, resourceDescriptorsB, Collections.<String>emptyList()));
322 
323 
324         StringWriter requiredResourceWriter = new StringWriter();
325         webResourceManager.requireResource(resourceB);
326         webResourceManager.includeResources(Arrays.<String>asList(resourceA), requiredResourceWriter, UrlMode.ABSOLUTE);
327         String result = requiredResourceWriter.toString();
328         assertFalse(result.contains(resourceB));
329     }
330 
331 
332     public void testIncludeResourcesWithResourceListIncludesDependences() throws Exception
333     {
334         String resourceA = "test.atlassian:a";
335         String resourceB = "test.atlassian:b";
336 
337         final List<ResourceDescriptor> resourceDescriptorsA = TestUtils.createResourceDescriptors("resourceA.css");
338         final List<ResourceDescriptor> resourceDescriptorsB = TestUtils.createResourceDescriptors("resourceB.css", "resourceB-more.css");
339         
340         // A depends on B
341         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceA)),
342             TestUtils.createWebResourceModuleDescriptor(resourceA, testPlugin, resourceDescriptorsA, Collections.singletonList(resourceB)));
343         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceB)),
344             TestUtils.createWebResourceModuleDescriptor(resourceB, testPlugin, resourceDescriptorsB, Collections.EMPTY_LIST));
345 
346         StringWriter requiredResourceWriter = new StringWriter();
347         webResourceManager.includeResources(Arrays.<String>asList(resourceA), requiredResourceWriter, UrlMode.ABSOLUTE);
348         String result = requiredResourceWriter.toString();
349         assertTrue(result.contains(resourceB));
350     }
351     
352     public void testIncludeResourcesWithResourceListIncludesDependencesFromSuperBatch() throws Exception
353     {
354         final String resourceA = "test.atlassian:a";
355         final String resourceB = "test.atlassian:b";
356 
357         ResourceBatchingConfiguration batchingConfiguration = new ResourceBatchingConfiguration()
358         {
359             public boolean isSuperBatchingEnabled()
360             {
361                 return true;
362             }
363             
364             public List<String> getSuperBatchModuleCompleteKeys()
365             {
366                 return Arrays.asList(resourceB);
367             }
368         };
369         
370         webResourceManager = new WebResourceManagerImpl(pluginResourceLocator, (WebResourceIntegration) mockWebResourceIntegration.proxy(), batchingConfiguration);
371         
372         final List<ResourceDescriptor> resourceDescriptorsA = TestUtils.createResourceDescriptors("resourceA.css");
373         final List<ResourceDescriptor> resourceDescriptorsB = TestUtils.createResourceDescriptors("resourceB.css");
374         
375         // A depends on B
376         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceA)),
377             TestUtils.createWebResourceModuleDescriptor(resourceA, testPlugin, resourceDescriptorsA, Collections.singletonList(resourceB)));
378         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(resourceB)),
379             TestUtils.createWebResourceModuleDescriptor(resourceB, testPlugin, resourceDescriptorsB, Collections.<String>emptyList()));
380 
381         StringWriter requiredResourceWriter = new StringWriter();
382         webResourceManager.includeResources(Arrays.<String>asList(resourceA), requiredResourceWriter, UrlMode.ABSOLUTE);
383         String result = requiredResourceWriter.toString();
384         assertTrue(result.contains(resourceB));
385     }
386 
387     public void testRequireResourcesAreClearedAfterIncludesResourcesIsCalled() throws Exception
388     {
389         final String moduleKey = "cool-resources";
390         final String completeModuleKey = "test.atlassian:" + moduleKey;
391 
392         final List<ResourceDescriptor> resourceDescriptors1 = TestUtils.createResourceDescriptors("cool.css", "more-cool.css", "cool.js");
393 
394         setupRequestCache();
395 
396         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(completeModuleKey)),
397             TestUtils.createWebResourceModuleDescriptor(completeModuleKey, testPlugin, resourceDescriptors1));
398 
399         // test requireResource() methods
400         webResourceManager.requireResource(completeModuleKey);
401         webResourceManager.includeResources(new StringWriter(), UrlMode.RELATIVE);
402         
403         StringWriter requiredResourceWriter = new StringWriter();
404         webResourceManager.includeResources(requiredResourceWriter, UrlMode.RELATIVE);
405         assertEquals("", requiredResourceWriter.toString());
406     }
407 
408     // testRequireResourceAndResourceTagMethods
409 
410     public void testRequireResourceAndResourceTagMethods() throws Exception
411     {
412         final String completeModuleKey = "test.atlassian:cool-resources";
413         final String staticBase = setupRequireResourceAndResourceTagMethods(false, completeModuleKey);
414 
415         // test requireResource() methods
416         StringWriter requiredResourceWriter = new StringWriter();
417         webResourceManager.requireResource(completeModuleKey);
418         String requiredResourceResult = webResourceManager.getRequiredResources(UrlMode.RELATIVE);
419         webResourceManager.includeResources(requiredResourceWriter, UrlMode.RELATIVE);
420         assertEquals(requiredResourceResult, requiredResourceWriter.toString());
421 
422         assertTrue(requiredResourceResult.contains("href=\"" + staticBase + "/" + completeModuleKey + "/" + completeModuleKey + ".css"));
423         assertTrue(requiredResourceResult.contains("src=\"" + staticBase + "/" + completeModuleKey + "/" + completeModuleKey + ".js"));
424 
425         // test resourceTag() methods
426         StringWriter resourceTagsWriter = new StringWriter();
427         webResourceManager.requireResource(completeModuleKey, resourceTagsWriter, UrlMode.RELATIVE);
428         String resourceTagsResult = webResourceManager.getResourceTags(completeModuleKey, UrlMode.RELATIVE);
429         assertEquals(resourceTagsResult, resourceTagsWriter.toString());
430 
431         // calling requireResource() or resourceTag() on a single webresource should be the same
432         assertEquals(requiredResourceResult, resourceTagsResult);
433     }
434 
435     public void testRequireResourceAndResourceTagMethodsWithAbsoluteUrlMode() throws Exception
436     {
437         testRequireResourceAndResourceTagMethods(UrlMode.ABSOLUTE, true);
438     }
439 
440     public void testRequireResourceAndResourceTagMethodsWithRelativeUrlMode() throws Exception
441     {
442         testRequireResourceAndResourceTagMethods(UrlMode.RELATIVE, false);
443     }
444 
445     public void testRequireResourceAndResourceTagMethodsWithAutoUrlMode() throws Exception
446     {
447         testRequireResourceAndResourceTagMethods(UrlMode.AUTO, false);
448     }
449 
450     private void testRequireResourceAndResourceTagMethods(UrlMode urlMode, boolean baseUrlExpected) throws Exception
451     {
452         final String completeModuleKey = "test.atlassian:cool-resources";
453         final String staticBase = setupRequireResourceAndResourceTagMethods(baseUrlExpected, completeModuleKey);
454 
455         // test requireResource() methods
456         final StringWriter requiredResourceWriter = new StringWriter();
457         webResourceManager.requireResource(completeModuleKey);
458         final String requiredResourceResult = webResourceManager.getRequiredResources(urlMode);
459         webResourceManager.includeResources(requiredResourceWriter, urlMode);
460         assertEquals(requiredResourceResult, requiredResourceWriter.toString());
461 
462         assertTrue(requiredResourceResult.contains("href=\"" + staticBase + "/" + completeModuleKey + "/" + completeModuleKey + ".css"));
463         assertTrue(requiredResourceResult.contains("src=\"" + staticBase + "/" + completeModuleKey + "/" + completeModuleKey + ".js"));
464 
465         // test resourceTag() methods
466         StringWriter resourceTagsWriter = new StringWriter();
467         webResourceManager.requireResource(completeModuleKey, resourceTagsWriter, urlMode);
468         String resourceTagsResult = webResourceManager.getResourceTags(completeModuleKey, urlMode);
469         assertEquals(resourceTagsResult, resourceTagsWriter.toString());
470 
471         // calling requireResource() or resourceTag() on a single webresource should be the same
472         assertEquals(requiredResourceResult, resourceTagsResult);
473     }
474 
475     private String setupRequireResourceAndResourceTagMethods(boolean baseUrlExpected, String completeModuleKey)
476         throws DocumentException
477     {
478         final List<ResourceDescriptor> descriptors = TestUtils.createResourceDescriptors("cool.css", "more-cool.css", "cool.js");
479 
480         final Map requestCache = new HashMap();
481         mockWebResourceIntegration.matchAndReturn("getRequestCache", requestCache);
482 
483         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(completeModuleKey)),
484             TestUtils.createWebResourceModuleDescriptor(completeModuleKey, testPlugin, descriptors));
485 
486         final String staticBase = (baseUrlExpected ? BASEURL : "") +
487             "/" + WebResourceManagerImpl.STATIC_RESOURCE_PREFIX  + "/" + SYSTEM_BUILD_NUMBER + "/" + SYSTEM_COUNTER +
488             "/" + testPlugin.getPluginInformation().getVersion() + "/" + WebResourceManagerImpl.STATIC_RESOURCE_SUFFIX + BatchPluginResource.URL_PREFIX;
489         return staticBase;
490     }
491 
492     // testRequireResourceWithCacheParameter
493 
494     public void testRequireResourceWithCacheParameter() throws Exception
495     {
496         final String completeModuleKey = "test.atlassian:no-cache-resources";
497 
498         final String expectedResult = setupRequireResourceWithCacheParameter(false, completeModuleKey);
499         assertTrue(webResourceManager.getResourceTags(completeModuleKey).contains(expectedResult));
500     }
501 
502     public void testRequireResourceWithCacheParameterAndAbsoluteUrlMode() throws Exception
503     {
504         testRequireResourceWithCacheParameter(UrlMode.ABSOLUTE, true);
505     }
506 
507     public void testRequireResourceWithCacheParameterAndRelativeUrlMode() throws Exception
508     {
509         testRequireResourceWithCacheParameter(UrlMode.RELATIVE, false);
510     }
511 
512     public void testRequireResourceWithCacheParameterAndAutoUrlMode() throws Exception
513     {
514         testRequireResourceWithCacheParameter(UrlMode.AUTO, false);
515     }
516 
517     private void testRequireResourceWithCacheParameter(UrlMode urlMode, boolean baseUrlExpected) throws Exception
518     {
519         final String completeModuleKey = "test.atlassian:no-cache-resources";
520         final String expectedResult = setupRequireResourceWithCacheParameter(baseUrlExpected, completeModuleKey);
521         assertTrue(webResourceManager.getResourceTags(completeModuleKey, urlMode).contains(expectedResult));
522     }
523 
524     private String setupRequireResourceWithCacheParameter(boolean baseUrlExpected, String completeModuleKey)
525         throws DocumentException
526     {
527         final Map<String, String> params = new HashMap<String, String>();
528         params.put("cache", "false");
529         ResourceDescriptor resourceDescriptor = TestUtils.createResourceDescriptor("no-cache.js", params);
530 
531         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(completeModuleKey)),
532                 TestUtils.createWebResourceModuleDescriptor(completeModuleKey, testPlugin,
533                 Collections.singletonList(resourceDescriptor)));
534 
535         return "src=\"" + (baseUrlExpected ? BASEURL : "") + BatchPluginResource.URL_PREFIX +
536                "/" + completeModuleKey + "/" + completeModuleKey + ".js?cache=false";
537     }
538 
539     // testGetStaticResourcePrefix
540 
541     public void testGetStaticResourcePrefix()
542     {
543         final String expectedPrefix = setupGetStaticResourcePrefix(false);
544         assertEquals(expectedPrefix, webResourceManager.getStaticResourcePrefix());
545     }
546 
547     public void testGetStaticResourcePrefixWithAbsoluteUrlMode()
548     {
549         testGetStaticResourcePrefix(UrlMode.ABSOLUTE, true);
550     }
551 
552     public void testGetStaticResourcePrefixWithRelativeUrlMode()
553     {
554         testGetStaticResourcePrefix(UrlMode.RELATIVE, false);
555     }
556 
557     public void testGetStaticResourcePrefixWithAutoUrlMode()
558     {
559         testGetStaticResourcePrefix(UrlMode.AUTO, false);
560     }
561 
562     private void testGetStaticResourcePrefix(UrlMode urlMode, boolean baseUrlExpected)
563     {
564         final String expectedPrefix = setupGetStaticResourcePrefix(baseUrlExpected);
565         assertEquals(expectedPrefix, webResourceManager.getStaticResourcePrefix(urlMode));
566     }
567 
568     private String setupGetStaticResourcePrefix(boolean baseUrlExpected)
569     {
570         return (baseUrlExpected ? BASEURL : "") +
571             "/" + WebResourceManagerImpl.STATIC_RESOURCE_PREFIX + "/" +
572             SYSTEM_BUILD_NUMBER + "/" + SYSTEM_COUNTER + "/" + WebResourceManagerImpl.STATIC_RESOURCE_SUFFIX;
573     }
574 
575     // testGetStaticResourcePrefixWithCounter
576 
577     public void testGetStaticResourcePrefixWithCounter()
578     {
579         final String resourceCounter = "456";
580         final String expectedPrefix = setupGetStaticResourcePrefixWithCounter(false, resourceCounter);
581         assertEquals(expectedPrefix, webResourceManager.getStaticResourcePrefix(resourceCounter));
582     }
583 
584     public void testGetStaticResourcePrefixWithCounterAndAbsoluteUrlMode()
585     {
586         testGetStaticResourcePrefixWithCounter(UrlMode.ABSOLUTE, true);
587     }
588 
589     public void testGetStaticResourcePrefixWithCounterAndRelativeUrlMode()
590     {
591         testGetStaticResourcePrefixWithCounter(UrlMode.RELATIVE, false);
592     }
593 
594     public void testGetStaticResourcePrefixWithCounterAndAutoUrlMode()
595     {
596         testGetStaticResourcePrefixWithCounter(UrlMode.AUTO, false);
597     }
598 
599     private void testGetStaticResourcePrefixWithCounter(UrlMode urlMode, boolean baseUrlExpected)
600     {
601         final String resourceCounter = "456";
602         final String expectedPrefix = setupGetStaticResourcePrefixWithCounter(baseUrlExpected, resourceCounter);
603         assertEquals(expectedPrefix, webResourceManager.getStaticResourcePrefix(resourceCounter, urlMode));
604     }
605 
606     private String setupGetStaticResourcePrefixWithCounter(boolean baseUrlExpected, String resourceCounter)
607     {
608         return (baseUrlExpected ? BASEURL : "") +
609             "/" + WebResourceManagerImpl.STATIC_RESOURCE_PREFIX + "/" +
610             SYSTEM_BUILD_NUMBER + "/" + SYSTEM_COUNTER + "/" + resourceCounter +
611             "/" + WebResourceManagerImpl.STATIC_RESOURCE_SUFFIX;
612     }
613 
614     // testGetStaticPluginResourcePrefix
615 
616     public void testGetStaticPluginResourcePrefix()
617     {
618         final String moduleKey = "confluence.extra.animal:animal";
619         final String resourceName = "foo.js";
620 
621         final String expectedPrefix = setupGetStaticPluginResourcePrefix(false, moduleKey, resourceName);        
622 
623         assertEquals(expectedPrefix, webResourceManager.getStaticPluginResource(moduleKey, resourceName));
624     }
625 
626     public void testGetStaticPluginResourcePrefixWithAbsoluteUrlMode()
627     {
628         testGetStaticPluginResourcePrefix(UrlMode.ABSOLUTE, true);
629     }
630 
631     public void testGetStaticPluginResourcePrefixWithRelativeUrlMode()
632     {
633         testGetStaticPluginResourcePrefix(UrlMode.RELATIVE, false);
634     }
635 
636     public void testGetStaticPluginResourcePrefixWithAutoUrlMode()
637     {
638         testGetStaticPluginResourcePrefix(UrlMode.AUTO, false);
639     }
640 
641     private void testGetStaticPluginResourcePrefix(UrlMode urlMode, boolean baseUrlExpected)
642     {
643         final String moduleKey = "confluence.extra.animal:animal";
644         final String resourceName = "foo.js";
645 
646         final String expectedPrefix = setupGetStaticPluginResourcePrefix(baseUrlExpected, moduleKey, resourceName);
647 
648         assertEquals(expectedPrefix, webResourceManager.getStaticPluginResource(moduleKey, resourceName, urlMode));
649     }
650 
651     private String setupGetStaticPluginResourcePrefix(boolean baseUrlExpected, String moduleKey, String resourceName)
652     {
653         final Plugin animalPlugin = new StaticPlugin();
654         animalPlugin.setKey("confluence.extra.animal");
655         animalPlugin.setPluginsVersion(Integer.parseInt(ANIMAL_PLUGIN_VERSION));
656 
657         mockPluginAccessor.expectAndReturn("getEnabledPluginModule", C.args(C.eq(moduleKey)),
658             TestUtils.createWebResourceModuleDescriptor(moduleKey, animalPlugin));
659 
660         return (baseUrlExpected ? BASEURL : "") +
661             "/" + WebResourceManagerImpl.STATIC_RESOURCE_PREFIX + "/" + SYSTEM_BUILD_NUMBER +
662             "/" + SYSTEM_COUNTER + "/" + ANIMAL_PLUGIN_VERSION + "/" + WebResourceManagerImpl.STATIC_RESOURCE_SUFFIX +
663             "/" + AbstractFileServerServlet.SERVLET_PATH + "/" + AbstractFileServerServlet.RESOURCE_URL_PREFIX +
664             "/" + moduleKey + "/" + resourceName;
665     }
666 
667     public void testGetRequiredResourcesWithFilter() throws Exception
668     {
669         final String moduleKey = "cool-resources";
670         final String completeModuleKey = "test.atlassian:" + moduleKey;
671 
672         final List<ResourceDescriptor> resourceDescriptors1 = TestUtils.createResourceDescriptors("cool.css", "cool.js", "more-cool.css");
673 
674         setupRequestCache();
675 
676         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(completeModuleKey)),
677             TestUtils.createWebResourceModuleDescriptor(completeModuleKey, testPlugin, resourceDescriptors1));
678 
679         // test includeResources(writer, type) method
680         webResourceManager.requireResource(completeModuleKey);
681 
682         String staticBase = BASEURL + "/" + WebResourceManagerImpl.STATIC_RESOURCE_PREFIX  + "/" + SYSTEM_BUILD_NUMBER
683             + "/" + SYSTEM_COUNTER + "/" + testPlugin.getPluginInformation().getVersion() + "/"
684             + WebResourceManagerImpl.STATIC_RESOURCE_SUFFIX + BatchPluginResource.URL_PREFIX;
685 
686         String cssRef = "href=\"" + staticBase + "/" + completeModuleKey + "/" + completeModuleKey + ".css";
687         String jsRef = "src=\"" + staticBase + "/" + completeModuleKey + "/" + completeModuleKey + ".js";
688 
689         // CSS
690         String requiredResourceResult = webResourceManager.getRequiredResources(UrlMode.ABSOLUTE, new CssWebResource());
691         assertTrue(requiredResourceResult.contains(cssRef));
692         assertFalse(requiredResourceResult.contains(jsRef));
693 
694         // JS
695         requiredResourceResult = webResourceManager.getRequiredResources(UrlMode.ABSOLUTE, new JavascriptWebResource());
696         assertFalse(requiredResourceResult.contains(cssRef));
697         assertTrue(requiredResourceResult.contains(jsRef));
698 
699         // BOTH
700         requiredResourceResult = webResourceManager.getRequiredResources(UrlMode.ABSOLUTE);
701         assertTrue(requiredResourceResult.contains(cssRef));
702         assertTrue(requiredResourceResult.contains(jsRef));
703     }
704 
705     public void testGetRequiredResourcesWithCustomFilters() throws Exception
706     {
707         WebResourceFilter atlassianFilter = new WebResourceFilter(){
708             public boolean matches(String resourceName)
709             {
710                 if (resourceName.contains("atlassian"))
711                     return true;
712                 return false;
713             }
714         };
715         WebResourceFilter bogusFilter = new WebResourceFilter() {
716             public boolean matches(String resourceName)
717             {
718                 return true;
719             }
720         };
721 
722         final String moduleKey = "cool-resources";
723         final String completeModuleKey = "test.atlassian:" + moduleKey;
724 
725         final List<ResourceDescriptor> resources = TestUtils.createResourceDescriptors(
726             "foo.css", "foo-bar.js",
727             "atlassian.css", "atlassian-plugins.js");
728 
729         setupRequestCache();
730         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(completeModuleKey)),
731             TestUtils.createWebResourceModuleDescriptor(completeModuleKey, testPlugin, resources));
732 
733         // easier to test which resources were included by the filter with batching turned off
734         System.setProperty(PluginResourceLocatorImpl.PLUGIN_WEBRESOURCE_BATCHING_OFF, "true");
735         try
736         {
737             webResourceManager.requireResource(completeModuleKey);
738             String atlassianResources = webResourceManager.getRequiredResources(UrlMode.RELATIVE, atlassianFilter);
739             assertEquals(-1, atlassianResources.indexOf("foo"));
740             assertTrue(atlassianResources.contains("atlassian.css"));
741             assertTrue(atlassianResources.contains("atlassian-plugins.js"));
742 
743             String allResources = webResourceManager.getRequiredResources(UrlMode.RELATIVE, bogusFilter);
744             for (ResourceDescriptor resource : resources)
745             {
746                 assertTrue(allResources.contains(resource.getName()));
747             }
748         }
749         finally
750         {
751             System.setProperty(PluginResourceLocatorImpl.PLUGIN_WEBRESOURCE_BATCHING_OFF, "false");
752         }
753     }
754 
755     public void testGetRequiredResourcesOrdersByType() throws Exception
756     {
757         final String moduleKey1 = "cool-resources";
758         final String moduleKey2 = "hot-resources";
759         final String completeModuleKey1 = "test.atlassian:" + moduleKey1;
760         final String completeModuleKey2 = "test.atlassian:" + moduleKey2;
761 
762         final List<ResourceDescriptor> resourceDescriptors1 = TestUtils.createResourceDescriptors("cool.js", "cool.css", "more-cool.css");
763         final List<ResourceDescriptor> resourceDescriptors2 = TestUtils.createResourceDescriptors("hot.js", "hot.css", "more-hot.css");
764 
765         final Plugin plugin = TestUtils.createTestPlugin();
766 
767         setupRequestCache();
768 
769         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(completeModuleKey1)),
770             TestUtils.createWebResourceModuleDescriptor(completeModuleKey1, plugin, resourceDescriptors1));
771         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq(completeModuleKey2)),
772                     TestUtils.createWebResourceModuleDescriptor(completeModuleKey2, plugin, resourceDescriptors2));
773 
774         // test includeResources(writer, type) method
775         webResourceManager.requireResource(completeModuleKey1);
776         webResourceManager.requireResource(completeModuleKey2);
777 
778         String staticBase = BASEURL + "/" + WebResourceManagerImpl.STATIC_RESOURCE_PREFIX  + "/" + SYSTEM_BUILD_NUMBER
779             + "/" + SYSTEM_COUNTER + "/" + plugin.getPluginInformation().getVersion() + "/"
780             + WebResourceManagerImpl.STATIC_RESOURCE_SUFFIX + BatchPluginResource.URL_PREFIX;
781 
782         String cssRef1 = "href=\"" + staticBase + "/" + completeModuleKey1 + "/" + completeModuleKey1 + ".css";
783         String cssRef2 = "href=\"" + staticBase + "/" + completeModuleKey2 + "/" + completeModuleKey2 + ".css";
784         String jsRef1 = "src=\"" + staticBase + "/" + completeModuleKey1 + "/" + completeModuleKey1 + ".js";
785         String jsRef2 = "src=\"" + staticBase + "/" + completeModuleKey2 + "/" + completeModuleKey2 + ".js";
786 
787         String requiredResourceResult = webResourceManager.getRequiredResources(UrlMode.ABSOLUTE);
788 
789         assertTrue(requiredResourceResult.contains(cssRef1));
790         assertTrue(requiredResourceResult.contains(cssRef2));
791         assertTrue(requiredResourceResult.contains(jsRef1));
792         assertTrue(requiredResourceResult.contains(jsRef2));
793 
794         int cssRef1Index = requiredResourceResult.indexOf(cssRef1);
795         int cssRef2Index = requiredResourceResult.indexOf(cssRef2);
796         int jsRef1Index = requiredResourceResult.indexOf(jsRef1);
797         int jsRef2Index = requiredResourceResult.indexOf(jsRef2);
798 
799         assertTrue(cssRef1Index < jsRef1Index);
800         assertTrue(cssRef2Index < jsRef2Index);
801         assertTrue(cssRef2Index < jsRef1Index);
802     }
803 
804     public void testRequireResourceInSuperbatch()
805     {
806         resourceBatchingConfiguration.enabled = true;
807         Map requestCache = setupRequestCache();
808         mockOutSuperbatchPluginAccesses();
809 
810         webResourceManager.requireResource("test.atlassian:superbatch");
811 
812         Collection resources = (Collection) requestCache.get(WebResourceManagerImpl.REQUEST_CACHE_RESOURCE_KEY);
813         assertEquals(0, resources.size());
814     }
815 
816     public void testRequireResourceWithDependencyInSuperbatch() throws DocumentException
817     {
818         resourceBatchingConfiguration.enabled = true;
819         mockOutSuperbatchPluginAccesses();
820 
821         Map requestCache = setupRequestCache();
822 
823         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", C.args(C.eq("test.atlassian:included-resource")),
824             TestUtils.createWebResourceModuleDescriptor("test.atlassian:included-resource", testPlugin, Collections.EMPTY_LIST, Collections.singletonList("test.atlassian:superbatch")));
825 
826         webResourceManager.requireResource("test.atlassian:included-resource");
827 
828         Collection resources = (Collection) requestCache.get(WebResourceManagerImpl.REQUEST_CACHE_RESOURCE_KEY);
829         assertEquals(1, resources.size());
830         assertEquals("test.atlassian:included-resource", resources.iterator().next());
831     }
832 
833     public void testSuperBatchResolution() throws DocumentException
834     {
835         TestUtils.setupSuperbatchTestContent(resourceBatchingConfiguration, mockPluginAccessor, testPlugin);
836         mockOutSuperbatchPluginAccesses();
837 
838         List<PluginResource> cssResources = webResourceManager.getSuperBatchResources(CssWebResource.FORMATTER);
839         assertEquals(2, cssResources.size());
840 
841         SuperBatchPluginResource superBatch1 = (SuperBatchPluginResource) cssResources.get(0);
842         assertEquals("batch.css", superBatch1.getResourceName());
843         assertTrue(superBatch1.getParams().isEmpty());
844 
845         SuperBatchPluginResource superBatch2 = (SuperBatchPluginResource) cssResources.get(1);
846         assertEquals("batch.css", superBatch2.getResourceName());
847         assertEquals("true", superBatch2.getParams().get("ieonly"));
848 
849         List<PluginResource> jsResources = webResourceManager.getSuperBatchResources(JavascriptWebResource.FORMATTER);
850         assertEquals(1, jsResources.size());
851         assertEquals("batch.js", jsResources.get(0).getResourceName());
852         assertEquals(0, jsResources.get(0).getParams().size());
853     }
854 
855     private void mockOutSuperbatchPluginAccesses()
856     {
857         mockOutPluginModule("test.atlassian:superbatch");
858         mockOutPluginModule("test.atlassian:superbatch2");
859         mockPluginAccessor.matchAndReturn("getPluginModule", "test.atlassian:missing-plugin", null);
860         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", "test.atlassian:missing-plugin", null);
861     }
862 
863     private void mockOutPluginModule(String moduleKey)
864     {
865         Plugin p = TestUtils.createTestPlugin();
866         WebResourceModuleDescriptor module = TestUtils.createWebResourceModuleDescriptor(moduleKey, p);
867         mockPluginAccessor.matchAndReturn("getPluginModule", moduleKey, module);
868         mockPluginAccessor.matchAndReturn("getEnabledPluginModule", moduleKey, module);
869     }
870 }