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