View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.dependency.tree;
20  
21  import java.io.File;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.io.OutputStreamWriter;
25  import java.io.StringReader;
26  import java.nio.file.Files;
27  import java.nio.file.Path;
28  import java.nio.file.Paths;
29  import java.util.ArrayList;
30  import java.util.Arrays;
31  import java.util.Comparator;
32  import java.util.List;
33  import java.util.Set;
34  import java.util.stream.Collectors;
35  
36  import jakarta.json.Json;
37  import jakarta.json.JsonArray;
38  import jakarta.json.JsonObject;
39  import jakarta.json.JsonReader;
40  import org.apache.maven.artifact.Artifact;
41  import org.apache.maven.execution.MavenSession;
42  import org.apache.maven.plugin.LegacySupport;
43  import org.apache.maven.plugin.testing.stubs.MavenProjectStub;
44  import org.apache.maven.plugins.dependency.AbstractDependencyMojoTestCase;
45  import org.apache.maven.project.MavenProject;
46  import org.apache.maven.shared.dependency.graph.DependencyNode;
47  import org.apache.maven.shared.dependency.graph.internal.DefaultDependencyNode;
48  
49  /**
50   * Tests <code>TreeMojo</code>.
51   *
52   * @author <a href="mailto:markhobson@gmail.com">Mark Hobson</a>
53   * @version $Id$
54   * @since 2.0
55   */
56  public class TestTreeMojo extends AbstractDependencyMojoTestCase {
57      // TestCase methods -------------------------------------------------------
58  
59      @Override
60      protected String getTestDirectoryName() {
61          return "tree";
62      }
63  
64      @Override
65      protected boolean shouldUseFlattenedPath() {
66          return false;
67      }
68  
69      /*
70       * @see org.apache.maven.plugin.testing.AbstractMojoTestCase#setUp()
71       */
72      @Override
73      protected void setUp() throws Exception {
74          // required for mojo lookups to work
75          super.setUp();
76  
77          MavenProject project = new MavenProjectStub();
78          getContainer().addComponent(project, MavenProject.class.getName());
79  
80          MavenSession session = newMavenSession(project);
81          getContainer().addComponent(session, MavenSession.class.getName());
82  
83          LegacySupport legacySupport = lookup(LegacySupport.class);
84          legacySupport.setSession(session);
85          installLocalRepository(legacySupport);
86      }
87  
88      // tests ------------------------------------------------------------------
89  
90      /**
91       * Tests the proper discovery and configuration of the mojo.
92       *
93       * @throws Exception in case of an error.
94       */
95      public void testTreeTestEnvironment() throws Exception {
96          File testPom = new File(getBasedir(), "target/test-classes/unit/tree-test/plugin-config.xml");
97          TreeMojo mojo = (TreeMojo) lookupMojo("tree", testPom);
98  
99          assertNotNull(mojo);
100         assertNotNull(mojo.getProject());
101         MavenProject project = mojo.getProject();
102         project.setArtifact(this.stubFactory.createArtifact("testGroupId", "project", "1.0"));
103 
104         Set<Artifact> artifacts = this.stubFactory.getScopedArtifacts();
105         Set<Artifact> directArtifacts = this.stubFactory.getReleaseAndSnapshotArtifacts();
106         artifacts.addAll(directArtifacts);
107 
108         project.setArtifacts(artifacts);
109         project.setDependencyArtifacts(directArtifacts);
110 
111         mojo.execute();
112 
113         DependencyNode rootNode = mojo.getDependencyGraph();
114         assertNodeEquals("testGroupId:project:jar:1.0:compile", rootNode);
115         assertEquals(2, rootNode.getChildren().size());
116 
117         List<String> actualNodes = Arrays.asList(
118                 createArtifactCoordinateString(rootNode.getChildren().get(0)),
119                 createArtifactCoordinateString(rootNode.getChildren().get(1)));
120         List<String> expectedNodes =
121                 Arrays.asList("testGroupId:release:jar:1.0:compile", "testGroupId:snapshot:jar:2.0-SNAPSHOT:compile");
122 
123         assertTrue(expectedNodes.containsAll(actualNodes));
124     }
125 
126     /**
127      * Test the DOT format serialization
128      *
129      * @throws Exception in case of an error.
130      */
131     public void testTreeDotSerializing() throws Exception {
132         List<String> contents = runTreeMojo("tree1.dot", "dot");
133         assertTrue(findString(contents, "digraph \"testGroupId:project:jar:1.0:compile\" {"));
134         assertTrue(findString(
135                 contents,
136                 "\"testGroupId:project:jar:1.0:compile\" -> \"testGroupId:snapshot:jar:2.0-SNAPSHOT:compile\""));
137         assertTrue(findString(
138                 contents, "\"testGroupId:project:jar:1.0:compile\" -> \"testGroupId:release:jar:1.0:compile\""));
139     }
140 
141     /**
142      * Test the GraphML format serialization
143      *
144      * @throws Exception in case of an error.
145      */
146     public void testTreeGraphMLSerializing() throws Exception {
147         List<String> contents = runTreeMojo("tree1.graphml", "graphml");
148 
149         assertTrue(findString(contents, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"));
150         assertTrue(findString(contents, "<y:NodeLabel>testGroupId:project:jar:1.0:compile</y:NodeLabel>"));
151         assertTrue(findString(contents, "<y:NodeLabel>testGroupId:snapshot:jar:2.0-SNAPSHOT:compile</y:NodeLabel>"));
152         assertTrue(findString(contents, "<y:NodeLabel>testGroupId:release:jar:1.0:compile</y:NodeLabel>"));
153         assertTrue(findString(contents, "<key for=\"node\" id=\"d0\" yfiles.type=\"nodegraphics\"/>"));
154         assertTrue(findString(contents, "<key for=\"edge\" id=\"d1\" yfiles.type=\"edgegraphics\"/>"));
155     }
156 
157     /**
158      * Test the TGF format serialization
159      *
160      * @throws Exception in case of an error.
161      */
162     public void testTreeTGFSerializing() throws Exception {
163         List<String> contents = runTreeMojo("tree1.tgf", "tgf");
164         assertTrue(findString(contents, "testGroupId:project:jar:1.0:compile"));
165         assertTrue(findString(contents, "testGroupId:snapshot:jar:2.0-SNAPSHOT:compile"));
166         assertTrue(findString(contents, "testGroupId:release:jar:1.0:compile"));
167     }
168 
169     /**
170      * Test the JSON format serialization on DependencyNodes with circular dependence
171      */
172     public void testTreeJsonCircularDependency() throws IOException {
173         String outputFileName = testDir.getAbsolutePath() + File.separator + "tree1.json";
174         File outputFile = new File(outputFileName);
175         Files.createDirectories(outputFile.getParentFile().toPath());
176         outputFile.createNewFile();
177 
178         Artifact artifact1 = this.stubFactory.createArtifact("testGroupId", "project1", "1.0");
179         Artifact artifact2 = this.stubFactory.createArtifact("testGroupId", "project2", "1.0");
180         DefaultDependencyNode node1 = new DefaultDependencyNode(artifact1);
181         DefaultDependencyNode node2 = new DefaultDependencyNode(artifact2);
182 
183         node1.setChildren(new ArrayList<DependencyNode>());
184         node2.setChildren(new ArrayList<DependencyNode>());
185 
186         node1.getChildren().add(node2);
187         node2.getChildren().add(node1);
188 
189         try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(outputFile))) {
190             JsonDependencyNodeVisitor jsonDependencyNodeVisitor = new JsonDependencyNodeVisitor(outputStreamWriter);
191 
192             jsonDependencyNodeVisitor.visit(node1);
193         }
194     }
195 
196     /*
197      * Test parsing of Json output and verify all key-value pairs
198      */
199     public void testTreeJsonParsing() throws Exception {
200         List<String> contents = runTreeMojo("tree2.json", "json");
201 
202         System.setProperty("jakarta.json.provider", "org.glassfish.json.JsonProviderImpl");
203         try (JsonReader reader = Json.createReader(new StringReader(String.join(System.lineSeparator(), contents)))) {
204             JsonObject root = reader.readObject();
205 
206             assertEquals(root.getString("groupId"), "testGroupId");
207             assertEquals(root.getString("artifactId"), "project");
208             assertEquals(root.getString("version"), "1.0");
209             assertEquals(root.getString("type"), "jar");
210             assertEquals(root.getString("scope"), "compile");
211             assertEquals(root.getString("classifier"), "");
212             assertEquals(root.getString("optional"), "false");
213 
214             JsonArray children = root.getJsonArray("children");
215             assertEquals(children.size(), 2);
216 
217             List<JsonObject> sortedChildren = children.stream()
218                     .map(JsonObject.class::cast)
219                     .sorted(Comparator.comparing(child -> child.getString("artifactId")))
220                     .collect(Collectors.toList());
221 
222             // Now that children are sorted, we can assert their values in a fixed order
223             JsonObject child0 = sortedChildren.get(0);
224             assertEquals(child0.getString("groupId"), "testGroupId");
225             assertEquals(child0.getString("artifactId"), "release");
226             assertEquals(child0.getString("version"), "1.0");
227             assertEquals(child0.getString("type"), "jar");
228             assertEquals(child0.getString("scope"), "compile");
229             assertEquals(child0.getString("classifier"), "");
230             assertEquals(child0.getString("optional"), "false");
231 
232             JsonObject child1 = sortedChildren.get(1);
233             assertEquals(child1.getString("groupId"), "testGroupId");
234             assertEquals(child1.getString("artifactId"), "snapshot");
235             assertEquals(child1.getString("version"), "2.0-SNAPSHOT");
236             assertEquals(child1.getString("type"), "jar");
237             assertEquals(child1.getString("scope"), "compile");
238             assertEquals(child1.getString("classifier"), "");
239             assertEquals(child1.getString("optional"), "false");
240         }
241     }
242 
243     /**
244      * Help finding content in the given list of string
245      *
246      * @param outputFile the outputFile.
247      * @param format The format.
248      * @throws Exception in case of an error.
249      * @return list of strings in the output file
250      */
251     private List<String> runTreeMojo(String outputFile, String format) throws Exception {
252         File testPom = new File(getBasedir(), "target/test-classes/unit/tree-test/plugin-config.xml");
253         Path outputFilePath = Paths.get(testDir.getAbsolutePath(), outputFile);
254         TreeMojo mojo = (TreeMojo) lookupMojo("tree", testPom);
255         setVariableValueToObject(mojo, "outputEncoding", "UTF-8");
256         setVariableValueToObject(mojo, "outputType", format);
257         setVariableValueToObject(mojo, "outputFile", outputFilePath.toFile());
258 
259         assertNotNull(mojo);
260         assertNotNull(mojo.getProject());
261         MavenProject project = mojo.getProject();
262         project.setArtifact(this.stubFactory.createArtifact("testGroupId", "project", "1.0"));
263 
264         Set<Artifact> artifacts = this.stubFactory.getScopedArtifacts();
265         Set<Artifact> directArtifacts = this.stubFactory.getReleaseAndSnapshotArtifacts();
266         artifacts.addAll(directArtifacts);
267 
268         project.setArtifacts(artifacts);
269         project.setDependencyArtifacts(directArtifacts);
270 
271         mojo.execute();
272 
273         List<String> contents = Files.readAllLines(outputFilePath);
274 
275         return contents;
276     }
277 
278     /**
279      * Help finding content in the given list of string
280      *
281      * @param contents The contents.
282      * @param str The content which should be checked for.
283      */
284     private boolean findString(List<String> contents, String str) {
285         for (String line : contents) {
286             if (line.contains(str)) {
287                 // if match then return here
288                 return true;
289             }
290         }
291 
292         // in case no match for the whole list
293         return false;
294     }
295 
296     // private methods --------------------------------------------------------
297 
298     private void assertNodeEquals(String expectedNode, DependencyNode actualNode) {
299         String[] tokens = expectedNode.split(":");
300 
301         assertNodeEquals(tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], actualNode);
302     }
303 
304     private void assertNodeEquals(
305             String expectedGroupId,
306             String expectedArtifactId,
307             String expectedType,
308             String expectedVersion,
309             String expectedScope,
310             DependencyNode actualNode) {
311         Artifact actualArtifact = actualNode.getArtifact();
312 
313         assertEquals("group id", expectedGroupId, actualArtifact.getGroupId());
314         assertEquals("artifact id", expectedArtifactId, actualArtifact.getArtifactId());
315         assertEquals("type", expectedType, actualArtifact.getType());
316         assertEquals("version", expectedVersion, actualArtifact.getVersion());
317         assertEquals("scope", expectedScope, actualArtifact.getScope());
318     }
319 
320     private String createArtifactCoordinateString(DependencyNode actualNode) {
321         return actualNode.getArtifact().getGroupId() + ":"
322                 + actualNode.getArtifact().getArtifactId() + ":"
323                 + actualNode.getArtifact().getType() + ":"
324                 + actualNode.getArtifact().getVersion() + ":"
325                 + actualNode.getArtifact().getScope();
326     }
327 }