Skip to content

Commit

Permalink
Merge pull request #746 from Vlatombe/web-fragment-new-hook
Browse files Browse the repository at this point in the history
Introduce new hook when jetty 12 hook doesn't trigger
  • Loading branch information
jglick authored Jan 24, 2025
2 parents e9ccae0 + dcf5d2e commit 44cae36
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 79 deletions.
160 changes: 160 additions & 0 deletions src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package org.jenkins.tools.test.hook;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.util.VersionNumber;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.stream.Collectors;
import org.jenkins.tools.test.exception.PluginCompatibilityTesterException;
import org.jenkins.tools.test.exception.PomExecutionException;
import org.jenkins.tools.test.maven.ExpressionEvaluator;
import org.jenkins.tools.test.maven.ExternalMavenRunner;
import org.jenkins.tools.test.model.hook.BeforeExecutionContext;
import org.jenkins.tools.test.model.hook.PluginCompatTesterHookBeforeExecution;
import org.kohsuke.MetaInfServices;

@MetaInfServices(PluginCompatTesterHookBeforeExecution.class)
public class JenkinsTestHarnessHook extends PluginCompatTesterHookBeforeExecution {
public static final String VERSION_WITH_WEB_FRAGMENTS = "2386.v82359624ea_05";
public static final String VERSION_BACKPORT_2244 = "2244.2247.ve6b_a_8191b_95f";
public static final String VERSION_BACKPORT_2270 = "2270.2272.vd890c8c611b_3";
public static final List<String> VALID_VERSIONS =
List.of(VERSION_BACKPORT_2244, VERSION_BACKPORT_2270, VERSION_WITH_WEB_FRAGMENTS);
private static final String PROPERTY_NAME = "jenkins-test-harness.version";

private static boolean usesWebFragment(@NonNull BeforeExecutionContext context) {
// We only want this hook to be enabled if the Jetty 12 hook is not enabled at the same time
var war = context.getConfig().getWar();
try (var jarFile = new JarFile(war)) {
var jenkinsCoreEntry = getJenkinsCoreEntry(jarFile);
if (jenkinsCoreEntry == null) {

Check warning on line 36 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 36 is only partially covered, one branch is missing
throw new IllegalArgumentException("Failed to find jenkins-core jar in " + war);

Check warning on line 37 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 37 is not covered by tests
}
try (var is = jarFile.getInputStream(jenkinsCoreEntry);
var bis = new BufferedInputStream(is);
var jis = new JarInputStream(bis)) {
return hasWebFragment(jis);
}
} catch (IOException e) {
throw new UncheckedIOException("Failed to determine whether " + war + " uses web fragments", e);

Check warning on line 45 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 44-45 are not covered by tests
}
}

@NonNull
private static VersionNumber getPropertyAsVersion(
@NonNull BeforeExecutionContext context, @NonNull String propertyName) throws PomExecutionException {
var expressionEvaluator = new ExpressionEvaluator(
context.getCloneDirectory(),
context.getPlugin().getModule(),
new ExternalMavenRunner(context.getConfig()));
return new VersionNumber(expressionEvaluator.evaluateString(propertyName));
}

private static boolean hasWebFragment(JarInputStream jis) throws IOException {
for (var entry = jis.getNextEntry(); entry != null; entry = jis.getNextEntry()) {
if ("META-INF/web-fragment.xml".equals(entry.getName())) {

Check warning on line 61 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 61 is only partially covered, one branch is missing
return true;

Check warning on line 62 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 62 is not covered by tests
}
}
return false;
}

private static JarEntry getJenkinsCoreEntry(JarFile jarFile) {
for (var entries = jarFile.entries(); entries.hasMoreElements(); ) {

Check warning on line 69 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 69 is only partially covered, one branch is missing
var entry = entries.nextElement();
if (entry.getName().startsWith("WEB-INF/lib/jenkins-core-")) {
return entry;
}
}
return null;

Check warning on line 75 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 75 is not covered by tests
}

private static VersionNumber getWinstoneVersion(File war) {
try (var jarFile = new JarFile(war)) {
var zipEntry = jarFile.getEntry("executable/winstone.jar");
if (zipEntry == null) {

Check warning on line 81 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 81 is only partially covered, one branch is missing
throw new IllegalArgumentException("Failed to find winstone.jar in " + war);

Check warning on line 82 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 82 is not covered by tests
}
try (var is = jarFile.getInputStream(zipEntry);
var bis = new BufferedInputStream(is);
var jis = new JarInputStream(bis)) {
var manifest = jis.getManifest();
if (manifest == null) {

Check warning on line 88 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 88 is only partially covered, one branch is missing
throw new IllegalArgumentException("Failed to read manifest in " + war);

Check warning on line 89 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 89 is not covered by tests
}
var version = manifest.getMainAttributes().getValue("Implementation-Version");
if (version == null) {

Check warning on line 92 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 92 is only partially covered, one branch is missing
throw new IllegalArgumentException("Failed to read Winstone version from manifest in " + war);

Check warning on line 93 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 93 is not covered by tests
}
return new VersionNumber(version);
}
} catch (IOException e) {
throw new UncheckedIOException("Failed to read Winstone version in " + war, e);

Check warning on line 98 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 97-98 are not covered by tests
}
}

/**
* Determines the version of Jenkins Test Harness to use depending on the original version.
*/
@NonNull
static VersionNumber determineNextVersion(@NonNull VersionNumber version) {
var first = true;
VersionNumber older = null;
for (var validVersion : VALID_VERSIONS.stream().map(VersionNumber::new).collect(Collectors.toList())) {
if (first) {
if (validVersion.isNewerThan(version)) {
return validVersion;
}
first = false;
}
if (validVersion.isOlderThan(version)) {
older = validVersion;
} else {
if (older != null) {

Check warning on line 119 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 119 is only partially covered, one branch is missing
return validVersion;
}
}
}
// Keep the version as is. If that happens, it means check() method returned true by mistake.
return version;
}

@Override
public boolean check(@NonNull BeforeExecutionContext context) {
var winstoneVersion = getWinstoneVersion(context.getConfig().getWar());
if (winstoneVersion.getDigitAt(0) < 7) {

Check warning on line 131 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 131 is only partially covered, one branch is missing
// Don't upgrade anything if winstone version is too old.
return false;

Check warning on line 133 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 133 is not covered by tests
}
try {
var existingVersion = getPropertyAsVersion(context, PROPERTY_NAME);
// If core uses web fragments, we need a version of jth with web fragments support
if (!usesWebFragment(context)) {

Check warning on line 138 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 138 is only partially covered, one branch is missing
return existingVersion.isOlderThan(new VersionNumber(VERSION_WITH_WEB_FRAGMENTS));
} else {
return VALID_VERSIONS.stream().map(VersionNumber::new).anyMatch(existingVersion::equals)
|| existingVersion.isOlderThan(new VersionNumber(VERSION_WITH_WEB_FRAGMENTS));
}
} catch (PomExecutionException e) {
return false;

Check warning on line 145 in src/main/java/org/jenkins/tools/test/hook/JenkinsTestHarnessHook.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 141-145 are not covered by tests
}
}

@Override
public void action(@NonNull BeforeExecutionContext context) throws PluginCompatibilityTesterException {
var version = getPropertyAsVersion(context, PROPERTY_NAME);
context.getArgs().add(String.format("-D%s=%s", PROPERTY_NAME, determineNextVersion(version)));
/*
* The version of JUnit 5 used at runtime must match the version of JUnit 5 used to compile the tests, but the
* inclusion of a newer test harness might cause the HPI plugin to try to use a newer version of JUnit 5 at
* runtime to satisfy upper bounds checks, so exclude JUnit 5 from upper bounds analysis.
*/
context.getUpperBoundsExcludes().add("org.junit.jupiter:junit-jupiter-api");
}
}
77 changes: 0 additions & 77 deletions src/main/java/org/jenkins/tools/test/hook/Jetty12Hook.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@ public abstract class PropertyVersionHook extends PluginCompatTesterHookBeforeEx

@Override
public boolean check(@NonNull BeforeExecutionContext context) {
return check(context, getProperty(), getMinimumVersion());
}

static boolean check(BeforeExecutionContext context, String property, String minimumVersion) {
MavenRunner runner = new ExternalMavenRunner(context.getConfig());
ExpressionEvaluator expressionEvaluator = new ExpressionEvaluator(
context.getCloneDirectory(), context.getPlugin().getModule(), runner);
try {
String version = expressionEvaluator.evaluateString(getProperty());
return new VersionNumber(version).isOlderThan(new VersionNumber(getMinimumVersion()));
String version = expressionEvaluator.evaluateString(property);
return new VersionNumber(version).isOlderThan(new VersionNumber(minimumVersion));
} catch (PomExecutionException e) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.jenkins.tools.test.hook;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasToString;

import hudson.util.VersionNumber;
import org.junit.jupiter.api.Test;

class JenkinsTestHarnessHookTest {
@Test
void nextVersion() {
assertThat(
JenkinsTestHarnessHook.determineNextVersion(new VersionNumber("2243")),
hasToString(JenkinsTestHarnessHook.VERSION_BACKPORT_2244));
assertThat(
JenkinsTestHarnessHook.determineNextVersion(new VersionNumber("2244")),
hasToString(JenkinsTestHarnessHook.VERSION_BACKPORT_2244));
assertThat(
JenkinsTestHarnessHook.determineNextVersion(new VersionNumber("2269")),
hasToString(JenkinsTestHarnessHook.VERSION_BACKPORT_2270));
assertThat(
JenkinsTestHarnessHook.determineNextVersion(new VersionNumber("2270")),
hasToString(JenkinsTestHarnessHook.VERSION_BACKPORT_2270));
assertThat(
JenkinsTestHarnessHook.determineNextVersion(new VersionNumber("2271")),
hasToString(JenkinsTestHarnessHook.VERSION_WITH_WEB_FRAGMENTS));
assertThat(JenkinsTestHarnessHook.determineNextVersion(new VersionNumber("2387")), hasToString("2387"));
}
}

0 comments on commit 44cae36

Please sign in to comment.