# HG changeset patch # User dfuchs # Date 1573143482 0 # Node ID 7f1daafda27b6b56b7ae365fe51ef8781e9f6751 # Parent 3c2e49d43ba30e70e13eb01cd5249b3088816fcf 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 diff -r 3c2e49d43ba3 -r 7f1daafda27b test/jdk/java/net/httpclient/AbstractThrowingPublishers.java --- 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)); diff -r 3c2e49d43ba3 -r 7f1daafda27b test/jdk/java/net/httpclient/AbstractThrowingPushPromises.java --- 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 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 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())); } diff -r 3c2e49d43ba3 -r 7f1daafda27b test/jdk/java/net/httpclient/AbstractThrowingSubscribers.java --- 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; diff -r 3c2e49d43ba3 -r 7f1daafda27b test/jdk/java/net/httpclient/ShortRequestBody.java --- 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 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) { diff -r 3c2e49d43ba3 -r 7f1daafda27b test/jdk/java/net/httpclient/ShortResponseBody.java --- 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 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); + } } } }