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