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