View Javadoc

1   package com.atlassian.plugin.webresource;
2   
3   import static java.util.Arrays.asList;
4   import static org.mockito.Matchers.eq;
5   import static org.mockito.Matchers.isA;
6   import static org.mockito.Mockito.mock;
7   import static org.mockito.Mockito.when;
8   
9   import com.atlassian.plugin.ModuleDescriptor;
10  import com.atlassian.plugin.Plugin;
11  import com.atlassian.plugin.PluginAccessor;
12  import com.atlassian.plugin.elements.ResourceDescriptor;
13  
14  import org.dom4j.DocumentException;
15  
16  import com.google.common.base.Supplier;
17  
18  import java.io.StringWriter;
19  import java.util.Arrays;
20  import java.util.Collection;
21  import java.util.Collections;
22  import java.util.HashMap;
23  import java.util.HashSet;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.regex.Matcher;
27  import java.util.regex.Pattern;
28  
29  import junit.framework.TestCase;
30  
31  public class TestWebResourceManagerImpl extends TestCase
32  {
33  
34      private WebResourceIntegration mockWebResourceIntegration = mock(WebResourceIntegration.class);
35      private PluginAccessor mockPluginAccessor = mock(PluginAccessor.class);
36      private final WebResourceUrlProvider mockUrlProvider = mock(WebResourceUrlProvider.class);
37      private ResourceBatchingConfiguration mockBatchingConfiguration = mock(ResourceBatchingConfiguration.class);
38      private PluginResourceLocator pluginResourceLocator;
39      private WebResourceManagerImpl webResourceManager;
40      private Plugin testPlugin;
41  
42      private static final String BASEURL = "http://www.foo.com";
43      private static final String SYSTEM_COUNTER = "123";
44      private static final String SYSTEM_BUILD_NUMBER = "650";
45  
46      @Override
47      protected void setUp() throws Exception
48      {
49          super.setUp();
50  
51          when(mockWebResourceIntegration.getPluginAccessor()).thenReturn(mockPluginAccessor);
52          when(mockWebResourceIntegration.getSuperBatchVersion()).thenReturn(SYSTEM_BUILD_NUMBER);
53  
54          pluginResourceLocator = new PluginResourceLocatorImpl(mockWebResourceIntegration, null, mockUrlProvider, mockBatchingConfiguration);
55          webResourceManager = new WebResourceManagerImpl(pluginResourceLocator, mockWebResourceIntegration, mockUrlProvider, mockBatchingConfiguration);
56  
57          when(mockUrlProvider.getBaseUrl()).thenReturn(BASEURL);
58          when(mockUrlProvider.getBaseUrl(UrlMode.ABSOLUTE)).thenReturn(BASEURL);
59          when(mockUrlProvider.getBaseUrl(UrlMode.AUTO)).thenReturn("");
60          when(mockUrlProvider.getBaseUrl(UrlMode.RELATIVE)).thenReturn("");
61          when(mockBatchingConfiguration.isPluginWebResourceBatchingEnabled()).thenReturn(true);
62          testPlugin = TestUtils.createTestPlugin();
63      }
64  
65      @Override
66      protected void tearDown() throws Exception
67      {
68          webResourceManager = null;
69          mockPluginAccessor = null;
70          mockWebResourceIntegration = null;
71          testPlugin = null;
72          mockBatchingConfiguration = null;
73  
74          super.tearDown();
75      }
76  
77      private void mockEnabledPluginModule(final String key)
78      {
79          final ModuleDescriptor md = TestUtils.createWebResourceModuleDescriptor(key, testPlugin);
80          mockEnabledPluginModule(key, md);
81      }
82  
83      private void mockEnabledPluginModule(final String key, final ModuleDescriptor md)
84      {
85          when(mockPluginAccessor.getEnabledPluginModule(key)).thenReturn(md);
86      }
87  
88      public void testRequireResources()
89      {
90          final String resource1 = "test.atlassian:cool-stuff";
91          final String resource2 = "test.atlassian:hot-stuff";
92  
93          mockEnabledPluginModule(resource1);
94          mockEnabledPluginModule(resource2);
95  
96          final Map<String, Object> requestCache = setupRequestCache();
97          webResourceManager.requireResource(resource1);
98          webResourceManager.requireResource(resource2);
99          webResourceManager.requireResource(resource1); // require again to test it only gets included once
100 
101         final Collection<?> resources = (Collection<?>) requestCache.get(WebResourceManagerImpl.REQUEST_CACHE_RESOURCE_KEY);
102         assertEquals(2, resources.size());
103         assertTrue(resources.contains(resource1));
104         assertTrue(resources.contains(resource2));
105     }
106 
107     public void testRequireResourcesWithCondition() throws ClassNotFoundException
108     {
109         final String resource1 = "test.atlassian:cool-stuff";
110         final String resource2 = "test.atlassian:hot-stuff";
111         final Plugin plugin = TestUtils.createTestPlugin("test.atlassian", "1", AlwaysTrueCondition.class, AlwaysFalseCondition.class);
112 
113         mockEnabledPluginModule(resource1,
114             new WebResourceModuleDescriptorBuilder(plugin, "cool-stuff").setCondition(AlwaysTrueCondition.class).addDescriptor("cool.js").build());
115         mockEnabledPluginModule(resource2,
116             new WebResourceModuleDescriptorBuilder(plugin, "hot-stuff").setCondition(AlwaysFalseCondition.class).addDescriptor("hot.js").build());
117 
118         setupRequestCache();
119         webResourceManager.requireResource(resource1);
120         webResourceManager.requireResource(resource2);
121 
122         final String tags = webResourceManager.getRequiredResources(UrlMode.AUTO);
123         assertTrue(tags.contains(resource1));
124         assertFalse(tags.contains(resource2));
125     }
126 
127     public void testRequireResourcesWithDependencies()
128     {
129         final String resource = "test.atlassian:cool-stuff";
130         final String dependencyResource = "test.atlassian:hot-stuff";
131 
132         // cool-stuff depends on hot-stuff
133         mockEnabledPluginModule(resource, TestUtils.createWebResourceModuleDescriptor(resource, testPlugin,
134             Collections.<ResourceDescriptor> emptyList(), Collections.singletonList(dependencyResource)));
135         mockEnabledPluginModule(dependencyResource, TestUtils.createWebResourceModuleDescriptor(dependencyResource, testPlugin));
136 
137         final Map<String, Object> requestCache = setupRequestCache();
138         webResourceManager.requireResource(resource);
139 
140         final Collection<?> resources = (Collection<?>) requestCache.get(WebResourceManagerImpl.REQUEST_CACHE_RESOURCE_KEY);
141         assertEquals(2, resources.size());
142         final Object[] resourceArray = resources.toArray();
143         assertEquals(dependencyResource, resourceArray[0]);
144         assertEquals(resource, resourceArray[1]);
145     }
146 
147     public void testRequireResourcesWithDependencyHiddenByCondition() throws ClassNotFoundException
148     {
149         final String resource1 = "test.atlassian:cool-stuff";
150         final String resource2 = "test.atlassian:hot-stuff";
151         final Plugin plugin = TestUtils.createTestPlugin("test.atlassian", "1", AlwaysTrueCondition.class, AlwaysFalseCondition.class);
152 
153         mockEnabledPluginModule(
154             resource1,
155             new WebResourceModuleDescriptorBuilder(plugin, "cool-stuff").setCondition(AlwaysTrueCondition.class).addDependency(resource2).addDescriptor(
156                 "cool.js").build());
157         mockEnabledPluginModule(resource2,
158             new WebResourceModuleDescriptorBuilder(plugin, "hot-stuff").setCondition(AlwaysFalseCondition.class).addDescriptor("hot.js").build());
159 
160         setupRequestCache();
161         webResourceManager.requireResource(resource1);
162 
163         final String tags = webResourceManager.getRequiredResources(UrlMode.AUTO);
164         assertTrue(tags.contains(resource1));
165         assertFalse(tags.contains(resource2));
166     }
167 
168     public void testRequireResourcesWithCyclicDependency()
169     {
170         final String resource1 = "test.atlassian:cool-stuff";
171         final String resource2 = "test.atlassian:hot-stuff";
172 
173         // cool-stuff and hot-stuff depend on each other
174         mockEnabledPluginModule(resource1, TestUtils.createWebResourceModuleDescriptor(resource1, testPlugin,
175             Collections.<ResourceDescriptor> emptyList(), Collections.singletonList(resource2)));
176         mockEnabledPluginModule(resource2, TestUtils.createWebResourceModuleDescriptor(resource2, testPlugin,
177             Collections.<ResourceDescriptor> emptyList(), Collections.singletonList(resource1)));
178 
179         final Map<String, Object> requestCache = setupRequestCache();
180         webResourceManager.requireResource(resource1);
181 
182         final Collection<?> resources = (Collection<?>) requestCache.get(WebResourceManagerImpl.REQUEST_CACHE_RESOURCE_KEY);
183         assertEquals(2, resources.size());
184         final Object[] resourceArray = resources.toArray();
185         assertEquals(resource2, resourceArray[0]);
186         assertEquals(resource1, resourceArray[1]);
187     }
188 
189     public void testRequireResourcesWithComplexCyclicDependency()
190     {
191         final String resourceA = "test.atlassian:a";
192         final String resourceB = "test.atlassian:b";
193         final String resourceC = "test.atlassian:c";
194         final String resourceD = "test.atlassian:d";
195         final String resourceE = "test.atlassian:e";
196 
197         // A depends on B, C
198         mockEnabledPluginModule(resourceA, TestUtils.createWebResourceModuleDescriptor(resourceA, testPlugin,
199             Collections.<ResourceDescriptor> emptyList(), Arrays.asList(resourceB, resourceC)));
200         // B depends on D
201         mockEnabledPluginModule(resourceB, TestUtils.createWebResourceModuleDescriptor(resourceB, testPlugin,
202             Collections.<ResourceDescriptor> emptyList(), Collections.singletonList(resourceD)));
203         // C has no dependencies
204         mockEnabledPluginModule(resourceC, TestUtils.createWebResourceModuleDescriptor(resourceC, testPlugin));
205         // D depends on E, A (cyclic dependency)
206         mockEnabledPluginModule(resourceD, TestUtils.createWebResourceModuleDescriptor(resourceD, testPlugin,
207             Collections.<ResourceDescriptor> emptyList(), Arrays.asList(resourceE, resourceA)));
208         // E has no dependencies
209         mockEnabledPluginModule(resourceE, TestUtils.createWebResourceModuleDescriptor(resourceE, testPlugin));
210 
211         final Map<String, Object> requestCache = setupRequestCache();
212         webResourceManager.requireResource(resourceA);
213         // requiring a resource already included by A's dependencies shouldn't change the order
214         webResourceManager.requireResource(resourceD);
215 
216         final Collection<?> resources = (Collection<?>) requestCache.get(WebResourceManagerImpl.REQUEST_CACHE_RESOURCE_KEY);
217         assertEquals(5, resources.size());
218         final Object[] resourceArray = resources.toArray();
219         assertEquals(resourceE, resourceArray[0]);
220         assertEquals(resourceD, resourceArray[1]);
221         assertEquals(resourceB, resourceArray[2]);
222         assertEquals(resourceC, resourceArray[3]);
223         assertEquals(resourceA, resourceArray[4]);
224     }
225 
226     public void testGetResourceContext() throws Exception
227     {
228         when(mockBatchingConfiguration.isContextBatchingEnabled()).thenReturn(true);
229         final String resourceA = "test.atlassian:a";
230         final String resourceB = "test.atlassian:b";
231         final String resourceC = "test.atlassian:c";
232 
233         final WebResourceModuleDescriptor descriptor1 = TestUtils.createWebResourceModuleDescriptor(resourceA, testPlugin,
234             TestUtils.createResourceDescriptors("resourceA.css"), Collections.<String> emptyList(), Collections.<String> emptySet());
235         final WebResourceModuleDescriptor descriptor2 = TestUtils.createWebResourceModuleDescriptor(resourceB, testPlugin,
236             TestUtils.createResourceDescriptors("resourceB.css"), Collections.<String> emptyList(), new HashSet<String>()
237             {
238                 {
239                     add("foo");
240                 }
241             });
242         final WebResourceModuleDescriptor descriptor3 = TestUtils.createWebResourceModuleDescriptor(resourceC, testPlugin,
243             TestUtils.createResourceDescriptors("resourceC.css"), Collections.<String> emptyList(), new HashSet<String>()
244             {
245                 {
246                     add("foo");
247                     add("bar");
248                 }
249             });
250 
251         final List<WebResourceModuleDescriptor> descriptors = Arrays.asList(descriptor1, descriptor2, descriptor3);
252 
253         when(mockPluginAccessor.getEnabledModuleDescriptorsByClass(WebResourceModuleDescriptor.class)).thenReturn(descriptors);
254         mockEnabledPluginModule(resourceA, descriptor1);
255         mockEnabledPluginModule(resourceB, descriptor2);
256         mockEnabledPluginModule(resourceC, descriptor3);
257 
258         setupRequestCache();
259 
260         // write includes for all resources for "foo":
261         webResourceManager.requireResourcesForContext("foo");
262         StringWriter writer = new StringWriter();
263         webResourceManager.includeResources(writer, UrlMode.AUTO);
264         String resources = writer.toString();
265         assertFalse(resources.contains(resourceA + ".css"));
266         assertFalse(resources.contains(resourceB + ".css"));
267         assertFalse(resources.contains(resourceC + ".css"));
268         assertTrue(resources.contains("/contextbatch/css/foo/batch.css"));
269         assertFalse(resources.contains("/contextbatch/css/bar/batch.css"));
270 
271         // write includes for all resources for "bar":
272         webResourceManager.requireResourcesForContext("bar");
273         writer = new StringWriter();
274         webResourceManager.includeResources(writer, UrlMode.AUTO);
275         resources = writer.toString();
276         assertFalse(resources.contains(resourceA + ".css"));
277         assertFalse(resources.contains(resourceB + ".css"));
278         assertFalse(resources.contains(resourceC + ".css"));
279         assertFalse(resources.contains("/contextbatch/css/foo/batch.css"));
280         assertTrue(resources.contains("/contextbatch/css/bar/batch.css"));
281     }
282 
283     public void testGetResourceContextWithCondition() throws ClassNotFoundException, DocumentException
284     {
285         when(mockBatchingConfiguration.isContextBatchingEnabled()).thenReturn(true);
286 
287         final String resource1 = "test.atlassian:cool-stuff";
288         final String resource2 = "test.atlassian:hot-stuff";
289         final Plugin plugin = TestUtils.createTestPlugin("test.atlassian", "1", AlwaysTrueCondition.class, AlwaysFalseCondition.class);
290         final WebResourceModuleDescriptor resource1Descriptor = new WebResourceModuleDescriptorBuilder(plugin, "cool-stuff").setCondition(
291             AlwaysTrueCondition.class).addDescriptor("cool.js").addContext("foo").build();
292         mockEnabledPluginModule(resource1, resource1Descriptor);
293 
294         final WebResourceModuleDescriptor resource2Descriptor = new WebResourceModuleDescriptorBuilder(plugin, "hot-stuff").setCondition(
295             AlwaysFalseCondition.class).addDescriptor("hot.js").addContext("foo").build();
296         mockEnabledPluginModule(resource2, resource2Descriptor);
297 
298         final String resource3 = "test.atlassian:warm-stuff";
299         final WebResourceModuleDescriptor resourceDescriptor3 = TestUtils.createWebResourceModuleDescriptor(resource3, testPlugin,
300             TestUtils.createResourceDescriptors("warm.js"), Collections.<String> emptyList(), new HashSet<String>()
301             {
302                 {
303                     add("foo");
304                 }
305             });
306         mockEnabledPluginModule(resource3, resourceDescriptor3);
307 
308         when(mockPluginAccessor.getEnabledModuleDescriptorsByClass(WebResourceModuleDescriptor.class)).thenReturn(
309             asList(resource1Descriptor, resource2Descriptor, resourceDescriptor3));
310 
311         setupRequestCache();
312         webResourceManager.requireResourcesForContext("foo");
313 
314         final String tags = webResourceManager.getRequiredResources(UrlMode.AUTO);
315         assertTrue(tags.contains(resource1));
316         assertFalse(tags.contains(resource2));
317         assertTrue(tags.contains("foo/batch.js"));
318     }
319 
320     private Map<String, Object> setupRequestCache()
321     {
322         final Map<String, Object> requestCache = new HashMap<String, Object>();
323         when(mockWebResourceIntegration.getRequestCache()).thenReturn(requestCache);
324         return requestCache;
325     }
326 
327     public void testRequireResourceWithDuplicateDependencies()
328     {
329         final String resourceA = "test.atlassian:a";
330         final String resourceB = "test.atlassian:b";
331         final String resourceC = "test.atlassian:c";
332         final String resourceD = "test.atlassian:d";
333         final String resourceE = "test.atlassian:e";
334 
335         // A depends on B, C
336         mockEnabledPluginModule(resourceA, TestUtils.createWebResourceModuleDescriptor(resourceA, testPlugin,
337             Collections.<ResourceDescriptor> emptyList(), Arrays.asList(resourceB, resourceC)));
338         // B depends on D
339         mockEnabledPluginModule(resourceB, TestUtils.createWebResourceModuleDescriptor(resourceB, testPlugin,
340             Collections.<ResourceDescriptor> emptyList(), Collections.singletonList(resourceD)));
341         // C depends on E
342         mockEnabledPluginModule(resourceC, TestUtils.createWebResourceModuleDescriptor(resourceC, testPlugin,
343             Collections.<ResourceDescriptor> emptyList(), Collections.singletonList(resourceE)));
344         // D depends on C
345         mockEnabledPluginModule(resourceD, TestUtils.createWebResourceModuleDescriptor(resourceD, testPlugin,
346             Collections.<ResourceDescriptor> emptyList(), Collections.singletonList(resourceC)));
347         // E has no dependencies
348         mockEnabledPluginModule(resourceE, TestUtils.createWebResourceModuleDescriptor(resourceE, testPlugin));
349 
350         final Map<String, Object> requestCache = setupRequestCache();
351         webResourceManager.requireResource(resourceA);
352 
353         final Collection<?> resources = (Collection<?>) requestCache.get(WebResourceManagerImpl.REQUEST_CACHE_RESOURCE_KEY);
354         assertEquals(5, resources.size());
355         final Object[] resourceArray = resources.toArray();
356         assertEquals(resourceE, resourceArray[0]);
357         assertEquals(resourceC, resourceArray[1]);
358         assertEquals(resourceD, resourceArray[2]);
359         assertEquals(resourceB, resourceArray[3]);
360         assertEquals(resourceA, resourceArray[4]);
361     }
362 
363     public void testRequireSingleResourceGetsDeps() throws Exception
364     {
365         final String resourceA = "test.atlassian:a";
366         final String resourceB = "test.atlassian:b";
367 
368         final List<ResourceDescriptor> resourceDescriptorsA = TestUtils.createResourceDescriptors("resourceA.css");
369         final List<ResourceDescriptor> resourceDescriptorsB = TestUtils.createResourceDescriptors("resourceB.css", "resourceB-more.css");
370 
371         // A depends on B
372         mockEnabledPluginModule(resourceA, TestUtils.createWebResourceModuleDescriptor(resourceA, testPlugin, resourceDescriptorsA,
373             Collections.singletonList(resourceB)));
374         mockEnabledPluginModule(resourceB, TestUtils.createWebResourceModuleDescriptor(resourceB, testPlugin, resourceDescriptorsB,
375             Collections.<String> emptyList()));
376 
377         final String s = webResourceManager.getResourceTags(resourceA, UrlMode.AUTO);
378         final int indexA = s.indexOf(resourceA);
379         final int indexB = s.indexOf(resourceB);
380 
381         assertNotSame(-1, indexA);
382         assertNotSame(-1, indexB);
383         assertTrue(indexB < indexA);
384     }
385 
386     public void testIncludeResourcesWithResourceList() throws Exception
387     {
388         final String resourceA = "test.atlassian:a";
389 
390         final List<ResourceDescriptor> resourceDescriptorsA = TestUtils.createResourceDescriptors("resourceA.css");
391 
392         mockEnabledPluginModule(resourceA, TestUtils.createWebResourceModuleDescriptor(resourceA, testPlugin, resourceDescriptorsA,
393             Collections.<String> emptyList()));
394 
395         final StringWriter requiredResourceWriter = new StringWriter();
396         webResourceManager.includeResources(Arrays.<String> asList(resourceA), requiredResourceWriter, UrlMode.ABSOLUTE);
397         final String result = requiredResourceWriter.toString();
398         assertTrue(result.contains(resourceA));
399     }
400 
401     public void testIncludeResourcesWithResourceListIgnoresRequireResource() throws Exception
402     {
403         final String resourceA = "test.atlassian:a";
404         final String resourceB = "test.atlassian:b";
405 
406         final List<ResourceDescriptor> resourceDescriptorsA = TestUtils.createResourceDescriptors("resourceA.css");
407         final List<ResourceDescriptor> resourceDescriptorsB = TestUtils.createResourceDescriptors("resourceB.css", "resourceB-more.css");
408 
409         final Map<String, Object> requestCache = new HashMap<String, Object>();
410         when(mockWebResourceIntegration.getRequestCache()).thenReturn(requestCache);
411 
412         mockEnabledPluginModule(resourceA, TestUtils.createWebResourceModuleDescriptor(resourceA, testPlugin, resourceDescriptorsA,
413             Collections.<String> emptyList()));
414 
415         mockEnabledPluginModule(resourceB, TestUtils.createWebResourceModuleDescriptor(resourceB, testPlugin, resourceDescriptorsB,
416             Collections.<String> emptyList()));
417 
418         final StringWriter requiredResourceWriter = new StringWriter();
419         webResourceManager.requireResource(resourceB);
420         webResourceManager.includeResources(Arrays.<String> asList(resourceA), requiredResourceWriter, UrlMode.ABSOLUTE);
421         final String result = requiredResourceWriter.toString();
422         assertFalse(result.contains(resourceB));
423     }
424 
425     public void testIncludeResourcesWithResourceListIncludesDependences() throws Exception
426     {
427         final String resourceA = "test.atlassian:a";
428         final String resourceB = "test.atlassian:b";
429 
430         final List<ResourceDescriptor> resourceDescriptorsA = TestUtils.createResourceDescriptors("resourceA.css");
431         final List<ResourceDescriptor> resourceDescriptorsB = TestUtils.createResourceDescriptors("resourceB.css", "resourceB-more.css");
432 
433         // A depends on B
434         mockEnabledPluginModule(resourceA, TestUtils.createWebResourceModuleDescriptor(resourceA, testPlugin, resourceDescriptorsA,
435             Collections.singletonList(resourceB)));
436         mockEnabledPluginModule(resourceB, TestUtils.createWebResourceModuleDescriptor(resourceB, testPlugin, resourceDescriptorsB,
437             Collections.<String> emptyList()));
438 
439         final StringWriter requiredResourceWriter = new StringWriter();
440         webResourceManager.includeResources(Arrays.<String> asList(resourceA), requiredResourceWriter, UrlMode.ABSOLUTE);
441         final String result = requiredResourceWriter.toString();
442         assertTrue(result.contains(resourceB));
443     }
444 
445     public void testIncludeResourcesWithResourceListIncludesDependencesFromSuperBatch() throws Exception
446     {
447         final String resourceA = "test.atlassian:a";
448         final String resourceB = "test.atlassian:b";
449 
450         final ResourceBatchingConfiguration mockBatchingConfiguration = mock(ResourceBatchingConfiguration.class);
451         when(mockBatchingConfiguration.isSuperBatchingEnabled()).thenReturn(true);
452         when(mockBatchingConfiguration.getSuperBatchModuleCompleteKeys()).thenReturn(Arrays.asList(resourceB));
453 
454         webResourceManager = new WebResourceManagerImpl(pluginResourceLocator, mockWebResourceIntegration, mockUrlProvider, mockBatchingConfiguration);
455 
456         final List<ResourceDescriptor> resourceDescriptorsA = TestUtils.createResourceDescriptors("resourceA.css");
457         final List<ResourceDescriptor> resourceDescriptorsB = TestUtils.createResourceDescriptors("resourceB.css");
458 
459         // A depends on B
460         mockEnabledPluginModule(resourceA, TestUtils.createWebResourceModuleDescriptor(resourceA, testPlugin, resourceDescriptorsA,
461             Collections.singletonList(resourceB)));
462         mockEnabledPluginModule(resourceB, TestUtils.createWebResourceModuleDescriptor(resourceB, testPlugin, resourceDescriptorsB,
463             Collections.<String> emptyList()));
464 
465         final StringWriter requiredResourceWriter = new StringWriter();
466         webResourceManager.includeResources(Arrays.<String> asList(resourceA), requiredResourceWriter, UrlMode.ABSOLUTE);
467         final String result = requiredResourceWriter.toString();
468         assertTrue(result.contains(resourceB));
469     }
470 
471     public void testIncludeResourcesWithSharedDependencies() throws Exception
472     {
473         final String resourceA = "test.atlassian:a";
474         final String resourceB = "test.atlassian:b";
475         final String resourceShared = "test.atlassian:c";
476 
477         when(mockBatchingConfiguration.getSuperBatchModuleCompleteKeys()).thenReturn(Arrays.asList(resourceB));
478 
479         webResourceManager = new WebResourceManagerImpl(pluginResourceLocator, mockWebResourceIntegration, mockUrlProvider, mockBatchingConfiguration);
480 
481         final List<ResourceDescriptor> resourceDescriptorsA = TestUtils.createResourceDescriptors("resourceA.css");
482         final List<ResourceDescriptor> resourceDescriptorsB = TestUtils.createResourceDescriptors("resourceB.css");
483         final List<ResourceDescriptor> resourceDescriptorsShared = TestUtils.createResourceDescriptors("resourceC.css");
484 
485         // A depends on C
486         mockEnabledPluginModule(resourceA, TestUtils.createWebResourceModuleDescriptor(resourceA, testPlugin, resourceDescriptorsA,
487             Collections.singletonList(resourceShared)));
488         // B depends on C
489         mockEnabledPluginModule(resourceB, TestUtils.createWebResourceModuleDescriptor(resourceB, testPlugin, resourceDescriptorsB,
490             Collections.singletonList(resourceShared)));
491         mockEnabledPluginModule(resourceShared, TestUtils.createWebResourceModuleDescriptor(resourceShared, testPlugin, resourceDescriptorsShared,
492             Collections.<String> emptyList()));
493 
494         final StringWriter requiredResourceWriter = new StringWriter();
495         webResourceManager.includeResources(Arrays.<String> asList(resourceA, resourceB), requiredResourceWriter, UrlMode.ABSOLUTE);
496         final String result = requiredResourceWriter.toString();
497         assertTrue(result.contains(resourceA));
498         assertTrue(result.contains(resourceB));
499 
500         final Pattern resourceCount = Pattern.compile("/download/batch/test\\.atlassian:c/test\\.atlassian:c.css");
501         final Matcher m = resourceCount.matcher(result);
502 
503         assertTrue(m.find());
504         assertFalse(m.find(m.end()));
505     }
506 
507     public void testRequireResourcesAreClearedAfterIncludesResourcesIsCalled() throws Exception
508     {
509         final String moduleKey = "cool-resources";
510         final String completeModuleKey = "test.atlassian:" + moduleKey;
511 
512         final List<ResourceDescriptor> resourceDescriptors1 = TestUtils.createResourceDescriptors("cool.css", "more-cool.css", "cool.js");
513 
514         setupRequestCache();
515 
516         mockEnabledPluginModule(completeModuleKey, TestUtils.createWebResourceModuleDescriptor(completeModuleKey, testPlugin, resourceDescriptors1));
517 
518         // test requireResource() methods
519         webResourceManager.requireResource(completeModuleKey);
520         webResourceManager.includeResources(new StringWriter(), UrlMode.RELATIVE);
521 
522         final StringWriter requiredResourceWriter = new StringWriter();
523         webResourceManager.includeResources(requiredResourceWriter, UrlMode.RELATIVE);
524         assertEquals("", requiredResourceWriter.toString());
525     }
526 
527     // testRequireResourceAndResourceTagMethods
528 
529     public void testRequireResourceAndResourceTagMethods() throws Exception
530     {
531         final String completeModuleKey = "test.atlassian:cool-resources";
532         final String staticBase = setupRequireResourceAndResourceTagMethods(false, completeModuleKey);
533 
534         // test requireResource() methods
535         final StringWriter requiredResourceWriter = new StringWriter();
536         webResourceManager.requireResource(completeModuleKey);
537         final String requiredResourceResult = webResourceManager.getRequiredResources(UrlMode.RELATIVE);
538         webResourceManager.includeResources(requiredResourceWriter, UrlMode.RELATIVE);
539         assertEquals(requiredResourceResult, requiredResourceWriter.toString());
540 
541         assertTrue(requiredResourceResult.contains("href=\"" + staticBase + "/" + completeModuleKey + "/" + completeModuleKey + ".css"));
542         assertTrue(requiredResourceResult.contains("src=\"" + staticBase + "/" + completeModuleKey + "/" + completeModuleKey + ".js"));
543 
544         // test resourceTag() methods
545         final StringWriter resourceTagsWriter = new StringWriter();
546         webResourceManager.requireResource(completeModuleKey, resourceTagsWriter, UrlMode.RELATIVE);
547         final String resourceTagsResult = webResourceManager.getResourceTags(completeModuleKey, UrlMode.RELATIVE);
548         assertEquals(resourceTagsResult, resourceTagsWriter.toString());
549 
550         // calling requireResource() or resourceTag() on a single webresource should be the same
551         assertEquals(requiredResourceResult, resourceTagsResult);
552     }
553 
554     public void testRequireResourceAndResourceTagMethodsWithAbsoluteUrlMode() throws Exception
555     {
556         testRequireResourceAndResourceTagMethods(UrlMode.ABSOLUTE, true);
557     }
558 
559     public void testRequireResourceAndResourceTagMethodsWithRelativeUrlMode() throws Exception
560     {
561         testRequireResourceAndResourceTagMethods(UrlMode.RELATIVE, false);
562     }
563 
564     public void testRequireResourceAndResourceTagMethodsWithAutoUrlMode() throws Exception
565     {
566         testRequireResourceAndResourceTagMethods(UrlMode.AUTO, false);
567     }
568 
569     private void testRequireResourceAndResourceTagMethods(final UrlMode urlMode, final boolean baseUrlExpected) throws Exception
570     {
571         final String completeModuleKey = "test.atlassian:cool-resources";
572         final String staticBase = setupRequireResourceAndResourceTagMethods(baseUrlExpected, completeModuleKey);
573 
574         // test requireResource() methods
575         final StringWriter requiredResourceWriter = new StringWriter();
576         webResourceManager.requireResource(completeModuleKey);
577         final String requiredResourceResult = webResourceManager.getRequiredResources(urlMode);
578         webResourceManager.includeResources(requiredResourceWriter, urlMode);
579         assertEquals(requiredResourceResult, requiredResourceWriter.toString());
580 
581         assertTrue(requiredResourceResult.contains("href=\"" + staticBase + "/" + completeModuleKey + "/" + completeModuleKey + ".css"));
582         assertTrue(requiredResourceResult.contains("src=\"" + staticBase + "/" + completeModuleKey + "/" + completeModuleKey + ".js"));
583 
584         // test resourceTag() methods
585         final StringWriter resourceTagsWriter = new StringWriter();
586         webResourceManager.requireResource(completeModuleKey, resourceTagsWriter, urlMode);
587         final String resourceTagsResult = webResourceManager.getResourceTags(completeModuleKey, urlMode);
588         assertEquals(resourceTagsResult, resourceTagsWriter.toString());
589 
590         // calling requireResource() or resourceTag() on a single webresource should be the same
591         assertEquals(requiredResourceResult, resourceTagsResult);
592     }
593 
594     private String setupRequireResourceAndResourceTagMethods(final boolean baseUrlExpected, final String completeModuleKey) throws DocumentException
595     {
596         final List<ResourceDescriptor> descriptors = TestUtils.createResourceDescriptors("cool.css", "more-cool.css", "cool.js");
597 
598         final Map<String, Object> requestCache = new HashMap<String, Object>();
599         when(mockWebResourceIntegration.getRequestCache()).thenReturn(requestCache);
600 
601         mockEnabledPluginModule(completeModuleKey, TestUtils.createWebResourceModuleDescriptor(completeModuleKey, testPlugin, descriptors));
602 
603         final String staticPrefix = (baseUrlExpected ? BASEURL : "") + "/" + WebResourceManagerImpl.STATIC_RESOURCE_PREFIX + "/" + SYSTEM_BUILD_NUMBER + "/" + SYSTEM_COUNTER + "/" + testPlugin.getPluginInformation().getVersion() + "/" + WebResourceManagerImpl.STATIC_RESOURCE_SUFFIX;
604         when(mockUrlProvider.getStaticResourcePrefix(eq("1"), isA(UrlMode.class))).thenReturn(staticPrefix);
605         return staticPrefix + BatchPluginResource.URL_PREFIX;
606     }
607 
608     // testRequireResourceWithCacheParameter
609 
610     public void testRequireResourceWithCacheParameter() throws Exception
611     {
612         final String completeModuleKey = "test.atlassian:no-cache-resources";
613 
614         final String expectedResult = setupRequireResourceWithCacheParameter(false, completeModuleKey);
615         assertTrue(webResourceManager.getResourceTags(completeModuleKey, UrlMode.AUTO).contains(expectedResult));
616     }
617 
618     public void testRequireResourceWithCacheParameterAndAbsoluteUrlMode() throws Exception
619     {
620         testRequireResourceWithCacheParameter(UrlMode.ABSOLUTE, true);
621     }
622 
623     public void testRequireResourceWithCacheParameterAndRelativeUrlMode() throws Exception
624     {
625         testRequireResourceWithCacheParameter(UrlMode.RELATIVE, false);
626     }
627 
628     public void testRequireResourceWithCacheParameterAndAutoUrlMode() throws Exception
629     {
630         testRequireResourceWithCacheParameter(UrlMode.AUTO, false);
631     }
632 
633     private void testRequireResourceWithCacheParameter(final UrlMode urlMode, final boolean baseUrlExpected) throws Exception
634     {
635         final String completeModuleKey = "test.atlassian:no-cache-resources";
636         final String expectedResult = setupRequireResourceWithCacheParameter(baseUrlExpected, completeModuleKey);
637         assertTrue(webResourceManager.getResourceTags(completeModuleKey, urlMode).contains(expectedResult));
638     }
639 
640     private String setupRequireResourceWithCacheParameter(final boolean baseUrlExpected, final String completeModuleKey) throws DocumentException
641     {
642         final Map<String, String> params = new HashMap<String, String>();
643         params.put("cache", "false");
644         final ResourceDescriptor resourceDescriptor = TestUtils.createResourceDescriptor("no-cache.js", params);
645 
646         mockEnabledPluginModule(completeModuleKey, TestUtils.createWebResourceModuleDescriptor(completeModuleKey, testPlugin,
647             Collections.singletonList(resourceDescriptor)));
648 
649         return "src=\"" + (baseUrlExpected ? BASEURL : "") + BatchPluginResource.URL_PREFIX + "/" + completeModuleKey + "/" + completeModuleKey + ".js?cache=false";
650     }
651 
652     public void testGetRequiredResourcesWithFilter() throws Exception
653     {
654         final String moduleKey = "cool-resources";
655         final String completeModuleKey = "test.atlassian:" + moduleKey;
656 
657         final List<ResourceDescriptor> resourceDescriptors1 = TestUtils.createResourceDescriptors("cool.css", "cool.js", "more-cool.css");
658 
659         setupRequestCache();
660 
661         mockEnabledPluginModule(completeModuleKey, TestUtils.createWebResourceModuleDescriptor(completeModuleKey, testPlugin, resourceDescriptors1));
662 
663         // test includeResources(writer, type) method
664         webResourceManager.requireResource(completeModuleKey);
665 
666         final String staticPrefix = BASEURL + "/" + WebResourceManagerImpl.STATIC_RESOURCE_PREFIX + "/" + SYSTEM_BUILD_NUMBER + "/" + SYSTEM_COUNTER + "/" + testPlugin.getPluginInformation().getVersion() + "/" + WebResourceManagerImpl.STATIC_RESOURCE_SUFFIX;
667         when(mockUrlProvider.getStaticResourcePrefix("1", UrlMode.ABSOLUTE)).thenReturn(staticPrefix);
668         final String staticBase = staticPrefix + BatchPluginResource.URL_PREFIX;
669 
670         final String cssRef = "href=\"" + staticBase + "/" + completeModuleKey + "/" + completeModuleKey + ".css";
671         final String jsRef = "src=\"" + staticBase + "/" + completeModuleKey + "/" + completeModuleKey + ".js";
672 
673         // CSS
674         String requiredResourceResult = webResourceManager.getRequiredResources(UrlMode.ABSOLUTE, new CssWebResource());
675         assertTrue(requiredResourceResult.contains(cssRef));
676         assertFalse(requiredResourceResult.contains(jsRef));
677 
678         // JS
679         requiredResourceResult = webResourceManager.getRequiredResources(UrlMode.ABSOLUTE, new JavascriptWebResource());
680         assertFalse(requiredResourceResult.contains(cssRef));
681         assertTrue(requiredResourceResult.contains(jsRef));
682 
683         // BOTH
684         requiredResourceResult = webResourceManager.getRequiredResources(UrlMode.ABSOLUTE);
685         assertTrue(requiredResourceResult.contains(cssRef));
686         assertTrue(requiredResourceResult.contains(jsRef));
687     }
688 
689     public void testGetRequiredResourcesWithCustomFilters() throws Exception
690     {
691         final WebResourceFilter atlassianFilter = new WebResourceFilter()
692         {
693             public boolean matches(final String resourceName)
694             {
695                 return resourceName.contains("atlassian");
696             }
697         };
698         final WebResourceFilter bogusFilter = new WebResourceFilter()
699         {
700             public boolean matches(final String resourceName)
701             {
702                 return true;
703             }
704         };
705 
706         final String moduleKey = "cool-resources";
707         final String completeModuleKey = "test.atlassian:" + moduleKey;
708 
709         final List<ResourceDescriptor> resources = TestUtils.createResourceDescriptors("foo.css", "foo-bar.js", "atlassian.css",
710             "atlassian-plugins.js");
711 
712         setupRequestCache();
713         mockEnabledPluginModule(completeModuleKey, TestUtils.createWebResourceModuleDescriptor(completeModuleKey, testPlugin, resources));
714 
715         // easier to test which resources were included by the filter with batching turned off
716         when(mockBatchingConfiguration.isPluginWebResourceBatchingEnabled()).thenReturn(false);
717         webResourceManager.requireResource(completeModuleKey);
718         final String atlassianResources = webResourceManager.getRequiredResources(UrlMode.RELATIVE, atlassianFilter);
719         assertEquals(-1, atlassianResources.indexOf("foo"));
720         assertTrue(atlassianResources.contains("atlassian.css"));
721         assertTrue(atlassianResources.contains("atlassian-plugins.js"));
722 
723         final String allResources = webResourceManager.getRequiredResources(UrlMode.RELATIVE, bogusFilter);
724         for (final ResourceDescriptor resource : resources)
725         {
726             assertTrue(allResources.contains(resource.getName()));
727         }
728     }
729 
730     public void testGetRequiredResourcesOrdersByType() throws Exception
731     {
732         final String moduleKey1 = "cool-resources";
733         final String moduleKey2 = "hot-resources";
734         final String completeModuleKey1 = "test.atlassian:" + moduleKey1;
735         final String completeModuleKey2 = "test.atlassian:" + moduleKey2;
736 
737         final List<ResourceDescriptor> resourceDescriptors1 = TestUtils.createResourceDescriptors("cool.js", "cool.css", "more-cool.css");
738         final List<ResourceDescriptor> resourceDescriptors2 = TestUtils.createResourceDescriptors("hot.js", "hot.css", "more-hot.css");
739 
740         final Plugin plugin = TestUtils.createTestPlugin();
741 
742         setupRequestCache();
743 
744         mockEnabledPluginModule(completeModuleKey1, TestUtils.createWebResourceModuleDescriptor(completeModuleKey1, plugin, resourceDescriptors1));
745         mockEnabledPluginModule(completeModuleKey2, TestUtils.createWebResourceModuleDescriptor(completeModuleKey2, plugin, resourceDescriptors2));
746 
747         // test includeResources(writer, type) method
748         webResourceManager.requireResource(completeModuleKey1);
749         webResourceManager.requireResource(completeModuleKey2);
750 
751         final String staticPrefix = BASEURL + "/" + WebResourceManagerImpl.STATIC_RESOURCE_PREFIX + "/" + SYSTEM_BUILD_NUMBER + "/" + SYSTEM_COUNTER + "/" + plugin.getPluginInformation().getVersion() + "/" + WebResourceManagerImpl.STATIC_RESOURCE_SUFFIX;
752         when(mockUrlProvider.getStaticResourcePrefix("1", UrlMode.ABSOLUTE)).thenReturn(staticPrefix);
753         final String staticBase = staticPrefix + BatchPluginResource.URL_PREFIX;
754 
755         final String cssRef1 = "href=\"" + staticBase + "/" + completeModuleKey1 + "/" + completeModuleKey1 + ".css";
756         final String cssRef2 = "href=\"" + staticBase + "/" + completeModuleKey2 + "/" + completeModuleKey2 + ".css";
757         final String jsRef1 = "src=\"" + staticBase + "/" + completeModuleKey1 + "/" + completeModuleKey1 + ".js";
758         final String jsRef2 = "src=\"" + staticBase + "/" + completeModuleKey2 + "/" + completeModuleKey2 + ".js";
759 
760         final String requiredResourceResult = webResourceManager.getRequiredResources(UrlMode.ABSOLUTE);
761 
762         assertTrue(requiredResourceResult.contains(cssRef1));
763         assertTrue(requiredResourceResult.contains(cssRef2));
764         assertTrue(requiredResourceResult.contains(jsRef1));
765         assertTrue(requiredResourceResult.contains(jsRef2));
766 
767         final int cssRef1Index = requiredResourceResult.indexOf(cssRef1);
768         final int cssRef2Index = requiredResourceResult.indexOf(cssRef2);
769         final int jsRef1Index = requiredResourceResult.indexOf(jsRef1);
770         final int jsRef2Index = requiredResourceResult.indexOf(jsRef2);
771 
772         assertTrue(cssRef1Index < jsRef1Index);
773         assertTrue(cssRef2Index < jsRef2Index);
774         assertTrue(cssRef2Index < jsRef1Index);
775     }
776 
777     public void testRequireResourceInSuperbatch() throws ClassNotFoundException
778     {
779         setupSuperBatch();
780 
781         final Map<String, Object> requestCache = setupRequestCache();
782         mockOutSuperbatchPluginAccesses();
783 
784         webResourceManager.requireResource("test.atlassian:superbatch");
785 
786         final Collection<?> resources = (Collection<?>) requestCache.get(WebResourceManagerImpl.REQUEST_CACHE_RESOURCE_KEY);
787         assertEquals(0, resources.size());
788     }
789 
790     public void testRequireResourceWithDependencyInSuperbatch() throws DocumentException, ClassNotFoundException
791     {
792         setupSuperBatch();
793 
794         mockOutSuperbatchPluginAccesses();
795 
796         final Map<String, Object> requestCache = setupRequestCache();
797 
798         mockEnabledPluginModule("test.atlassian:included-resource", TestUtils.createWebResourceModuleDescriptor("test.atlassian:included-resource",
799             testPlugin, Collections.<ResourceDescriptor> emptyList(), Collections.singletonList("test.atlassian:superbatch")));
800 
801         webResourceManager.requireResource("test.atlassian:included-resource");
802 
803         final Collection<?> resources = (Collection<?>) requestCache.get(WebResourceManagerImpl.REQUEST_CACHE_RESOURCE_KEY);
804         assertEquals(1, resources.size());
805         assertEquals("test.atlassian:included-resource", resources.iterator().next());
806     }
807 
808     public void testSuperBatchResolution() throws DocumentException, ClassNotFoundException
809     {
810         setupSuperBatch();
811 
812         TestUtils.setupSuperbatchTestContent(mockPluginAccessor, testPlugin);
813 
814         final List<PluginResource> cssResources = webResourceManager.getSuperBatchResources(CssWebResource.FORMATTER);
815         assertEquals(2, cssResources.size());
816 
817         final SuperBatchPluginResource superBatch1 = (SuperBatchPluginResource) cssResources.get(0);
818         assertEquals("batch.css", superBatch1.getResourceName());
819         assertTrue(superBatch1.getParams().isEmpty());
820 
821         final SuperBatchPluginResource superBatch2 = (SuperBatchPluginResource) cssResources.get(1);
822         assertEquals("batch.css", superBatch2.getResourceName());
823         assertEquals("true", superBatch2.getParams().get("ieonly"));
824 
825         final List<PluginResource> jsResources = webResourceManager.getSuperBatchResources(JavascriptWebResource.FORMATTER);
826         assertEquals(1, jsResources.size());
827         assertEquals("batch.js", jsResources.get(0).getResourceName());
828         assertEquals(0, jsResources.get(0).getParams().size());
829     }
830 
831     // First part: execute some WRM code on in a new context on top of a non-empty context and write out.
832     //             Check that the expected resources are included in the inner write out and that the outer resources aren't included
833     // Second part: after the inner execution is completed, write the outer resources out, checking that we didn't get the inner resources and that we did get
834     //              the resources which were in the context before the inner execution.
835     public void testNestedContextExclusivity() throws Exception
836     {
837 
838         final String moduleKey1 = "cool-resources";
839         final String moduleKey2 = "hot-resources";
840         final String completeModuleKey1 = "test.atlassian:" + moduleKey1;
841         final String completeModuleKey2 = "test.atlassian:" + moduleKey2;
842 
843         final List<ResourceDescriptor> resourceDescriptors1 = TestUtils.createResourceDescriptors("cool.js", "cool.css", "more-cool.css");
844         final List<ResourceDescriptor> resourceDescriptors2 = TestUtils.createResourceDescriptors("hot.js", "hot.css", "more-hot.css");
845 
846         final Plugin plugin = TestUtils.createTestPlugin();
847 
848         setupRequestCache();
849 
850         mockEnabledPluginModule(completeModuleKey1, TestUtils.createWebResourceModuleDescriptor(completeModuleKey1, plugin, resourceDescriptors1));
851         mockEnabledPluginModule(completeModuleKey2, TestUtils.createWebResourceModuleDescriptor(completeModuleKey2, plugin, resourceDescriptors2));
852 
853         final String cssRef1 = completeModuleKey1 + "/" + completeModuleKey1 + ".css";
854         final String cssRef2 = completeModuleKey2 + "/" + completeModuleKey2 + ".css";
855         final String jsRef1 = completeModuleKey1 + "/" + completeModuleKey1 + ".js";
856         final String jsRef2 = completeModuleKey2 + "/" + completeModuleKey2 + ".js";
857 
858         // test includeResources(writer, type) method
859         webResourceManager.requireResource(completeModuleKey1);
860         webResourceManager.executeInNewContext(new Supplier<Void>()
861         {
862             public Void get()
863             {
864                 webResourceManager.requireResource(completeModuleKey2);
865 
866                 final StringWriter writer = new StringWriter();
867                 webResourceManager.includeResources(writer, UrlMode.AUTO);
868                 final String resources = writer.toString();
869 
870                 assertFalse(resources.contains(cssRef1));
871                 assertTrue(resources.contains(cssRef2));
872 
873                 assertFalse(resources.contains(jsRef1));
874                 assertTrue(resources.contains(jsRef2));
875                 return null;
876             }
877         });
878 
879         final StringWriter writer = new StringWriter();
880         webResourceManager.includeResources(writer, UrlMode.AUTO);
881         final String resources = writer.toString();
882 
883         assertTrue(resources.contains(cssRef1));
884         assertFalse(resources.contains(cssRef2));
885 
886         assertTrue(resources.contains(jsRef1));
887         assertFalse(resources.contains(jsRef2));
888 
889     }
890 
891     // Require and include some resource in an inner context with an empty outer context. Check that the inner context gets the expected resources.
892     // After the inner code is executed, check that we didn't get any resources in the outer context
893     public void testNestedContextOnEmptyBase() throws Exception
894     {
895         final String moduleKey1 = "cool-resources";
896         final String completeModuleKey1 = "test.atlassian:" + moduleKey1;
897 
898         final List<ResourceDescriptor> resourceDescriptors1 = TestUtils.createResourceDescriptors("cool.js", "cool.css", "more-cool.css");
899 
900         final Plugin plugin = TestUtils.createTestPlugin();
901 
902         setupRequestCache();
903 
904         mockEnabledPluginModule(completeModuleKey1, TestUtils.createWebResourceModuleDescriptor(completeModuleKey1, plugin, resourceDescriptors1));
905 
906         final String cssRef1 = completeModuleKey1 + "/" + completeModuleKey1 + ".css";
907         final String jsRef1 = completeModuleKey1 + "/" + completeModuleKey1 + ".js";
908 
909         // test includeResources(writer, type) method
910         webResourceManager.executeInNewContext(new Supplier<Void>()
911         {
912             public Void get()
913             {
914                 webResourceManager.requireResource(completeModuleKey1);
915 
916                 final StringWriter writer = new StringWriter();
917                 webResourceManager.includeResources(writer, UrlMode.AUTO);
918                 final String resources = writer.toString();
919 
920                 assertTrue(resources.contains(cssRef1));
921                 assertTrue(resources.contains(jsRef1));
922 
923                 return null;
924             }
925         });
926 
927         final StringWriter writer = new StringWriter();
928         webResourceManager.includeResources(writer, UrlMode.AUTO);
929         final String resources = writer.toString();
930 
931         assertFalse(resources.contains(cssRef1));
932         assertFalse(resources.contains(jsRef1));
933     }
934 
935     // Require (but not include) some resource in an inner context with an empty outer context. After the inner code is executed,
936     // check that we didn't get any resources in the outer context
937     public void testNestedContextWithoutWriting() throws Exception
938     {
939         final String moduleKey1 = "cool-resources";
940         final String completeModuleKey1 = "test.atlassian:" + moduleKey1;
941 
942         final List<ResourceDescriptor> resourceDescriptors1 = TestUtils.createResourceDescriptors("cool.js", "cool.css", "more-cool.css");
943 
944         final Plugin plugin = TestUtils.createTestPlugin();
945 
946         setupRequestCache();
947 
948         mockEnabledPluginModule(completeModuleKey1, TestUtils.createWebResourceModuleDescriptor(completeModuleKey1, plugin, resourceDescriptors1));
949 
950         final String cssRef1 = completeModuleKey1 + "/" + completeModuleKey1 + ".css";
951         final String jsRef1 = completeModuleKey1 + "/" + completeModuleKey1 + ".js";
952 
953         // test includeResources(writer, type) method
954         webResourceManager.executeInNewContext(new Supplier<Void>()
955         {
956             public Void get()
957             {
958                 webResourceManager.requireResource(completeModuleKey1);
959                 return null;
960             }
961         });
962 
963         final StringWriter writer = new StringWriter();
964         webResourceManager.includeResources(writer, UrlMode.AUTO);
965         final String resources = writer.toString();
966 
967         // check that we didn't get these resources (meaning that the require resources call never caused the module to be included)
968         assertFalse(resources.contains(cssRef1));
969         assertFalse(resources.contains(jsRef1));
970     }
971 
972     // push content into the outer-most context, nest multiple non-empty executeInNewContext calls, ensuring that the expected content is
973     // written in each context
974     public void testMultipleNestedContexts() throws Exception
975     {
976         final String moduleKey1 = "cool-resources";
977         final String moduleKey2 = "hot-resources";
978         final String moduleKey3 = "warm-resources";
979         final String completeModuleKey1 = "test.atlassian:" + moduleKey1;
980         final String completeModuleKey2 = "test.atlassian:" + moduleKey2;
981         final String completeModuleKey3 = "test.atlassian:" + moduleKey3;
982 
983         final List<ResourceDescriptor> resourceDescriptors1 = TestUtils.createResourceDescriptors("cool.js", "cool.css", "more-cool.css");
984         final List<ResourceDescriptor> resourceDescriptors2 = TestUtils.createResourceDescriptors("hot.js", "hot.css", "more-hot.css");
985         final List<ResourceDescriptor> resourceDescriptors3 = TestUtils.createResourceDescriptors("warm.js", "warm.css", "more-warm.css");
986 
987         final Plugin plugin = TestUtils.createTestPlugin();
988 
989         setupRequestCache();
990 
991         mockEnabledPluginModule(completeModuleKey1, TestUtils.createWebResourceModuleDescriptor(completeModuleKey1, plugin, resourceDescriptors1));
992         mockEnabledPluginModule(completeModuleKey2, TestUtils.createWebResourceModuleDescriptor(completeModuleKey2, plugin, resourceDescriptors2));
993         mockEnabledPluginModule(completeModuleKey3, TestUtils.createWebResourceModuleDescriptor(completeModuleKey3, plugin, resourceDescriptors3));
994 
995         final String cssRef1 = completeModuleKey1 + "/" + completeModuleKey1 + ".css";
996         final String cssRef2 = completeModuleKey2 + "/" + completeModuleKey2 + ".css";
997         final String cssRef3 = completeModuleKey3 + "/" + completeModuleKey3 + ".css";
998         final String jsRef1 = completeModuleKey1 + "/" + completeModuleKey1 + ".js";
999         final String jsRef2 = completeModuleKey2 + "/" + completeModuleKey2 + ".js";
1000         final String jsRef3 = completeModuleKey3 + "/" + completeModuleKey3 + ".js";
1001 
1002         setupRequestCache();
1003 
1004         // require module 1 for outermost resource
1005         webResourceManager.requireResource(completeModuleKey1);
1006 
1007         // nest middle resource context (requests module 2)
1008         webResourceManager.executeInNewContext(new Supplier<Void>()
1009         {
1010             public Void get()
1011             {
1012                 webResourceManager.requireResource(completeModuleKey2);
1013                 // nest inner-most resource context (requests module 3)
1014                 webResourceManager.executeInNewContext(new Supplier<Void>()
1015                 {
1016                     public Void get()
1017                     {
1018                         webResourceManager.requireResource(completeModuleKey3);
1019 
1020                         // check that the inner-most context only got module 3
1021                         final StringWriter writer = new StringWriter();
1022                         webResourceManager.includeResources(writer, UrlMode.AUTO);
1023                         final String resources = writer.toString();
1024 
1025                         assertTrue(resources.contains(cssRef3));
1026                         assertTrue(resources.contains(jsRef3));
1027                         assertFalse(resources.contains(cssRef1));
1028                         assertFalse(resources.contains(jsRef1));
1029                         assertFalse(resources.contains(cssRef2));
1030                         assertFalse(resources.contains(jsRef2));
1031 
1032                         return null;
1033                     }
1034                 });
1035 
1036                 // check that the middle context only got module 2
1037                 final StringWriter writer = new StringWriter();
1038                 webResourceManager.includeResources(writer, UrlMode.AUTO);
1039                 final String resources = writer.toString();
1040 
1041                 assertTrue(resources.contains(cssRef2));
1042                 assertTrue(resources.contains(jsRef2));
1043                 assertFalse(resources.contains(cssRef1));
1044                 assertFalse(resources.contains(jsRef1));
1045                 assertFalse(resources.contains(cssRef3));
1046                 assertFalse(resources.contains(jsRef3));
1047 
1048                 return null;
1049             }
1050         });
1051 
1052         // check that the outer context only got module 1
1053 
1054         final StringWriter writer = new StringWriter();
1055         webResourceManager.includeResources(writer, UrlMode.AUTO);
1056         final String resources = writer.toString();
1057 
1058         assertTrue(resources.contains(cssRef1));
1059         assertTrue(resources.contains(jsRef1));
1060         assertFalse(resources.contains(cssRef2));
1061         assertFalse(resources.contains(jsRef2));
1062         assertFalse(resources.contains(cssRef3));
1063         assertFalse(resources.contains(jsRef3));
1064     }
1065 
1066     private void setupSuperBatch()
1067     {
1068         when(mockBatchingConfiguration.isSuperBatchingEnabled()).thenReturn(true);
1069         when(mockBatchingConfiguration.getSuperBatchModuleCompleteKeys()).thenReturn(
1070             Arrays.asList("test.atlassian:superbatch", "test.atlassian:superbatch2", "test.atlassian:missing-plugin"));
1071     }
1072 
1073     private void mockOutSuperbatchPluginAccesses() throws ClassNotFoundException
1074     {
1075         mockOutPluginModule("test.atlassian:superbatch");
1076         mockOutPluginModule("test.atlassian:superbatch2");
1077         when(mockPluginAccessor.getPluginModule("test.atlassian:missing-plugin")).thenReturn(null);
1078         when(mockPluginAccessor.getEnabledPluginModule("test.atlassian:missing-plugin")).thenReturn(null);
1079     }
1080 
1081     private void mockOutPluginModule(final String moduleKey) throws ClassNotFoundException
1082     {
1083         final Plugin p = TestUtils.createTestPlugin();
1084         final ModuleDescriptor module = TestUtils.createWebResourceModuleDescriptor(moduleKey, p);
1085         when(mockPluginAccessor.getPluginModule(moduleKey)).thenReturn(module);
1086         when(mockPluginAccessor.getEnabledPluginModule(moduleKey)).thenReturn(module);
1087     }
1088 }