blob: ef7713d67ececf0ed5c01eca421c3c9caf7e7b92 [file] [log] [blame]
package org.apache.maven.shared.release.phase;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import static org.junit.Assert.assertFalse;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.apache.commons.lang.SystemUtils;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.repository.DefaultArtifactRepository;
import org.apache.maven.artifact.repository.MavenArtifactRepository;
import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
import org.apache.maven.model.Profile;
import org.apache.maven.model.Repository;
import org.apache.maven.project.DefaultProjectBuildingRequest;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuilder;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.project.ProjectBuildingRequest.RepositoryMerging;
import org.apache.maven.project.ProjectBuildingResult;
import org.apache.maven.project.ProjectSorter;
import org.apache.maven.repository.internal.MavenRepositorySystemSession;
import org.apache.maven.shared.release.PlexusJUnit4TestCase;
import org.apache.maven.shared.release.config.ReleaseDescriptorBuilder;
import org.sonatype.aether.impl.internal.SimpleLocalRepositoryManager;
import org.sonatype.aether.repository.WorkspaceReader;
import org.sonatype.aether.repository.WorkspaceRepository;
import org.xmlunit.builder.DiffBuilder;
import org.xmlunit.diff.Comparison;
import org.xmlunit.diff.ComparisonResult;
import org.xmlunit.diff.ComparisonType;
import org.xmlunit.diff.DefaultNodeMatcher;
import org.xmlunit.diff.Diff;
import org.xmlunit.diff.DifferenceEvaluator;
import org.xmlunit.diff.ElementSelectors;
/**
* Base class for some release tests.
*
* @author <a href="mailto:brett@apache.org">Brett Porter</a>
*/
public abstract class AbstractReleaseTestCase
extends PlexusJUnit4TestCase
{
protected ProjectBuilder projectBuilder;
protected ArtifactRepository localRepository;
protected ReleasePhase phase;
@Override
public void setUp()
throws Exception
{
super.setUp();
projectBuilder = lookup( ProjectBuilder.class );
ArtifactRepositoryLayout layout = lookup( ArtifactRepositoryLayout.class, "default" );
String localRepoPath = getTestFile( "target/local-repository" ).getAbsolutePath().replace( '\\', '/' );
localRepository = new MavenArtifactRepository( "local", "file://" + localRepoPath, layout, null, null );
}
protected Path getWorkingDirectory( String workingDir )
{
return Paths.get( getBasedir(), "target/test-classes" ).resolve( Paths.get( "projects", workingDir ) ) ;
}
protected List<MavenProject> createReactorProjects( String path, String subpath )
throws Exception
{
return createReactorProjects( path, path, subpath );
}
protected ReleaseDescriptorBuilder createReleaseDescriptorBuilder( List<MavenProject> reactorProjects )
{
ReleaseDescriptorBuilder builder = new ReleaseDescriptorBuilder();
for ( MavenProject project : reactorProjects )
{
String projectKey = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
builder.putOriginalVersion( projectKey, project.getVersion() );
builder.addProjectPomFile( projectKey, project.getFile().getPath() );
}
return builder;
}
/**
*
* @param sourcePath sourceDirectory to copy from
* @param targetPath targetDirectory to copy to
* @param executionRoot sub directory of targetPath in case the root pom.xml is not used (e.g. flat projects)
* @return all Maven projects
* @throws Exception if any occurs
*/
protected List<MavenProject> createReactorProjects( String sourcePath, String targetPath, String executionRoot )
throws Exception
{
final Path testCaseRootFrom = Paths.get( getBasedir(), "src/test/resources" ).resolve( Paths.get( "projects", sourcePath ) ) ;
final Path testCaseRootTo = getWorkingDirectory( targetPath );
// Recopy the test resources since they are modified in some tests
Files.walkFileTree( testCaseRootFrom, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile( Path file, BasicFileAttributes attrs )
throws IOException
{
Path relPath = testCaseRootFrom.relativize( file );
if ( !relPath.toFile().getName().startsWith( "expected-" ) )
{
Files.createDirectories( testCaseRootTo.resolve( relPath ).getParent() );
Files.copy( file, testCaseRootTo.resolve( relPath ), StandardCopyOption.REPLACE_EXISTING );
}
return FileVisitResult.CONTINUE;
}
});
Path projectFile;
if ( executionRoot == null )
{
projectFile = testCaseRootTo.resolve( "pom.xml" );
}
else
{
projectFile = testCaseRootTo.resolve( Paths.get( executionRoot, "pom.xml" ) );
}
List<ArtifactRepository> repos =
Collections.<ArtifactRepository>singletonList( new DefaultArtifactRepository( "central",
getRemoteRepositoryURL(),
new DefaultRepositoryLayout() ) );
Repository repository = new Repository();
repository.setId( "central" );
repository.setUrl( getRemoteRepositoryURL() );
Profile profile = new Profile();
profile.setId( "profile" );
profile.addRepository( repository );
ProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest();
buildingRequest.setLocalRepository( localRepository );
buildingRequest.setRemoteRepositories( repos );
buildingRequest.setPluginArtifactRepositories( repos );
buildingRequest.setRepositoryMerging( RepositoryMerging.REQUEST_DOMINANT );
MavenRepositorySystemSession repositorySession = new MavenRepositorySystemSession();
repositorySession.setLocalRepositoryManager( new SimpleLocalRepositoryManager( localRepository.getBasedir() ) );
buildingRequest.setRepositorySession( repositorySession );
buildingRequest.addProfile( profile );
buildingRequest.setActiveProfileIds( Arrays.asList( profile.getId() ) );
buildingRequest.setResolveDependencies( true );
List<ProjectBuildingResult> buildingResults =
projectBuilder.build( Collections.singletonList( projectFile.toFile() ), true, buildingRequest );
List<MavenProject> reactorProjects = new ArrayList<>();
for ( ProjectBuildingResult buildingResult : buildingResults )
{
reactorProjects.add( buildingResult.getProject() ) ;
}
WorkspaceReader simpleReactorReader = new SimpleReactorWorkspaceReader( reactorProjects );
repositorySession.setWorkspaceReader( simpleReactorReader );
ProjectSorter sorter = new ProjectSorter( reactorProjects );
reactorProjects = sorter.getSortedProjects();
List<MavenProject> resolvedProjects = new ArrayList<>( reactorProjects.size() );
for ( MavenProject project : reactorProjects )
{
resolvedProjects.add( projectBuilder.build( project.getFile(), buildingRequest ).getProject() );
}
return resolvedProjects;
}
protected static Map<String,MavenProject> getProjectsAsMap( List<MavenProject> reactorProjects )
{
Map<String,MavenProject> map = new HashMap<>();
for ( MavenProject project : reactorProjects )
{
map.put( ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ), project );
}
return map;
}
protected boolean comparePomFiles( List<MavenProject> reactorProjects )
throws IOException
{
return comparePomFiles( reactorProjects, true );
}
protected boolean comparePomFiles( List<MavenProject> reactorProjects, boolean normalizeLineEndings )
throws IOException
{
comparePomFiles( reactorProjects, "", normalizeLineEndings );
// TODO: return void since this is redundant
return true;
}
protected void comparePomFiles( List<MavenProject> reactorProjects, String expectedFileSuffix )
throws IOException
{
comparePomFiles( reactorProjects, expectedFileSuffix, true );
}
protected void comparePomFiles( List<MavenProject> reactorProjects, String expectedFileSuffix, boolean normalizeLineEndings )
throws IOException
{
for ( MavenProject project : reactorProjects )
{
comparePomFiles( project, expectedFileSuffix, normalizeLineEndings );
}
}
protected void comparePomFiles( MavenProject project, String expectedFileSuffix )
throws IOException
{
comparePomFiles( project, expectedFileSuffix, true );
}
protected void comparePomFiles( MavenProject project, String expectedFileSuffix, boolean normalizeLineEndings )
throws IOException
{
File actualFile = project.getFile();
File expectedFile = new File( actualFile.getParentFile(), "expected-pom" + expectedFileSuffix + ".xml" );
comparePomFiles( expectedFile, actualFile, normalizeLineEndings, false );
}
protected void comparePomFiles( File expectedFile, File actualFile )
throws IOException
{
comparePomFiles( expectedFile, actualFile, true, false );
}
protected void comparePomFiles( File expectedFile, File actualFile, boolean normalizeLineEndings, boolean ignoreComments )
throws IOException
{
StringBuffer sb = new StringBuffer( "Check the transformed POM " + actualFile );
sb.append( SystemUtils.LINE_SEPARATOR );
final String remoteRepositoryURL = getRemoteRepositoryURL();
DiffBuilder diffBuilder = DiffBuilder.compare( expectedFile ).withTest( actualFile );
if ( normalizeLineEndings )
{
diffBuilder = diffBuilder.normalizeWhitespace();
}
if ( ignoreComments )
{
diffBuilder.ignoreComments();
}
// Order of elements has changed between M2 and M3, so match by name
diffBuilder.withNodeMatcher( new DefaultNodeMatcher( ElementSelectors.byName ) ).checkForSimilar();
diffBuilder.withDifferenceEvaluator( new DifferenceEvaluator()
{
@Override
public ComparisonResult evaluate( Comparison comparison, ComparisonResult outcome )
{
if ( "${remoterepo}".equals( comparison.getControlDetails().getValue() ) &&
remoteRepositoryURL.equals( comparison.getTestDetails().getValue() ) )
{
return ComparisonResult.EQUAL;
}
else if ( outcome == ComparisonResult.DIFFERENT
&& comparison.getType() == ComparisonType.CHILD_NODELIST_SEQUENCE )
{
// Order of elements has changed between M2 and M3
return ComparisonResult.EQUAL;
}
else if ( outcome == ComparisonResult.DIFFERENT
&& comparison.getType() == ComparisonType.TEXT_VALUE
&& "${project.build.directory}/site".equals( comparison.getTestDetails().getValue() ) )
{
// M2 was target/site, M3 is ${project.build.directory}/site
return ComparisonResult.EQUAL;
}
else
{
return outcome;
}
}
} );
Diff diff = diffBuilder.build();
sb.append( diff.toString() );
assertFalse( sb.toString(), diff.hasDifferences() );
}
private String getRemoteRepositoryURL()
throws IOException
{
File testFile = getTestFile( "src/test/remote-repository" );
if (testFile.getAbsolutePath().equals( testFile.getCanonicalPath() ) )
{
return "file://" + getTestFile( "src/test/remote-repository" ).getAbsolutePath().replace( '\\', '/' );
}
return "file://" + getTestFile( "src/test/remote-repository" ).getCanonicalPath().replace( '\\', '/' );
}
public static String getPath( File file )
throws IOException
{
return file.toPath().toRealPath( LinkOption.NOFOLLOW_LINKS ).toString();
}
/**
* WorkspaceReader to find versions and artifacts from reactor
*/
private static final class SimpleReactorWorkspaceReader
implements WorkspaceReader
{
private final List<MavenProject> reactorProjects;
private SimpleReactorWorkspaceReader( List<MavenProject> reactorProjects )
{
this.reactorProjects = reactorProjects;
}
@Override
public WorkspaceRepository getRepository()
{
return null;
}
@Override
public List<String> findVersions( org.sonatype.aether.artifact.Artifact artifact )
{
for ( MavenProject mavenProject : reactorProjects )
{
if ( Objects.equals( artifact.toString(), mavenProject.getArtifact().toString() ) )
{
return Collections.singletonList( mavenProject.getArtifact().getVersion() );
}
}
return Collections.emptyList();
}
@Override
public File findArtifact( org.sonatype.aether.artifact.Artifact artifact )
{
for ( MavenProject mavenProject : reactorProjects )
{
String pom = mavenProject.getGroupId() + ':' + mavenProject.getArtifactId() + ":pom:"
+ mavenProject.getVersion();
if ( Objects.equals( artifact.toString(), pom ) )
{
return mavenProject.getFile();
}
else if ( Objects.equals( artifact.toString(), mavenProject.getArtifact().toString() ) )
{
// just an existing, content doesn't matter
return mavenProject.getFile();
}
}
return null;
}
}
}