8233403: Improve verbosity of some httpclient tests
authordfuchs
Thu, 07 Nov 2019 16:18:02 +0000
changeset 58968 7f1daafda27b
parent 58967 3c2e49d43ba3
child 58969 a4430bb9f97d
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
test/jdk/java/net/httpclient/AbstractThrowingPublishers.java
test/jdk/java/net/httpclient/AbstractThrowingPushPromises.java
test/jdk/java/net/httpclient/AbstractThrowingSubscribers.java
test/jdk/java/net/httpclient/ShortRequestBody.java
test/jdk/java/net/httpclient/ShortResponseBody.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));
--- 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);
+                    }
                 }
             }
         }