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.exclusion;
20  
21  import java.io.File;
22  import java.io.PrintWriter;
23  import java.io.StringWriter;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Collections;
27  import java.util.HashSet;
28  import java.util.List;
29  
30  import org.apache.maven.RepositoryUtils;
31  import org.apache.maven.artifact.Artifact;
32  import org.apache.maven.execution.MavenSession;
33  import org.apache.maven.model.Dependency;
34  import org.apache.maven.model.Exclusion;
35  import org.apache.maven.model.InputLocation;
36  import org.apache.maven.model.InputSource;
37  import org.apache.maven.plugin.MojoExecutionException;
38  import org.apache.maven.plugin.logging.Log;
39  import org.apache.maven.plugins.dependency.AbstractDependencyMojoTestCase;
40  import org.apache.maven.plugins.dependency.testUtils.stubs.DependencyProjectStub;
41  import org.apache.maven.plugins.dependency.utils.ResolverUtil;
42  import org.apache.maven.project.MavenProject;
43  
44  import static org.assertj.core.api.Assertions.assertThat;
45  import static org.assertj.core.api.Assertions.assertThatCode;
46  import static org.assertj.core.api.Assertions.assertThatThrownBy;
47  import static org.mockito.ArgumentMatchers.any;
48  import static org.mockito.Mockito.mock;
49  import static org.mockito.Mockito.when;
50  
51  public class AnalyzeExclusionsMojoTest extends AbstractDependencyMojoTestCase {
52  
53      private AnalyzeExclusionsMojo mojo;
54  
55      private MavenProject project;
56  
57      private TestLog testLog;
58  
59      private ResolverUtil resolverUtil;
60  
61      @Override
62      protected String getTestDirectoryName() {
63          return "analyze-exclusions";
64      }
65  
66      @Override
67      protected boolean shouldCreateFiles() {
68          return true;
69      }
70  
71      @Override
72      protected boolean shouldUseFlattenedPath() {
73          return false;
74      }
75  
76      @Override
77      protected void setUp() throws Exception {
78          // required for mojo lookups to work
79          super.setUp();
80  
81          project = new DependencyProjectStub();
82          project.setName("projectName");
83          project.setGroupId("testGroupId");
84          project.setArtifactId("testArtifactId");
85          project.setVersion("1.0.0");
86  
87          getContainer().addComponent(project, MavenProject.class.getName());
88  
89          MavenSession session = newMavenSession(project);
90          getContainer().addComponent(session, MavenSession.class.getName());
91  
92          resolverUtil = mock(ResolverUtil.class);
93          getContainer().addComponent(resolverUtil, ResolverUtil.class.getName());
94  
95          File testPom = new File(getBasedir(), "target/test-classes/unit/analyze-exclusions/plugin-config.xml");
96          mojo = (AnalyzeExclusionsMojo) lookupMojo("analyze-exclusions", testPom);
97          assertNotNull(mojo);
98  
99          testLog = new TestLog();
100         mojo.setLog(testLog);
101     }
102 
103     public void testShallThrowExceptionWhenFailOnWarning() throws Exception {
104         List<Dependency> dependencies = new ArrayList<>();
105         Dependency withInvalidExclusion = dependency("a", "b");
106         withInvalidExclusion.addExclusion(exclusion("invalid", "invalid"));
107         dependencies.add(withInvalidExclusion);
108         project.setDependencies(dependencies);
109         Artifact artifact = stubFactory.createArtifact("a", "b", "1.0");
110         project.setArtifacts(new HashSet<>(Collections.singletonList(artifact)));
111         setVariableValueToObject(mojo, "exclusionFail", true);
112 
113         assertThatThrownBy(() -> mojo.execute())
114                 .isInstanceOf(MojoExecutionException.class)
115                 .hasMessageContaining("Invalid exclusions found");
116 
117         assertThat(testLog.getContent()).startsWith("[error]");
118     }
119 
120     public void testShallLogWarningWhenFailOnWarningIsFalse() throws Exception {
121         List<Dependency> dependencies = new ArrayList<>();
122         Dependency withInvalidExclusion = dependency("a", "b");
123         withInvalidExclusion.addExclusion(exclusion("invalid", "invalid"));
124         dependencies.add(withInvalidExclusion);
125         project.setDependencies(dependencies);
126         Artifact artifact = stubFactory.createArtifact("a", "b", "1.0");
127         project.setArtifacts(new HashSet<>(Collections.singletonList(artifact)));
128         setVariableValueToObject(mojo, "exclusionFail", false);
129 
130         mojo.execute();
131 
132         assertThat(testLog.getContent()).startsWith("[warn]");
133     }
134 
135     public void testShallExitWithoutAnalyzeWhenNoDependencyHasExclusion() throws Exception {
136         List<Dependency> dependencies = new ArrayList<>();
137         dependencies.add(dependency("a", "c"));
138         project.setDependencies(dependencies);
139         mojo.execute();
140         assertThat(testLog.getContent()).startsWith("[debug] No dependencies defined with exclusions - exiting");
141     }
142 
143     public void testShallNotReportInvalidExclusionForWildcardGroupIdAndArtifactId() throws Exception {
144         Dependency dependencyWithWildcardExclusion = dependency("a", "b");
145         dependencyWithWildcardExclusion.addExclusion(exclusion("*", "*"));
146         project.setDependencies(Collections.singletonList(dependencyWithWildcardExclusion));
147         Artifact artifact = stubFactory.createArtifact("a", "b", "1.0");
148         project.setArtifacts(new HashSet<>(Collections.singletonList(artifact)));
149 
150         when(resolverUtil.collectDependencies(any()))
151                 .thenReturn(Collections.singletonList(new org.eclipse.aether.graph.Dependency(
152                         RepositoryUtils.toArtifact(stubFactory.createArtifact("whatever", "ok", "1.0")), "")));
153 
154         mojo.execute();
155 
156         assertThat(testLog.getContent()).doesNotContain("[warn]     a:b:", "[warn]         - *:*");
157     }
158 
159     public void testCanResolveMultipleArtifactsWithEqualGroupIdAndArtifactId() throws Exception {
160         Dependency dependency1 = dependency("a", "b");
161         Dependency dependency2 = dependency("a", "b", "compile", "native");
162         dependency1.addExclusion(exclusion("c", "d"));
163         dependency2.addExclusion(exclusion("c", "d"));
164         project.setDependencies(Arrays.asList(dependency1, dependency2));
165         Artifact artifact1 = stubFactory.createArtifact("a", "b", "1.0");
166         Artifact artifact2 = stubFactory.createArtifact("a", "b", "1.0", "compile", "jar", "native");
167         project.setArtifacts(new HashSet<>(Arrays.asList(artifact1, artifact2)));
168 
169         assertThatCode(() -> mojo.execute()).doesNotThrowAnyException();
170     }
171 
172     public void testShallNotLogWhenExclusionIsValid() throws Exception {
173         List<Dependency> dependencies = new ArrayList<>();
174         Dependency dependency = dependency("a", "b");
175         dependency.addExclusion(exclusion("ok", "ok"));
176         dependencies.add(dependency);
177         project.setDependencies(dependencies);
178         Artifact artifact = stubFactory.createArtifact("a", "b", "1.0");
179 
180         project.setArtifacts(new HashSet<>(Collections.singletonList(artifact)));
181         setVariableValueToObject(mojo, "exclusionFail", true);
182 
183         when(resolverUtil.collectDependencies(any()))
184                 .thenReturn(Collections.singletonList(new org.eclipse.aether.graph.Dependency(
185                         RepositoryUtils.toArtifact(stubFactory.createArtifact("ok", "ok", "1.0")), "")));
186 
187         assertThatCode(() -> mojo.execute()).doesNotThrowAnyException();
188     }
189 
190     public void testThatLogContainProjectName() throws Exception {
191         List<Dependency> dependencies = new ArrayList<>();
192         Dependency withInvalidExclusion = dependency("a", "b");
193         withInvalidExclusion.addExclusion(exclusion("invalid", "invalid"));
194         dependencies.add(withInvalidExclusion);
195         project.setDependencies(dependencies);
196         Artifact artifact = stubFactory.createArtifact("a", "b", "1.0");
197         project.setArtifacts(new HashSet<>(Collections.singletonList(artifact)));
198 
199         mojo.execute();
200 
201         assertThat(testLog.getContent()).contains("[warn] projectName defines following unnecessary excludes");
202     }
203 
204     private Dependency dependency(String groupId, String artifactId) {
205         Dependency dependency = new Dependency();
206         dependency.setGroupId(groupId);
207         dependency.setArtifactId(artifactId);
208         dependency.setVersion("1.0");
209         dependency.setScope("compile");
210         dependency.setType("jar");
211         dependency.setClassifier("");
212         dependency.setLocation("", new InputLocation(1, 1));
213         return dependency;
214     }
215 
216     private Dependency dependency(String groupId, String artifactId, String scope, String classifier) {
217         Dependency dependency = new Dependency();
218         dependency.setGroupId(groupId);
219         dependency.setArtifactId(artifactId);
220         dependency.setVersion("1.0");
221         dependency.setScope(scope);
222         dependency.setType("jar");
223         dependency.setClassifier(classifier);
224         dependency.setLocation("", new InputLocation(1, 1));
225         return dependency;
226     }
227 
228     private Exclusion exclusion(String groupId, String artifactId) {
229         Exclusion exclusion = new Exclusion();
230         exclusion.setGroupId(groupId);
231         exclusion.setArtifactId(artifactId);
232         InputSource inputSource = new InputSource();
233         inputSource.setModelId("testGroupId:testArtifactId:1.0.0");
234         exclusion.setLocation("", new InputLocation(1, 1, inputSource));
235         return exclusion;
236     }
237 
238     static class TestLog implements Log {
239         StringBuilder sb = new StringBuilder();
240 
241         /**
242          * {@inheritDoc}
243          */
244         @Override
245         public void debug(CharSequence content) {
246             print("debug", content);
247         }
248 
249         /**
250          * {@inheritDoc}
251          */
252         @Override
253         public void debug(CharSequence content, Throwable error) {
254             print("debug", content, error);
255         }
256 
257         /**
258          * {@inheritDoc}
259          */
260         @Override
261         public void debug(Throwable error) {
262             print("debug", error);
263         }
264 
265         /**
266          * {@inheritDoc}
267          */
268         @Override
269         public void info(CharSequence content) {
270             print("info", content);
271         }
272 
273         /**
274          * {@inheritDoc}
275          */
276         @Override
277         public void info(CharSequence content, Throwable error) {
278             print("info", content, error);
279         }
280 
281         /**
282          * {@inheritDoc}
283          */
284         @Override
285         public void info(Throwable error) {
286             print("info", error);
287         }
288 
289         /**
290          * {@inheritDoc}
291          */
292         @Override
293         public void warn(CharSequence content) {
294             print("warn", content);
295         }
296 
297         /**
298          * {@inheritDoc}
299          */
300         @Override
301         public void warn(CharSequence content, Throwable error) {
302             print("warn", content, error);
303         }
304 
305         /**
306          * {@inheritDoc}
307          */
308         @Override
309         public void warn(Throwable error) {
310             print("warn", error);
311         }
312 
313         /**
314          * {@inheritDoc}
315          */
316         @Override
317         public void error(CharSequence content) {
318             print("error", content);
319         }
320 
321         /**
322          * {@inheritDoc}
323          */
324         @Override
325         public void error(CharSequence content, Throwable error) {
326             StringWriter sWriter = new StringWriter();
327             PrintWriter pWriter = new PrintWriter(sWriter);
328 
329             error.printStackTrace(pWriter);
330 
331             System.err.println(
332                     "[error] " + content.toString() + System.lineSeparator() + System.lineSeparator() + sWriter);
333         }
334 
335         /**
336          * @see org.apache.maven.plugin.logging.Log#error(java.lang.Throwable)
337          */
338         @Override
339         public void error(Throwable error) {
340             StringWriter sWriter = new StringWriter();
341             PrintWriter pWriter = new PrintWriter(sWriter);
342 
343             error.printStackTrace(pWriter);
344 
345             System.err.println("[error] " + sWriter);
346         }
347 
348         /**
349          * @see org.apache.maven.plugin.logging.Log#isDebugEnabled()
350          */
351         @Override
352         public boolean isDebugEnabled() {
353             return false;
354         }
355 
356         /**
357          * @see org.apache.maven.plugin.logging.Log#isInfoEnabled()
358          */
359         @Override
360         public boolean isInfoEnabled() {
361             return true;
362         }
363 
364         /**
365          * @see org.apache.maven.plugin.logging.Log#isWarnEnabled()
366          */
367         @Override
368         public boolean isWarnEnabled() {
369             return true;
370         }
371 
372         /**
373          * @see org.apache.maven.plugin.logging.Log#isErrorEnabled()
374          */
375         @Override
376         public boolean isErrorEnabled() {
377             return true;
378         }
379 
380         private void print(String prefix, CharSequence content) {
381             sb.append("[")
382                     .append(prefix)
383                     .append("] ")
384                     .append(content.toString())
385                     .append(System.lineSeparator());
386         }
387 
388         private void print(String prefix, Throwable error) {
389             StringWriter sWriter = new StringWriter();
390             PrintWriter pWriter = new PrintWriter(sWriter);
391 
392             error.printStackTrace(pWriter);
393 
394             sb.append("[").append(prefix).append("] ").append(sWriter).append(System.lineSeparator());
395         }
396 
397         private void print(String prefix, CharSequence content, Throwable error) {
398             StringWriter sWriter = new StringWriter();
399             PrintWriter pWriter = new PrintWriter(sWriter);
400 
401             error.printStackTrace(pWriter);
402 
403             sb.append("[")
404                     .append(prefix)
405                     .append("] ")
406                     .append(content.toString())
407                     .append(System.lineSeparator())
408                     .append(System.lineSeparator());
409             sb.append(sWriter).append(System.lineSeparator());
410         }
411 
412         protected String getContent() {
413             return sb.toString();
414         }
415     }
416 }