1   package com.atlassian.plugins.codegen;
2   
3   import java.io.File;
4   import java.io.IOException;
5   import java.io.InputStream;
6   import java.util.UUID;
7   
8   import com.atlassian.plugins.codegen.ComponentDeclaration.Visibility;
9   import com.atlassian.plugins.codegen.modules.PluginModuleLocation;
10  
11  import com.google.common.collect.ImmutableMap;
12  
13  import org.apache.commons.io.FileUtils;
14  import org.apache.commons.io.IOUtils;
15  import org.dom4j.Document;
16  import org.dom4j.DocumentHelper;
17  import org.dom4j.Node;
18  import org.junit.Before;
19  import org.junit.Test;
20  
21  import static com.atlassian.fugue.Option.some;
22  import static com.atlassian.plugins.codegen.ClassId.fullyQualified;
23  import static junit.framework.Assert.assertEquals;
24  import static junit.framework.Assert.assertNotNull;
25  import static junit.framework.Assert.assertNull;
26  import static junit.framework.Assert.assertTrue;
27  
28  public class PluginXmlRewriterTest
29  {
30      protected static final String CLASS = "com.atlassian.test.MyClass";
31      protected static final String INTERFACE = "com.atlassian.test.MyInterface";
32      protected static final ComponentImport IMPORT = ComponentImport.componentImport(fullyQualified(INTERFACE));
33       
34      protected File tempDir;
35      protected File resourcesDir;
36      protected File pluginXml;
37      protected ComponentDeclaration.Builder componentBuilder = ComponentDeclaration.builder(fullyQualified(CLASS), "my-key");
38  
39      protected PluginXmlRewriter rewriter;
40      
41      @Before
42      public void setup() throws Exception
43      {
44          final File sysTempDir = new File("target");
45          String dirName = UUID.randomUUID().toString();
46          tempDir = new File(sysTempDir, dirName);
47          resourcesDir = new File(tempDir, "resources");
48          pluginXml = new File(resourcesDir, "atlassian-plugin.xml");
49  
50          tempDir.mkdirs();
51          resourcesDir.mkdirs();
52  
53          InputStream is = this.getClass().getResourceAsStream("/empty-plugin.xml");
54          IOUtils.copy(is, FileUtils.openOutputStream(pluginXml));
55          
56          rewriter = new PluginXmlRewriter(pluginXml);
57      }
58      
59      @Test
60      public void canConstructXmlRewriterFromLocation() throws Exception
61      {
62          PluginModuleLocation location = new PluginModuleLocation.Builder(new File(tempDir, "src"))
63              .resourcesDirectory(resourcesDir)
64              .testDirectory(new File(tempDir, "test-src"))
65              .build();
66          rewriter = new PluginXmlRewriter(location);
67          
68          // make an arbitrary change
69          long oldFileSize = FileUtils.sizeOf(pluginXml);
70          rewriter.applyChanges(addPluginParam());
71          
72          // verify that it found the XML file in its expected location and modified it
73          assertTrue(FileUtils.sizeOf(pluginXml) != oldFileSize);
74      }
75      
76      @Test
77      public void i18nResourceIsNotAddedByDefault() throws Exception
78      {
79          Document xml = applyChanges(addPluginParam());
80          
81          assertEquals(0, xml.selectNodes("//resource").size());
82      }
83  
84      @Test
85      public void i18nResourceIsAddedIfI18nPropertiesArePresent() throws Exception
86      {
87          Document xml = applyChanges(addI18nProperty());
88          
89          assertNotNull(xml.selectSingleNode("//resource"));
90      }
91  
92      @Test
93      public void i18nResourceHasType() throws Exception
94      {
95          Document xml = applyChanges(addI18nProperty());
96          
97          assertEquals("i18n", xml.selectSingleNode("//resource/@type").getText());
98      }
99  
100     @Test
101     public void i18nResourceHasName() throws Exception
102     {
103         Document xml = applyChanges(addI18nProperty());
104         
105         assertEquals("i18n", xml.selectSingleNode("//resource/@name").getText());
106     }
107 
108     @Test
109     public void i18nResourceHasLocation() throws Exception
110     {
111         Document xml = applyChanges(addI18nProperty());
112         
113         assertEquals(ProjectRewriter.DEFAULT_I18N_NAME, xml.selectSingleNode("//resource/@location").getText());
114     }
115 
116     @Test
117     public void pluginParamIsAdded() throws Exception
118     {
119         Document xml = applyChanges(addPluginParam());
120         
121         assertNotNull(xml.selectSingleNode("//plugin-info/param"));
122     }
123     
124     @Test
125     public void pluginParamHasName() throws Exception
126     {
127         Document xml = applyChanges(addPluginParam());
128         
129         assertEquals("foo", xml.selectSingleNode("//plugin-info/param/@name").getText());
130     }
131 
132     @Test
133     public void pluginParamHasValue() throws Exception
134     {
135         Document xml = applyChanges(addPluginParam());
136         
137         assertEquals("bar", xml.selectSingleNode("//plugin-info/param").getText());
138     }
139 
140     @Test
141     public void secondPluginParamIsAdded() throws Exception
142     {
143         applyChanges(addPluginParam());
144         Document xml = applyChanges(new PluginProjectChangeset().withPluginParameters(ImmutableMap.of("second", "thing")));
145         
146         assertEquals(2, xml.selectNodes("//plugin-info/param").size());
147     }
148 
149     @Test
150     public void secondPluginParamHasValue() throws Exception
151     {
152         applyChanges(addPluginParam());
153         Document xml = applyChanges(new PluginProjectChangeset().withPluginParameters(ImmutableMap.of("second", "thing")));
154         
155         assertEquals("thing", xml.selectSingleNode("//plugin-info/param[@name='second']").getText());
156     }
157 
158     @Test
159     public void pluginParamCannotBeOverwritten() throws Exception
160     {
161         applyChanges(addPluginParam());
162         Document xml = applyChanges(new PluginProjectChangeset().withPluginParameters(ImmutableMap.of("foo", "baz")));
163         
164         assertEquals("bar", xml.selectSingleNode("//plugin-info/param").getText());
165     }
166     
167     @Test
168     public void componentImportIsAdded() throws Exception
169     {
170         Document xml = applyChanges(componentImport(IMPORT));
171         
172         assertNotNull(xml.selectSingleNode("//component-import"));
173     }
174 
175     @Test
176     public void componentImportHasInterface() throws Exception
177     {
178         Document xml = applyChanges(componentImport(IMPORT));
179         
180         assertEquals(INTERFACE, xml.selectSingleNode("//component-import/@interface").getText());
181     }
182 
183     @Test
184     public void componentImportHasDefaultKey() throws Exception
185     {
186         Document xml = applyChanges(componentImport(IMPORT));
187         
188         assertEquals("myInterface", xml.selectSingleNode("//component-import/@key").getText());
189     }
190     
191     @Test
192     public void componentImportHasSpecifiedKey() throws Exception
193     {
194         Document xml = applyChanges(componentImport(IMPORT.key(some("new-key"))));
195         
196         assertEquals("new-key", xml.selectSingleNode("//component-import/@key").getText());
197     }
198     
199     @Test
200     public void componentImportHasNoFilterByDefault() throws Exception
201     {
202         Document xml = applyChanges(componentImport(IMPORT));
203         
204         assertNull(xml.selectSingleNode("//component-import/@filter"));
205     }
206     
207     @Test
208     public void componentImportHasSpecifiedFilter() throws Exception
209     {
210         Document xml = applyChanges(componentImport(IMPORT.filter(some("my-filter"))));
211         
212         assertEquals("my-filter", xml.selectSingleNode("//component-import/@filter").getText());
213     }
214     
215     @Test
216     public void componentIsAdded() throws Exception
217     {
218         Document xml = applyChanges(component());
219         
220         assertNotNull(xml.selectSingleNode("//component"));
221     }
222 
223     @Test
224     public void componentHasKey() throws Exception
225     {
226         Document xml = applyChanges(component());
227         
228         assertEquals("my-key", xml.selectSingleNode("//component/@key").getText());
229     }
230 
231     @Test
232     public void componentHasClass() throws Exception
233     {
234         Document xml = applyChanges(component());
235         
236         assertEquals(CLASS, xml.selectSingleNode("//component/@class").getText());
237     }
238 
239     @Test
240     public void componentHasNoNameByDefault() throws Exception
241     {
242         Document xml = applyChanges(component());
243         
244         assertNull(xml.selectSingleNode("//component/@name"));
245     }
246 
247     @Test
248     public void componentHasSpecifiedName() throws Exception
249     {
250         componentBuilder.name(some("my-name"));
251         Document xml = applyChanges(component());
252         
253         assertEquals("my-name", xml.selectSingleNode("//component/@name").getText());
254     }
255 
256     @Test
257     public void componentHasNoNameI18nKeyByDefault() throws Exception
258     {
259         Document xml = applyChanges(component());
260         
261         assertNull(xml.selectSingleNode("//component/@i18n-name-key"));
262     }
263 
264     @Test
265     public void componentHasSpecifiedNameI18nKey() throws Exception
266     {
267         componentBuilder.nameI18nKey(some("name-key"));
268         Document xml = applyChanges(component());
269         
270         assertEquals("name-key", xml.selectSingleNode("//component/@i18n-name-key").getText());
271     }
272 
273     @Test
274     public void componentIsPublicByDefault() throws Exception
275     {
276         Document xml = applyChanges(component());
277         
278         assertEquals("true", xml.selectSingleNode("//component/@public").getText());
279     }
280 
281     @Test
282     public void componentIsPrivateIfSpecified() throws Exception
283     {
284         componentBuilder.visibility(Visibility.PRIVATE);
285         Document xml = applyChanges(component());
286         
287         assertNull(xml.selectSingleNode("//component/@public"));
288     }
289 
290     @Test
291     public void componentHasNoAliasByDefault() throws Exception
292     {
293         Document xml = applyChanges(component());
294         
295         assertNull(xml.selectSingleNode("//component/@alias"));
296     }
297 
298     @Test
299     public void componentHasSpecifiedAlias() throws Exception
300     {
301         componentBuilder.alias(some("my-alias"));
302         Document xml = applyChanges(component());
303         
304         assertEquals("my-alias", xml.selectSingleNode("//component/@alias").getText());
305     }
306 
307     @Test
308     public void componentHasNoDescriptionByDefault() throws Exception
309     {
310         Document xml = applyChanges(component());
311         
312         assertNull(xml.selectSingleNode("//component/description"));
313     }
314 
315     @Test
316     public void componentHasSpecifiedDescription() throws Exception
317     {
318         componentBuilder.description(some("desc"));
319         Document xml = applyChanges(component());
320         
321         assertEquals("desc", xml.selectSingleNode("//component/description").getText());
322     }
323 
324     @Test
325     public void componentHasNoDescriptionI18nKeyByDefault() throws Exception
326     {
327         componentBuilder.description(some("desc"));
328         Document xml = applyChanges(component());
329         
330         assertNull(xml.selectSingleNode("//component/description/@key"));
331     }
332 
333     @Test
334     public void componentHasSpecifiedDescriptionI18nKey() throws Exception
335     {
336         componentBuilder.description(some("desc")).descriptionI18nKey(some("desc-key"));
337         Document xml = applyChanges(component());
338         
339         assertEquals("desc-key", xml.selectSingleNode("//component/description/@key").getText());
340     }
341 
342     @Test
343     public void componentHasNoInterfaceByDefault() throws Exception
344     {
345         Document xml = applyChanges(component());
346         
347         assertNull(xml.selectSingleNode("//component/interface"));
348     }
349 
350     @Test
351     public void componentHasSpecifiedInterface() throws Exception
352     {
353         componentBuilder.interfaceId(some(fullyQualified(INTERFACE)));
354         Document xml = applyChanges(component());
355         
356         assertEquals(INTERFACE, xml.selectSingleNode("//component/interface").getText());
357     }
358 
359     @Test
360     public void componentHasNoServicePropertiesByDefault() throws Exception
361     {
362         Document xml = applyChanges(component());
363         
364         assertNull(xml.selectSingleNode("//component/service-properties"));
365     }
366 
367     @Test
368     public void componentHasSpecifiedServiceProperties() throws Exception
369     {
370         componentBuilder.serviceProperties(ImmutableMap.of("foo", "bar"));
371         Document xml = applyChanges(component());
372         
373         assertEquals("bar", xml.selectSingleNode("//component/service-properties/entry[@key='foo']/@value").getText());
374     }
375 
376     @Test
377     public void moduleDescriptorIsAdded() throws Exception
378     {
379         String module = "<my-module>has some content</my-module>";
380         Document xml = applyChanges(moduleDescriptor(module));
381         
382         assertEquals("has some content", xml.selectSingleNode("//my-module").getText());
383     }
384 
385     @Test(expected=IOException.class)
386     public void cannotAddMalformedModuleDescriptor() throws Exception
387     {
388         String module = "<my-module>has some content</boo>";
389         rewriter.applyChanges(moduleDescriptor(module));
390     }
391     
392     @Test
393     public void moduleDescriptorIsPlacedAfterOtherModuleDescriptorsOfSameElementName() throws Exception
394     {
395         String module1 = "<foo-module type=\"test\">has some content</foo-module>";
396         String module2 = "<bar-module type=\"test\">has some content</bar-module>";
397         applyChanges(moduleDescriptor(module1).with(moduleDescriptor(module2)));
398         
399         String module3 = "<foo-module type=\"test\">another one</foo-module>";
400         Document xml = applyChanges(moduleDescriptor(module3));
401         
402         assertEquals("bar-module", ((Node) xml.selectNodes("//*[@type='test']").get(1)).getName());
403     }
404     
405     protected PluginProjectChangeset addPluginParam()
406     {
407         return new PluginProjectChangeset().withPluginParameters(ImmutableMap.of("foo", "bar"));
408     }
409     
410     protected PluginProjectChangeset addI18nProperty()
411     {
412         return new PluginProjectChangeset().withI18nProperties(ImmutableMap.of("foo", "bar"));
413     }
414     
415     protected PluginProjectChangeset componentImport(ComponentImport componentImport)
416     {
417         return new PluginProjectChangeset().withComponentImports(componentImport);
418     }
419 
420     protected PluginProjectChangeset component()
421     {
422         return new PluginProjectChangeset().withComponentDeclarations(componentBuilder.build());
423     }
424     
425     protected PluginProjectChangeset moduleDescriptor(String content)
426     {
427         return new PluginProjectChangeset().withModuleDescriptor(ModuleDescriptor.moduleDescriptor(content));
428     }
429     
430     protected Document applyChanges(PluginProjectChangeset changes) throws Exception
431     {
432         rewriter.applyChanges(changes);
433         return DocumentHelper.parseText(FileUtils.readFileToString(pluginXml));
434     }
435 }