From 01120eb2f0c2b0ecb59daa4d31643e5e3a3beed2 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Tue, 9 Nov 2010 10:40:51 +0000 Subject: [PATCH] Using random port for HTTP integration tests --- .../AbstractHttpRequestFactoryTestCase.java | 25 ++--- .../http/client/FreePortScanner.java | 99 +++++++++++++++++++ .../client/RestTemplateIntegrationTests.java | 48 ++++----- 3 files changed, 139 insertions(+), 33 deletions(-) create mode 100644 org.springframework.web/src/test/java/org/springframework/http/client/FreePortScanner.java diff --git a/org.springframework.web/src/test/java/org/springframework/http/client/AbstractHttpRequestFactoryTestCase.java b/org.springframework.web/src/test/java/org/springframework/http/client/AbstractHttpRequestFactoryTestCase.java index 9f53c5d233..bb632d9f5d 100644 --- a/org.springframework.web/src/test/java/org/springframework/http/client/AbstractHttpRequestFactoryTestCase.java +++ b/org.springframework.web/src/test/java/org/springframework/http/client/AbstractHttpRequestFactoryTestCase.java @@ -30,7 +30,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.junit.AfterClass; -import static org.junit.Assert.*; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @@ -42,17 +41,21 @@ import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.util.FileCopyUtils; +import static org.junit.Assert.*; + public abstract class AbstractHttpRequestFactoryTestCase { private ClientHttpRequestFactory factory; private static Server jettyServer; - private static final String BASE_URL = "http://localhost:8889"; + private static String baseUrl; @BeforeClass public static void startJettyServer() throws Exception { - jettyServer = new Server(8889); + int port = FreePortScanner.getFreePort(); + jettyServer = new Server(port); + baseUrl = "http://localhost:" + port; Context jettyContext = new Context(jettyServer, "/"); jettyContext.addServlet(new ServletHolder(new EchoServlet()), "/echo"); jettyContext.addServlet(new ServletHolder(new StatusServlet(200)), "/status/ok"); @@ -83,7 +86,7 @@ public abstract class AbstractHttpRequestFactoryTestCase { @Test public void status() throws Exception { - URI uri = new URI(BASE_URL + "/status/notfound"); + URI uri = new URI(baseUrl + "/status/notfound"); ClientHttpRequest request = factory.createRequest(uri, HttpMethod.GET); assertEquals("Invalid HTTP method", HttpMethod.GET, request.getMethod()); @@ -94,7 +97,7 @@ public abstract class AbstractHttpRequestFactoryTestCase { @Test public void echo() throws Exception { - ClientHttpRequest request = factory.createRequest(new URI(BASE_URL + "/echo"), HttpMethod.PUT); + ClientHttpRequest request = factory.createRequest(new URI(baseUrl + "/echo"), HttpMethod.PUT); assertEquals("Invalid HTTP method", HttpMethod.PUT, request.getMethod()); String headerName = "MyHeader"; String headerValue1 = "value1"; @@ -114,7 +117,7 @@ public abstract class AbstractHttpRequestFactoryTestCase { @Test(expected = IllegalStateException.class) public void multipleWrites() throws Exception { - ClientHttpRequest request = factory.createRequest(new URI(BASE_URL + "/echo"), HttpMethod.POST); + ClientHttpRequest request = factory.createRequest(new URI(baseUrl + "/echo"), HttpMethod.POST); byte[] body = "Hello World".getBytes("UTF-8"); FileCopyUtils.copy(body, request.getBody()); ClientHttpResponse response = request.execute(); @@ -128,7 +131,7 @@ public abstract class AbstractHttpRequestFactoryTestCase { @Test(expected = UnsupportedOperationException.class) public void headersAfterExecute() throws Exception { - ClientHttpRequest request = factory.createRequest(new URI(BASE_URL + "/echo"), HttpMethod.POST); + ClientHttpRequest request = factory.createRequest(new URI(baseUrl + "/echo"), HttpMethod.POST); request.getHeaders().add("MyHeader", "value"); byte[] body = "Hello World".getBytes("UTF-8"); FileCopyUtils.copy(body, request.getBody()); @@ -154,7 +157,7 @@ public abstract class AbstractHttpRequestFactoryTestCase { private void assertHttpMethod(String path, HttpMethod method) throws Exception { ClientHttpResponse response = null; try { - ClientHttpRequest request = factory.createRequest(new URI(BASE_URL + "/methods/" + path), method); + ClientHttpRequest request = factory.createRequest(new URI(baseUrl + "/methods/" + path), method); response = request.execute(); assertEquals("Invalid method", path.toUpperCase(Locale.ENGLISH), request.getMethod().name()); } @@ -169,9 +172,9 @@ public abstract class AbstractHttpRequestFactoryTestCase { public void redirect() throws Exception { ClientHttpResponse response = null; try { - ClientHttpRequest request = factory.createRequest(new URI(BASE_URL + "/redirect"), HttpMethod.PUT); + ClientHttpRequest request = factory.createRequest(new URI(baseUrl + "/redirect"), HttpMethod.PUT); response = request.execute(); - assertEquals("Invalid Location value", new URI(BASE_URL + "/status/ok"), response.getHeaders().getLocation()); + assertEquals("Invalid Location value", new URI(baseUrl + "/status/ok"), response.getHeaders().getLocation()); } finally { if (response != null) { @@ -180,7 +183,7 @@ public abstract class AbstractHttpRequestFactoryTestCase { } } try { - ClientHttpRequest request = factory.createRequest(new URI(BASE_URL + "/redirect"), HttpMethod.GET); + ClientHttpRequest request = factory.createRequest(new URI(baseUrl + "/redirect"), HttpMethod.GET); response = request.execute(); assertNull("Invalid Location value", response.getHeaders().getLocation()); diff --git a/org.springframework.web/src/test/java/org/springframework/http/client/FreePortScanner.java b/org.springframework.web/src/test/java/org/springframework/http/client/FreePortScanner.java new file mode 100644 index 0000000000..3591eaca44 --- /dev/null +++ b/org.springframework.web/src/test/java/org/springframework/http/client/FreePortScanner.java @@ -0,0 +1,99 @@ +/* + * Copyright 2002-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.http.client; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.util.Random; + +import org.springframework.util.Assert; + +/** + * Utility class that finds free BSD ports for use in testing scenario's. + * + * @author Ben Hale + * @author Arjen Poutsma + */ +public abstract class FreePortScanner { + + private static final int MIN_SAFE_PORT = 1024; + + private static final int MAX_PORT = 65535; + + private static final Random random = new Random(); + + /** + * Returns the number of a free port in the default range. + */ + public static int getFreePort() { + return getFreePort(MIN_SAFE_PORT, MAX_PORT); + } + + /** + * Returns the number of a free port in the given range. + */ + public static int getFreePort(int minPort, int maxPort) { + Assert.isTrue(minPort > 0, "'minPort' must be larger than 0"); + Assert.isTrue(maxPort > minPort, "'maxPort' must be larger than minPort"); + int portRange = maxPort - minPort; + int candidatePort; + int searchCounter = 0; + do { + if (++searchCounter > portRange) { + throw new IllegalStateException( + String.format("There were no ports available in the range %d to %d", minPort, maxPort)); + } + candidatePort = getRandomPort(minPort, portRange); + } + while (!isPortAvailable(candidatePort)); + + return candidatePort; + } + + private static int getRandomPort(int minPort, int portRange) { + return minPort + random.nextInt(portRange); + } + + private static boolean isPortAvailable(int port) { + ServerSocket serverSocket; + try { + serverSocket = new ServerSocket(); + } + catch (IOException ex) { + throw new IllegalStateException("Unable to create ServerSocket.", ex); + } + + try { + InetSocketAddress sa = new InetSocketAddress(port); + serverSocket.bind(sa); + return true; + } + catch (IOException ex) { + return false; + } + finally { + try { + serverSocket.close(); + } + catch (IOException ex) { + // ignore + } + } + } + +} diff --git a/org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java b/org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java index 4ec373c1e2..ffde34977b 100644 --- a/org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java +++ b/org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java @@ -39,7 +39,6 @@ import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.junit.AfterClass; -import static org.junit.Assert.*; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @@ -56,10 +55,13 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.client.CommonsClientHttpRequestFactory; +import org.springframework.http.client.FreePortScanner; import org.springframework.util.FileCopyUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import static org.junit.Assert.*; + /** @author Arjen Poutsma */ public class RestTemplateIntegrationTests { @@ -69,20 +71,22 @@ public class RestTemplateIntegrationTests { private static String helloWorld = "H\u00e9llo W\u00f6rld"; - private static final String URI = "http://localhost:8889"; + private static String baseUrl; private static MediaType contentType; @BeforeClass public static void startJettyServer() throws Exception { - jettyServer = new Server(8889); + int port = FreePortScanner.getFreePort(); + jettyServer = new Server(port); + baseUrl = "http://localhost:" + port; Context jettyContext = new Context(jettyServer, "/"); byte[] bytes = helloWorld.getBytes("UTF-8"); contentType = new MediaType("text", "plain", Collections.singletonMap("charset", "utf-8")); jettyContext.addServlet(new ServletHolder(new GetServlet(bytes, contentType)), "/get"); jettyContext.addServlet(new ServletHolder(new GetServlet(new byte[0], contentType)), "/get/nothing"); jettyContext.addServlet( - new ServletHolder(new PostServlet(helloWorld, URI + "/post/1", bytes, contentType)), + new ServletHolder(new PostServlet(helloWorld, baseUrl + "/post/1", bytes, contentType)), "/post"); jettyContext.addServlet(new ServletHolder(new ErrorServlet(404)), "/errors/notfound"); jettyContext.addServlet(new ServletHolder(new ErrorServlet(500)), "/errors/server"); @@ -105,13 +109,13 @@ public class RestTemplateIntegrationTests { @Test public void getString() { - String s = template.getForObject(URI + "/{method}", String.class, "get"); + String s = template.getForObject(baseUrl + "/{method}", String.class, "get"); assertEquals("Invalid content", helloWorld, s); } @Test public void getEntity() { - ResponseEntity entity = template.getForEntity(URI + "/{method}", String.class, "get"); + ResponseEntity entity = template.getForEntity(baseUrl + "/{method}", String.class, "get"); assertEquals("Invalid content", helloWorld, entity.getBody()); assertFalse("No headers", entity.getHeaders().isEmpty()); assertEquals("Invalid content-type", contentType, entity.getHeaders().getContentType()); @@ -120,14 +124,14 @@ public class RestTemplateIntegrationTests { @Test public void getNoResponse() { - String s = template.getForObject(URI + "/get/nothing", String.class); + String s = template.getForObject(baseUrl + "/get/nothing", String.class); assertEquals("Invalid content", "", s); } @Test public void postForLocation() throws URISyntaxException { - URI location = template.postForLocation(URI + "/{method}", helloWorld, "post"); - assertEquals("Invalid location", new URI(URI + "/post/1"), location); + URI location = template.postForLocation(baseUrl + "/{method}", helloWorld, "post"); + assertEquals("Invalid location", new URI(baseUrl + "/post/1"), location); } @Test @@ -135,20 +139,20 @@ public class RestTemplateIntegrationTests { HttpHeaders entityHeaders = new HttpHeaders(); entityHeaders.setContentType(new MediaType("text", "plain", Charset.forName("ISO-8859-15"))); HttpEntity entity = new HttpEntity(helloWorld, entityHeaders); - URI location = template.postForLocation(URI + "/{method}", entity, "post"); - assertEquals("Invalid location", new URI(URI + "/post/1"), location); + URI location = template.postForLocation(baseUrl + "/{method}", entity, "post"); + assertEquals("Invalid location", new URI(baseUrl + "/post/1"), location); } @Test public void postForObject() throws URISyntaxException { - String s = template.postForObject(URI + "/{method}", helloWorld, String.class, "post"); + String s = template.postForObject(baseUrl + "/{method}", helloWorld, String.class, "post"); assertEquals("Invalid content", helloWorld, s); } @Test public void notFound() { try { - template.execute(URI + "/errors/notfound", HttpMethod.GET, null, null); + template.execute(baseUrl + "/errors/notfound", HttpMethod.GET, null, null); fail("HttpClientErrorException expected"); } catch (HttpClientErrorException ex) { @@ -161,7 +165,7 @@ public class RestTemplateIntegrationTests { @Test public void serverError() { try { - template.execute(URI + "/errors/server", HttpMethod.GET, null, null); + template.execute(baseUrl + "/errors/server", HttpMethod.GET, null, null); fail("HttpServerErrorException expected"); } catch (HttpServerErrorException ex) { @@ -173,20 +177,20 @@ public class RestTemplateIntegrationTests { @Test public void optionsForAllow() throws URISyntaxException { - Set allowed = template.optionsForAllow(new URI(URI + "/get")); + Set allowed = template.optionsForAllow(new URI(baseUrl + "/get")); assertEquals("Invalid response", EnumSet.of(HttpMethod.GET, HttpMethod.OPTIONS, HttpMethod.HEAD, HttpMethod.TRACE), allowed); } @Test public void uri() throws InterruptedException, URISyntaxException { - String result = template.getForObject(URI + "/uri/{query}", String.class, "Z\u00fcrich"); + String result = template.getForObject(baseUrl + "/uri/{query}", String.class, "Z\u00fcrich"); assertEquals("Invalid request URI", "/uri/Z%C3%BCrich", result); - result = template.getForObject(URI + "/uri/query={query}", String.class, "foo@bar"); + result = template.getForObject(baseUrl + "/uri/query={query}", String.class, "foo@bar"); assertEquals("Invalid request URI", "/uri/query=foo@bar", result); - result = template.getForObject(URI + "/uri/query={query}", String.class, "T\u014dky\u014d"); + result = template.getForObject(baseUrl + "/uri/query={query}", String.class, "T\u014dky\u014d"); assertEquals("Invalid request URI", "/uri/query=T%C5%8Dky%C5%8D", result); } @@ -199,7 +203,7 @@ public class RestTemplateIntegrationTests { Resource logo = new ClassPathResource("/org/springframework/http/converter/logo.jpg"); parts.add("logo", logo); - template.postForLocation(URI + "/multipart", parts); + template.postForLocation(baseUrl + "/multipart", parts); } @Test @@ -208,7 +212,7 @@ public class RestTemplateIntegrationTests { requestHeaders.set("MyHeader", "MyValue"); HttpEntity requestEntity = new HttpEntity(requestHeaders); ResponseEntity response = - template.exchange(URI + "/{method}", HttpMethod.GET, requestEntity, String.class, "get"); + template.exchange(baseUrl + "/{method}", HttpMethod.GET, requestEntity, String.class, "get"); assertEquals("Invalid content", helloWorld, response.getBody()); } @@ -218,8 +222,8 @@ public class RestTemplateIntegrationTests { requestHeaders.set("MyHeader", "MyValue"); requestHeaders.setContentType(MediaType.TEXT_PLAIN); HttpEntity requestEntity = new HttpEntity(helloWorld, requestHeaders); - HttpEntity result = template.exchange(URI + "/{method}", HttpMethod.POST, requestEntity, null, "post"); - assertEquals("Invalid location", new URI(URI + "/post/1"), result.getHeaders().getLocation()); + HttpEntity result = template.exchange(baseUrl + "/{method}", HttpMethod.POST, requestEntity, null, "post"); + assertEquals("Invalid location", new URI(baseUrl + "/post/1"), result.getHeaders().getLocation()); assertFalse(result.hasBody()); }