1   package com.atlassian.plugins.codegen;
2   
3   import com.atlassian.plugins.codegen.ComponentDeclaration.Visibility;
4   import com.atlassian.plugins.codegen.XmlMatchers.XmlWrapper;
5   
6   import com.google.common.collect.ImmutableMap;
7   
8   import org.apache.commons.io.FileUtils;
9   import org.junit.After;
10  import org.junit.Before;
11  import org.junit.Test;
12  
13  import static com.atlassian.fugue.Option.some;
14  import static com.atlassian.plugins.codegen.ClassId.fullyQualified;
15  import static com.atlassian.plugins.codegen.I18nString.i18nString;
16  import static com.atlassian.plugins.codegen.ModuleDescriptor.moduleDescriptor;
17  import static com.atlassian.plugins.codegen.PluginParameter.pluginParameter;
18  import static com.atlassian.plugins.codegen.PluginProjectChangeset.changeset;
19  import static com.atlassian.plugins.codegen.XmlMatchers.node;
20  import static com.atlassian.plugins.codegen.XmlMatchers.nodeCount;
21  import static com.atlassian.plugins.codegen.XmlMatchers.nodeName;
22  import static com.atlassian.plugins.codegen.XmlMatchers.nodeTextEquals;
23  import static com.atlassian.plugins.codegen.XmlMatchers.nodes;
24  import static org.hamcrest.MatcherAssert.assertThat;
25  import static org.hamcrest.Matchers.equalTo;
26  import static org.hamcrest.Matchers.notNullValue;
27  import static org.hamcrest.Matchers.nullValue;
28  
29  public class PluginXmlRewriterTest
30  {
31      protected static final String CLASS = "com.atlassian.test.MyClass";
32      protected static final String INTERFACE = "com.atlassian.test.MyInterface";
33      protected static final String INTERFACE2 = "com.atlassian.test.MyInterface2";
34      protected static final ComponentImport IMPORT = ComponentImport.componentImport(fullyQualified(INTERFACE));
35      protected static final ComponentImport IMPORT2 = ComponentImport.componentImport(fullyQualified(INTERFACE2));
36      
37      protected ComponentDeclaration.Builder componentBuilder = ComponentDeclaration.builder(fullyQualified(CLASS), "my-key");
38  
39      protected ProjectHelper helper;
40      protected PluginXmlRewriter rewriter;
41      
42      @Before
43      public void setup() throws Exception
44      {
45          helper = new ProjectHelper();
46          usePluginXml("empty-plugin.xml");
47      }
48      
49      private void usePluginXml(String path) throws Exception
50      {
51          helper.usePluginXml(path);
52          rewriter = new PluginXmlRewriter(helper.location);
53      }
54      
55      @After
56      public void deleteTempDir() throws Exception
57      {
58          helper.destroy();
59      }
60      
61      @Test
62      public void i18nResourceIsNotAddedByDefault() throws Exception
63      {
64          assertThat(applyChanges(addPluginParam()),
65                     nodes("//resource[@type='i18n']", nodeCount(0)));
66      }
67  
68      @Test
69      public void i18nResourceIsAddedIfI18nPropertiesArePresent() throws Exception
70      {
71          assertThat(applyChanges(addI18nProperty()),
72                     nodes("//resource[@type='i18n']", nodeCount(1)));
73      }
74  
75      @Test
76      public void i18nResourceHasDefaultName() throws Exception
77      {
78          assertThat(applyChanges(addI18nProperty()),
79                     node("//resource[@type='i18n']/@name", nodeTextEquals("i18n")));
80      }
81  
82      @Test
83      public void i18nResourceHasDefaultLocation() throws Exception
84      {
85          assertThat(applyChanges(addI18nProperty()),
86                     node("//resource[@type='i18n']/@location", nodeTextEquals(ProjectHelper.PLUGIN_KEY)));
87      }
88  
89      @Test
90      public void i18nResourceWithSameNameIsNotAdded() throws Exception
91      {
92          usePluginXml("plugin-with-same-i18n-name.xml");
93          
94          assertThat(applyChanges(addI18nProperty()),
95                     nodes("//resource[@type='i18n']", nodeCount(1)));
96      }
97      
98      @Test
99      public void i18nResourceWithSameNameIsNotOverwritten() throws Exception
100     {
101         usePluginXml("plugin-with-same-i18n-name.xml");
102 
103         assertThat(applyChanges(addI18nProperty()),
104                    node("//resource[@type='i18n']/@location", nodeTextEquals("nonstandard-location")));
105     }
106 
107     @Test
108     public void i18nResourceWithSameLocationIsNotAdded() throws Exception
109     {
110         usePluginXml("plugin-with-same-i18n-location.xml");
111         
112         assertThat(applyChanges(addI18nProperty()),
113                    nodes("//resource[@type='i18n']", nodeCount(1)));
114     }
115     
116     @Test
117     public void i18nResourceWithSameLocationIsNotOverwritten() throws Exception
118     {
119         usePluginXml("plugin-with-same-i18n-location.xml");
120 
121         assertThat(applyChanges(addI18nProperty()),
122                    node("//resource[@type='i18n']/@name", nodeTextEquals("nonstandard-name")));
123     }
124     
125     @Test
126     public void pluginParamIsAdded() throws Exception
127     {
128         assertThat(applyChanges(addPluginParam()),
129                    nodes("//plugin-info/param", nodeCount(1)));
130     }
131     
132     @Test
133     public void pluginParamHasName() throws Exception
134     {
135         assertThat(applyChanges(addPluginParam()),
136                    node("//plugin-info/param/@name", nodeTextEquals("foo")));
137     }
138 
139     @Test
140     public void pluginParamHasValue() throws Exception
141     {
142         assertThat(applyChanges(addPluginParam()),
143                    node("//plugin-info/param", nodeTextEquals("bar")));
144     }
145 
146     @Test
147     public void secondPluginParamIsAdded() throws Exception
148     {
149         applyChanges(addPluginParam());
150 
151         assertThat(applyChanges(changeset().with(pluginParameter("second", "thing"))),
152                    nodes("//plugin-info/param", nodeCount(2)));
153     }
154 
155     @Test
156     public void secondPluginParamHasValue() throws Exception
157     {
158         applyChanges(addPluginParam());
159 
160         assertThat(applyChanges(changeset().with(pluginParameter("second", "thing"))),
161                    node("//plugin-info/param[@name='second']", nodeTextEquals("thing")));
162     }
163 
164     @Test
165     public void duplicatePluginParamIsNotAdded() throws Exception
166     {
167         applyChanges(addPluginParam());
168 
169         assertThat(applyChanges(changeset().with(pluginParameter("foo", "baz"))),
170                    nodes("//plugin-info/param", nodeCount(1)));
171     }
172 
173     @Test
174     public void pluginParamCannotBeOverwritten() throws Exception
175     {
176         applyChanges(addPluginParam());
177 
178         assertThat(applyChanges(changeset().with(pluginParameter("second", "thing"))),
179                    node("//plugin-info/param", nodeTextEquals("bar")));
180     }
181     
182     @Test
183     public void componentImportIsAdded() throws Exception
184     {
185         assertThat(applyChanges(changeset().with(IMPORT)),
186                    node("//component-import", notNullValue()));
187     }
188 
189     @Test
190     public void componentImportHasInterface() throws Exception
191     {
192         assertThat(applyChanges(changeset().with(IMPORT)),
193                    node("//component-import/@interface", nodeTextEquals(INTERFACE)));
194     }
195 
196     @Test
197     public void componentImportHasDefaultKey() throws Exception
198     {
199         assertThat(applyChanges(changeset().with(IMPORT)),
200                    node("//component-import/@key", nodeTextEquals("myInterface")));
201     }
202     
203     @Test
204     public void componentImportHasSpecifiedKey() throws Exception
205     {
206         assertThat(applyChanges(changeset().with(IMPORT.key(some("new-key")))),
207                    node("//component-import/@key", nodeTextEquals("new-key")));
208     }
209     
210     @Test
211     public void componentImportHasNoFilterByDefault() throws Exception
212     {
213         assertThat(applyChanges(changeset().with(IMPORT)),
214                    node("//component-import/@filter", nullValue()));
215     }
216     
217     @Test
218     public void componentImportHasSpecifiedFilter() throws Exception
219     {
220         assertThat(applyChanges(changeset().with(IMPORT.filter(some("my-filter")))),
221                    node("//component-import/@filter", nodeTextEquals("my-filter")));
222     }
223     
224     @Test
225     public void duplicateComponentImportKeyCannotBeAdded() throws Exception
226     {
227         applyChanges(changeset().with(IMPORT.key(some("my-key"))));
228 
229         assertThat(applyChanges(changeset().with(IMPORT2.key(some("my-key")))),
230                    nodes("//component-import", nodeCount(1)));
231     }
232 
233     @Test
234     public void componentImportWithSameKeyIsNotOverwritten() throws Exception
235     {
236         applyChanges(changeset().with(IMPORT.key(some("my-key"))));
237 
238         assertThat(applyChanges(changeset().with(IMPORT2.key(some("my-key")))),
239                    node("//component-import/@interface", nodeTextEquals(INTERFACE)));
240     }
241     
242     @Test
243     public void componentWithSameInterfaceCannotBeAdded() throws Exception
244     {
245         applyChanges(changeset().with(IMPORT.key(some("key1"))));
246 
247         assertThat(applyChanges(changeset().with(IMPORT.key(some("key2")))),
248                    nodes("//component-import", nodeCount(1)));
249     }
250 
251     @Test
252     public void componentWithSameInterfaceInSubElementCannotBeAdded() throws Exception
253     {
254         usePluginXml("plugin-with-import-interface-element.xml");
255 
256         assertThat(applyChanges(changeset().with(IMPORT.key(some("key2")))),
257                    nodes("//component-import", nodeCount(1)));
258     }
259     
260     @Test
261     public void componentCannotBeAddedIfAlternateInterfaceIsAlreadyUsed() throws Exception
262     {
263         applyChanges(changeset().with(IMPORT.key(some("key1"))));
264 
265         assertThat(applyChanges(changeset().with(IMPORT2.alternateInterfaces(fullyQualified(INTERFACE)).key(some("key2")))),
266                    nodes("//component-import", nodeCount(1)));
267     }
268     
269     @Test
270     public void componentIsAdded() throws Exception
271     {
272         assertThat(applyChanges(changeset().with(componentBuilder.build())),
273                    node("//component", notNullValue()));
274     }
275 
276     @Test
277     public void componentHasKey() throws Exception
278     {
279         assertThat(applyChanges(changeset().with(componentBuilder.build())),
280                    node("//component/@key", nodeTextEquals("my-key")));
281     }
282 
283     @Test
284     public void componentHasClass() throws Exception
285     {
286         assertThat(applyChanges(changeset().with(componentBuilder.build())),
287                    node("//component/@class", nodeTextEquals(CLASS)));
288     }
289 
290     @Test
291     public void componentHasNoNameByDefault() throws Exception
292     {
293         assertThat(applyChanges(changeset().with(componentBuilder.build())),
294                    node("//component/@name", nullValue()));
295     }
296 
297     @Test
298     public void componentHasSpecifiedName() throws Exception
299     {
300         componentBuilder.name(some("my-name"));
301         
302         assertThat(applyChanges(changeset().with(componentBuilder.build())),
303                    node("//component/@name", nodeTextEquals("my-name")));
304     }
305 
306     @Test
307     public void componentHasNoNameI18nKeyByDefault() throws Exception
308     {
309         assertThat(applyChanges(changeset().with(componentBuilder.build())),
310                    node("//component/@i18n-name-key", nullValue()));
311     }
312 
313     @Test
314     public void componentHasSpecifiedNameI18nKey() throws Exception
315     {
316         componentBuilder.nameI18nKey(some("name-key"));
317         
318         assertThat(applyChanges(changeset().with(componentBuilder.build())),
319                    node("//component/@i18n-name-key", nodeTextEquals("name-key")));
320     }
321 
322     @Test
323     public void componentIsPrivateByDefault() throws Exception
324     {
325         assertThat(applyChanges(changeset().with(componentBuilder.build())),
326                    node("//component/@public", nullValue()));
327     }
328 
329     @Test
330     public void componentIsPublicIfSpecified() throws Exception
331     {
332         componentBuilder.visibility(Visibility.PUBLIC);
333         
334         assertThat(applyChanges(changeset().with(componentBuilder.build())),
335                    node("//component/@public", nodeTextEquals("true")));
336     }
337 
338     @Test
339     public void componentHasNoAliasByDefault() throws Exception
340     {
341         assertThat(applyChanges(changeset().with(componentBuilder.build())),
342                    node("//component/@alias", nullValue()));
343     }
344 
345     @Test
346     public void componentHasSpecifiedAlias() throws Exception
347     {
348         componentBuilder.alias(some("my-alias"));
349         
350         assertThat(applyChanges(changeset().with(componentBuilder.build())),
351                    node("//component/@alias", nodeTextEquals("my-alias")));
352     }
353 
354     @Test
355     public void componentHasNoDescriptionByDefault() throws Exception
356     {
357         assertThat(applyChanges(changeset().with(componentBuilder.build())),
358                    node("//component/description", nullValue()));
359     }
360 
361     @Test
362     public void componentHasSpecifiedDescription() throws Exception
363     {
364         componentBuilder.description(some("desc"));
365         
366         assertThat(applyChanges(changeset().with(componentBuilder.build())),
367                    node("//component/description", nodeTextEquals("desc")));
368     }
369 
370     @Test
371     public void componentHasNoDescriptionI18nKeyByDefault() throws Exception
372     {
373         componentBuilder.description(some("desc"));
374         
375         assertThat(applyChanges(changeset().with(componentBuilder.build())),
376                    node("//component/description/@key", nullValue()));
377     }
378 
379     @Test
380     public void componentHasSpecifiedDescriptionI18nKey() throws Exception
381     {
382         componentBuilder.description(some("desc")).descriptionI18nKey(some("desc-key"));
383         
384         assertThat(applyChanges(changeset().with(componentBuilder.build())),
385                    node("//component/description/@key", nodeTextEquals("desc-key")));
386     }
387 
388     @Test
389     public void componentHasNoInterfaceByDefault() throws Exception
390     {
391         assertThat(applyChanges(changeset().with(componentBuilder.build())),
392                    node("//component/interface", nullValue()));
393     }
394 
395     @Test
396     public void componentHasSpecifiedInterface() throws Exception
397     {
398         componentBuilder.interfaceId(some(fullyQualified(INTERFACE)));
399         
400         assertThat(applyChanges(changeset().with(componentBuilder.build())),
401                    node("//component/interface", nodeTextEquals(INTERFACE)));
402     }
403 
404     @Test
405     public void componentHasNoServicePropertiesByDefault() throws Exception
406     {
407         assertThat(applyChanges(changeset().with(componentBuilder.build())),
408                    node("//component/service-properties", nullValue()));
409     }
410 
411     @Test
412     public void componentHasSpecifiedServiceProperties() throws Exception
413     {
414         componentBuilder.serviceProperties(ImmutableMap.of("foo", "bar"));
415         
416         assertThat(applyChanges(changeset().with(componentBuilder.build())),
417                    node("//component/service-properties/entry[@key='foo']/@value", nodeTextEquals("bar")));
418     }
419 
420     @Test
421     public void duplicateComponentKeyCannotBeAdded() throws Exception
422     {
423         ComponentDeclaration firstComponent = componentBuilder.name(some("first")).build();
424         ComponentDeclaration secondComponentWithSameKey = componentBuilder.name(some("different")).build();
425         applyChanges(changeset().with(firstComponent));
426         
427         assertThat(applyChanges(changeset().with(secondComponentWithSameKey)),
428                    nodes("//component", nodeCount(1)));
429     }
430 
431     @Test
432     public void componentWithSameKeyIsNotOverwritten() throws Exception
433     {
434         ComponentDeclaration firstComponent = componentBuilder.name(some("first")).build();
435         ComponentDeclaration secondComponentWithSameKey = componentBuilder.name(some("different")).build();
436         applyChanges(changeset().with(firstComponent));
437         
438         assertThat(applyChanges(changeset().with(secondComponentWithSameKey)),
439                    node("//component/@name", nodeTextEquals("first")));
440     }
441     
442     @Test
443     public void moduleDescriptorIsAdded() throws Exception
444     {
445         String module = "<my-module>has some content</my-module>";
446         assertThat(applyChanges(changeset().with(moduleDescriptor(module))),
447                    node("//my-module", nodeTextEquals("has some content")));
448     }
449     
450     @Test
451     public void moduleDescriptorIsPlacedAfterOtherModuleDescriptorsOfSameElementName() throws Exception
452     {
453         String module1 = "<foo-module type=\"test\">has some content</foo-module>";
454         String module2 = "<bar-module type=\"test\">has some content</bar-module>";
455         applyChanges(changeset().with(moduleDescriptor(module1), moduleDescriptor(module2)));
456         
457         String module3 = "<foo-module type=\"test\">another one</foo-module>";
458         assertThat(applyChanges(changeset().with(moduleDescriptor(module3))),
459                    node("//*[@type='test'][2]", nodeName(equalTo("bar-module"))));
460     }
461     
462     @Test
463     public void moduleDescriptorWithSameTypeAndKeyCannotBeAdded() throws Exception
464     {
465         String module1 = "<my-module key=\"my-key\">good</my-module>";
466         String module2 = "<my-module key=\"my-key\">worse</my-module>";
467         applyChanges(changeset().with(moduleDescriptor(module1)));
468         
469         assertThat(applyChanges(changeset().with(moduleDescriptor(module2))),
470                    nodes("//my-module", nodeCount(1)));
471     }
472 
473     @Test
474     public void moduleDescriptorWithSameTypeAndKeyIsNotOverwritten() throws Exception
475     {
476         String module1 = "<my-module key=\"my-key\">good</my-module>";
477         String module2 = "<my-module key=\"my-key\">worse</my-module>";
478         applyChanges(changeset().with(moduleDescriptor(module1)));
479         
480         assertThat(applyChanges(changeset().with(moduleDescriptor(module2))),
481                    node("//my-module", nodeTextEquals("good")));
482     }
483     
484     protected PluginProjectChangeset addPluginParam()
485     {
486         return new PluginProjectChangeset().with(pluginParameter("foo", "bar"));
487     }
488     
489     protected PluginProjectChangeset addI18nProperty()
490     {
491         return new PluginProjectChangeset().with(i18nString("foo", "bar"));
492     }
493     
494     protected XmlWrapper applyChanges(PluginProjectChangeset changes) throws Exception
495     {
496         rewriter.applyChanges(changes);
497         return XmlMatchers.xml(FileUtils.readFileToString(helper.pluginXml));
498     }
499 }