1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.shared.release.phase;
20
21 import javax.inject.Inject;
22
23 import java.io.File;
24 import java.io.IOException;
25 import java.nio.file.FileVisitResult;
26 import java.nio.file.Files;
27 import java.nio.file.LinkOption;
28 import java.nio.file.Path;
29 import java.nio.file.Paths;
30 import java.nio.file.SimpleFileVisitor;
31 import java.nio.file.StandardCopyOption;
32 import java.nio.file.attribute.BasicFileAttributes;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Objects;
39
40 import org.apache.maven.artifact.ArtifactUtils;
41 import org.apache.maven.artifact.factory.ArtifactFactory;
42 import org.apache.maven.artifact.repository.ArtifactRepository;
43 import org.apache.maven.artifact.repository.DefaultArtifactRepository;
44 import org.apache.maven.artifact.repository.MavenArtifactRepository;
45 import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
46 import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
47 import org.apache.maven.lifecycle.LifecycleExecutionException;
48 import org.apache.maven.model.Profile;
49 import org.apache.maven.model.Repository;
50 import org.apache.maven.project.DefaultProjectBuildingRequest;
51 import org.apache.maven.project.MavenProject;
52 import org.apache.maven.project.ProjectBuilder;
53 import org.apache.maven.project.ProjectBuildingRequest;
54 import org.apache.maven.project.ProjectBuildingRequest.RepositoryMerging;
55 import org.apache.maven.project.ProjectBuildingResult;
56 import org.apache.maven.project.ProjectSorter;
57 import org.apache.maven.project.artifact.InvalidDependencyVersionException;
58 import org.apache.maven.scm.manager.ScmManager;
59 import org.apache.maven.shared.release.config.ReleaseDescriptorBuilder;
60 import org.apache.maven.shared.release.scm.ScmTranslator;
61 import org.apache.maven.shared.release.scm.SubversionScmTranslator;
62 import org.apache.maven.shared.release.stubs.ScmManagerStub;
63 import org.codehaus.plexus.PlexusContainer;
64 import org.codehaus.plexus.testing.PlexusTestConfiguration;
65 import org.eclipse.aether.DefaultRepositorySystemSession;
66 import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
67 import org.eclipse.aether.repository.LocalRepository;
68 import org.eclipse.aether.repository.WorkspaceReader;
69 import org.eclipse.aether.repository.WorkspaceRepository;
70 import org.junit.jupiter.api.BeforeEach;
71 import org.xmlunit.builder.DiffBuilder;
72 import org.xmlunit.diff.Comparison;
73 import org.xmlunit.diff.ComparisonResult;
74 import org.xmlunit.diff.ComparisonType;
75 import org.xmlunit.diff.DefaultNodeMatcher;
76 import org.xmlunit.diff.Diff;
77 import org.xmlunit.diff.DifferenceEvaluator;
78 import org.xmlunit.diff.ElementSelectors;
79
80 import static org.codehaus.plexus.testing.PlexusExtension.getBasedir;
81 import static org.codehaus.plexus.testing.PlexusExtension.getTestFile;
82 import static org.junit.jupiter.api.Assertions.assertFalse;
83
84
85
86
87
88
89 public abstract class AbstractReleaseTestCase implements PlexusTestConfiguration {
90 @Inject
91 protected ProjectBuilder projectBuilder;
92
93 protected ArtifactRepository localRepository;
94
95 @Inject
96 private ArtifactFactory artifactFactory;
97
98 @Inject
99 private ArtifactRepositoryLayout layout;
100
101 protected ScmManagerStub scmManager;
102
103 @Override
104 public void customizeContainer(PlexusContainer container) {
105 scmManager = new ScmManagerStub();
106 container.addComponent(scmManager, ScmManager.class.getName());
107 container.addComponent(new SubversionScmTranslator(), ScmTranslator.class, "stub-provider");
108 }
109
110 @BeforeEach
111 void setupAbstractReleaseTestCase() throws Exception {
112 String localRepoPath =
113 getTestFile("target/local-repository").getAbsolutePath().replace('\\', '/');
114 localRepository = new MavenArtifactRepository("local", "file://" + localRepoPath, layout, null, null);
115 }
116
117 protected Path getWorkingDirectory(String workingDir) {
118 return Paths.get(getBasedir(), "target/test-classes").resolve(Paths.get("projects", workingDir));
119 }
120
121 protected List<MavenProject> createReactorProjects(String path, String subpath) throws Exception {
122 return createReactorProjects(path, path, subpath);
123 }
124
125 protected ReleaseDescriptorBuilder createReleaseDescriptorBuilder(List<MavenProject> reactorProjects) {
126 ReleaseDescriptorBuilder builder = new ReleaseDescriptorBuilder();
127 for (MavenProject project : reactorProjects) {
128 builder.putOriginalVersion(project.getGroupId() + ':' + project.getArtifactId(), project.getVersion());
129 }
130 return builder;
131 }
132
133
134
135
136
137
138
139
140
141 protected List<MavenProject> createReactorProjects(String sourcePath, String targetPath, String executionRoot)
142 throws Exception {
143 final Path testCaseRootFrom =
144 Paths.get(getBasedir(), "src/test/resources").resolve(Paths.get("projects", sourcePath));
145
146 final Path testCaseRootTo = getWorkingDirectory(targetPath);
147
148
149 Files.walkFileTree(testCaseRootFrom, new SimpleFileVisitor<Path>() {
150
151 @Override
152 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
153 Path relPath = testCaseRootFrom.relativize(file);
154
155 if (!relPath.toFile().getName().startsWith("expected-")) {
156 Files.createDirectories(testCaseRootTo.resolve(relPath).getParent());
157
158 Files.copy(file, testCaseRootTo.resolve(relPath), StandardCopyOption.REPLACE_EXISTING);
159 }
160
161 return FileVisitResult.CONTINUE;
162 }
163 });
164
165 Path projectFile;
166 if (executionRoot == null) {
167 projectFile = testCaseRootTo.resolve("pom.xml");
168 } else {
169 projectFile = testCaseRootTo.resolve(Paths.get(executionRoot, "pom.xml"));
170 }
171
172 List<ArtifactRepository> repos = Collections.singletonList(
173 new DefaultArtifactRepository("central", getRemoteRepositoryURL(), new DefaultRepositoryLayout()));
174
175 Repository repository = new Repository();
176 repository.setId("central");
177 repository.setUrl(getRemoteRepositoryURL());
178
179 Profile profile = new Profile();
180 profile.setId("profile");
181 profile.addRepository(repository);
182
183 ProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest();
184 buildingRequest.setLocalRepository(localRepository);
185 buildingRequest.setRemoteRepositories(repos);
186 buildingRequest.setPluginArtifactRepositories(repos);
187 buildingRequest.setRepositoryMerging(RepositoryMerging.REQUEST_DOMINANT);
188 DefaultRepositorySystemSession repositorySession = new DefaultRepositorySystemSession();
189 repositorySession.setLocalRepositoryManager(new SimpleLocalRepositoryManagerFactory()
190 .newInstance(repositorySession, new LocalRepository(localRepository.getBasedir())));
191 buildingRequest.setRepositorySession(repositorySession);
192 buildingRequest.addProfile(profile);
193 buildingRequest.setActiveProfileIds(Collections.singletonList(profile.getId()));
194 buildingRequest.setResolveDependencies(true);
195
196 List<ProjectBuildingResult> buildingResults =
197 projectBuilder.build(Collections.singletonList(projectFile.toFile()), true, buildingRequest);
198
199 List<MavenProject> reactorProjects = new ArrayList<>();
200 for (ProjectBuildingResult buildingResult : buildingResults) {
201 reactorProjects.add(buildingResult.getProject());
202 }
203
204 WorkspaceReader simpleReactorReader = new SimpleReactorWorkspaceReader(reactorProjects);
205 repositorySession.setWorkspaceReader(simpleReactorReader);
206
207 ProjectSorter sorter = new ProjectSorter(reactorProjects);
208 reactorProjects = sorter.getSortedProjects();
209
210 List<MavenProject> resolvedProjects = new ArrayList<>(reactorProjects.size());
211 for (MavenProject project : reactorProjects) {
212 MavenProject resolvedProject =
213 projectBuilder.build(project.getFile(), buildingRequest).getProject();
214
215
216 if (resolvedProject.getDependencyArtifacts() == null) {
217 try {
218 resolvedProject.setDependencyArtifacts(
219 resolvedProject.createArtifacts(artifactFactory, null, null));
220 } catch (InvalidDependencyVersionException e) {
221 throw new LifecycleExecutionException(e);
222 }
223 }
224
225
226
227 if (resolvedProject.getDependencyArtifacts().size()
228 >= resolvedProject.getArtifacts().size()
229 && resolvedProject.getDependencyArtifacts().stream().noneMatch(a -> a.getVersion() == null)) {
230 resolvedProject.setArtifacts(resolvedProject.getDependencyArtifacts());
231 }
232
233 resolvedProjects.add(resolvedProject);
234 }
235 return resolvedProjects;
236 }
237
238 protected static Map<String, MavenProject> getProjectsAsMap(List<MavenProject> reactorProjects) {
239 Map<String, MavenProject> map = new HashMap<>();
240 for (MavenProject project : reactorProjects) {
241 map.put(ArtifactUtils.versionlessKey(project.getGroupId(), project.getArtifactId()), project);
242 }
243 return map;
244 }
245
246 protected void comparePomFiles(List<MavenProject> reactorProjects) throws IOException {
247 comparePomFiles(reactorProjects, true);
248 }
249
250 protected void comparePomFiles(List<MavenProject> reactorProjects, boolean normalizeLineEndings)
251 throws IOException {
252 comparePomFiles(reactorProjects, "", normalizeLineEndings);
253 }
254
255 protected void comparePomFiles(List<MavenProject> reactorProjects, String expectedFileSuffix) throws IOException {
256 comparePomFiles(reactorProjects, expectedFileSuffix, true);
257 }
258
259 protected void comparePomFiles(
260 List<MavenProject> reactorProjects, String expectedFileSuffix, boolean normalizeLineEndings)
261 throws IOException {
262 for (MavenProject project : reactorProjects) {
263 comparePomFiles(project, expectedFileSuffix, normalizeLineEndings);
264 }
265 }
266
267 protected void comparePomFiles(MavenProject project, String expectedFileSuffix) throws IOException {
268 comparePomFiles(project, expectedFileSuffix, true);
269 }
270
271 protected void comparePomFiles(MavenProject project, String expectedFileSuffix, boolean normalizeLineEndings)
272 throws IOException {
273 File actualFile = project.getFile();
274 File expectedFile = new File(actualFile.getParentFile(), "expected-pom" + expectedFileSuffix + ".xml");
275
276 comparePomFiles(expectedFile, actualFile, normalizeLineEndings, false);
277 }
278
279 protected void comparePomFiles(
280 File expectedFile, File actualFile, boolean normalizeLineEndings, boolean ignoreComments)
281 throws IOException {
282 StringBuffer sb = new StringBuffer("Check the transformed POM " + actualFile);
283 sb.append(System.lineSeparator());
284
285 final String remoteRepositoryURL = getRemoteRepositoryURL();
286
287 DiffBuilder diffBuilder = DiffBuilder.compare(expectedFile).withTest(actualFile);
288 if (normalizeLineEndings) {
289 diffBuilder = diffBuilder.normalizeWhitespace();
290 }
291 if (ignoreComments) {
292 diffBuilder.ignoreComments();
293 }
294
295 diffBuilder
296 .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byName))
297 .checkForSimilar();
298
299 diffBuilder.withDifferenceEvaluator(new DifferenceEvaluator() {
300 @Override
301 public ComparisonResult evaluate(Comparison comparison, ComparisonResult outcome) {
302 if ("${remoterepo}".equals(comparison.getControlDetails().getValue())
303 && remoteRepositoryURL.equals(
304 comparison.getTestDetails().getValue())) {
305 return ComparisonResult.EQUAL;
306 } else if (outcome == ComparisonResult.DIFFERENT
307 && comparison.getType() == ComparisonType.CHILD_NODELIST_SEQUENCE) {
308
309 return ComparisonResult.EQUAL;
310 } else if (outcome == ComparisonResult.DIFFERENT
311 && comparison.getType() == ComparisonType.TEXT_VALUE
312 && "${project.build.directory}/site"
313 .equals(comparison.getTestDetails().getValue())) {
314
315 return ComparisonResult.EQUAL;
316 } else {
317 return outcome;
318 }
319 }
320 });
321
322 Diff diff = diffBuilder.build();
323
324 sb.append(diff.toString());
325
326 assertFalse(diff.hasDifferences(), sb.toString());
327 }
328
329 private String getRemoteRepositoryURL() throws IOException {
330 File testFile = getTestFile("src/test/remote-repository");
331 if (testFile.getAbsolutePath().equals(testFile.getCanonicalPath())) {
332 return "file://"
333 + getTestFile("src/test/remote-repository")
334 .getAbsolutePath()
335 .replace('\\', '/');
336 }
337 return "file://"
338 + getTestFile("src/test/remote-repository").getCanonicalPath().replace('\\', '/');
339 }
340
341 public static String getPath(File file) throws IOException {
342 return file.toPath().toRealPath(LinkOption.NOFOLLOW_LINKS).toString();
343 }
344
345
346
347
348 private static final class SimpleReactorWorkspaceReader implements WorkspaceReader {
349 private final List<MavenProject> reactorProjects;
350
351 private SimpleReactorWorkspaceReader(List<MavenProject> reactorProjects) {
352 this.reactorProjects = reactorProjects;
353 }
354
355 @Override
356 public WorkspaceRepository getRepository() {
357 return null;
358 }
359
360 @Override
361 public List<String> findVersions(org.eclipse.aether.artifact.Artifact artifact) {
362 for (MavenProject mavenProject : reactorProjects) {
363 if (Objects.equals(
364 artifact.toString(), mavenProject.getArtifact().toString())) {
365 return Collections.singletonList(mavenProject.getArtifact().getVersion());
366 }
367 }
368 return Collections.emptyList();
369 }
370
371 @Override
372 public File findArtifact(org.eclipse.aether.artifact.Artifact artifact) {
373 for (MavenProject mavenProject : reactorProjects) {
374 String pom = mavenProject.getGroupId() + ':' + mavenProject.getArtifactId() + ":pom:"
375 + mavenProject.getVersion();
376 if (Objects.equals(artifact.toString(), pom)) {
377 return mavenProject.getFile();
378 } else if (Objects.equals(
379 artifact.toString(), mavenProject.getArtifact().toString())) {
380
381 return mavenProject.getFile();
382 }
383 }
384 return null;
385 }
386 }
387 }