8233403: Improve verbosity of some httpclient tests

Improve the verbosity of some httpclient tests to help diagnosis of intermittent failures. Also fixes ShortRequestBody test.

Reviewed-by: chegar
This commit is contained in:
Daniel Fuchs 2019-11-07 16:18:02 +00:00
parent 24bff84cb3
commit eaba9fe23b
5 changed files with 167 additions and 46 deletions

View File

@ -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.HttpServer;
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 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
}
}
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 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
}
@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 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
}
@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 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
}
@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 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
}
@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));

View File

@ -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 @@ public abstract class AbstractThrowingPushPromises implements HttpServerAdapters
}
}
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 @@ public abstract class AbstractThrowingPushPromises implements HttpServerAdapters
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()));
}

View File

@ -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.HttpServer;
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 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters
}
}
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 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters
}
@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;

View File

@ -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.InetAddress;
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 @@ public class ShortRequestBody {
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 @@ public class ShortRequestBody {
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 @@ public class ShortRequestBody {
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 @@ public class ShortRequestBody {
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 @@ public class ShortRequestBody {
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) {

View File

@ -57,6 +57,8 @@ import java.util.concurrent.ThreadFactory;
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 @@ public class ShortResponseBody {
};
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 @@ public class ShortResponseBody {
}
@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 @@ public class ShortResponseBody {
{ 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 @@ public class ShortResponseBody {
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 @@ public class ShortResponseBody {
((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 @@ public class ShortResponseBody {
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);
}
}
}
}