Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ target/
.classpath
.settings
.project
nbactions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
invoker.goals=test
51 changes: 51 additions & 0 deletions src/it/override-test-dependencies-smokes/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>2.30</version>
<relativePath/>
</parent>
<groupId>org.jenkins-ci.tools.hpi.its</groupId>
<artifactId>override-test-dependencies-smokes</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>hpi</packaging>
<properties>
<jenkins.version>1.642.3</jenkins.version>
<hpi-plugin.version>@project.version@</hpi-plugin.version>
<!-- TODO cannot include `invoker.goals=-DoverrideVersions=…,… test` in invoker.properties since then that is misinterpreted as multiple arguments for invoker.goals -->
<overrideVersions>org.jenkins-ci.plugins.workflow:workflow-api:2.17,org.jenkins-ci.plugins.workflow:workflow-cps:2.32</overrideVersions>
<!-- TODO <useUpperBounds>true</useUpperBounds> -->
<test>SampleTest</test>
<maven.test.redirectTestOutputToFile>false</maven.test.redirectTestOutputToFile>
<surefire.rerunFailingTestsCount>0</surefire.rerunFailingTestsCount>
</properties>
<repositories>
<repository>
<id>repo.jenkins-ci.org</id>
<url>https://repo.jenkins-ci.org/public/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>repo.jenkins-ci.org</id>
<url>https://repo.jenkins-ci.org/public/</url>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-step-api</artifactId>
<version>2.10</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-cps</artifactId>
<version>2.30</version>
<scope>test</scope>
</dependency>
<!-- TODO also need to test a dependency with <classifier>tests</classifier>, maybe set via common property -->
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?jelly escape-by-default='true'?>
<div/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package test;

import com.google.common.collect.ImmutableMap;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.Map;
import java.util.jar.Manifest;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.jvnet.hudson.test.JenkinsRule;

public class SampleTest {

@Rule
public JenkinsRule r = new JenkinsRule();

@Test
public void smokes() throws Exception {
Map<String, String> expectedVersions = ImmutableMap.of("workflow-cps", "2.32", "workflow-api", "2.17");
Enumeration<URL> manifests = SampleTest.class.getClassLoader().getResources("META-INF/MANIFEST.MF");
while (manifests.hasMoreElements()) {
URL url = manifests.nextElement();
try (InputStream is = url.openStream()) {
Manifest mf = new Manifest(is);
String pluginName = mf.getMainAttributes().getValue("Short-Name");
String expectedVersion = expectedVersions.get(pluginName);
if (expectedVersion != null) {
assertEquals("wrong version for " + pluginName + " as classpath entry", expectedVersion, mf.getMainAttributes().getValue("Plugin-Version"));
}
}
}
for (Map.Entry<String, String> entry : expectedVersions.entrySet()) {
assertEquals("wrong version for " + entry.getKey() + " as plugin", entry.getValue(), r.jenkins.pluginManager.getPlugin(entry.getKey()).getVersion());
}
}

}
3 changes: 3 additions & 0 deletions src/it/override-test-dependencies-smokes/verify.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def log = new File(basedir, 'build.log').text
// TODO add anything needed, or delete this file
true
102 changes: 97 additions & 5 deletions src/main/java/org/jenkinsci/maven/plugins/hpi/TestDependencyMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,65 @@

import org.apache.commons.io.FileUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;

import java.io.*;
import java.util.Set;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.artifact.DefaultArtifact;
import org.apache.maven.artifact.resolver.AbstractArtifactResolutionException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.plugins.annotations.Parameter;

/**
* Places test-dependency plugins into somewhere the test harness can pick up.
*
* <p>
* See {@code TestPluginManager.loadBundledPlugins()} where the test harness uses it.
*
* @author Kohsuke Kawaguchi
* <p>Additionally, it may adjust the classpath for {@code surefire:test} to run tests
* against different versions of various dependencies than what was configured in the POM.
*/
@Mojo(name="resolve-test-dependencies", requiresDependencyResolution = ResolutionScope.TEST)
public class TestDependencyMojo extends AbstractHpiMojo {

/**
* List of dependency version overrides in the form {@code groupId:artifactId:version} to apply during testing.
* Must correspond to dependencies already present in the project model.
*/
@Parameter(property="overrideVersions")
private List<String> overrideVersions;

/**
* Whether to update all transitive dependencies to the upper bounds.
* Effectively causes same behavior as the {@code requireUpperBoundDeps} Enforcer rule would,
* if the specified dependencies were to be written to the POM.
* Intended for use in conjunction with {@link #overrideVersions}.
*/
@Parameter(property="useUpperBounds")
private boolean useUpperBounds;

@Override
public void execute() throws MojoExecutionException, MojoFailureException {
Map<String, String> overrides = new HashMap<>(); // groupId:artifactId → version
if (overrideVersions != null) {
for (String override : overrideVersions) {
Matcher m = Pattern.compile("([^:]+:[^:]+):([^:]+)").matcher(override);
if (!m.matches()) {
throw new MojoExecutionException("illegal override: " + override);
}
overrides.put(m.group(1), m.group(2));
}
}
File testDir = new File(project.getBuild().getTestOutputDirectory(),"test-dependencies");
testDir.mkdirs();

Expand All @@ -41,13 +79,67 @@ public void execute() throws MojoExecutionException, MojoFailureException {

getLog().debug("Copying " + artifactId + " as a test dependency");
File dst = new File(testDir, artifactId + ".hpi");
FileUtils.copyFile(a.getHpi().getFile(),dst);
File src;
String version = overrides.get(a.getGroupId() + ":" + artifactId);
if (version != null) {
src = replace(a.getHpi().artifact, version).getFile();
} else {
src = a.getHpi().getFile();
}
FileUtils.copyFile(src, dst);
w.write(artifactId + "\n");
}

w.close();
} catch (IOException e) {
throw new MojoExecutionException("Failed to copy dependency plugins",e);
}

if (overrideVersions != null) {
if (useUpperBounds) {
throw new MojoExecutionException("TODO useUpperBounds not yet supported");
}
List<String> additionalClasspathElements = new ArrayList<>();
List<String> classpathDependencyExcludes = new ArrayList<>();
for (Map.Entry<String, String> entry : overrides.entrySet()) {
String key = entry.getKey();
classpathDependencyExcludes.add(key);
String[] groupArt = key.split(":");
String groupId = groupArt[0];
String artifactId = groupArt[1];
String version = entry.getValue();
// Cannot use MavenProject.getArtifactMap since we may have multiple dependencies of different classifiers.
boolean found = false;
for (Object _a : project.getArtifacts()) {
Artifact a = (Artifact) _a;
if (!a.getGroupId().equals(groupId) || !a.getArtifactId().equals(artifactId)) {
continue;
}
found = true;
if (a.getArtifactHandler().isAddedToClasspath()) { // everything is added to test CP, so no need to check scope
additionalClasspathElements.add(replace(a, version).getFile().getAbsolutePath());
}
}
if (!found) {
throw new MojoExecutionException("could not find dependency " + key);
}
}
Properties properties = project.getProperties();
getLog().info("Replacing POM-defined classpath elements " + classpathDependencyExcludes + " with " + additionalClasspathElements);
// cf. http://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html
properties.setProperty("maven.test.additionalClasspath", StringUtils.join(additionalClasspathElements, ','));
properties.setProperty("maven.test.dependency.excludes", StringUtils.join(classpathDependencyExcludes, ','));
}
}

private Artifact replace(Artifact a, String version) throws MojoExecutionException {
Artifact a2 = new DefaultArtifact(a.getGroupId(), a.getArtifactId(), VersionRange.createFromVersion(version), a.getScope(), a.getType(), a.getClassifier(), a.getArtifactHandler(), a.isOptional());
try {
artifactResolver.resolve(a2, remoteRepos, localRepository);
} catch (AbstractArtifactResolutionException x) {
throw new MojoExecutionException("could not find " + a + " in version " + version + ": " + x, x);
}
return a2;
}

}