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