Skip to content

Commit

Permalink
Introduce RealJenkinsRule.createSyntheticPlugin to allow synthetic …
Browse files Browse the repository at this point in the history
…plugins to be installed dynamically and break compatibility for existing callers of `RealJenkinsRule.addSyntheticPlugin` (#920)
  • Loading branch information
dwnusbaum authored Feb 19, 2025
1 parent 052a595 commit 5992ad4
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 19 deletions.
52 changes: 35 additions & 17 deletions src/main/java/org/jvnet/hudson/test/RealJenkinsRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -293,13 +293,32 @@ public RealJenkinsRule addPlugins(String... plugins) {
* (You will need to {@code .header("Plugin-Dependencies", "variant:0")} to use this API.)
* Then use {@code @OptionalExtension} on all your test extensions.
* These will then be loaded only in {@link RealJenkinsRule}-based tests requesting this plugin.
* @param pkg the Java package containing any classes and resources you want included
* @return a builder
* @param plugin the configured {@link SyntheticPlugin}
*/
public SyntheticPlugin addSyntheticPlugin(Package pkg) {
SyntheticPlugin p = new SyntheticPlugin(pkg.getName());
syntheticPlugins.add(p);
return p;
public RealJenkinsRule addSyntheticPlugin(SyntheticPlugin plugin) {
syntheticPlugins.add(plugin);
return this;
}

/**
* Creates a test-only plugin based on sources defined in this module, but does not install it.
* <p>See {@link #addSyntheticPlugin} for more details. Prefer that method if you simply want the
* plugin to be installed automatically.
* @see #addSyntheticPlugin
* @param plugin the configured {@link SyntheticPlugin}
* @return the JPI file for the plugin
*/
@SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "irrelevant, this is test code")
public File createSyntheticPlugin(SyntheticPlugin plugin) throws IOException, URISyntaxException {
File pluginJpi = new File(tmp.allocate("synthetic-plugin"), plugin.shortName + ".jpi");
if (war == null) {
throw new IllegalStateException("createSyntheticPlugin may only be invoked from within a test method");
}
try (JarFile jf = new JarFile(war)) {
String jenkinsVersion = jf.getManifest().getMainAttributes().getValue("Jenkins-Version");
plugin.writeTo(pluginJpi, jenkinsVersion);
}
return pluginJpi;
}

/**
Expand Down Expand Up @@ -1700,15 +1719,21 @@ private static class OutputPayload implements Serializable {
* Alternative to {@link #addPlugins} or {@link TestExtension} that lets you build a test-only plugin on the fly.
* ({@link ExtensionList#add(Object)} can also be used for certain cases, but not if you need to define new types.)
*/
public final class SyntheticPlugin {
public static final class SyntheticPlugin {
private final String pkg;
private String shortName;
private String version = "1-SNAPSHOT";
private Map<String, String> headers = new HashMap<>();

SyntheticPlugin(String pkg) {
this.pkg = pkg;
shortName = "synthetic-" + pkg.replace('.', '-');
/**
* Creates a new synthetic plugin builder.
* @see RealJenkinsRule#addSyntheticPlugin
* @see RealJenkinsRule#createSyntheticPlugin
* @param pkg the Java package containing any classes and resources you want included
*/
public SyntheticPlugin(Package pkg) {
this.pkg = pkg.getName();
shortName = "synthetic-" + this.pkg.replace('.', '-');
}

/**
Expand Down Expand Up @@ -1744,13 +1769,6 @@ public SyntheticPlugin header(String key, String value) {
return this;
}

/**
* @return back to the rule builder
*/
public RealJenkinsRule done() {
return RealJenkinsRule.this;
}

void writeTo(File jpi, String defaultJenkinsVersion) throws IOException, URISyntaxException {
var mani = new Manifest();
var attr = mani.getMainAttributes();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import static org.hamcrest.Matchers.is;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.RealJenkinsRule.SyntheticPlugin;
import org.jvnet.hudson.test.sample.plugin.CustomJobProperty;
import org.jvnet.hudson.test.sample.plugin.Stuff;

Expand All @@ -40,7 +41,7 @@ public final class RealJenkinsRuleSyntheticPluginTest {
@Rule public RealJenkinsRule rr = new RealJenkinsRule().prepareHomeLazily(true);

@Test public void smokes() throws Throwable {
rr.addSyntheticPlugin(Stuff.class.getPackage()).done();
rr.addSyntheticPlugin(new SyntheticPlugin(Stuff.class.getPackage()));
rr.then(RealJenkinsRuleSyntheticPluginTest::_smokes);
}

Expand All @@ -50,12 +51,20 @@ private static void _smokes(JenkinsRule r) throws Throwable {
}

@Test public void classFilter() throws Throwable {
rr.addSyntheticPlugin(CustomJobProperty.class.getPackage()).done().withLogger(ClassFilterImpl.class, Level.FINE);
rr.addSyntheticPlugin(new SyntheticPlugin(CustomJobProperty.class.getPackage())).withLogger(ClassFilterImpl.class, Level.FINE);
rr.then(r -> {
var p = r.createFreeStyleProject();
p.addProperty(new CustomJobProperty("expected in XML"));
assertThat(p.getConfigFile().asString(), containsString("expected in XML"));
});
}

@Test public void dynamicLoad() throws Throwable {
var pluginJpi = rr.createSyntheticPlugin(new SyntheticPlugin(Stuff.class.getPackage()));
rr.then(r -> {
r.jenkins.pluginManager.dynamicLoad(pluginJpi);
assertThat(r.createWebClient().goTo("stuff", "text/plain").getWebResponse().getContentAsString(),
is(Jenkins.get().getLegacyInstanceId()));
});
}
}

0 comments on commit 5992ad4

Please sign in to comment.