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.buildcache.checksum;
20  
21  import java.lang.reflect.Method;
22  import java.nio.charset.StandardCharsets;
23  import java.nio.file.Files;
24  import java.nio.file.Path;
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.List;
28  import java.util.Properties;
29  import java.util.SortedMap;
30  
31  import org.apache.maven.artifact.handler.ArtifactHandler;
32  import org.apache.maven.buildcache.MultiModuleSupport;
33  import org.apache.maven.buildcache.NormalizedModelProvider;
34  import org.apache.maven.buildcache.ProjectInputCalculator;
35  import org.apache.maven.buildcache.RemoteCacheRepository;
36  import org.apache.maven.buildcache.hash.HashFactory;
37  import org.apache.maven.buildcache.xml.CacheConfig;
38  import org.apache.maven.buildcache.xml.build.DigestItem;
39  import org.apache.maven.buildcache.xml.build.ProjectsInputInfo;
40  import org.apache.maven.execution.MavenSession;
41  import org.apache.maven.model.Dependency;
42  import org.apache.maven.project.MavenProject;
43  import org.eclipse.aether.RepositorySystem;
44  import org.eclipse.aether.RepositorySystemSession;
45  import org.junit.jupiter.api.BeforeEach;
46  import org.junit.jupiter.api.Test;
47  import org.junit.jupiter.api.extension.ExtendWith;
48  import org.junit.jupiter.api.io.TempDir;
49  import org.mockito.Mock;
50  import org.mockito.junit.jupiter.MockitoExtension;
51  import org.mockito.junit.jupiter.MockitoSettings;
52  import org.mockito.quality.Strictness;
53  
54  import static org.junit.jupiter.api.Assertions.assertEquals;
55  import static org.mockito.Mockito.mock;
56  import static org.mockito.Mockito.never;
57  import static org.mockito.Mockito.verify;
58  import static org.mockito.Mockito.when;
59  
60  /**
61   * Regression tests for:
62   * - #411: avoid resolving/downloading reactor dependencies when their versions are dynamic (e.g. LATEST)
63   * - #417: system-scoped dependencies must be hashed from systemPath without Aether resolution
64   */
65  @ExtendWith(MockitoExtension.class)
66  @MockitoSettings(strictness = Strictness.LENIENT)
67  class MavenProjectInputReactorAndSystemScopeRegressionTest {
68  
69      @Mock
70      private MavenProject project;
71  
72      @Mock
73      private MavenSession session;
74  
75      @Mock
76      private RepositorySystem repoSystem;
77  
78      @Mock
79      private RepositorySystemSession repositorySystemSession;
80  
81      @Mock
82      private NormalizedModelProvider normalizedModelProvider;
83  
84      @Mock
85      private MultiModuleSupport multiModuleSupport;
86  
87      @Mock
88      private ProjectInputCalculator projectInputCalculator;
89  
90      @Mock
91      private CacheConfig config;
92  
93      @Mock
94      private RemoteCacheRepository remoteCache;
95  
96      @Mock
97      private org.apache.maven.artifact.handler.manager.ArtifactHandlerManager artifactHandlerManager;
98  
99      @TempDir
100     Path tempDir;
101 
102     private MavenProjectInput mavenProjectInput;
103 
104     @BeforeEach
105     void setUp() {
106         when(session.getRepositorySession()).thenReturn(repositorySystemSession);
107         when(project.getBasedir()).thenReturn(tempDir.toFile());
108         when(project.getProperties()).thenReturn(new Properties());
109         when(config.getDefaultGlob()).thenReturn("*");
110         when(config.isProcessPlugins()).thenReturn("false");
111         when(config.getGlobalExcludePaths()).thenReturn(new ArrayList<>());
112         when(config.calculateProjectVersionChecksum()).thenReturn(Boolean.FALSE);
113         when(config.getHashFactory()).thenReturn(HashFactory.SHA1);
114 
115         org.apache.maven.model.Build build = new org.apache.maven.model.Build();
116         build.setDirectory(tempDir.toString());
117         build.setOutputDirectory(tempDir.resolve("target/classes").toString());
118         build.setTestOutputDirectory(tempDir.resolve("target/test-classes").toString());
119         build.setSourceDirectory(tempDir.resolve("src/main/java").toString());
120         build.setTestSourceDirectory(tempDir.resolve("src/test/java").toString());
121         build.setResources(new ArrayList<>());
122         build.setTestResources(new ArrayList<>());
123         when(project.getBuild()).thenReturn(build);
124 
125         when(project.getDependencies()).thenReturn(new ArrayList<>());
126         when(project.getBuildPlugins()).thenReturn(new ArrayList<>());
127         when(project.getModules()).thenReturn(new ArrayList<>());
128         when(project.getPackaging()).thenReturn("jar");
129 
130         ArtifactHandler handler = mock(ArtifactHandler.class);
131         when(handler.getClassifier()).thenReturn(null);
132         when(handler.getExtension()).thenReturn("jar");
133         when(artifactHandlerManager.getArtifactHandler(org.mockito.ArgumentMatchers.anyString()))
134                 .thenReturn(handler);
135 
136         mavenProjectInput = new MavenProjectInput(
137                 project,
138                 normalizedModelProvider,
139                 multiModuleSupport,
140                 projectInputCalculator,
141                 session,
142                 config,
143                 repoSystem,
144                 remoteCache,
145                 artifactHandlerManager);
146     }
147 
148     @Test
149     void testSystemScopeDependencyHashedFromSystemPathWithoutAetherResolution() throws Exception {
150         Path systemJar = tempDir.resolve("local-lib.jar");
151         Files.write(systemJar, "abc".getBytes(StandardCharsets.UTF_8));
152 
153         Dependency dependency = new Dependency();
154         dependency.setGroupId("com.example");
155         dependency.setArtifactId("local-lib");
156         dependency.setVersion("1.0");
157         dependency.setType("jar");
158         dependency.setScope("system");
159         dependency.setSystemPath(systemJar.toString());
160         dependency.setOptional(true);
161 
162         Method resolveArtifact = MavenProjectInput.class.getDeclaredMethod("resolveArtifact", Dependency.class);
163         resolveArtifact.setAccessible(true);
164         DigestItem digest = (DigestItem) resolveArtifact.invoke(mavenProjectInput, dependency);
165 
166         String expectedHash = HashFactory.SHA1.createAlgorithm().hash(systemJar);
167         assertEquals(expectedHash, digest.getHash());
168 
169         verify(repoSystem, never())
170                 .resolveArtifact(org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any());
171     }
172 
173     @Test
174     void testDynamicVersionReactorDependencyUsesProjectChecksumAndAvoidsAetherResolution() throws Exception {
175         Dependency dependency = new Dependency();
176         dependency.setGroupId("com.example");
177         dependency.setArtifactId("reactor-artifact");
178         dependency.setVersion("LATEST");
179         dependency.setType("jar");
180 
181         when(multiModuleSupport.tryToResolveProject("com.example", "reactor-artifact", "LATEST"))
182                 .thenReturn(java.util.Optional.empty());
183 
184         MavenProject reactorProject = mock(MavenProject.class);
185         when(reactorProject.getGroupId()).thenReturn("com.example");
186         when(reactorProject.getArtifactId()).thenReturn("reactor-artifact");
187         when(reactorProject.getVersion()).thenReturn("1.0-SNAPSHOT");
188         when(session.getAllProjects()).thenReturn(Collections.singletonList(reactorProject));
189 
190         ProjectsInputInfo projectInfo = mock(ProjectsInputInfo.class);
191         when(projectInfo.getChecksum()).thenReturn("reactorChecksum");
192         when(projectInputCalculator.calculateInput(reactorProject)).thenReturn(projectInfo);
193 
194         Method getMutableDependenciesHashes =
195                 MavenProjectInput.class.getDeclaredMethod("getMutableDependenciesHashes", String.class, List.class);
196         getMutableDependenciesHashes.setAccessible(true);
197 
198         SortedMap<String, String> hashes = (SortedMap<String, String>)
199                 getMutableDependenciesHashes.invoke(mavenProjectInput, "", Collections.singletonList(dependency));
200 
201         assertEquals("reactorChecksum", hashes.get("com.example:reactor-artifact:jar"));
202 
203         verify(repoSystem, never())
204                 .resolveArtifact(org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any());
205         verify(projectInputCalculator).calculateInput(reactorProject);
206     }
207 }