Skip to content

Commit

Permalink
[SUREFIRE-2179] Support adding additional Maven artifacts to the test
Browse files Browse the repository at this point in the history
classpath
  • Loading branch information
kwin committed Jun 23, 2023
1 parent 128bd4d commit b4ae9f0
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.math.BigDecimal;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
Expand All @@ -41,6 +42,8 @@
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipFile;

import org.apache.maven.artifact.Artifact;
Expand Down Expand Up @@ -118,6 +121,12 @@
import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest;
import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult;
import org.codehaus.plexus.logging.Logger;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.eclipse.aether.util.artifact.JavaScopes;
import org.eclipse.aether.util.filter.DependencyFilterUtils;

import static java.lang.Integer.parseInt;
import static java.util.Arrays.asList;
Expand Down Expand Up @@ -281,6 +290,17 @@ public abstract class AbstractSurefireMojo extends AbstractMojo implements Suref
@Parameter(property = "maven.test.additionalClasspath")
private String[] additionalClasspathElements;

/**
* Maven coordinates in the format {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>} of additional artifacts.
* Those artifacts are automatically resolved from the repository (including their transitive dependencies).
* Afterwards they are appended including their transitive dependencies to the classpath
* (after the ones from {@link #additionalClasspathElements}).
*
* @since 3.2
*/
@Parameter(property = "maven.test.additionalClasspathArtifacts")
private String[] additionalClasspathArtifacts;

/**
* The test source directory containing test class sources.
* Important <b>only</b> for TestNG HTML reports.
Expand Down Expand Up @@ -2526,8 +2546,9 @@ protected ClassLoaderConfiguration getClassLoaderConfiguration() {
* Generates the test classpath.
*
* @return the classpath elements
* @throws MojoFailureException
*/
private TestClassPath generateTestClasspath() {
private TestClassPath generateTestClasspath() throws MojoFailureException {
Set<Artifact> classpathArtifacts = getProject().getArtifacts();

if (getClasspathDependencyScopeExclude() != null
Expand All @@ -2542,8 +2563,57 @@ private TestClassPath generateTestClasspath() {
classpathArtifacts = filterArtifacts(classpathArtifacts, dependencyFilter);
}

Set<String> additionalClasspathElements = new HashSet<>();
if (getAdditionalClasspathElements() != null) {
Arrays.stream(getAdditionalClasspathElements()).forEach(additionalClasspathElements::add);
}
if (getAdditionalClasspathArtifacts() != null) {
resolveDependencies(Arrays.stream(getAdditionalClasspathArtifacts())).stream()
.map(File::getAbsolutePath)
.forEach(additionalClasspathElements::add);
}
return new TestClassPath(
classpathArtifacts, getMainBuildPath(), getTestClassesDirectory(), getAdditionalClasspathElements());
classpathArtifacts, getMainBuildPath(), getTestClassesDirectory(), additionalClasspathElements);
}

Set<File> resolveDependencies(Stream<String> mavenCoordinates) throws MojoFailureException {
Set<File> files = new HashSet<>();
try {
mavenCoordinates.map(this::resolveArtifact).forEach(files::addAll);
} catch (IllegalStateException e) {
throw new MojoFailureException(e.getMessage(), e.getCause());
}
return files;
}

/** Resolves the artifact and its transitive runtime dependencies
* @param mavenCoordinates
* @return a collection of file paths (pointing to the local repository)
* @throws IllegalStateException in case resolving fails
*/
private Collection<File> resolveArtifact(String mavenCoordinates) throws IllegalStateException {
org.eclipse.aether.artifact.Artifact resolverArtifact =
new org.eclipse.aether.artifact.DefaultArtifact(mavenCoordinates);
getConsoleLogger().debug("Resolving artifact " + mavenCoordinates);
DependencyFilter filter = DependencyFilterUtils.classpathFilter(JavaScopes.RUNTIME);
List<ArtifactResult> results;
try {
results = surefireDependencyResolver.resolveDependencies(
repoSession,
project.getRemoteProjectRepositories(),
new Dependency(resolverArtifact, null),
filter);
} catch (DependencyResolutionException e) {
throw new IllegalStateException(e);
}
Collection<File> files = results.stream()
.map(ArtifactResult::getArtifact)
.map(org.eclipse.aether.artifact.Artifact::getFile)
.collect(Collectors.toSet());
getConsoleLogger()
.debug("Resolved artifact " + mavenCoordinates + " added the following files to the classpath: "
+ files);
return files;
}

/**
Expand Down Expand Up @@ -3474,6 +3544,14 @@ public void setAdditionalClasspathElements(String[] additionalClasspathElements)
this.additionalClasspathElements = additionalClasspathElements;
}

public String[] getAdditionalClasspathArtifacts() {
return additionalClasspathArtifacts;
}

public void setAdditionalClasspathArtifacts(String[] additionalClasspathArtifacts) {
this.additionalClasspathArtifacts = additionalClasspathArtifacts;
}

public String[] getClasspathDependencyExcludes() {
return classpathDependencyExcludes;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.DependencyRequest;
Expand Down Expand Up @@ -143,17 +144,9 @@ private Set<Artifact> resolveDependencies(
throws MojoExecutionException {

try {

CollectRequest collectRequest = new CollectRequest();
collectRequest.setRoot(dependency);
collectRequest.setRepositories(repositories);

DependencyRequest request = new DependencyRequest();
request.setCollectRequest(collectRequest);
request.setFilter(DependencyFilterUtils.classpathFilter(JavaScopes.RUNTIME));

DependencyResult dependencyResult = repositorySystem.resolveDependencies(session, request);
return dependencyResult.getArtifactResults().stream()
List<ArtifactResult> results = resolveDependencies(
session, repositories, dependency, DependencyFilterUtils.classpathFilter(JavaScopes.RUNTIME));
return results.stream()
.map(ArtifactResult::getArtifact)
.map(RepositoryUtils::toArtifact)
.collect(Collectors.toSet());
Expand All @@ -163,6 +156,25 @@ private Set<Artifact> resolveDependencies(
}
}

public List<ArtifactResult> resolveDependencies(
RepositorySystemSession session,
List<RemoteRepository> repositories,
org.eclipse.aether.graph.Dependency dependency,
DependencyFilter dependencyFilter)
throws DependencyResolutionException {

CollectRequest collectRequest = new CollectRequest();
collectRequest.setRoot(dependency);
collectRequest.setRepositories(repositories);

DependencyRequest request = new DependencyRequest();
request.setCollectRequest(collectRequest);
request.setFilter(dependencyFilter);

DependencyResult dependencyResult = repositorySystem.resolveDependencies(session, request);
return dependencyResult.getArtifactResults();
}

@Nonnull
Set<Artifact> getProviderClasspath(
RepositorySystemSession session,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ final class TestClassPath {
private final Iterable<Artifact> artifacts;
private final File classesDirectory;
private final File testClassesDirectory;
private final String[] additionalClasspathElements;
private final Iterable<String> additionalClasspathElements;

TestClassPath(
Iterable<Artifact> artifacts,
File classesDirectory,
File testClassesDirectory,
String[] additionalClasspathElements) {
Iterable<String> additionalClasspathElements) {
this.artifacts = artifacts;
this.classesDirectory = classesDirectory;
this.testClassesDirectory = testClassesDirectory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ private StartupConfiguration startupConfigurationForProvider(ProviderInfo provid
File classesDir = mockFile("classes");
File testClassesDir = mockFile("test-classes");
TestClassPath testClassPath =
new TestClassPath(new ArrayList<Artifact>(), classesDir, testClassesDir, new String[0]);
new TestClassPath(new ArrayList<Artifact>(), classesDir, testClassesDir, Collections.emptyList());

Artifact common = new DefaultArtifact(
"org.apache.maven.surefire",
Expand Down Expand Up @@ -711,7 +711,7 @@ public void shouldCreateStartupConfigWithModularPath() throws Exception {
File testClassesDirectory = new File(baseDir, "mock-dir");
mojo.setTestClassesDirectory(testClassesDirectory);
TestClassPath testClassPath = new TestClassPath(
Collections.<Artifact>emptySet(), classesDirectory, testClassesDirectory, new String[0]);
Collections.<Artifact>emptySet(), classesDirectory, testClassesDirectory, Collections.emptyList());

ProviderInfo providerInfo = mock(ProviderInfo.class);
when(providerInfo.getProviderName()).thenReturn("provider mock");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,39 @@ Additional Classpath Elements
</project>
+---+

Since version 3.2.0 the <<<additionalClasspathArtifacts>>> parameter can be used to add arbitrary artifacts to your test execution classpath (via their regular Maven coordinates).
Those are resolved from the repository like regular Maven dependencies and afterwards added as additional classpath elements to the end of the classpath, so you cannot use these to
override project dependencies or resources (except those which are filtered with <<<classpathDependencyExclude>>>).
Note that even transitive dependencies (both <<<compile>>> + <<<runtime>>> scope) are added implicitly.

+---+
<project>
[...]
<build>
<plugins>
<plugin>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<configuration>
<additionalClasspathArtifacts>
<additionalClasspathArtifact>myGroupId:myArtifactId:1.0.0</additionalClasspathArtifact>
<additionalClasspathArtifact>myGroupId:myOtherArtifactId:1.2.0</additionalClasspathArtifact>
</additionalClasspathArtifacts>
</configuration>
</plugin>
</plugins>
</build>
[...]
</project>
+---+
Removing Dependency Classpath Elements

Dependencies can be removed from the test classpath using the parameters <<<classpathDependencyExcludes>>> and
<<<classpathDependencyScopeExclude>>>. A list of specific dependencies can be removed from the
classpath by specifying the <<<groupId:artifactId>>> to be removed.
classpath by specifying the <<<groupId:artifactId>>> to be removed. Details of the pattern matching mechanism
are outlined in the goal parameter description for <<<classpathDependencyScopeExcludes>>>.
It is important to note that this filtering is only applied to the effective project dependencies (this includes transitive project dependencies).

+---+
<project>
Expand Down

0 comments on commit b4ae9f0

Please sign in to comment.