diff --git a/src/main/java/org/jvnet/hudson/test/JenkinsRule.java b/src/main/java/org/jvnet/hudson/test/JenkinsRule.java index 193eeca52..739c838b9 100644 --- a/src/main/java/org/jvnet/hudson/test/JenkinsRule.java +++ b/src/main/java/org/jvnet/hudson/test/JenkinsRule.java @@ -693,8 +693,11 @@ public Thread newThread(Runnable r) { // use a bigger buffer as Stapler traces can get pretty large on deeply nested URL config.setRequestHeaderSize(12 * 1024); connector.setHost("localhost"); - if (System.getProperty("port")!=null) + if (System.getProperty("port") != null) { connector.setPort(Integer.parseInt(System.getProperty("port"))); + } else if (localPort != 0) { + connector.setPort(localPort); + } server.addConnector(connector); server.start(); diff --git a/src/main/java/org/jvnet/hudson/test/RestartableJenkinsRule.java b/src/main/java/org/jvnet/hudson/test/RestartableJenkinsRule.java index 8346a02b8..6dc2edee4 100644 --- a/src/main/java/org/jvnet/hudson/test/RestartableJenkinsRule.java +++ b/src/main/java/org/jvnet/hudson/test/RestartableJenkinsRule.java @@ -10,6 +10,8 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.ServerSocket; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.LinkOption; @@ -62,8 +64,38 @@ public class RestartableJenkinsRule implements MethodRule { */ public File home; + /** + * TCP/IP port that the server is listening on. + */ + private final int port; + private static final Logger LOGGER = Logger.getLogger(HudsonTestCase.class.getName()); + public static class Builder { + private int port; + + public Builder() { + this.port = 0; + } + + public Builder withReusedPort() { + this.port = getRandomPort(); + return this; + } + + public RestartableJenkinsRule build() { + return new RestartableJenkinsRule(this.port); + } + } + + public RestartableJenkinsRule() { + this.port = 0; + } + + private RestartableJenkinsRule(int port) { + this.port = port; + } + @Override public Statement apply(final Statement base, FrameworkMethod method, Object target) { this.description = Description.createTestDescription( @@ -279,7 +311,18 @@ private void run() throws Throwable { } protected JenkinsRule createJenkinsRule(Description description) { - return new JenkinsRule(); + JenkinsRule result = new JenkinsRule(); + if (port != 0) { + result.localPort = port; + } + return result; } + private static synchronized int getRandomPort() { + try (ServerSocket s = new ServerSocket(0)) { + return s.getLocalPort(); + } catch (IOException e) { + throw new UncheckedIOException("Unable to find free port", e); + } + } } diff --git a/src/test/java/org/jvnet/hudson/test/RestartableJenkinsRuleTest.java b/src/test/java/org/jvnet/hudson/test/RestartableJenkinsRuleTest.java new file mode 100644 index 000000000..2721b3c22 --- /dev/null +++ b/src/test/java/org/jvnet/hudson/test/RestartableJenkinsRuleTest.java @@ -0,0 +1,55 @@ +package org.jvnet.hudson.test; + +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assume.assumeThat; + +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.Rule; +import org.junit.Test; + +public class RestartableJenkinsRuleTest { + + @Rule public RestartableJenkinsRule noPortReuse = new RestartableJenkinsRule(); + + @Rule + public RestartableJenkinsRule portReuse = + new RestartableJenkinsRule.Builder().withReusedPort().build(); + + @Test + public void testNoPortReuse() throws Exception { + assumeThat( + "This test requires a custom port to not be set.", + System.getProperty("port"), + nullValue()); + + AtomicInteger port = new AtomicInteger(); + noPortReuse.then( + s -> { + port.set(noPortReuse.j.getURL().getPort()); + }); + noPortReuse.then( + s -> { + assertNotEquals(port.get(), noPortReuse.j.getURL().getPort()); + }); + } + + @Test + public void testPortReuse() throws Exception { + assumeThat( + "This test requires a custom port to not be set.", + System.getProperty("port"), + nullValue()); + + AtomicInteger port = new AtomicInteger(); + portReuse.then( + s -> { + port.set(portReuse.j.getURL().getPort()); + }); + portReuse.then( + s -> { + assertEquals(port.get(), portReuse.j.getURL().getPort()); + }); + } +}