8233403: Improve verbosity of some httpclient tests
Summary: improve the verbosity of some httpclient tests to help diagnosis of intermittent failures. Also fixes ShortRequestBody test.
Reviewed-by: chegar
--- a/test/jdk/java/net/httpclient/AbstractThrowingPublishers.java Thu Nov 07 15:56:56 2019 +0100
+++ b/test/jdk/java/net/httpclient/AbstractThrowingPublishers.java Thu Nov 07 16:18:02 2019 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,8 +25,10 @@
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsServer;
import jdk.test.lib.net.SimpleSSLContext;
+import org.testng.ITestContext;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@@ -132,6 +134,17 @@
}
}
+ protected boolean stopAfterFirstFailure() {
+ return Boolean.getBoolean("jdk.internal.httpclient.debug");
+ }
+
+ @BeforeMethod
+ void beforeMethod(ITestContext context) {
+ if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
+ throw new RuntimeException("some tests failed");
+ }
+ }
+
@AfterClass
static final void printFailedTests() {
out.println("\n=========================");
@@ -217,7 +230,10 @@
}
@DataProvider(name = "subscribeProvider")
- public Object[][] subscribeProvider() {
+ public Object[][] subscribeProvider(ITestContext context) {
+ if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
+ return new Object[0][];
+ }
return variants(List.of(
new UncheckedCustomExceptionThrower(),
new UncheckedIOExceptionThrower()),
@@ -225,7 +241,10 @@
}
@DataProvider(name = "requestProvider")
- public Object[][] requestProvider() {
+ public Object[][] requestProvider(ITestContext context) {
+ if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
+ return new Object[0][];
+ }
return variants(List.of(
new UncheckedCustomExceptionThrower(),
new UncheckedIOExceptionThrower()),
@@ -233,7 +252,10 @@
}
@DataProvider(name = "nextRequestProvider")
- public Object[][] nextRequestProvider() {
+ public Object[][] nextRequestProvider(ITestContext context) {
+ if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
+ return new Object[0][];
+ }
return variants(List.of(
new UncheckedCustomExceptionThrower(),
new UncheckedIOExceptionThrower()),
@@ -241,28 +263,40 @@
}
@DataProvider(name = "beforeCancelProviderIO")
- public Object[][] beforeCancelProviderIO() {
+ public Object[][] beforeCancelProviderIO(ITestContext context) {
+ if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
+ return new Object[0][];
+ }
return variants(List.of(
new UncheckedIOExceptionThrower()),
EnumSet.of(Where.BEFORE_CANCEL));
}
@DataProvider(name = "afterCancelProviderIO")
- public Object[][] afterCancelProviderIO() {
+ public Object[][] afterCancelProviderIO(ITestContext context) {
+ if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
+ return new Object[0][];
+ }
return variants(List.of(
new UncheckedIOExceptionThrower()),
EnumSet.of(Where.AFTER_CANCEL));
}
@DataProvider(name = "beforeCancelProviderCustom")
- public Object[][] beforeCancelProviderCustom() {
+ public Object[][] beforeCancelProviderCustom(ITestContext context) {
+ if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
+ return new Object[0][];
+ }
return variants(List.of(
new UncheckedCustomExceptionThrower()),
EnumSet.of(Where.BEFORE_CANCEL));
}
@DataProvider(name = "afterCancelProviderCustom")
- public Object[][] afterCancelProvider() {
+ public Object[][] afterCancelProvider(ITestContext context) {
+ if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
+ return new Object[0][];
+ }
return variants(List.of(
new UncheckedCustomExceptionThrower()),
EnumSet.of(Where.AFTER_CANCEL));
--- a/test/jdk/java/net/httpclient/AbstractThrowingPushPromises.java Thu Nov 07 15:56:56 2019 +0100
+++ b/test/jdk/java/net/httpclient/AbstractThrowingPushPromises.java Thu Nov 07 16:18:02 2019 +0000
@@ -42,11 +42,12 @@
*/
import jdk.test.lib.net.SimpleSSLContext;
+import org.testng.ITestContext;
import org.testng.annotations.AfterTest;
import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
import javax.net.ssl.SSLContext;
import java.io.BufferedReader;
@@ -146,6 +147,17 @@
}
}
+ protected boolean stopAfterFirstFailure() {
+ return Boolean.getBoolean("jdk.internal.httpclient.debug");
+ }
+
+ @BeforeMethod
+ void beforeMethod(ITestContext context) {
+ if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
+ throw new RuntimeException("some tests failed");
+ }
+ }
+
@AfterClass
static final void printFailedTests() {
out.println("\n=========================");
@@ -208,27 +220,38 @@
private Object[][] variants(List<Thrower> throwers) {
String[] uris = uris();
- Object[][] result = new Object[uris.length * 2 * throwers.size()][];
+ // reduce traces by always using the same client if
+ // stopAfterFirstFailure is requested.
+ List<Boolean> sameClients = stopAfterFirstFailure()
+ ? List.of(true)
+ : List.of(false, true);
+ Object[][] result = new Object[uris.length * sameClients.size() * throwers.size()][];
int i = 0;
for (Thrower thrower : throwers) {
- for (boolean sameClient : List.of(false, true)) {
+ for (boolean sameClient : sameClients) {
for (String uri : uris()) {
result[i++] = new Object[]{uri, sameClient, thrower};
}
}
}
- assert i == uris.length * 2 * throwers.size();
+ assert i == uris.length * sameClients.size() * throwers.size();
return result;
}
@DataProvider(name = "ioVariants")
- public Object[][] ioVariants() {
+ public Object[][] ioVariants(ITestContext context) {
+ if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
+ return new Object[0][];
+ }
return variants(List.of(
new UncheckedIOExceptionThrower()));
}
@DataProvider(name = "customVariants")
- public Object[][] customVariants() {
+ public Object[][] customVariants(ITestContext context) {
+ if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
+ return new Object[0][];
+ }
return variants(List.of(
new UncheckedCustomExceptionThrower()));
}
--- a/test/jdk/java/net/httpclient/AbstractThrowingSubscribers.java Thu Nov 07 15:56:56 2019 +0100
+++ b/test/jdk/java/net/httpclient/AbstractThrowingSubscribers.java Thu Nov 07 16:18:02 2019 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,8 +25,10 @@
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsServer;
import jdk.test.lib.net.SimpleSSLContext;
+import org.testng.ITestContext;
import org.testng.annotations.AfterTest;
import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@@ -131,6 +133,17 @@
}
}
+ protected boolean stopAfterFirstFailure() {
+ return Boolean.getBoolean("jdk.internal.httpclient.debug");
+ }
+
+ @BeforeMethod
+ void beforeMethod(ITestContext context) {
+ if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
+ throw new RuntimeException("some tests failed");
+ }
+ }
+
@AfterClass
static final void printFailedTests() {
out.println("\n=========================");
@@ -182,7 +195,10 @@
}
@DataProvider(name = "variants")
- public Object[][] variants() {
+ public Object[][] variants(ITestContext context) {
+ if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
+ return new Object[0][];
+ }
String[] uris = uris();
Object[][] result = new Object[uris.length * 2 * 2][];
int i = 0;
--- a/test/jdk/java/net/httpclient/ShortRequestBody.java Thu Nov 07 15:56:56 2019 +0100
+++ b/test/jdk/java/net/httpclient/ShortRequestBody.java Thu Nov 07 16:18:02 2019 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,6 +29,7 @@
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
+import java.net.SocketException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
@@ -76,6 +77,7 @@
BYTE_ARRAY_BODY.length,
fileSize(FILE_BODY) };
static final int[] BODY_OFFSETS = new int[] { 0, +1, -1, +2, -2, +3, -3 };
+ static final String MARKER = "ShortRequestBody";
// A delegating Body Publisher. Subtypes will have a concrete body type.
static abstract class AbstractDelegateRequestBody
@@ -134,7 +136,7 @@
try (Server server = new Server()) {
for (Supplier<HttpClient> cs : clientSuppliers) {
err.println("\n---- next supplier ----\n");
- URI uri = new URI("http://localhost:" + server.getPort() + "/");
+ URI uri = new URI("http://localhost:" + server.getPort() + "/" + MARKER);
// sanity ( 6 requests to keep client and server offsets easy to workout )
success(cs, uri, new StringRequestBody(STRING_BODY, 0));
@@ -248,44 +250,56 @@
int offset = 0;
while (!closed) {
+ err.println("Server: waiting for connection");
try (Socket s = ss.accept()) {
err.println("Server: got connection");
InputStream is = s.getInputStream();
- readRequestHeaders(is);
+ try {
+ String headers = readRequestHeaders(is);
+ if (headers == null) continue;
+ } catch (SocketException ex) {
+ err.println("Ignoring unexpected exception while reading headers: " + ex);
+ ex.printStackTrace(err);
+ // proceed in order to update count etc..., even though
+ // we know that read() will fail;
+ }
byte[] ba = new byte[1024];
int length = BODY_LENGTHS[count % 3];
length += BODY_OFFSETS[offset];
err.println("Server: count=" + count + ", offset=" + offset);
err.println("Server: expecting " +length+ " bytes");
- int read = is.readNBytes(ba, 0, length);
- err.println("Server: actually read " + read + " bytes");
-
- // Update the counts before replying, to prevent the
- // client-side racing reset with this thread.
- count++;
- if (count % 6 == 0) // 6 is the number of failure requests per offset
- offset++;
- if (count % 42 == 0) {
- count = 0; // reset, for second iteration
- offset = 0;
+ int read = 0;
+ try {
+ read = is.readNBytes(ba, 0, length);
+ err.println("Server: actually read " + read + " bytes");
+ } finally {
+ // Update the counts before replying, to prevent the
+ // client-side racing reset with this thread.
+ count++;
+ if (count % 6 == 0) // 6 is the number of failure requests per offset
+ offset++;
+ if (count % 42 == 0) {
+ count = 0; // reset, for second iteration
+ offset = 0;
+ }
}
-
if (read < length) {
// no need to reply, client has already closed
// ensure closed
if (is.read() != -1)
- new AssertionError("Unexpected read");
+ new AssertionError("Unexpected read: " + read);
} else {
OutputStream os = s.getOutputStream();
err.println("Server: writing "
+ RESPONSE.getBytes(US_ASCII).length + " bytes");
os.write(RESPONSE.getBytes(US_ASCII));
}
-
- } catch (IOException e) {
- if (!closed)
- System.out.println("Unexpected" + e);
+ } catch (Throwable e) {
+ if (!closed) {
+ err.println("Unexpected: " + e);
+ e.printStackTrace();
+ }
}
}
}
@@ -306,9 +320,14 @@
static final byte[] requestEnd = new byte[] {'\r', '\n', '\r', '\n' };
// Read until the end of a HTTP request headers
- static void readRequestHeaders(InputStream is) throws IOException {
- int requestEndCount = 0, r;
+ static String readRequestHeaders(InputStream is) throws IOException {
+ int requestEndCount = 0, r, eol = -1;
+ StringBuilder headers = new StringBuilder();
while ((r = is.read()) != -1) {
+ if (r == '\r' && eol < 0) {
+ eol = headers.length();
+ }
+ headers.append((char) r);
if (r == requestEnd[requestEndCount]) {
requestEndCount++;
if (requestEndCount == 4) {
@@ -318,6 +337,11 @@
requestEndCount = 0;
}
}
+
+ if (eol <= 0) return null;
+ String requestLine = headers.toString().substring(0, eol);
+ if (!requestLine.contains(MARKER)) return null;
+ return headers.toString();
}
static int fileSize(Path p) {
--- a/test/jdk/java/net/httpclient/ShortResponseBody.java Thu Nov 07 15:56:56 2019 +0100
+++ b/test/jdk/java/net/httpclient/ShortResponseBody.java Thu Nov 07 16:18:02 2019 +0000
@@ -57,6 +57,8 @@
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Stream;
import jdk.test.lib.net.SimpleSSLContext;
+import org.testng.ITestContext;
+import org.testng.annotations.BeforeMethod;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
@@ -106,6 +108,13 @@
};
final ExecutorService service = Executors.newCachedThreadPool(factory);
+ @BeforeMethod
+ void beforeMethod(ITestContext context) {
+ if (context.getFailedTests().size() > 0) {
+ throw new RuntimeException("some tests failed");
+ }
+ }
+
@DataProvider(name = "sanity")
public Object[][] sanity() {
return new Object[][]{
@@ -129,7 +138,7 @@
}
@DataProvider(name = "uris")
- public Object[][] variants() {
+ public Object[][] variants(ITestContext context) {
String[][] cases = new String[][] {
// The length query string is the total number of bytes in the reply,
// including headers, before the server closes the connection. The
@@ -188,6 +197,13 @@
{ httpsURIClsImed, "no bytes"},
};
+ if (context.getFailedTests().size() > 0) {
+ // Shorten the log output by preventing useless
+ // skip traces to be printed for subsequent methods
+ // if one of the previous @Test method has failed.
+ return new Object[0][];
+ }
+
List<Object[]> list = new ArrayList<>();
Arrays.asList(cases).stream()
.map(e -> new Object[] {e[0], e[1], true}) // reuse client
@@ -469,7 +485,9 @@
try {
ss.close();
} catch (IOException e) {
- throw new UncheckedIOException("Unexpected", e);
+ out.println("Unexpected exception while closing server: " + e);
+ e.printStackTrace(out);
+ throw new UncheckedIOException("Unexpected: ", e);
}
}
}
@@ -494,9 +512,12 @@
((SSLSocket)s).startHandshake();
}
out.println("Server: got connection, closing immediately ");
- } catch (IOException e) {
- if (!closed)
- throw new UncheckedIOException("Unexpected", e);
+ } catch (Throwable e) {
+ if (!closed) {
+ out.println("Unexpected exception in server: " + e);
+ e.printStackTrace(out);
+ throw new RuntimeException("Unexpected: ", e);
+ }
}
}
}
@@ -565,9 +586,12 @@
os.write(responseBytes[i]);
os.flush();
}
- } catch (IOException e) {
- if (!closed)
- throw new UncheckedIOException("Unexpected", e);
+ } catch (Throwable e) {
+ if (!closed) {
+ out.println("Unexpected exception in server: " + e);
+ e.printStackTrace(out);
+ throw new RuntimeException("Unexpected: " + e, e);
+ }
}
}
}