1   package com.atlassian.plugins.codegen;
2   
3   import java.io.File;
4   import java.io.InputStream;
5   import java.util.ArrayList;
6   import java.util.List;
7   import java.util.UUID;
8   
9   import com.atlassian.plugins.codegen.ArtifactDependency.Scope;
10  import com.atlassian.plugins.codegen.XmlMatchers.XmlWrapper;
11  
12  import org.apache.commons.io.FileUtils;
13  import org.hamcrest.Description;
14  import org.hamcrest.Matcher;
15  import org.hamcrest.Matchers;
16  import org.hamcrest.TypeSafeDiagnosingMatcher;
17  import org.junit.After;
18  import org.junit.Before;
19  import org.junit.Test;
20  
21  import static com.atlassian.plugins.codegen.AmpsSystemPropertyVariable.ampsSystemPropertyVariable;
22  import static com.atlassian.plugins.codegen.ArtifactDependency.dependency;
23  import static com.atlassian.plugins.codegen.ArtifactId.artifactId;
24  import static com.atlassian.plugins.codegen.BundleInstruction.importPackage;
25  import static com.atlassian.plugins.codegen.BundleInstruction.privatePackage;
26  import static com.atlassian.plugins.codegen.MavenPlugin.mavenPlugin;
27  import static com.atlassian.plugins.codegen.PluginArtifact.pluginArtifact;
28  import static com.atlassian.plugins.codegen.PluginArtifact.ArtifactType.BUNDLED_ARTIFACT;
29  import static com.atlassian.plugins.codegen.PluginArtifact.ArtifactType.PLUGIN_ARTIFACT;
30  import static com.atlassian.plugins.codegen.VersionId.noVersion;
31  import static com.atlassian.plugins.codegen.VersionId.version;
32  import static com.atlassian.plugins.codegen.VersionId.versionProperty;
33  import static com.atlassian.plugins.codegen.XmlMatchers.node;
34  import static com.atlassian.plugins.codegen.XmlMatchers.nodeCount;
35  import static com.atlassian.plugins.codegen.XmlMatchers.nodeText;
36  import static com.atlassian.plugins.codegen.XmlMatchers.nodeTextEquals;
37  import static com.atlassian.plugins.codegen.XmlMatchers.nodes;
38  import static org.apache.commons.io.IOUtils.closeQuietly;
39  import static org.hamcrest.MatcherAssert.assertThat;
40  import static org.hamcrest.Matchers.any;
41  import static org.hamcrest.Matchers.equalTo;
42  import static org.hamcrest.Matchers.notNullValue;
43  import static org.hamcrest.Matchers.nullValue;
44  
45  public class MavenProjectRewriterTest
46  {
47      private static final ArtifactId CUSTOM_ARTIFACT = artifactId("com.atlassian.dogs", "cooper");
48      private static final ArtifactId CUSTOM_ARTIFACT2 = artifactId("com.atlassian.dogs", "sailor");
49      private static final ArtifactId MAVEN_ARTIFACT = artifactId("maven-dependency-plugin");
50      
51      private static final String TEST_POM = "test-pom.xml";
52      private static final String TEST_POM_WITH_CONFIG = "test-pom-with-config.xml";
53      private static final String TEST_POM_WITH_INSTRUCTIONS = "test-pom-with-instructions.xml";
54      private static final String TEST_POM_WITH_MAVEN_PLUGIN = "test-pom-with-maven-plugin.xml";
55  
56      private static final ArtifactDependency NEW_DEPENDENCY =
57          dependency(CUSTOM_ARTIFACT, "1.0", Scope.PROVIDED);
58  
59      private static final int INITIAL_MAVEN_PLUGIN_COUNT = 1;  // see test-pom.xml
60      private static final String NEW_MAVEN_PLUGIN_CONFIG =
61          "<executions><execution>" +
62              "<id>EXECUTION_ID</id>" +
63              "<phase>PHASE</phase>" +
64              "<goals><goal>GOAL1</goal><goal>GOAL2</goal></goals>" +
65              "<configuration>" +
66                  "<param1>value1</param1>" +
67              "</configuration>" +
68          "</execution></executions>";
69      private static final String MAVEN_PLUGIN_CONFIG_WITH_CONFLICTING_EXECUTION =
70          "<executions><execution>" +
71              "<id>copy-storage-plugin</id>" +
72              "<phase>WRONG</phase>" +
73              "<goals><goal>NO</goal></goals>" +
74          "</execution></executions>";
75  
76      private static final MavenPlugin NEW_MAVEN_PLUGIN_NO_VERSION =
77          mavenPlugin(MAVEN_ARTIFACT, noVersion(), NEW_MAVEN_PLUGIN_CONFIG);
78      private static final MavenPlugin NEW_MAVEN_PLUGIN_WITH_VERSION =
79          mavenPlugin(MAVEN_ARTIFACT, version("1.0"), NEW_MAVEN_PLUGIN_CONFIG);
80      private static final MavenPlugin NEW_MAVEN_PLUGIN_WITH_GROUP_ID =
81          mavenPlugin(CUSTOM_ARTIFACT, version("1.0"), NEW_MAVEN_PLUGIN_CONFIG);
82  
83      private static final com.atlassian.plugins.codegen.PluginArtifact NEW_BUNDLED_ARTIFACT =
84          pluginArtifact(BUNDLED_ARTIFACT, CUSTOM_ARTIFACT, noVersion());
85      private static final com.atlassian.plugins.codegen.PluginArtifact NEW_BUNDLED_ARTIFACT_WITH_VERSION =
86          pluginArtifact(BUNDLED_ARTIFACT, CUSTOM_ARTIFACT, version("1.0"));
87      private static final com.atlassian.plugins.codegen.PluginArtifact NEW_BUNDLED_ARTIFACT_WITH_VERSION_PROPERTY =
88          pluginArtifact(BUNDLED_ARTIFACT, CUSTOM_ARTIFACT, versionProperty("dog.version", "1.0"));
89      private static final com.atlassian.plugins.codegen.PluginArtifact NEW_PLUGIN_ARTIFACT =
90          pluginArtifact(PLUGIN_ARTIFACT, CUSTOM_ARTIFACT, noVersion());
91      
92      private static final BundleInstruction NEW_IMPORT_PACKAGE =
93          importPackage("com.atlassian.random", "2.0.1");
94      private static final BundleInstruction DUPLICATE_IMPORT_PACKAGE =
95          importPackage("com.atlassian.plugins.rest.common*", "1.0.5");
96      private static final BundleInstruction NEW_PRIVATE_PACKAGE =
97          privatePackage("com.atlassian.random");
98      
99      private static final PluginProjectChangeset changeset = new PluginProjectChangeset();
100     
101     private File tempDir;
102     private File pom;
103     private MavenProjectRewriter rewriter;
104     
105     @Before
106     public void setup() throws Exception
107     {
108         final File sysTempDir = new File("target");
109         String dirName = UUID.randomUUID().toString();
110         tempDir = new File(sysTempDir, dirName);
111         tempDir.mkdirs();
112         
113         pom = new File(tempDir, "pom.xml");
114     }
115     
116     @After
117     public void deleteTempDir() throws Exception
118     {
119         FileUtils.deleteDirectory(tempDir);
120     }
121 
122     @Test
123     public void dependencyIsAdded() throws Exception
124     {
125         assertThat(applyChanges(TEST_POM, changeset.with(NEW_DEPENDENCY)),
126                    nodes("//dependencies/dependency", nodeCount(3)));
127     }
128 
129     @Test
130     public void dependencyHasGroupId() throws Exception
131     {
132         assertThat(applyChanges(TEST_POM, changeset.with(NEW_DEPENDENCY)),
133                    node("//dependencies/dependency[3]/groupId", nodeTextEquals(CUSTOM_ARTIFACT.getGroupId().get())));
134     }
135 
136     @Test
137     public void dependencyHasArtifactId() throws Exception
138     {
139         assertThat(applyChanges(TEST_POM, changeset.with(NEW_DEPENDENCY)),
140                    node("//dependencies/dependency[3]/artifactId", nodeTextEquals(CUSTOM_ARTIFACT.getArtifactId())));
141     }
142 
143     @Test
144     public void dependencyHasVersion() throws Exception
145     {
146         assertThat(applyChanges(TEST_POM, changeset.with(NEW_DEPENDENCY)),
147                    node("//dependencies/dependency[3]/version", nodeTextEquals(NEW_DEPENDENCY.getVersionId().toString())));
148     }
149 
150     @Test
151     public void dependencyHasScope() throws Exception
152     {
153         assertThat(applyChanges(TEST_POM, changeset.with(NEW_DEPENDENCY)),
154                    node("//dependencies/dependency[3]/scope", nodeTextEquals("provided")));
155     }
156     
157     @Test
158     public void duplicateDependencyIsNotAdded() throws Exception
159     {
160         ArtifactDependency existing = dependency("com.atlassian.plugins", "test-artifact-1", "1.0", Scope.PROVIDED);
161         
162         assertThat(applyChanges(TEST_POM, changeset.with(existing)),
163                    nodes("//dependencies/dependency", nodeCount(2)));
164     }
165     
166     @Test
167     public void dependencyWithVersionPropertyUsesPropertyNameForVersion() throws Exception
168     {
169         ArtifactDependency dependency = dependency(CUSTOM_ARTIFACT, versionProperty("dog.version", "1.0"), Scope.PROVIDED);
170         
171         assertThat(applyChanges(TEST_POM, changeset.with(dependency)),
172                    node("//dependencies/dependency[3]/version", nodeTextEquals("${dog.version}")));
173     }
174 
175     @Test
176     public void dependencyWithVersionPropertyAddsPropertyNameAndValueToProperties() throws Exception
177     {
178         ArtifactDependency dependency = dependency(CUSTOM_ARTIFACT, versionProperty("dog.version", "1.0"), Scope.PROVIDED);
179         
180         assertThat(applyChanges(TEST_POM, changeset.with(dependency)),
181                    node("//properties/dog.version", nodeTextEquals("1.0")));
182     }
183 
184     @Test
185     public void dependencyWithVersionPropertyDoesNotOverwriteExistingProperty() throws Exception
186     {
187         ArtifactDependency dependency = dependency(CUSTOM_ARTIFACT, versionProperty("dog.version", "1.0"), Scope.PROVIDED);
188         ArtifactDependency dependency2 = dependency(CUSTOM_ARTIFACT2, versionProperty("dog.version", "3.5"), Scope.PROVIDED);
189         
190         assertThat(applyChanges(TEST_POM, changeset.with(dependency, dependency2)),
191                    node("//properties/dog.version", nodeTextEquals("1.0")));
192     }
193     
194     @Test
195     public void mavenPluginIsAdded() throws Exception
196     {
197         assertThat(applyChanges(TEST_POM, changeset.with(NEW_MAVEN_PLUGIN_NO_VERSION)),
198                    nodes("//build/plugins/plugin", nodeCount(INITIAL_MAVEN_PLUGIN_COUNT + 1)));
199     }
200     
201     @Test
202     public void mavenPluginHasArtifactId() throws Exception
203     {
204         assertThat(applyChanges(TEST_POM, changeset.with(NEW_MAVEN_PLUGIN_NO_VERSION)),
205                    node("//build/plugins/plugin[2]/artifactId", nodeTextEquals(MAVEN_ARTIFACT.getArtifactId())));
206     }
207     
208     @Test
209     public void mavenPluginHasExecutionId() throws Exception
210     {
211         assertThat(applyChanges(TEST_POM, changeset.with(NEW_MAVEN_PLUGIN_NO_VERSION)),
212                    node("//build/plugins/plugin[2]/executions/execution/id", nodeTextEquals("EXECUTION_ID")));
213     }
214 
215     @Test
216     public void mavenPluginHasExecutionPhase() throws Exception
217     {
218         assertThat(applyChanges(TEST_POM, changeset.with(NEW_MAVEN_PLUGIN_NO_VERSION)),
219                    node("//build/plugins/plugin[2]/executions/execution/phase", nodeTextEquals("PHASE")));
220     }
221 
222     @Test
223     public void mavenPluginHasExecutionGoal1() throws Exception
224     {
225         assertThat(applyChanges(TEST_POM, changeset.with(NEW_MAVEN_PLUGIN_NO_VERSION)),
226                    node("//build/plugins/plugin[2]/executions/execution/goals/goal[1]", nodeTextEquals("GOAL1")));
227     }
228 
229     @Test
230     public void mavenPluginHasExecutionGoal2() throws Exception
231     {
232         assertThat(applyChanges(TEST_POM, changeset.with(NEW_MAVEN_PLUGIN_NO_VERSION)),
233                    node("//build/plugins/plugin[2]/executions/execution/goals/goal[1]", nodeTextEquals("GOAL1")));
234     }
235 
236     @Test
237     public void mavenPluginHasExecutionConfig() throws Exception
238     {
239         assertThat(applyChanges(TEST_POM, changeset.with(NEW_MAVEN_PLUGIN_NO_VERSION)),
240                    node("//build/plugins/plugin[2]/executions/execution/configuration/param1", nodeTextEquals("value1")));
241     }
242 
243     @Test
244     public void mavenPluginWithNoGroupIdHasNoGroupId() throws Exception
245     {
246         assertThat(applyChanges(TEST_POM, changeset.with(NEW_MAVEN_PLUGIN_NO_VERSION)),
247                    node("//build/plugins/plugin[2]/groupId", nullValue()));
248     }
249 
250     @Test
251     public void mavenPluginWithGroupIdHasGroupId() throws Exception
252     {
253         assertThat(applyChanges(TEST_POM, changeset.with(NEW_MAVEN_PLUGIN_WITH_GROUP_ID)),
254                    node("//build/plugins/plugin[2]/groupId", nodeTextEquals(CUSTOM_ARTIFACT.getGroupId().get())));
255     }
256 
257     @Test
258     public void mavenPluginWithNoVersionHasNoVersion() throws Exception
259     {
260         assertThat(applyChanges(TEST_POM, changeset.with(NEW_MAVEN_PLUGIN_NO_VERSION)),
261                    node("//build/plugins/plugin[2]/version", nullValue()));
262     }
263 
264     @Test
265     public void mavenPluginWithVersionHasVersion() throws Exception
266     {
267         assertThat(applyChanges(TEST_POM, changeset.with(NEW_MAVEN_PLUGIN_WITH_VERSION)),
268                    node("//build/plugins/plugin[2]/version", nodeTextEquals(NEW_MAVEN_PLUGIN_WITH_VERSION.getVersionId().toString())));
269     }
270 
271     @Test
272     public void mavenPluginExecutionIsAddedToExistingPlugin() throws Exception
273     {
274         assertThat(applyChanges(TEST_POM_WITH_MAVEN_PLUGIN, changeset.with(NEW_MAVEN_PLUGIN_NO_VERSION)),
275                    node("//build/plugins/plugin[2]/executions/execution[2]/id", nodeTextEquals("EXECUTION_ID")));
276     }
277 
278     @Test
279     public void mavenPluginExecutionWithDuplicateIdIsNotAdded() throws Exception
280     {
281         MavenPlugin pluginWithConflictingExecutionConfig =
282             mavenPlugin(MAVEN_ARTIFACT, noVersion(), MAVEN_PLUGIN_CONFIG_WITH_CONFLICTING_EXECUTION);
283 
284         assertThat(applyChanges(TEST_POM_WITH_MAVEN_PLUGIN, changeset.with(pluginWithConflictingExecutionConfig)),
285                    nodes("//build/plugins/plugin[2]/executions/execution", nodeCount(1)));
286     }
287 
288     @Test
289     public void mavenPluginExecutionWithDuplicateIdDoesNotOverwriteExistingConfig() throws Exception
290     {
291         MavenPlugin pluginWithConflictingExecutionConfig =
292             mavenPlugin(MAVEN_ARTIFACT, noVersion(), MAVEN_PLUGIN_CONFIG_WITH_CONFLICTING_EXECUTION);
293 
294         assertThat(applyChanges(TEST_POM_WITH_MAVEN_PLUGIN, changeset.with(pluginWithConflictingExecutionConfig)),
295                    node("//build/plugins/plugin[2]/executions/execution/phase", nodeTextEquals("process-resources")));
296     }
297 
298     @Test
299     public void configElementIsCreatedForBundleInstruction() throws Exception
300     {
301         assertThat(applyChanges(TEST_POM, changeset.with(NEW_IMPORT_PACKAGE)),
302                    node("//build/plugins/plugin[1]/configuration", notNullValue()));
303     }
304 
305     @Test
306     public void instructionsElementIsCreatedWithinNewlyCreatedConfigElement() throws Exception
307     {
308         assertThat(applyChanges(TEST_POM, changeset.with(NEW_IMPORT_PACKAGE)),
309                    node("//build/plugins/plugin[1]/configuration/instructions", notNullValue()));
310     }
311 
312     @Test
313     public void bundleInstructionIsAddedToNewInstructionsInNewConfig() throws Exception
314     {
315         assertThat(applyChanges(TEST_POM, changeset.with(NEW_IMPORT_PACKAGE)),
316                    node("//build/plugins/plugin[1]/configuration/instructions/Import-Package",
317                         nodeTextEquals("com.atlassian.random;version=\"2.0.1\"")));
318     }
319 
320     @Test
321     public void instructionsElementIsCreatedWithinExistingConfigElement() throws Exception
322     {
323         assertThat(applyChanges(TEST_POM_WITH_CONFIG, changeset.with(NEW_IMPORT_PACKAGE)),
324                    node("//build/plugins/plugin[1]/configuration/instructions", notNullValue()));
325     }
326 
327     @Test
328     public void bundleInstructionIsAddedToNewInstructionsInExistingConfig() throws Exception
329     {
330         assertThat(applyChanges(TEST_POM_WITH_CONFIG, changeset.with(NEW_IMPORT_PACKAGE)),
331                    node("//build/plugins/plugin[1]/configuration/instructions/Import-Package",
332                         nodeTextEquals("com.atlassian.random;version=\"2.0.1\"")));
333     }
334 
335     @Test
336     public void bundleInstructionIsAddedToExistingCategory() throws Exception
337     {
338         assertThat(applyChanges(TEST_POM_WITH_INSTRUCTIONS, changeset.with(NEW_IMPORT_PACKAGE)),
339                    node("//build/plugins/plugin[1]/configuration/instructions/Import-Package",
340                         nodeText(delimitedList(",", Matchers.<String>iterableWithSize(4)))));
341     }
342 
343     @SuppressWarnings("unchecked")
344     @Test
345     public void bundleInstructionIsInsertedInPackageOrderWithinExistingCategory() throws Exception
346     {
347         assertThat(applyChanges(TEST_POM_WITH_INSTRUCTIONS, changeset.with(NEW_IMPORT_PACKAGE)),
348                    node("//build/plugins/plugin[1]/configuration/instructions/Import-Package",
349                         nodeText(delimitedList(",",
350                                                Matchers.<String>hasItems(any(String.class), 
351                                                    equalTo("com.atlassian.random;version=\"2.0.1\""),
352                                                    any(String.class), any(String.class))))));
353     }
354 
355     @SuppressWarnings("unchecked")
356     @Test
357     public void existingInstructionsArePreservedWhenAddingNewInstructionInCategory() throws Exception
358     {
359         assertThat(applyChanges(TEST_POM_WITH_INSTRUCTIONS, changeset.with(NEW_IMPORT_PACKAGE)),
360                    node("//build/plugins/plugin[1]/configuration/instructions/Import-Package",
361                         nodeText(delimitedList(",",
362                                                Matchers.<String>hasItems(equalTo("com.atlassian.plugin.*;version=\"${atlassian.plugins.version}\""),
363                                                    any(String.class), any(String.class), any(String.class))))));
364     }
365 
366     @Test
367     public void bundleInstructionIsNotInsertedIfPackageIsAlreadyPresentInCategory() throws Exception
368     {
369         assertThat(applyChanges(TEST_POM_WITH_INSTRUCTIONS, changeset.with(DUPLICATE_IMPORT_PACKAGE)),
370                    node("//build/plugins/plugin[1]/configuration/instructions/Import-Package",
371                         nodeText(delimitedList(",",
372                                                Matchers.<String>iterableWithSize(3)))));
373     }
374 
375     @SuppressWarnings("unchecked")
376     @Test
377     public void bundleInstructionDoesNotOverwriteInstructionForSamePackage() throws Exception
378     {
379         assertThat(applyChanges(TEST_POM_WITH_INSTRUCTIONS, changeset.with(DUPLICATE_IMPORT_PACKAGE)),
380                    node("//build/plugins/plugin[1]/configuration/instructions/Import-Package",
381                         nodeText(delimitedList(",",
382                                                Matchers.<String>hasItems(equalTo("com.atlassian.plugins.rest.common*;version=\"1.0.5\";resolution:=optional"))))));
383     }
384 
385     @Test
386     public void bundleInstructionIsAddedToNewCategoryInExistingInstructions() throws Exception
387     {
388         assertThat(applyChanges(TEST_POM_WITH_INSTRUCTIONS, changeset.with(NEW_PRIVATE_PACKAGE)),
389                    node("//build/plugins/plugin[1]/configuration/instructions/Private-Package", nodeTextEquals("com.atlassian.random")));
390     }
391 
392     @Test
393     public void configElementIsCreatedForBundledArtifact() throws Exception
394     {
395         assertThat(applyChanges(TEST_POM, changeset.with(NEW_BUNDLED_ARTIFACT)),
396                    node("//build/plugins/plugin[1]/configuration", notNullValue()));
397     }
398 
399     @Test
400     public void bundledArtifactHasGroupId() throws Exception
401     {
402         assertThat(applyChanges(TEST_POM, changeset.with(NEW_BUNDLED_ARTIFACT)),
403                    node("//build/plugins/plugin[1]/configuration/bundledArtifacts/bundledArtifact/groupId",
404                         nodeTextEquals(CUSTOM_ARTIFACT.getGroupId().get())));
405     }
406 
407     @Test
408     public void bundledArtifactHasArtifactId() throws Exception
409     {
410         assertThat(applyChanges(TEST_POM, changeset.with(NEW_BUNDLED_ARTIFACT)),
411                    node("//build/plugins/plugin[1]/configuration/bundledArtifacts/bundledArtifact/artifactId",
412                         nodeTextEquals(CUSTOM_ARTIFACT.getArtifactId())));
413     }
414 
415     @Test
416     public void pluginArtifactHasArtifactId() throws Exception
417     {
418         assertThat(applyChanges(TEST_POM, changeset.with(NEW_PLUGIN_ARTIFACT)),
419                    node("//build/plugins/plugin[1]/configuration/pluginArtifacts/pluginArtifact/artifactId",
420                         nodeTextEquals(CUSTOM_ARTIFACT.getArtifactId())));
421     }
422 
423     @Test
424     public void bundledArtifactWithNoVersionHasNoVersion() throws Exception
425     {
426         assertThat(applyChanges(TEST_POM, changeset.with(NEW_BUNDLED_ARTIFACT)),
427                    node("//build/plugins/plugin[1]/configuration/bundledArtifacts/bundledArtifact/version",
428                         nullValue()));
429     }
430     
431     @Test
432     public void bundledArtifactWithVersionHasVersion() throws Exception
433     {
434         assertThat(applyChanges(TEST_POM, changeset.with(NEW_BUNDLED_ARTIFACT_WITH_VERSION)),
435                    node("//build/plugins/plugin[1]/configuration/bundledArtifacts/bundledArtifact/version",
436                         nodeTextEquals("1.0")));
437     }
438 
439     @Test
440     public void bundledArtifactWithVersionPropertyUsesPropertyName() throws Exception
441     {
442         assertThat(applyChanges(TEST_POM, changeset.with(NEW_BUNDLED_ARTIFACT_WITH_VERSION_PROPERTY)),
443                    node("//build/plugins/plugin[1]/configuration/bundledArtifacts/bundledArtifact/version",
444                         nodeTextEquals("${dog.version}")));
445     }
446 
447     @Test
448     public void bundledArtifactWithVersionPropertyAddsPropertyNameAndValueToProperties() throws Exception
449     {
450         assertThat(applyChanges(TEST_POM, changeset.with(NEW_BUNDLED_ARTIFACT_WITH_VERSION_PROPERTY)),
451                    node("//properties/dog.version", nodeTextEquals("1.0")));
452     }
453 
454     @Test
455     public void existingBundledArtifactIsNotAddedAgain() throws Exception
456     {
457         assertThat(applyChanges(TEST_POM_WITH_CONFIG, changeset.with(NEW_BUNDLED_ARTIFACT)),
458                    nodes("//build/plugins/plugin[1]/configuration/bundledArtifacts/bundledArtifact", nodeCount(1)));
459     }
460 
461     @Test
462     public void configElementIsCreatedForAmpsSystemProperty() throws Exception
463     {
464         assertThat(applyChanges(TEST_POM, changeset.with(ampsSystemPropertyVariable("newVariable", "bar"))),
465                    node("//build/plugins/plugin[1]/configuration", notNullValue()));
466     }
467     
468     @Test
469     public void variablesElementIsCreatedForAmpsSystemProperty() throws Exception
470     {
471         assertThat(applyChanges(TEST_POM, changeset.with(ampsSystemPropertyVariable("newVariable", "bar"))),
472                    node("//build/plugins/plugin[1]/configuration/systemPropertyVariables", notNullValue()));
473     }
474     
475     @Test
476     public void ampsSystemPropertyHasNameAndValue() throws Exception
477     {
478         assertThat(applyChanges(TEST_POM, changeset.with(ampsSystemPropertyVariable("newVariable", "bar"))),
479                    node("//build/plugins/plugin[1]/configuration/systemPropertyVariables/newVariable", nodeTextEquals("bar")));
480     }
481 
482     @Test
483     public void ampsSystemPropertyIsAddedToList() throws Exception
484     {
485         assertThat(applyChanges(TEST_POM_WITH_CONFIG, changeset.with(ampsSystemPropertyVariable("newVariable", "bar"))),
486                    node("//build/plugins/plugin[1]/configuration/systemPropertyVariables/newVariable", nodeTextEquals("bar")));
487     }
488     
489     @Test
490     public void ampsSystemPropertyDoesNotOverwriteVariableWithDifferentName() throws Exception
491     {
492         assertThat(applyChanges(TEST_POM_WITH_CONFIG, changeset.with(ampsSystemPropertyVariable("newVariable", "bar"))),
493                    node("//build/plugins/plugin[1]/configuration/systemPropertyVariables/existingVariable", nodeTextEquals("foo")));
494     }
495     
496     @Test
497     public void ampsSystemPropertyDoesNotOverwriteVariableWithSameName() throws Exception
498     {
499         assertThat(applyChanges(TEST_POM_WITH_CONFIG, changeset.with(ampsSystemPropertyVariable("existingVariable", "bar"))),
500                    node("//build/plugins/plugin[1]/configuration/systemPropertyVariables/existingVariable", nodeTextEquals("foo")));
501     }
502     
503     protected XmlWrapper applyChanges(String pomTemplateName, PluginProjectChangeset changes) throws Exception
504     {
505         InputStream is = getClass().getClassLoader().getResourceAsStream(pomTemplateName);
506         FileUtils.copyInputStreamToFile(is, pom);
507         closeQuietly(is);
508 
509         rewriter = new MavenProjectRewriter(pom);
510         rewriter.applyChanges(changes);
511         
512         return XmlMatchers.xml(FileUtils.readFileToString(pom), "project");
513     }
514     
515 
516     public static Matcher<String> delimitedList(final String delimiter, final Matcher<Iterable<String>> listMatcher)
517     {
518         return new TypeSafeDiagnosingMatcher<String>()
519         {
520             protected boolean matchesSafely(String s, Description mismatchDescription)
521             {
522                 String[] parts = s.split(delimiter);
523                 List<String> trimmed = new ArrayList<String>(parts.length);
524                 for (String p : parts)
525                 {
526                     trimmed.add(p.trim());
527                 }
528                 if (!listMatcher.matches(trimmed))
529                 {
530                     listMatcher.describeMismatch(trimmed, mismatchDescription);
531                     return false;
532                 }
533                 return true;
534             }
535 
536             public void describeTo(Description description)
537             {
538                 description.appendText("list delimited by '" + delimiter + "' ");
539                 listMatcher.describeTo(description);
540             }
541         };
542     }
543 }