View Javadoc
1   package org.apache.maven.shared.dependency.analyzer;
2   /*
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *  http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   */
20  
21  import org.apache.maven.artifact.Artifact;
22  import org.apache.maven.artifact.DefaultArtifact;
23  import org.apache.maven.artifact.handler.ArtifactHandler;
24  import org.apache.maven.artifact.handler.DefaultArtifactHandler;
25  import org.apache.maven.artifact.versioning.VersionRange;
26  import org.apache.maven.project.MavenProject;
27  import org.apache.maven.shared.invoker.InvocationRequest;
28  import org.apache.maven.shared.invoker.InvocationResult;
29  import org.apache.maven.shared.test.plugin.BuildTool;
30  import org.apache.maven.shared.test.plugin.ProjectTool;
31  import org.apache.maven.shared.test.plugin.RepositoryTool;
32  import org.apache.maven.shared.test.plugin.TestToolsException;
33  import org.codehaus.plexus.PlexusTestCase;
34  import org.junit.Before;
35  import org.junit.Test;
36  import org.junit.runner.RunWith;
37  import org.junit.runners.JUnit4;
38  
39  import java.io.File;
40  import java.util.Arrays;
41  import java.util.Collections;
42  import java.util.HashSet;
43  import java.util.List;
44  import java.util.Map;
45  import java.util.Properties;
46  import java.util.Set;
47  
48  /**
49   * Tests <code>DefaultProjectDependencyAnalyzer</code>.
50   *
51   * @author <a href="mailto:markhobson@gmail.com">Mark Hobson</a>
52   * @see DefaultProjectDependencyAnalyzer
53   */
54  @RunWith( JUnit4.class )
55  public class DefaultProjectDependencyAnalyzerTest
56      extends PlexusTestCase
57  {
58      private BuildTool buildTool;
59  
60      private ProjectTool projectTool;
61  
62      private ProjectDependencyAnalyzer analyzer;
63  
64      private static File localRepo;
65  
66      /*
67       * @see org.codehaus.plexus.PlexusTestCase#setUp()
68       */
69      @Before
70      public void setUp()
71          throws Exception
72      {
73          super.setUp();
74  
75          buildTool = (BuildTool) lookup( BuildTool.ROLE );
76  
77          projectTool = (ProjectTool) lookup( ProjectTool.ROLE );
78  
79          if ( localRepo == null )
80          {
81              RepositoryTool repositoryTool = (RepositoryTool) lookup( RepositoryTool.ROLE );
82              localRepo = repositoryTool.findLocalRepositoryDirectory();
83              System.out.println( "Local repository: " + localRepo );
84          }
85  
86          analyzer = (ProjectDependencyAnalyzer) lookup( ProjectDependencyAnalyzer.class.getName() );
87      }
88  
89      @Test
90      public void testPom()
91          throws TestToolsException, ProjectDependencyAnalyzerException
92      {
93          compileProject( "pom/pom.xml" );
94  
95          MavenProject project = getProject( "pom/pom.xml" );
96  
97          ProjectDependencyAnalysis actualAnalysis = analyzer.analyze( project );
98  
99          ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis();
100 
101         assertEquals( expectedAnalysis, actualAnalysis );
102     }
103 
104     @Test
105     public void testJarWithNoDependencies()
106         throws TestToolsException, ProjectDependencyAnalyzerException
107     {
108         compileProject( "jarWithNoDependencies/pom.xml" );
109 
110         MavenProject project = getProject( "jarWithNoDependencies/pom.xml" );
111 
112         ProjectDependencyAnalysis actualAnalysis = analyzer.analyze( project );
113 
114         ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis();
115 
116         assertEquals( expectedAnalysis, actualAnalysis );
117     }
118 
119     @Test
120     public void testJava8methodRefs()
121         throws TestToolsException, ProjectDependencyAnalyzerException
122     {
123         // Only visible through constant pool analysis (supported for JDK8+)
124         compileProject( "java8methodRefs/pom.xml" );
125 
126         MavenProject project = getProject( "java8methodRefs/pom.xml" );
127 
128         ProjectDependencyAnalysis actualAnalysis = analyzer.analyze( project );
129 
130         Artifact project1 = createArtifact( "commons-io", "commons-io", "jar", "2.4", "compile" );
131         Artifact project2 = createArtifact( "commons-lang", "commons-lang", "jar", "2.6", "compile" );
132         Set<Artifact> usedDeclaredArtifacts = new HashSet<>( Arrays.asList( project1, project2 ) );
133 
134         ProjectDependencyAnalysis expectedAnalysis =
135             new ProjectDependencyAnalysis( usedDeclaredArtifacts, new HashSet<>(), new HashSet<>(),
136                     new HashSet<>() );
137 
138         assertEquals( expectedAnalysis, actualAnalysis );
139     }
140 
141     @Test
142     public void testInlinedStaticReference()
143         throws TestToolsException, ProjectDependencyAnalyzerException
144     {
145         // Only visible through constant pool analysis (supported for JDK8+)
146         compileProject( "inlinedStaticReference/pom.xml" );
147 
148         MavenProject project = getProject( "inlinedStaticReference/pom.xml" );
149 
150         ProjectDependencyAnalysis actualAnalysis = analyzer.analyze( project );
151 
152         Artifact project1 = createArtifact( "dom4j", "dom4j", "jar", "1.6.1", "compile" );
153         Set<Artifact> usedDeclaredArtifacts = Collections.singleton( project1 );
154 
155         ProjectDependencyAnalysis expectedAnalysis =
156             new ProjectDependencyAnalysis( usedDeclaredArtifacts, new HashSet<>(), new HashSet<>(),
157                     new HashSet<>() );
158 
159         assertEquals( expectedAnalysis, actualAnalysis );
160     }
161 
162     @Test
163     public void testJarWithCompileDependency()
164         throws TestToolsException, ProjectDependencyAnalyzerException
165     {
166         compileProject( "jarWithCompileDependency/pom.xml" );
167 
168         MavenProject project2 = getProject( "jarWithCompileDependency/project2/pom.xml" );
169 
170         if ( project2.getBuild().getOutputDirectory().contains( "${" ) )
171         {
172             // if Maven version used as dependency is upgraded to >= 2.2.0
173             throw new TestToolsException( "output directory was not interpolated: "
174                 + project2.getBuild().getOutputDirectory() );
175         }
176 
177         ProjectDependencyAnalysis actualAnalysis = analyzer.analyze( project2 );
178         
179         assertTrue( "Incorrectly classified Guava as testonly",
180                    actualAnalysis.getTestArtifactsWithNonTestScope().isEmpty() );
181 
182         Artifact project1 = createArtifact( "org.apache.maven.shared.dependency-analyzer.tests",
183                                             "jarWithCompileDependency1", "jar", "1.0", "compile" );
184         Artifact guava = createArtifact( "com.google.guava", "guava", "jar", "30.1.1-android", "compile" );
185         Set<Artifact> usedDeclaredArtifacts = new HashSet<>( Arrays.asList( project1, guava ) );
186         ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set<Artifact>) null, null,
187                 null );
188 
189         assertEquals( expectedAnalysis, actualAnalysis );
190     }
191 
192     @Test
193     public void testForceDeclaredDependenciesUsage()
194         throws TestToolsException, ProjectDependencyAnalyzerException
195     {
196         compileProject( "jarWithTestDependency/pom.xml" );
197 
198         MavenProject project2 = getProject( "jarWithTestDependency/project2/pom.xml" );
199 
200         ProjectDependencyAnalysis analysis = analyzer.analyze( project2 );
201 
202         try
203         {
204             analysis.forceDeclaredDependenciesUsage( new String[] {
205                 "org.apache.maven.shared.dependency-analyzer.tests:jarWithTestDependency1" } );
206             fail( "failure expected since junit dependency is declared-used" );
207         }
208         catch ( ProjectDependencyAnalyzerException pdae )
209         {
210             assertTrue( pdae.getMessage().contains( "Trying to force use of dependencies which are "
211                 + "declared but already detected as used: "
212                 + "[org.apache.maven.shared.dependency-analyzer.tests:jarWithTestDependency1]" ) );
213         }
214 
215         try
216         {
217             analysis.forceDeclaredDependenciesUsage( new String[] { "undefined:undefined" } );
218             fail( "failure expected since undefined dependency is not declared" );
219         }
220         catch ( ProjectDependencyAnalyzerException pdae )
221         {
222             assertTrue( pdae.getMessage().contains( "Trying to force use of dependencies which are "
223                 + "not declared: [undefined:undefined]" ) );
224         }
225     }
226 
227     @Test
228     public void testJarWithTestDependency()
229         throws TestToolsException, ProjectDependencyAnalyzerException
230     {
231         compileProject( "jarWithTestDependency/pom.xml" );
232 
233         MavenProject project2 = getProject( "jarWithTestDependency/project2/pom.xml" );
234 
235         ProjectDependencyAnalysis actualAnalysis = analyzer.analyze( project2 );
236         
237         Artifact project1 = createArtifact( "org.apache.maven.shared.dependency-analyzer.tests",
238                                             "jarWithTestDependency1", "jar", "1.0", "test" );
239         Artifact junit = createArtifact( "junit", "junit", "jar", "3.8.1", "test" );
240 
241         Set<Artifact> usedDeclaredArtifacts = new HashSet<>( Arrays.asList( project1, junit ) );
242         ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set<Artifact>) null, null, null );
243 
244         assertEquals( expectedAnalysis, actualAnalysis );
245     }
246 
247     @Test
248     public void testJarWithXmlTransitiveDependency()
249         throws TestToolsException, ProjectDependencyAnalyzerException
250     {
251         compileProject( "jarWithXmlTransitiveDependency/pom.xml" );
252 
253         MavenProject project = getProject( "jarWithXmlTransitiveDependency/pom.xml" );
254 
255         ProjectDependencyAnalysis actualAnalysis = analyzer.analyze( project );
256 
257         Artifact jdom = createArtifact( "dom4j", "dom4j", "jar", "1.6.1", "compile" );
258         Set<Artifact> usedDeclaredArtifacts = Collections.singleton( jdom );
259 
260         ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set<Artifact>) null, null,
261                 null );
262 
263         // MSHARED-47: usedUndeclaredArtifacts=[xml-apis:xml-apis:jar:1.0.b2:compile]
264         // assertEquals( expectedAnalysis, actualAnalysis );
265     }
266 
267     @Test
268     public void testJarWithCompileScopedTestDependency()
269             throws TestToolsException, ProjectDependencyAnalyzerException
270     {
271         compileProject( "jarWithCompileScopedTestDependency/pom.xml" );
272 
273         MavenProject project2 = getProject( "jarWithCompileScopedTestDependency/project2/pom.xml" );
274 
275         ProjectDependencyAnalysis actualAnalysis = analyzer.analyze( project2 );
276 
277         Artifact artifact1 = createArtifact( "org.apache.maven.shared.dependency-analyzer.tests",
278                 "jarWithCompileScopedTestDependency1", "jar", "1.0", "test" );
279         Artifact junit = createArtifact( "junit", "junit", "jar", "3.8.1", "compile" );
280 
281         Set<Artifact> usedDeclaredArtifacts = new HashSet<>( Arrays.asList( artifact1, junit ) );
282         Set<Artifact> nonTestScopedTestArtifacts = Collections.singleton( junit );
283         ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set<Artifact>) null, null,
284                 nonTestScopedTestArtifacts );
285 
286         assertEquals( expectedAnalysis, actualAnalysis );
287     }
288 
289     @Test
290     public void testJarWithRuntimeScopedTestDependency() throws TestToolsException, ProjectDependencyAnalyzerException
291     {
292         // We can't effectively analyze runtime dependencies at this time
293         compileProject( "jarWithRuntimeScopedTestDependency/pom.xml" );
294 
295         MavenProject project2 = getProject( "jarWithRuntimeScopedTestDependency/project2/pom.xml" );
296 
297         ProjectDependencyAnalysis actualAnalysis = analyzer.analyze( project2 );
298 
299         Artifact artifact1 = createArtifact( "org.apache.maven.shared.dependency-analyzer.tests",
300                 "jarWithRuntimeScopedTestDependency1", "jar", "1.0", "test" );
301         Artifact junit = createArtifact( "junit", "junit", "jar", "3.8.1", "runtime" );
302 
303         Set<Artifact> usedDeclaredArtifacts = new HashSet<>( Arrays.asList( artifact1, junit ) );
304         ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set<Artifact>) null, null,
305                 null );
306 
307         assertEquals( expectedAnalysis, actualAnalysis );
308     }
309 
310     @Test
311     public void testMultimoduleProject()
312         throws TestToolsException, ProjectDependencyAnalyzerException
313     {
314         compileProject( "multimoduleProject/pom.xml" );
315 
316         // difficult to create multi-module project with Maven 2.x, so here's hacky solution
317         // to get a inter-module dependency
318         MavenProject project = getProject( "multimoduleProject/module2/pom.xml" );
319         @SuppressWarnings( "unchecked" )
320         Set<Artifact> dependencyArtifacts = project.getArtifacts();
321         for ( Artifact artifact : dependencyArtifacts )
322         {
323             if ( artifact.getArtifactId().equals( "test-module1" ) )
324             {
325                 File dir = getTestFile( "target/test-classes/", "multimoduleProject/module1/target/classes/" );
326                 artifact.setFile( dir );
327             }
328         }
329 
330         ProjectDependencyAnalysis actualAnalysis = analyzer.analyze( project );
331 
332         Artifact junit = createArtifact( "org.apache.maven.its.dependency", "test-module1", "jar", "1.0", "compile" );
333         Set<Artifact> usedDeclaredArtifacts = Collections.singleton( junit );
334 
335         ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( usedDeclaredArtifacts, (Set<Artifact>) null, null,
336                 null );
337 
338         assertEquals( expectedAnalysis, actualAnalysis );
339     }
340 
341     @Test
342     public void testTypeUseAnnotationDependency()
343             throws TestToolsException, ProjectDependencyAnalyzerException
344     {
345         Properties properties = new Properties();
346         properties.put( "maven.compiler.source", "1.8" );
347         properties.put( "maven.compiler.target", "1.8" );
348         compileProject( "typeUseAnnotationDependency/pom.xml", properties);
349 
350         MavenProject usage = getProject( "typeUseAnnotationDependency/usage/pom.xml" );
351 
352         ProjectDependencyAnalysis actualAnalysis = analyzer.analyze( usage );
353 
354         Artifact annotation = createArtifact( "org.apache.maven.shared.dependency-analyzer.tests",
355                                             "typeUseAnnotationDependencyAnnotation", "jar", "1.0", "compile" );
356         Set<Artifact> usedDeclaredArtifacts = Collections.singleton( annotation );
357         ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis(usedDeclaredArtifacts, (Set<Artifact>) null, null,
358                 null );
359 
360         assertEquals( expectedAnalysis, actualAnalysis );
361     }
362 
363     @Test
364     public void testTypeUseAnnotationDependencyOnLocalVariable()
365             throws TestToolsException, ProjectDependencyAnalyzerException
366     {
367         Properties properties = new Properties();
368         properties.put( "maven.compiler.source", "1.8" );
369         properties.put( "maven.compiler.target", "1.8" );
370         compileProject( "typeUseAnnotationDependency/pom.xml", properties);
371 
372         MavenProject usage = getProject( "typeUseAnnotationDependency/usageLocalVar/pom.xml" );
373 
374         ProjectDependencyAnalysis actualAnalysis = analyzer.analyze( usage );
375 
376         Artifact annotation = createArtifact( "org.apache.maven.shared.dependency-analyzer.tests",
377                                             "typeUseAnnotationDependencyAnnotation", "jar", "1.0", "compile" );
378         Set<Artifact> usedDeclaredArtifacts = Collections.singleton( annotation );
379         ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis(usedDeclaredArtifacts, (Set<Artifact>) null, null,
380                 null);
381 
382         assertEquals( expectedAnalysis, actualAnalysis );
383     }
384 
385     @Test
386     public void testUnnamedPackageClassReference()
387         throws TestToolsException, ProjectDependencyAnalyzerException
388     {
389         // Only visible through constant pool analysis (supported for JDK8+)
390         compileProject( "unnamedPackageClassReference/pom.xml" );
391 
392         MavenProject project = getProject( "unnamedPackageClassReference/pom.xml" );
393 
394         ProjectDependencyAnalysis actualAnalysis = analyzer.analyze( project );
395 
396         Artifact dnsjava = createArtifact( "dnsjava", "dnsjava", "jar", "2.1.8", "compile" );
397         // we don't use any dnsjava classes so this should show up as an unused dep
398         Set<Artifact> unusedDeclaredArtifacts = Collections.singleton( dnsjava );
399 
400         ProjectDependencyAnalysis expectedAnalysis =
401             new ProjectDependencyAnalysis( new HashSet<>(), new HashSet<>(), unusedDeclaredArtifacts,
402                 new HashSet<>() );
403 
404         assertEquals( expectedAnalysis, actualAnalysis );
405     }
406 
407     @Test
408     public void testJarWithClassInUnnamedPackage()
409             throws TestToolsException, ProjectDependencyAnalyzerException
410     {
411         compileProject( "jarWithClassInUnnamedPackage/pom.xml" );
412 
413         MavenProject project2 = getProject( "jarWithClassInUnnamedPackage/project2/pom.xml" );
414 
415         ProjectDependencyAnalysis actualAnalysis = analyzer.analyze( project2 );
416 
417         Artifact project1 = createArtifact( "org.apache.maven.shared.dependency-analyzer.tests",
418                                             "jarWithClassInUnnamedPackage1", "jar", "1.0", "compile" );
419         Set<Artifact> unusedDeclaredArtifacts = Collections.singleton( project1 );
420         ProjectDependencyAnalysis expectedAnalysis = new ProjectDependencyAnalysis( null, null, unusedDeclaredArtifacts );
421 
422         assertEquals( expectedAnalysis, actualAnalysis );
423     }
424 
425     @Test
426     public void testUsedUndeclaredClassReference()
427         throws TestToolsException, ProjectDependencyAnalyzerException
428     {
429         compileProject( "usedUndeclaredReference/pom.xml" );
430 
431         MavenProject project = getProject( "usedUndeclaredReference/pom.xml" );
432 
433         ProjectDependencyAnalysis actualAnalysis = analyzer.analyze( project );
434 
435         Artifact xmlApis = createArtifact( "xml-apis", "xml-apis", "jar", "1.0.b2", "compile" );
436         Set<Artifact> expectedUsedUndeclaredArtifacts = Collections.singleton( xmlApis );
437 
438         assertEquals( expectedUsedUndeclaredArtifacts, actualAnalysis.getUsedUndeclaredArtifacts() );
439 
440         Map<Artifact, Set<String>> expectedUsedUndeclaredArtifactsWithClasses =
441                 Collections.singletonMap(xmlApis, Collections.singleton("org.apache.xmlcommons.Version") );
442 
443         assertEquals( expectedUsedUndeclaredArtifactsWithClasses, actualAnalysis.getUsedUndeclaredArtifactsWithClasses() );
444     }
445 
446     // private methods --------------------------------------------------------
447 
448     private void compileProject( String pomPath )
449         throws TestToolsException
450     {
451         compileProject( pomPath, new Properties() );
452     }
453 
454     private void compileProject(String pomPath, Properties properties) throws TestToolsException {
455         File pom = getTestFile( "target/test-classes/", pomPath );
456         if ( !properties.containsKey( "maven.compiler.source" ) )
457         {
458           properties.put( "maven.compiler.source", "1.8" );
459           properties.put( "maven.compiler.target", "1.8" );
460         }
461         
462         String httpsProtocols = System.getProperty( "https.protocols" );
463         if ( httpsProtocols != null )
464         {
465             properties.put( "https.protocols", httpsProtocols );
466         }
467 
468         List<String> goals = Arrays.asList( "clean", "install" );
469         File log = new File( pom.getParentFile(), "build.log" );
470 
471         // TODO: don't install test artifacts to local repository
472         InvocationRequest request = buildTool.createBasicInvocationRequest( pom, properties, goals, log );
473         request.setLocalRepositoryDirectory( localRepo );
474         InvocationResult result = buildTool.executeMaven( request );
475 
476         assertNull( "Error compiling test project", result.getExecutionException() );
477         assertEquals( "Error compiling test project", 0, result.getExitCode() );
478     }
479 
480     private MavenProject getProject( String pomPath )
481         throws TestToolsException
482     {
483         File pom = getTestFile( "target/test-classes/", pomPath );
484 
485         return projectTool.readProjectWithDependencies( pom );
486     }
487 
488     private Artifact createArtifact( String groupId, String artifactId, String type, String version, String scope )
489     {
490         VersionRange versionRange = VersionRange.createFromVersion( version );
491         ArtifactHandler handler = new DefaultArtifactHandler();
492 
493         return new DefaultArtifact( groupId, artifactId, versionRange, scope, type, null, handler );
494     }
495 }