changeset 55546:3ae57bbf9585

8226602: Test convenience reactive primitives from java.net.http with RS TCK Reviewed-by: chegar, dfuchs
author prappo
date Tue, 02 Jul 2019 13:25:51 +0100
parents 8a153a932d0f
children c7a7728eeddc
files test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodyPublishersFromPublisher.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodyPublishersNoBody.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodyPublishersOfByteArray.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodyPublishersOfByteArrays.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodyPublishersOfFile.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodyPublishersOfInputStream.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodyPublishersOfSubByteArray.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersBuffering.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersDiscarding.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersFromLineSubscriber.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersFromSubscriber.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersMapping.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfByteArray.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfByteArrayConsumer.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfFile.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfInputStream.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfLines.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfPublisher.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfPublisher1.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfPublisherPublisher.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfString.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersReplacing.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/S.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/SPublisherOfStream.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/STest.java test/jdk/java/net/httpclient/reactivestreams-tck-tests/TckDriver.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/FlowAdapters.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/Processor.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/Publisher.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/Subscriber.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/Subscription.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/example/unicast/AsyncIterablePublisher.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/example/unicast/AsyncSubscriber.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/example/unicast/InfiniteIncrementNumberPublisher.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/example/unicast/NumberIterablePublisher.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/example/unicast/RangePublisher.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/example/unicast/SyncSubscriber.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/IdentityProcessorVerification.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/PublisherVerification.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/SubscriberBlackboxVerification.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/SubscriberWhiteboxVerification.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/TestEnvironment.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/WithHelperPublisher.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/FlowPublisherVerification.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/FlowSubscriberBlackboxVerification.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/FlowSubscriberWhiteboxVerification.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/IdentityFlowProcessorVerification.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/Function.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/HelperPublisher.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/InfiniteHelperPublisher.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/NonFatal.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/Optional.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/PublisherVerificationRules.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/SubscriberBlackboxVerificationRules.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/SubscriberBufferOverflowException.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/SubscriberWhiteboxVerificationRules.java test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/TestException.java
diffstat 57 files changed, 9984 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodyPublishersFromPublisher.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowPublisherVerification;
+
+import java.net.http.HttpRequest.BodyPublishers;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Flow.Publisher;
+import java.util.stream.Stream;
+
+/* See TckDriver.java for more information */
+public class BodyPublishersFromPublisher
+        extends FlowPublisherVerification<ByteBuffer> {
+
+    public BodyPublishersFromPublisher() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Publisher<ByteBuffer> createFlowPublisher(long nElements) {
+        Stream<ByteBuffer> buffers =
+                Stream.generate(() -> S.bufferOfNRandomBytes(1024))
+                      .limit(nElements);
+        Publisher<ByteBuffer> pub = S.publisherOfStream(buffers);
+        return BodyPublishers.fromPublisher(pub);
+    }
+
+    @Override
+    public Publisher<ByteBuffer> createFailedFlowPublisher() {
+        return BodyPublishers.fromPublisher(S.newErroredPublisher());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodyPublishersNoBody.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowPublisherVerification;
+
+import java.net.http.HttpRequest.BodyPublishers;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Flow.Publisher;
+
+/* See TckDriver.java for more information */
+public class BodyPublishersNoBody
+        extends FlowPublisherVerification<ByteBuffer> {
+
+    public BodyPublishersNoBody() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Publisher<ByteBuffer> createFlowPublisher(long nElements) {
+        return BodyPublishers.noBody();
+    }
+
+    @Override
+    public Publisher<ByteBuffer> createFailedFlowPublisher() {
+        return null;
+    }
+
+    @Override
+    public long maxElementsFromPublisher() {
+        return 0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodyPublishersOfByteArray.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowPublisherVerification;
+
+import java.net.http.HttpRequest.BodyPublishers;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Flow.Publisher;
+
+/* See TckDriver.java for more information */
+public class BodyPublishersOfByteArray
+        extends FlowPublisherVerification<ByteBuffer> {
+
+    private static final int ELEMENT_SIZE = 16 * 1024;
+
+    public BodyPublishersOfByteArray() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Publisher<ByteBuffer> createFlowPublisher(long nElements) {
+        byte[] b = S.arrayOfNRandomBytes(nElements * ELEMENT_SIZE);
+        return BodyPublishers.ofByteArray(b);
+    }
+
+    @Override
+    public Publisher<ByteBuffer> createFailedFlowPublisher() {
+        return null;
+    }
+
+    @Override
+    public long maxElementsFromPublisher() {
+        return 21;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodyPublishersOfByteArrays.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowPublisherVerification;
+
+import java.net.http.HttpRequest.BodyPublishers;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.concurrent.Flow.Publisher;
+
+/* See TckDriver.java for more information */
+public class BodyPublishersOfByteArrays
+        extends FlowPublisherVerification<ByteBuffer> {
+
+    private static final int ELEMENT_SIZE = 16 * 1024;
+
+    public BodyPublishersOfByteArrays() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Publisher<ByteBuffer> createFlowPublisher(long nElements) {
+        byte[] bytes = S.arrayOfNRandomBytes(ELEMENT_SIZE);
+        return BodyPublishers.ofByteArrays(
+                Collections.nCopies((int) nElements, bytes));
+    }
+
+    @Override
+    public Publisher<ByteBuffer> createFailedFlowPublisher() {
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodyPublishersOfFile.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowPublisherVerification;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.http.HttpRequest.BodyPublishers;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.concurrent.Flow.Publisher;
+import java.util.concurrent.atomic.AtomicLong;
+
+/* See TckDriver.java for more information */
+public class BodyPublishersOfFile
+        extends FlowPublisherVerification<ByteBuffer> {
+
+    private static final int ELEMENT_SIZE = 16 * 1024;
+    private static final AtomicLong UNIQUE_NUMBERS = new AtomicLong();
+
+    public BodyPublishersOfFile() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Publisher<ByteBuffer> createFlowPublisher(long nElements) {
+        try {
+            Path f = createFile(nElements * ELEMENT_SIZE);
+            return BodyPublishers.ofFile(f);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    private static Path createFile(long nBytes) throws IOException {
+        String name = "f" + UNIQUE_NUMBERS.getAndIncrement();
+        Path f = Files.createFile(Path.of(name));
+        return Files.write(f, S.arrayOfNRandomBytes(nBytes));
+    }
+
+    @Override
+    public Publisher<ByteBuffer> createFailedFlowPublisher() {
+        return null;
+    }
+
+    @Override
+    public long maxElementsFromPublisher() {
+        return 21;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodyPublishersOfInputStream.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowPublisherVerification;
+
+import java.io.InputStream;
+import java.net.http.HttpRequest.BodyPublishers;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Flow.Publisher;
+import java.util.function.Supplier;
+
+/* See TckDriver.java for more information */
+public class BodyPublishersOfInputStream
+        extends FlowPublisherVerification<ByteBuffer> {
+
+    public BodyPublishersOfInputStream() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Publisher<ByteBuffer> createFlowPublisher(long nElements) {
+        Supplier<InputStream> s = () -> S.inputStreamOfNReads((int) nElements);
+        return BodyPublishers.ofInputStream(s);
+    }
+
+    @Override
+    public Publisher<ByteBuffer> createFailedFlowPublisher() {
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodyPublishersOfSubByteArray.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowPublisherVerification;
+
+import java.net.http.HttpRequest.BodyPublishers;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Flow.Publisher;
+
+/* See TckDriver.java for more information */
+public class BodyPublishersOfSubByteArray
+        extends FlowPublisherVerification<ByteBuffer> {
+
+    private static final int ELEMENT_SIZE = 16 * 1024;
+
+    public BodyPublishersOfSubByteArray() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Publisher<ByteBuffer> createFlowPublisher(long nElements) {
+        int prefixLen = S.randomIntUpTo(13);
+        int postfixLen = S.randomIntUpTo(17);
+        byte[] b = S.arrayOfNRandomBytes(nElements * ELEMENT_SIZE);
+        byte[] contents = new byte[prefixLen + b.length + postfixLen];
+        System.arraycopy(b, 0, contents, prefixLen, b.length);
+        return BodyPublishers.ofByteArray(contents, prefixLen, b.length);
+    }
+
+    @Override
+    public Publisher<ByteBuffer> createFailedFlowPublisher() {
+        return null;
+    }
+
+    @Override
+    public long maxElementsFromPublisher() {
+        return 21;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersBuffering.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowSubscriberBlackboxVerification;
+
+import java.net.http.HttpResponse.BodySubscribers;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.Flow.Subscriber;
+
+/* See TckDriver.java for more information */
+public class BodySubscribersBuffering
+        extends FlowSubscriberBlackboxVerification<List<ByteBuffer>> {
+
+    public BodySubscribersBuffering() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Subscriber<List<ByteBuffer>> createFlowSubscriber() {
+        return BodySubscribers.buffering(BodySubscribers.discarding(),
+                                         S.randomIntUpTo(1024) + 1);
+    }
+
+    @Override
+    public List<ByteBuffer> createElement(int element) {
+        return S.listOfBuffersFromBufferOfNBytes(element % 17);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersDiscarding.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowSubscriberBlackboxVerification;
+
+import java.net.http.HttpResponse.BodySubscribers;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.Flow.Subscriber;
+
+/* See TckDriver.java for more information */
+public class BodySubscribersDiscarding
+        extends FlowSubscriberBlackboxVerification<List<ByteBuffer>> {
+
+    public BodySubscribersDiscarding() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Subscriber<List<ByteBuffer>> createFlowSubscriber() {
+        return BodySubscribers.discarding();
+    }
+
+    @Override
+    public List<ByteBuffer> createElement(int element) {
+        return S.listOfBuffersFromBufferOfNBytes(element % 17);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersFromLineSubscriber.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowSubscriberBlackboxVerification;
+
+import java.net.http.HttpResponse.BodySubscribers;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.Flow.Subscriber;
+
+/* See TckDriver.java for more information */
+public class BodySubscribersFromLineSubscriber
+        extends FlowSubscriberBlackboxVerification<List<ByteBuffer>> {
+
+    public BodySubscribersFromLineSubscriber() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Subscriber<List<ByteBuffer>> createFlowSubscriber() {
+        return BodySubscribers.fromLineSubscriber(
+                S.nonCompliantSubscriber());
+    }
+
+    @Override
+    public List<ByteBuffer> createElement(int element) {
+        return S.scatterBuffer(
+                S.bufferOfNRandomASCIIBytes(element % 17));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersFromSubscriber.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowSubscriberBlackboxVerification;
+
+import java.net.http.HttpResponse.BodySubscribers;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.Flow.Subscriber;
+
+/* See TckDriver.java for more information */
+public class BodySubscribersFromSubscriber
+        extends FlowSubscriberBlackboxVerification<List<ByteBuffer>> {
+
+    public BodySubscribersFromSubscriber() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Subscriber<List<ByteBuffer>> createFlowSubscriber() {
+        Subscriber<List<ByteBuffer>> sub = S.nonCompliantSubscriber();
+        return BodySubscribers.fromSubscriber(sub);
+    }
+
+    @Override
+    public List<ByteBuffer> createElement(int element) {
+        return S.scatterBuffer(
+                S.bufferOfNRandomASCIIBytes(element % 17));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersMapping.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowSubscriberBlackboxVerification;
+
+import java.net.http.HttpResponse.BodySubscribers;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.Flow.Subscriber;
+
+/* See TckDriver.java for more information */
+public class BodySubscribersMapping
+        extends FlowSubscriberBlackboxVerification<List<ByteBuffer>> {
+
+    public BodySubscribersMapping() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Subscriber<List<ByteBuffer>> createFlowSubscriber() {
+        return BodySubscribers.mapping(BodySubscribers.ofByteArray(),
+                                       bytes -> bytes.length);
+    }
+
+    @Override
+    public List<ByteBuffer> createElement(int element) {
+        return S.listOfBuffersFromBufferOfNBytes(element % 17);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfByteArray.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowSubscriberBlackboxVerification;
+
+import java.net.http.HttpResponse.BodySubscribers;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.Flow.Subscriber;
+
+/* See TckDriver.java for more information */
+public class BodySubscribersOfByteArray
+        extends FlowSubscriberBlackboxVerification<List<ByteBuffer>> {
+
+    public BodySubscribersOfByteArray() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Subscriber<List<ByteBuffer>> createFlowSubscriber() {
+        return BodySubscribers.ofByteArray();
+    }
+
+    @Override
+    public List<ByteBuffer> createElement(int element) {
+        return S.listOfBuffersFromBufferOfNBytes(element % 17);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfByteArrayConsumer.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowSubscriberBlackboxVerification;
+
+import java.net.http.HttpResponse.BodySubscribers;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.Flow.Subscriber;
+
+/* See TckDriver.java for more information */
+public class BodySubscribersOfByteArrayConsumer
+        extends FlowSubscriberBlackboxVerification<List<ByteBuffer>> {
+
+    public BodySubscribersOfByteArrayConsumer() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Subscriber<List<ByteBuffer>> createFlowSubscriber() {
+        return BodySubscribers.ofByteArrayConsumer(bytes -> { });
+    }
+
+    @Override
+    public List<ByteBuffer> createElement(int element) {
+        return S.listOfBuffersFromBufferOfNBytes(element % 17);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfFile.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowSubscriberBlackboxVerification;
+
+import java.net.http.HttpResponse.BodySubscribers;
+import java.nio.ByteBuffer;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.concurrent.Flow.Subscriber;
+
+/* See TckDriver.java for more information */
+public class BodySubscribersOfFile
+        extends FlowSubscriberBlackboxVerification<List<ByteBuffer>> {
+
+    public BodySubscribersOfFile() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Subscriber<List<ByteBuffer>> createFlowSubscriber() {
+        return BodySubscribers.ofFile(Path.of("f1.bin"));
+    }
+
+    @Override
+    public List<ByteBuffer> createElement(int element) {
+        return S.listOfBuffersFromBufferOfNBytes(element % 17);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfInputStream.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowSubscriberBlackboxVerification;
+
+import java.net.http.HttpResponse.BodySubscribers;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.Flow.Subscriber;
+
+/* See TckDriver.java for more information */
+public class BodySubscribersOfInputStream
+        extends FlowSubscriberBlackboxVerification<List<ByteBuffer>> {
+
+    public BodySubscribersOfInputStream() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Subscriber<List<ByteBuffer>> createFlowSubscriber() {
+        return BodySubscribers.ofInputStream();
+    }
+
+    @Override
+    public List<ByteBuffer> createElement(int element) {
+        return S.listOfBuffersFromBufferOfNBytes(element % 17);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfLines.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowSubscriberBlackboxVerification;
+
+import java.net.http.HttpResponse.BodySubscribers;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.concurrent.Flow.Subscriber;
+
+/* See TckDriver.java for more information */
+public class BodySubscribersOfLines
+        extends FlowSubscriberBlackboxVerification<List<ByteBuffer>> {
+
+    public BodySubscribersOfLines() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Subscriber<List<ByteBuffer>> createFlowSubscriber() {
+        return BodySubscribers.ofLines(StandardCharsets.UTF_8);
+    }
+
+    @Override
+    public List<ByteBuffer> createElement(int element) {
+        return S.scatterBuffer(
+                S.bufferOfNRandomASCIIBytes(element % 17));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfPublisher.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowSubscriberBlackboxVerification;
+
+import java.net.http.HttpResponse.BodySubscriber;
+import java.net.http.HttpResponse.BodySubscribers;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.Flow.Publisher;
+import java.util.concurrent.Flow.Subscriber;
+import java.util.concurrent.Flow.Subscription;
+
+/* See TckDriver.java for more information */
+public class BodySubscribersOfPublisher
+        extends FlowSubscriberBlackboxVerification<List<ByteBuffer>> {
+
+    public BodySubscribersOfPublisher() {
+        super(new TestEnvironment(450L));
+    }
+
+    /* The reason for overriding this method is that BodySubscribers.ofPublisher
+       is somewhat tricky. It is not an independent Subscriber, but rather
+       an adaptor from Subscriber to Publisher. Until the Subscriber that
+       subscribed to that resulting Publisher requests anything, nothing
+       happens. */
+    @Override
+    public void triggerFlowRequest(
+            Subscriber<? super List<ByteBuffer>> subscriber)
+    {
+        BodySubscriber<Publisher<List<ByteBuffer>>> sub =
+                (BodySubscriber<Publisher<List<ByteBuffer>>>) subscriber;
+        CompletionStage<Publisher<List<ByteBuffer>>> body = sub.getBody();
+        Publisher<List<ByteBuffer>> pub = body.toCompletableFuture().join();
+        pub.subscribe(new Subscriber<>() {
+
+            @Override
+            public void onSubscribe(Subscription subscription) {
+                subscription.request(Integer.MAX_VALUE);
+            }
+
+            @Override public void onNext(List<ByteBuffer> item) { }
+            @Override public void onError(Throwable throwable) { }
+            @Override public void onComplete() { }
+        });
+    }
+
+    @Override
+    public Subscriber<List<ByteBuffer>> createFlowSubscriber() {
+        return BodySubscribers.ofPublisher();
+    }
+
+    @Override
+    public List<ByteBuffer> createElement(int element) {
+        return S.listOfBuffersFromBufferOfNBytes(element % 17);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfPublisher1.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowSubscriberBlackboxVerification;
+
+import java.net.http.HttpResponse.BodySubscriber;
+import java.net.http.HttpResponse.BodySubscribers;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.Flow.Publisher;
+import java.util.concurrent.Flow.Subscriber;
+import java.util.concurrent.Flow.Subscription;
+
+/* See TckDriver.java for more information */
+public class BodySubscribersOfPublisher1
+        extends FlowSubscriberBlackboxVerification<List<ByteBuffer>> {
+
+    public BodySubscribersOfPublisher1() {
+        super(new TestEnvironment(450L));
+    }
+
+    /* The reason for overriding this method is that BodySubscribers.ofPublisher
+       is somewhat tricky. It is not an independent Subscriber, but rather
+       an adaptor from Subscriber to Publisher. Until the Subscriber that
+       subscribed to that resulting Publisher requests anything, nothing
+       happens. */
+    @Override
+    public void triggerFlowRequest(
+            Subscriber<? super List<ByteBuffer>> subscriber)
+    {
+        BodySubscriber<Publisher<List<ByteBuffer>>> sub =
+                (BodySubscriber<Publisher<List<ByteBuffer>>>) subscriber;
+        CompletionStage<Publisher<List<ByteBuffer>>> body = sub.getBody();
+        Publisher<List<ByteBuffer>> pub = body.toCompletableFuture().join();
+        pub.subscribe(new Subscriber<>() {
+
+            Subscription sub;
+
+            @Override
+            public void onSubscribe(Subscription subscription) {
+                (sub = subscription).request(1);
+            }
+
+            @Override public void onNext(List<ByteBuffer> item) {
+                sub.request(1);
+            }
+
+            @Override public void onError(Throwable throwable) { }
+            @Override public void onComplete() { }
+        });
+    }
+
+    @Override
+    public Subscriber<List<ByteBuffer>> createFlowSubscriber() {
+        return BodySubscribers.ofPublisher();
+    }
+
+    @Override
+    public List<ByteBuffer> createElement(int element) {
+        return S.listOfBuffersFromBufferOfNBytes(element % 17);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfPublisherPublisher.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowPublisherVerification;
+
+import java.net.http.HttpResponse.BodySubscriber;
+import java.net.http.HttpResponse.BodySubscribers;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.Flow.Publisher;
+import java.util.stream.Stream;
+
+/* See TckDriver.java for more information */
+public class BodySubscribersOfPublisherPublisher
+        extends FlowPublisherVerification<List<ByteBuffer>> {
+
+    public BodySubscribersOfPublisherPublisher() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Publisher<List<ByteBuffer>> createFlowPublisher(long nElements) {
+        BodySubscriber<Publisher<List<ByteBuffer>>> sub =
+                BodySubscribers.ofPublisher();
+        Stream<List<ByteBuffer>> buffers =
+                Stream.generate(() -> S.listOfBuffersFromBufferOfNBytes(1024))
+                      .limit(nElements);
+        Publisher<List<ByteBuffer>> pub = S.publisherOfStream(buffers);
+        pub.subscribe(sub);
+        return sub.getBody().toCompletableFuture().join();
+    }
+
+    @Override
+    public Publisher<List<ByteBuffer>> createFailedFlowPublisher() {
+        BodySubscriber<Publisher<List<ByteBuffer>>> sub =
+                BodySubscribers.ofPublisher();
+        Publisher<List<ByteBuffer>> pub = S.newErroredPublisher();
+        pub.subscribe(sub);
+        return sub.getBody().toCompletableFuture().join();
+    }
+
+    @Override
+    public long maxElementsFromPublisher() {
+        return 21;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersOfString.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowSubscriberBlackboxVerification;
+
+import java.net.http.HttpResponse.BodySubscribers;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.concurrent.Flow.Subscriber;
+
+/* See TckDriver.java for more information */
+public class BodySubscribersOfString
+        extends FlowSubscriberBlackboxVerification<List<ByteBuffer>> {
+
+    public BodySubscribersOfString() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Subscriber<List<ByteBuffer>> createFlowSubscriber() {
+        return BodySubscribers.ofString(StandardCharsets.UTF_8);
+    }
+
+    @Override
+    public List<ByteBuffer> createElement(int element) {
+        return S.scatterBuffer(
+                S.bufferOfNRandomASCIIBytes(element % 17));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/BodySubscribersReplacing.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowSubscriberBlackboxVerification;
+
+import java.net.http.HttpResponse.BodySubscribers;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.Flow.Subscriber;
+
+/* See TckDriver.java for more information */
+public class BodySubscribersReplacing
+        extends FlowSubscriberBlackboxVerification<List<ByteBuffer>> {
+
+    public BodySubscribersReplacing() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Subscriber<List<ByteBuffer>> createFlowSubscriber() {
+        /* it doesn't matter what we are replacing with */
+        return BodySubscribers.replacing(Boolean.TRUE);
+    }
+
+    @Override
+    public List<ByteBuffer> createElement(int element) {
+        return S.listOfBuffersFromBufferOfNBytes(element % 17);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/S.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Random;
+import java.util.concurrent.Flow.Publisher;
+import java.util.concurrent.Flow.Subscriber;
+import java.util.concurrent.Flow.Subscription;
+import java.util.stream.Stream;
+
+/*
+ * S for Support.
+ *
+ * Auxiliary methods for tests that check conformance with reactive streams
+ * specification.
+ *
+ * Short name is for the sake of convenience calling this class' static methods.
+ * It could've been called Support or TckSupport, but then we would need to
+ * place this class in its own package so as to use "import static".
+ */
+public class S {
+
+    private static final Random RANDOM = new SecureRandom();
+
+    private S() { }
+
+    public static List<ByteBuffer> listOfBuffersFromBufferOfNBytes(int nBytes) {
+        return scatterBuffer(bufferOfNRandomBytes(nBytes));
+    }
+
+    /*
+     * Spreads the remaining contents of the given byte buffer across a number
+     * of buffers put into a list.
+     */
+    public static List<ByteBuffer> scatterBuffer(ByteBuffer src) {
+        List<ByteBuffer> buffers = new ArrayList<>();
+        while (src.hasRemaining()) {
+            // We do not allow empty buffers ~~~~~~~~~~~~~~~~v
+            int capacity = RANDOM.nextInt(src.remaining()) + 1;
+            ByteBuffer b = ByteBuffer.allocate(capacity);
+            for (int i = 0; i < capacity; i++) {
+                b.put(src.get());
+            }
+            b.flip();
+            buffers.add(b);
+        }
+        return List.copyOf(buffers);
+    }
+
+    public static ByteBuffer bufferOfNRandomBytes(int capacity) {
+        return ByteBuffer.wrap(arrayOfNRandomBytes(capacity));
+    }
+
+    public static byte[] arrayOfNRandomBytes(int nBytes) {
+        byte[] contents = new byte[nBytes];
+        RANDOM.nextBytes(contents);
+        return contents;
+    }
+
+    public static InputStream inputStreamOfNReads(long n) {
+        return new NReadsInputStream(n);
+    }
+
+    /*
+     * Convenience method for testing publishers.
+     */
+    public static byte[] arrayOfNRandomBytes(long nBytes) {
+        return arrayOfNRandomBytes((int) nBytes);
+    }
+
+    public static ByteBuffer bufferOfNRandomASCIIBytes(int capacity) {
+        String alphaNumeric = "abcdefghijklmnopqrstuvwxyz1234567890";
+        StringBuilder builder = new StringBuilder(capacity);
+        for (int i = 0; i < capacity; i++) {
+            int idx = RANDOM.nextInt(alphaNumeric.length());
+            builder.append(alphaNumeric.charAt(idx));
+        }
+        return ByteBuffer.wrap(builder.toString().getBytes(
+                StandardCharsets.US_ASCII));
+    }
+
+    /*
+     * Returns a simple non-compliant Subscriber.
+     *
+     * This Subscriber is useful for testing our adaptors and wrappers, to make
+     * sure they do not delegate RS compliance to the underlying (and foreign to
+     * java.net.http codebase) Subscribers, but rather comply themselves.
+     *
+     * Here's an example:
+     *
+     *     public void onSubscribe(Subscription s) {
+     *         delegate.onSubscribe(s);
+     *     }
+     *
+     * The snippet above cannot be considered a good implementation of a
+     * Subscriber if `delegate` is an unknown Subscriber. In this case the
+     * implementation should independently check all the rules from the RS spec
+     * related to subscribers.
+     */
+    public static <T> Subscriber<T> nonCompliantSubscriber() {
+        return new Subscriber<>() {
+
+            @Override
+            public void onSubscribe(Subscription subscription) {
+                subscription.request(Long.MAX_VALUE);
+            }
+
+            @Override
+            public void onNext(T item) { }
+
+            @Override
+            public void onError(Throwable throwable) { }
+
+            @Override
+            public void onComplete() { }
+        };
+    }
+
+    public static int randomIntUpTo(int bound) {
+        return RANDOM.nextInt(bound);
+    }
+
+    /*
+     * Signals an error to its subscribers immediately after subscription.
+     */
+    public static <T> Publisher<T> newErroredPublisher() {
+        return subscriber -> {
+            subscriber.onSubscribe(new Subscription() {
+                @Override
+                public void request(long n) { }
+
+                @Override
+                public void cancel() { }
+            });
+            subscriber.onError(new IOException());
+        };
+    }
+
+    /*
+     * Publishes the elements obtained from the stream and signals completion.
+     * Can be cancelled, but cannot signal an error.
+     *
+     * This trivial ad-hoc implementation of Publisher was created so as to
+     * publish lists of byte buffers. We can publish ByteBuffer, but we can't
+     * seem to publish List<ByteBuffer> since there's no readily available
+     * publisher of those, nor there's a simple adaptor.
+     */
+    public static <T> Publisher<T> publisherOfStream(Stream<? extends T> stream)
+    {
+        if (stream == null) {
+            throw new NullPointerException();
+        }
+        return new Publisher<T>() {
+            @Override
+            public void subscribe(Subscriber<? super T> subscriber) {
+                if (subscriber == null) {
+                    throw new NullPointerException();
+                }
+                Subscription subscription = new Subscription() {
+
+                    boolean inOnNext; // recursion control
+                    volatile boolean cancelled;
+                    long demand;
+                    final Iterator<? extends T> supply = stream.iterator();
+
+                    @Override
+                    public void request(long n) {
+                        demand = demand + n < 0 ? Long.MAX_VALUE : demand + n;
+                        if (inOnNext) {
+                            return;
+                        }
+                        if (cancelled)
+                            return;
+                        if (n <= 0) {
+                            cancelled = true;
+                            subscriber.onError(new IllegalArgumentException(
+                                    "non-positive subscription request"));
+                            return;
+                        }
+                        while (supply.hasNext() && demand > 0 && !cancelled) {
+                            demand--;
+                            inOnNext = true;
+                            try {
+                                T item = supply.next();
+                                subscriber.onNext(item);
+                            } finally {
+                                inOnNext = false;
+                            }
+                        }
+                        if (!supply.hasNext()) {
+                            cancelled = true;
+                            subscriber.onComplete();
+                        }
+                    }
+
+                    @Override
+                    public void cancel() {
+                        cancelled = true;
+                    }
+                };
+                subscriber.onSubscribe(subscription);
+            }
+        };
+    }
+
+    static final class NReadsInputStream extends InputStream {
+
+        private static final int EOF = -1;
+        private long readsLeft;
+
+        NReadsInputStream(long n) {
+            if (n < 0) {
+                throw new IllegalArgumentException(String.valueOf(n));
+            }
+            this.readsLeft = n;
+        }
+
+        @Override
+        public int read() {
+            if (readsLeft == 0L) {
+                return EOF;
+            }
+            readsLeft--;
+            return S.randomIntUpTo(256);
+        }
+
+        @Override
+        public int read(byte[] b, int off, int len) {
+            Objects.checkFromIndexSize(off, len, b.length);
+            // Must return 0 if len == 0,
+            // even if there are no more reads left
+            if (len == 0) {
+                return 0;
+            }
+            if (readsLeft == 0L) {
+                return EOF;
+            }
+            readsLeft--;
+            // At least one byte MUST be read, but we can read
+            // less than `len` bytes
+            int r = RANDOM.nextInt(len) + 1;
+            for (int i = 0; i < r; i++) {
+                b[i] = (byte) randomIntUpTo(256);
+            }
+            return r;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/SPublisherOfStream.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.FlowPublisherVerification;
+
+import java.util.concurrent.Flow.Publisher;
+import java.util.stream.LongStream;
+import java.util.stream.Stream;
+
+/* See TckDriver.java for more information */
+public class SPublisherOfStream
+        extends FlowPublisherVerification<Long> {
+
+    public SPublisherOfStream() {
+        super(new TestEnvironment(450L));
+    }
+
+    @Override
+    public Publisher<Long> createFlowPublisher(long nElements) {
+        Stream<Long> s = LongStream.range(0, nElements).boxed();
+        return S.publisherOfStream(s);
+    }
+
+    @Override
+    public Publisher<Long> createFailedFlowPublisher() {
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/STest.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+public class STest {
+
+    @DataProvider(name = "bufferSizes")
+    public static Object[][] bufferSizes() {
+        return new Object[][]{
+                { 1},
+                { 2},
+                { 3},
+                { 4},
+                {16},
+                {17},
+        };
+    }
+
+    @DataProvider
+    public static Object[][] inputStream() {
+        return new Object[][] {
+                {  0,   1},
+                {  1,   2},
+                {  1,   3},
+                {  1,   4},
+                {  2,   1},
+                {  2,   2},
+                {  2,   3},
+                {  2,   4},
+                {  2,  13},
+                {  3,   1},
+                {  3,   2},
+                {  3,   3},
+                {  3,   4},
+                {  3,  17},
+                {  4,   1},
+                {  4,   2},
+                {  4,   3},
+                {  4,   4},
+                {  4,   5},
+                { 13,   1},
+                { 13,   2},
+                { 13,  13},
+                { 16,  18},
+                { 17,   2},
+                {255,   1},
+                {256, 255},
+                {257, 267},
+        };
+    }
+
+    @Test
+    public void testScatter0() {
+        List<ByteBuffer> buffers = S.scatterBuffer(
+                ByteBuffer.allocate(0));
+        assertEquals(buffers.size(), 0);
+    }
+
+    @Test(dataProvider = "bufferSizes")
+    public void testScatterN(int n) {
+        final ByteBuffer src = S.bufferOfNRandomBytes(n);
+        final int srcLength = src.remaining();
+        ByteBuffer copy = ByteBuffer.wrap(Arrays.copyOf(src.array(),
+                                                        src.array().length));
+        List<ByteBuffer> buffers = S.scatterBuffer(src);
+        int m = 0;
+        for (ByteBuffer b : buffers) {
+            m += b.remaining();
+            while (b.hasRemaining() & copy.hasRemaining()) {
+                assertEquals(b.get(), copy.get());
+            }
+        }
+        assertEquals(m, srcLength);
+    }
+
+    @Test(dataProvider = "inputStream")
+    public void testInputStreamOfNReads(int n, int capacity) throws IOException {
+        InputStream s = S.inputStreamOfNReads(n);
+        int count = 0;
+        byte[] b = new byte[capacity];
+        while (s.read(b) != -1) {
+            count++;
+        }
+        assertEquals(count, n);
+        assertTrue(s.read() == -1);
+        assertTrue(s.read(b) == -1);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck-tests/TckDriver.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8226602
+ * @summary Tests convenience reactive primitives with RS TCK
+ *
+ * @library ../reactivestreams-tck
+ * @build S
+ *
+ * @compile -encoding UTF-8 SPublisherOfStream.java
+ *
+ * @compile -encoding UTF-8 BodyPublishersFromPublisher.java
+ * @compile -encoding UTF-8 BodyPublishersNoBody.java
+ * @compile -encoding UTF-8 BodyPublishersOfByteArray.java
+ * @compile -encoding UTF-8 BodyPublishersOfByteArrays.java
+ * @compile -encoding UTF-8 BodyPublishersOfFile.java
+ * @compile -encoding UTF-8 BodyPublishersOfInputStream.java
+ * @compile -encoding UTF-8 BodyPublishersOfSubByteArray.java
+ *
+ * @compile -encoding UTF-8 BodySubscribersBuffering.java
+ * @compile -encoding UTF-8 BodySubscribersDiscarding.java
+ * @compile -encoding UTF-8 BodySubscribersFromLineSubscriber.java
+ * @compile -encoding UTF-8 BodySubscribersFromSubscriber.java
+ * @compile -encoding UTF-8 BodySubscribersMapping.java
+ * @compile -encoding UTF-8 BodySubscribersOfByteArray.java
+ * @compile -encoding UTF-8 BodySubscribersOfByteArrayConsumer.java
+ * @compile -encoding UTF-8 BodySubscribersOfFile.java
+ * @compile -encoding UTF-8 BodySubscribersOfInputStream.java
+ * @compile -encoding UTF-8 BodySubscribersOfLines.java
+ * @compile -encoding UTF-8 BodySubscribersOfPublisher.java
+ * @compile -encoding UTF-8 BodySubscribersOfPublisher1.java
+ * @compile -encoding UTF-8 BodySubscribersOfPublisherPublisher.java
+ * @compile -encoding UTF-8 BodySubscribersOfString.java
+ * @compile -encoding UTF-8 BodySubscribersReplacing.java
+ *
+ * @run testng/othervm STest
+ * @run testng/othervm SPublisherOfStream
+ *
+ * @run testng/othervm BodyPublishersFromPublisher
+ * @run testng/othervm BodyPublishersNoBody
+ * @run testng/othervm BodyPublishersOfByteArray
+ * @run testng/othervm BodyPublishersOfByteArrays
+ * @run testng/othervm BodyPublishersOfFile
+ * @run testng/othervm BodyPublishersOfInputStream
+ * @run testng/othervm BodyPublishersOfSubByteArray
+ *
+ * @run testng/othervm BodySubscribersBuffering
+ * @run testng/othervm BodySubscribersDiscarding
+ * @run testng/othervm BodySubscribersFromLineSubscriber
+ * @run testng/othervm BodySubscribersFromSubscriber
+ * @run testng/othervm BodySubscribersMapping
+ * @run testng/othervm BodySubscribersOfByteArray
+ * @run testng/othervm BodySubscribersOfByteArrayConsumer
+ * @run testng/othervm BodySubscribersOfFile
+ * @run testng/othervm BodySubscribersOfInputStream
+ * @run testng/othervm BodySubscribersOfLines
+ * @run testng/othervm BodySubscribersOfPublisher
+ * @run testng/othervm BodySubscribersOfPublisher1
+ * @run testng/othervm BodySubscribersOfPublisherPublisher
+ * @run testng/othervm BodySubscribersOfString
+ * @run testng/othervm BodySubscribersReplacing
+ *
+ * @key randomness
+ */
+public class TckDriver {
+   /*
+        #### General Information
+
+        1. This JTREG test aggregates multiple TestNG tests. This is because
+        these tests share a common library (reactivestreams-tck), and we don't
+        want this library to be compiled separately for each of those tests.
+
+        2. Tests that use RS TCK are compiled with the UTF-8 encoding. This is
+        performed for the sake of reactivestreams-tck. We don't want to patch
+        the TCK because of the extra merging work in the future, should we bring
+        update(s) from the RS repo.
+
+        #### Tests
+
+        1. The purpose of each test should be easily digestible. The name of the
+        test is derived from the very entity the test exercises. For example,
+
+            the BodyPublishersOfFile test exercises the BodyPublisher obtained
+            by calling BodyPublishers.ofFile(Path)
+
+            the BodySubscribersOfFile test exercises the BodySubscriber obtained
+            by calling BodySubscribers.ofFile(Path)
+
+        2. RS TCK requires PublisherVerification tests to produce publishers
+        capable of emitting a certain number of elements. In order to achieve
+        this, we use some knowledge of the internal workings of our publishers.
+        An example would be a chunk size a publisher uses to deliver a portion
+        of data. Without knowing that it is not possible to guarantee that the
+        publisher will emit a particular number of elements.
+
+        3. Typically our publishers cannot be created in a known failed state.
+        In this case the corresponding `createFailedFlowPublisher` method
+        returns `null`.
+
+        4. SubscriberBlackBoxVerification uses the `createElement(int element)`
+        method. Our implementations usually cap the amount of data created by
+        this method, because it's not known beforehand how big the `element`
+        value is. Hence, sometimes there's code like as follows:
+
+            @Override
+            public List<ByteBuffer> createElement(int element) {
+                return scatterBuffer(
+                        bufferOfNRandomASCIIBytes(element % 17));
+            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
+            }
+
+        5. The amount of testing RS TCK performs on a publisher seems to depend
+        on the number of elements this publisher reports it can emit. Sometimes
+        a code like the following can be seen in the tests:
+
+                @Override public long maxElementsFromPublisher() {
+                    return 21;
+                ~~~~~~~~~~~^
+                }
+
+        This magic number is a result of trial and error and seems to unlock
+        most of the tests. Reporting big values (e.g. Long.MAX_VALUE - 1) is
+        not an option for most of our publishers because they require to have
+        all the elements upfront.
+
+        6. It doesn't seem currently feasible to provide SubscriberWhiteboxVerification
+        tests as a) it's not clear how much better the coverage is and b) it's
+        significantly harder to code that.
+
+        #### S (Support)
+
+        Support utilities are being tested (STest) too.
+    */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/FlowAdapters.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams;
+
+import java.util.concurrent.Flow;
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Bridge between Reactive Streams API and the Java 9 {@link java.util.concurrent.Flow} API.
+ */
+public final class FlowAdapters {
+    /** Utility class. */
+    private FlowAdapters() {
+        throw new IllegalStateException("No instances!");
+    }
+
+    /**
+     * Converts a Flow Publisher into a Reactive Streams Publisher.
+     * @param <T> the element type
+     * @param flowPublisher the source Flow Publisher to convert
+     * @return the equivalent Reactive Streams Publisher
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> org.reactivestreams.Publisher<T> toPublisher(
+            Flow.Publisher<? extends T> flowPublisher) {
+        requireNonNull(flowPublisher, "flowPublisher");
+        final org.reactivestreams.Publisher<T> publisher;
+        if (flowPublisher instanceof FlowPublisherFromReactive) {
+            publisher = (org.reactivestreams.Publisher<T>)(((FlowPublisherFromReactive<T>)flowPublisher).reactiveStreams);
+        } else if (flowPublisher instanceof org.reactivestreams.Publisher) {
+            publisher = (org.reactivestreams.Publisher<T>)flowPublisher;
+        } else {
+            publisher = new ReactivePublisherFromFlow<T>(flowPublisher);
+        }
+        return publisher;
+    }
+
+    /**
+     * Converts a Reactive Streams Publisher into a Flow Publisher.
+     * @param <T> the element type
+     * @param reactiveStreamsPublisher the source Reactive Streams Publisher to convert
+     * @return the equivalent Flow Publisher
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> Flow.Publisher<T> toFlowPublisher(
+            org.reactivestreams.Publisher<? extends T> reactiveStreamsPublisher
+    ) {
+        requireNonNull(reactiveStreamsPublisher, "reactiveStreamsPublisher");
+        final Flow.Publisher<T> flowPublisher;
+        if (reactiveStreamsPublisher instanceof ReactivePublisherFromFlow) {
+            flowPublisher = (Flow.Publisher<T>)(((ReactivePublisherFromFlow<T>)reactiveStreamsPublisher).flow);
+        } else if (reactiveStreamsPublisher instanceof Flow.Publisher) {
+            flowPublisher = (Flow.Publisher<T>)reactiveStreamsPublisher;
+        } else {
+            flowPublisher = new FlowPublisherFromReactive<T>(reactiveStreamsPublisher);
+        }
+        return flowPublisher;
+    }
+
+    /**
+     * Converts a Flow Processor into a Reactive Streams Processor.
+     * @param <T> the input value type
+     * @param <U> the output value type
+     * @param flowProcessor the source Flow Processor to convert
+     * @return the equivalent Reactive Streams Processor
+     */
+    @SuppressWarnings("unchecked")
+    public static <T, U> org.reactivestreams.Processor<T, U> toProcessor(
+            Flow.Processor<? super T, ? extends U> flowProcessor
+    ) {
+        requireNonNull(flowProcessor, "flowProcessor");
+        final org.reactivestreams.Processor<T, U> processor;
+        if (flowProcessor instanceof FlowToReactiveProcessor) {
+            processor = (org.reactivestreams.Processor<T, U>)(((FlowToReactiveProcessor<T, U>)flowProcessor).reactiveStreams);
+        } else if (flowProcessor instanceof org.reactivestreams.Processor) {
+            processor = (org.reactivestreams.Processor<T, U>)flowProcessor;
+        } else {
+            processor = new ReactiveToFlowProcessor<T, U>(flowProcessor);
+        }
+        return processor;
+    }
+
+    /**
+     * Converts a Reactive Streams Processor into a Flow Processor.
+     * @param <T> the input value type
+     * @param <U> the output value type
+     * @param reactiveStreamsProcessor the source Reactive Streams Processor to convert
+     * @return the equivalent Flow Processor
+     */
+    @SuppressWarnings("unchecked")
+    public static <T, U> Flow.Processor<T, U> toFlowProcessor(
+            org.reactivestreams.Processor<? super T, ? extends U> reactiveStreamsProcessor
+        ) {
+        requireNonNull(reactiveStreamsProcessor, "reactiveStreamsProcessor");
+        final Flow.Processor<T, U> flowProcessor;
+        if (reactiveStreamsProcessor instanceof ReactiveToFlowProcessor) {
+            flowProcessor = (Flow.Processor<T, U>)(((ReactiveToFlowProcessor<T, U>)reactiveStreamsProcessor).flow);
+        } else if (reactiveStreamsProcessor instanceof Flow.Processor) {
+            flowProcessor = (Flow.Processor<T, U>)reactiveStreamsProcessor;
+        } else {
+            flowProcessor = new FlowToReactiveProcessor<T, U>(reactiveStreamsProcessor);
+        }
+        return flowProcessor;
+    }
+
+    /**
+     * Converts a Reactive Streams Subscriber into a Flow Subscriber.
+     * @param <T> the input and output value type
+     * @param reactiveStreamsSubscriber the Reactive Streams Subscriber instance to convert
+     * @return the equivalent Flow Subscriber
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> Flow.Subscriber<T> toFlowSubscriber(org.reactivestreams.Subscriber<T> reactiveStreamsSubscriber) {
+        requireNonNull(reactiveStreamsSubscriber, "reactiveStreamsSubscriber");
+        final Flow.Subscriber<T> flowSubscriber;
+        if (reactiveStreamsSubscriber instanceof ReactiveToFlowSubscriber) {
+            flowSubscriber = (Flow.Subscriber<T>)((ReactiveToFlowSubscriber<T>)reactiveStreamsSubscriber).flow;
+        } else if (reactiveStreamsSubscriber instanceof Flow.Subscriber) {
+            flowSubscriber = (Flow.Subscriber<T>)reactiveStreamsSubscriber;
+        } else {
+            flowSubscriber = new FlowToReactiveSubscriber<T>(reactiveStreamsSubscriber);
+        }
+        return flowSubscriber;
+    }
+
+    /**
+     * Converts a Flow Subscriber into a Reactive Streams Subscriber.
+     * @param <T> the input and output value type
+     * @param flowSubscriber the Flow Subscriber instance to convert
+     * @return the equivalent Reactive Streams Subscriber
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> org.reactivestreams.Subscriber<T> toSubscriber(Flow.Subscriber<T> flowSubscriber) {
+        requireNonNull(flowSubscriber, "flowSubscriber");
+        final org.reactivestreams.Subscriber<T> subscriber;
+        if (flowSubscriber instanceof FlowToReactiveSubscriber) {
+            subscriber = (org.reactivestreams.Subscriber<T>)((FlowToReactiveSubscriber<T>)flowSubscriber).reactiveStreams;
+        } else if (flowSubscriber instanceof org.reactivestreams.Subscriber) {
+            subscriber = (org.reactivestreams.Subscriber<T>)flowSubscriber;
+        } else {
+            subscriber = new ReactiveToFlowSubscriber<T>(flowSubscriber);
+        }
+        return subscriber;
+    }
+
+    /**
+     * Wraps a Reactive Streams Subscription and converts the calls to a Flow Subscription.
+     */
+    static final class FlowToReactiveSubscription implements Flow.Subscription {
+        final org.reactivestreams.Subscription reactiveStreams;
+
+        public FlowToReactiveSubscription(org.reactivestreams.Subscription reactive) {
+            this.reactiveStreams = reactive;
+        }
+
+        @Override
+        public void request(long n) {
+            reactiveStreams.request(n);
+        }
+
+        @Override
+        public void cancel() {
+            reactiveStreams.cancel();
+        }
+
+    }
+
+    /**
+     * Wraps a Flow Subscription and converts the calls to a Reactive Streams Subscription.
+     */
+    static final class ReactiveToFlowSubscription implements org.reactivestreams.Subscription {
+        final Flow.Subscription flow;
+
+        public ReactiveToFlowSubscription(Flow.Subscription flow) {
+            this.flow = flow;
+        }
+
+        @Override
+        public void request(long n) {
+            flow.request(n);
+        }
+
+        @Override
+        public void cancel() {
+            flow.cancel();
+        }
+
+
+    }
+
+    /**
+     * Wraps a Reactive Streams Subscriber and forwards methods of the Flow Subscriber to it.
+     * @param <T> the element type
+     */
+    static final class FlowToReactiveSubscriber<T> implements Flow.Subscriber<T> {
+        final org.reactivestreams.Subscriber<? super T> reactiveStreams;
+
+        public FlowToReactiveSubscriber(org.reactivestreams.Subscriber<? super T> reactive) {
+            this.reactiveStreams = reactive;
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            reactiveStreams.onSubscribe((subscription == null) ? null : new ReactiveToFlowSubscription(subscription));
+        }
+
+        @Override
+        public void onNext(T item) {
+            reactiveStreams.onNext(item);
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            reactiveStreams.onError(throwable);
+        }
+
+        @Override
+        public void onComplete() {
+            reactiveStreams.onComplete();
+        }
+
+    }
+
+    /**
+     * Wraps a Flow Subscriber and forwards methods of the Reactive Streams Subscriber to it.
+     * @param <T> the element type
+     */
+    static final class ReactiveToFlowSubscriber<T> implements org.reactivestreams.Subscriber<T> {
+        final Flow.Subscriber<? super T> flow;
+
+        public ReactiveToFlowSubscriber(Flow.Subscriber<? super T> flow) {
+            this.flow = flow;
+        }
+
+        @Override
+        public void onSubscribe(org.reactivestreams.Subscription subscription) {
+            flow.onSubscribe((subscription == null) ? null : new FlowToReactiveSubscription(subscription));
+        }
+
+        @Override
+        public void onNext(T item) {
+            flow.onNext(item);
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            flow.onError(throwable);
+        }
+
+        @Override
+        public void onComplete() {
+            flow.onComplete();
+        }
+
+    }
+
+    /**
+     * Wraps a Flow Processor and forwards methods of the Reactive Streams Processor to it.
+     * @param <T> the input type
+     * @param <U> the output type
+     */
+    static final class ReactiveToFlowProcessor<T, U> implements org.reactivestreams.Processor<T, U> {
+        final Flow.Processor<? super T, ? extends U> flow;
+
+        public ReactiveToFlowProcessor(Flow.Processor<? super T, ? extends U> flow) {
+            this.flow = flow;
+        }
+
+        @Override
+        public void onSubscribe(org.reactivestreams.Subscription subscription) {
+            flow.onSubscribe((subscription == null) ? null : new FlowToReactiveSubscription(subscription));
+        }
+
+        @Override
+        public void onNext(T t) {
+            flow.onNext(t);
+        }
+
+        @Override
+        public void onError(Throwable t) {
+            flow.onError(t);
+        }
+
+        @Override
+        public void onComplete() {
+            flow.onComplete();
+        }
+
+        @Override
+        public void subscribe(org.reactivestreams.Subscriber<? super U> s) {
+            flow.subscribe((s == null) ? null : new FlowToReactiveSubscriber<U>(s));
+        }
+    }
+
+    /**
+     * Wraps a Reactive Streams Processor and forwards methods of the Flow Processor to it.
+     * @param <T> the input type
+     * @param <U> the output type
+     */
+    static final class FlowToReactiveProcessor<T, U> implements Flow.Processor<T, U> {
+        final org.reactivestreams.Processor<? super T, ? extends U> reactiveStreams;
+
+        public FlowToReactiveProcessor(org.reactivestreams.Processor<? super T, ? extends U> reactive) {
+            this.reactiveStreams = reactive;
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            reactiveStreams.onSubscribe((subscription == null) ? null : new ReactiveToFlowSubscription(subscription));
+        }
+
+        @Override
+        public void onNext(T t) {
+            reactiveStreams.onNext(t);
+        }
+
+        @Override
+        public void onError(Throwable t) {
+            reactiveStreams.onError(t);
+        }
+
+        @Override
+        public void onComplete() {
+            reactiveStreams.onComplete();
+        }
+
+        @Override
+        public void subscribe(Flow.Subscriber<? super U> s) {
+            reactiveStreams.subscribe((s == null) ? null : new ReactiveToFlowSubscriber<U>(s));
+        }
+    }
+
+    /**
+     * Reactive Streams Publisher that wraps a Flow Publisher.
+     * @param <T> the element type
+     */
+    static final class ReactivePublisherFromFlow<T> implements org.reactivestreams.Publisher<T> {
+        final Flow.Publisher<? extends T> flow;
+
+        public ReactivePublisherFromFlow(Flow.Publisher<? extends T> flowPublisher) {
+            this.flow = flowPublisher;
+        }
+
+        @Override
+        public void subscribe(org.reactivestreams.Subscriber<? super T> reactive) {
+            flow.subscribe((reactive == null) ? null : new FlowToReactiveSubscriber<T>(reactive));
+        }
+    }
+
+    /**
+     * Flow Publisher that wraps a Reactive Streams Publisher.
+     * @param <T> the element type
+     */
+    static final class FlowPublisherFromReactive<T> implements Flow.Publisher<T> {
+
+        final org.reactivestreams.Publisher<? extends T> reactiveStreams;
+
+        public FlowPublisherFromReactive(org.reactivestreams.Publisher<? extends T> reactivePublisher) {
+            this.reactiveStreams = reactivePublisher;
+        }
+
+        @Override
+        public void subscribe(Flow.Subscriber<? super T> flow) {
+            reactiveStreams.subscribe((flow == null) ? null : new ReactiveToFlowSubscriber<T>(flow));
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/Processor.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams;
+
+/**
+ * A Processor represents a processing stage—which is both a {@link Subscriber}
+ * and a {@link Publisher} and obeys the contracts of both.
+ *
+ * @param <T> the type of element signaled to the {@link Subscriber}
+ * @param <R> the type of element signaled by the {@link Publisher}
+ */
+public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/Publisher.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams;
+
+/**
+ * A {@link Publisher} is a provider of a potentially unbounded number of sequenced elements, publishing them according to
+ * the demand received from its {@link Subscriber}(s).
+ * <p>
+ * A {@link Publisher} can serve multiple {@link Subscriber}s subscribed {@link #subscribe(Subscriber)} dynamically
+ * at various points in time.
+ *
+ * @param <T> the type of element signaled.
+ */
+public interface Publisher<T> {
+
+    /**
+     * Request {@link Publisher} to start streaming data.
+     * <p>
+     * This is a "factory method" and can be called multiple times, each time starting a new {@link Subscription}.
+     * <p>
+     * Each {@link Subscription} will work for only a single {@link Subscriber}.
+     * <p>
+     * A {@link Subscriber} should only subscribe once to a single {@link Publisher}.
+     * <p>
+     * If the {@link Publisher} rejects the subscription attempt or otherwise fails it will
+     * signal the error via {@link Subscriber#onError}.
+     *
+     * @param s the {@link Subscriber} that will consume signals from this {@link Publisher}
+     */
+    public void subscribe(Subscriber<? super T> s);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/Subscriber.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams;
+
+/**
+ * Will receive call to {@link #onSubscribe(Subscription)} once after passing an instance of {@link Subscriber} to {@link Publisher#subscribe(Subscriber)}.
+ * <p>
+ * No further notifications will be received until {@link Subscription#request(long)} is called.
+ * <p>
+ * After signaling demand:
+ * <ul>
+ * <li>One or more invocations of {@link #onNext(Object)} up to the maximum number defined by {@link Subscription#request(long)}</li>
+ * <li>Single invocation of {@link #onError(Throwable)} or {@link Subscriber#onComplete()} which signals a terminal state after which no further events will be sent.
+ * </ul>
+ * <p>
+ * Demand can be signaled via {@link Subscription#request(long)} whenever the {@link Subscriber} instance is capable of handling more.
+ *
+ * @param <T> the type of element signaled.
+ */
+public interface Subscriber<T> {
+    /**
+     * Invoked after calling {@link Publisher#subscribe(Subscriber)}.
+     * <p>
+     * No data will start flowing until {@link Subscription#request(long)} is invoked.
+     * <p>
+     * It is the responsibility of this {@link Subscriber} instance to call {@link Subscription#request(long)} whenever more data is wanted.
+     * <p>
+     * The {@link Publisher} will send notifications only in response to {@link Subscription#request(long)}.
+     *
+     * @param s
+     *            {@link Subscription} that allows requesting data via {@link Subscription#request(long)}
+     */
+    public void onSubscribe(Subscription s);
+
+    /**
+     * Data notification sent by the {@link Publisher} in response to requests to {@link Subscription#request(long)}.
+     *
+     * @param t the element signaled
+     */
+    public void onNext(T t);
+
+    /**
+     * Failed terminal state.
+     * <p>
+     * No further events will be sent even if {@link Subscription#request(long)} is invoked again.
+     *
+     * @param t the throwable signaled
+     */
+    public void onError(Throwable t);
+
+    /**
+     * Successful terminal state.
+     * <p>
+     * No further events will be sent even if {@link Subscription#request(long)} is invoked again.
+     */
+    public void onComplete();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/Subscription.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams;
+
+/**
+ * A {@link Subscription} represents a one-to-one lifecycle of a {@link Subscriber} subscribing to a {@link Publisher}.
+ * <p>
+ * It can only be used once by a single {@link Subscriber}.
+ * <p>
+ * It is used to both signal desire for data and cancel demand (and allow resource cleanup).
+ *
+ */
+public interface Subscription {
+    /**
+     * No events will be sent by a {@link Publisher} until demand is signaled via this method.
+     * <p>
+     *  It can be called however often and whenever needed—but if the outstanding cumulative demand ever becomes Long.MAX_VALUE or more,
+     *  it may be treated by the {@link Publisher} as "effectively unbounded".
+     * <p>
+     * Whatever has been requested can be sent by the {@link Publisher} so only signal demand for what can be safely handled.
+     * <p>
+     * A {@link Publisher} can send less than is requested if the stream ends but
+     * then must emit either {@link Subscriber#onError(Throwable)} or {@link Subscriber#onComplete()}.
+     *
+     * @param n the strictly positive number of elements to requests to the upstream {@link Publisher}
+     */
+    public void request(long n);
+
+    /**
+     * Request the {@link Publisher} to stop sending data and clean up resources.
+     * <p>
+     * Data may still be sent to meet previously signalled demand after calling cancel.
+     */
+    public void cancel();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/example/unicast/AsyncIterablePublisher.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.example.unicast;
+
+import org.reactivestreams.Publisher;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+
+import java.util.Iterator;
+import java.util.Collections;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * AsyncIterablePublisher is an implementation of Reactive Streams `Publisher`
+ * which executes asynchronously, using a provided `Executor` and produces elements
+ * from a given `Iterable` in a "unicast" configuration to its `Subscribers`.
+ *
+ * NOTE: The code below uses a lot of try-catches to show the reader where exceptions can be expected, and where they are forbidden.
+ */
+public class AsyncIterablePublisher<T> implements Publisher<T> {
+  private final static int DEFAULT_BATCHSIZE = 1024;
+
+  private final Iterable<T> elements; // This is our data source / generator
+  private final Executor executor; // This is our thread pool, which will make sure that our Publisher runs asynchronously to its Subscribers
+  private final int batchSize; // In general, if one uses an `Executor`, one should be nice nad not hog a thread for too long, this is the cap for that, in elements
+
+  public AsyncIterablePublisher(final Iterable<T> elements, final Executor executor) {
+    this(elements, DEFAULT_BATCHSIZE, executor);
+  }
+
+  public AsyncIterablePublisher(final Iterable<T> elements, final int batchSize, final Executor executor) {
+    if (elements == null) throw null;
+    if (executor == null) throw null;
+    if (batchSize < 1) throw new IllegalArgumentException("batchSize must be greater than zero!");
+    this.elements = elements;
+    this.executor = executor;
+    this.batchSize = batchSize;
+  }
+
+  @Override
+  public void subscribe(final Subscriber<? super T> s) {
+    // As per rule 1.11, we have decided to support multiple subscribers in a unicast configuration
+    // for this `Publisher` implementation.
+    // As per 2.13, this method must return normally (i.e. not throw)
+    new SubscriptionImpl(s).init();
+  }
+
+  // These represent the protocol of the `AsyncIterablePublishers` SubscriptionImpls
+  static interface Signal {};
+  enum Cancel implements Signal { Instance; };
+  enum Subscribe implements Signal { Instance; };
+  enum Send implements Signal { Instance; };
+  static final class Request implements Signal {
+    final long n;
+    Request(final long n) {
+      this.n = n;
+    }
+  };
+
+  // This is our implementation of the Reactive Streams `Subscription`,
+  // which represents the association between a `Publisher` and a `Subscriber`.
+  final class SubscriptionImpl implements Subscription, Runnable {
+    final Subscriber<? super T> subscriber; // We need a reference to the `Subscriber` so we can talk to it
+    private boolean cancelled = false; // This flag will track whether this `Subscription` is to be considered cancelled or not
+    private long demand = 0; // Here we track the current demand, i.e. what has been requested but not yet delivered
+    private Iterator<T> iterator; // This is our cursor into the data stream, which we will send to the `Subscriber`
+
+    SubscriptionImpl(final Subscriber<? super T> subscriber) {
+      // As per rule 1.09, we need to throw a `java.lang.NullPointerException` if the `Subscriber` is `null`
+      if (subscriber == null) throw null;
+      this.subscriber = subscriber;
+    }
+
+    // This `ConcurrentLinkedQueue` will track signals that are sent to this `Subscription`, like `request` and `cancel`
+    private final ConcurrentLinkedQueue<Signal> inboundSignals = new ConcurrentLinkedQueue<Signal>();
+
+    // We are using this `AtomicBoolean` to make sure that this `Subscription` doesn't run concurrently with itself,
+    // which would violate rule 1.3 among others (no concurrent notifications).
+    private final AtomicBoolean on = new AtomicBoolean(false);
+
+    // This method will register inbound demand from our `Subscriber` and validate it against rule 3.9 and rule 3.17
+    private void doRequest(final long n) {
+      if (n < 1)
+        terminateDueTo(new IllegalArgumentException(subscriber + " violated the Reactive Streams rule 3.9 by requesting a non-positive number of elements."));
+      else if (demand + n < 1) {
+        // As governed by rule 3.17, when demand overflows `Long.MAX_VALUE` we treat the signalled demand as "effectively unbounded"
+        demand = Long.MAX_VALUE;  // Here we protect from the overflow and treat it as "effectively unbounded"
+        doSend(); // Then we proceed with sending data downstream
+      } else {
+        demand += n; // Here we record the downstream demand
+        doSend(); // Then we can proceed with sending data downstream
+      }
+    }
+
+    // This handles cancellation requests, and is idempotent, thread-safe and not synchronously performing heavy computations as specified in rule 3.5
+    private void doCancel() {
+      cancelled = true;
+    }
+
+    // Instead of executing `subscriber.onSubscribe` synchronously from within `Publisher.subscribe`
+    // we execute it asynchronously, this is to avoid executing the user code (`Iterable.iterator`) on the calling thread.
+    // It also makes it easier to follow rule 1.9
+    private void doSubscribe() {
+      try {
+        iterator = elements.iterator();
+        if (iterator == null)
+          iterator = Collections.<T>emptyList().iterator(); // So we can assume that `iterator` is never null
+      } catch(final Throwable t) {
+        subscriber.onSubscribe(new Subscription() { // We need to make sure we signal onSubscribe before onError, obeying rule 1.9
+          @Override public void cancel() {}
+          @Override public void request(long n) {}
+        });
+        terminateDueTo(t); // Here we send onError, obeying rule 1.09
+      }
+
+      if (!cancelled) {
+        // Deal with setting up the subscription with the subscriber
+        try {
+          subscriber.onSubscribe(this);
+        } catch(final Throwable t) { // Due diligence to obey 2.13
+          terminateDueTo(new IllegalStateException(subscriber + " violated the Reactive Streams rule 2.13 by throwing an exception from onSubscribe.", t));
+        }
+
+        // Deal with already complete iterators promptly
+        boolean hasElements = false;
+        try {
+          hasElements = iterator.hasNext();
+        } catch(final Throwable t) {
+          terminateDueTo(t); // If hasNext throws, there's something wrong and we need to signal onError as per 1.2, 1.4,
+        }
+
+        // If we don't have anything to deliver, we're already done, so lets do the right thing and
+        // not wait for demand to deliver `onComplete` as per rule 1.2 and 1.3
+        if (!hasElements) {
+          try {
+            doCancel(); // Rule 1.6 says we need to consider the `Subscription` cancelled when `onComplete` is signalled
+            subscriber.onComplete();
+          } catch(final Throwable t) { // As per rule 2.13, `onComplete` is not allowed to throw exceptions, so we do what we can, and log this.
+            (new IllegalStateException(subscriber + " violated the Reactive Streams rule 2.13 by throwing an exception from onComplete.", t)).printStackTrace(System.err);
+          }
+        }
+      }
+    }
+
+    // This is our behavior for producing elements downstream
+    private void doSend() {
+      try {
+        // In order to play nice with the `Executor` we will only send at-most `batchSize` before
+        // rescheduing ourselves and relinquishing the current thread.
+        int leftInBatch = batchSize;
+        do {
+          T next;
+          boolean hasNext;
+          try {
+            next = iterator.next(); // We have already checked `hasNext` when subscribing, so we can fall back to testing -after- `next` is called.
+            hasNext = iterator.hasNext(); // Need to keep track of End-of-Stream
+          } catch (final Throwable t) {
+            terminateDueTo(t); // If `next` or `hasNext` throws (they can, since it is user-provided), we need to treat the stream as errored as per rule 1.4
+            return;
+          }
+          subscriber.onNext(next); // Then we signal the next element downstream to the `Subscriber`
+          if (!hasNext) { // If we are at End-of-Stream
+            doCancel(); // We need to consider this `Subscription` as cancelled as per rule 1.6
+            subscriber.onComplete(); // Then we signal `onComplete` as per rule 1.2 and 1.5
+          }
+        } while (!cancelled           // This makes sure that rule 1.8 is upheld, i.e. we need to stop signalling "eventually"
+                 && --leftInBatch > 0 // This makes sure that we only send `batchSize` number of elements in one go (so we can yield to other Runnables)
+                 && --demand > 0);    // This makes sure that rule 1.1 is upheld (sending more than was demanded)
+
+        if (!cancelled && demand > 0) // If the `Subscription` is still alive and well, and we have demand to satisfy, we signal ourselves to send more data
+          signal(Send.Instance);
+      } catch(final Throwable t) {
+        // We can only get here if `onNext` or `onComplete` threw, and they are not allowed to according to 2.13, so we can only cancel and log here.
+        doCancel(); // Make sure that we are cancelled, since we cannot do anything else since the `Subscriber` is faulty.
+        (new IllegalStateException(subscriber + " violated the Reactive Streams rule 2.13 by throwing an exception from onNext or onComplete.", t)).printStackTrace(System.err);
+      }
+    }
+
+    // This is a helper method to ensure that we always `cancel` when we signal `onError` as per rule 1.6
+    private void terminateDueTo(final Throwable t) {
+      cancelled = true; // When we signal onError, the subscription must be considered as cancelled, as per rule 1.6
+      try {
+        subscriber.onError(t); // Then we signal the error downstream, to the `Subscriber`
+      } catch(final Throwable t2) { // If `onError` throws an exception, this is a spec violation according to rule 1.9, and all we can do is to log it.
+        (new IllegalStateException(subscriber + " violated the Reactive Streams rule 2.13 by throwing an exception from onError.", t2)).printStackTrace(System.err);
+      }
+    }
+
+    // What `signal` does is that it sends signals to the `Subscription` asynchronously
+    private void signal(final Signal signal) {
+      if (inboundSignals.offer(signal)) // No need to null-check here as ConcurrentLinkedQueue does this for us
+        tryScheduleToExecute(); // Then we try to schedule it for execution, if it isn't already
+    }
+
+    // This is the main "event loop" if you so will
+    @Override public final void run() {
+      if(on.get()) { // establishes a happens-before relationship with the end of the previous run
+        try {
+          final Signal s = inboundSignals.poll(); // We take a signal off the queue
+          if (!cancelled) { // to make sure that we follow rule 1.8, 3.6 and 3.7
+
+            // Below we simply unpack the `Signal`s and invoke the corresponding methods
+            if (s instanceof Request)
+              doRequest(((Request)s).n);
+            else if (s == Send.Instance)
+              doSend();
+            else if (s == Cancel.Instance)
+              doCancel();
+            else if (s == Subscribe.Instance)
+              doSubscribe();
+          }
+        } finally {
+          on.set(false); // establishes a happens-before relationship with the beginning of the next run
+          if(!inboundSignals.isEmpty()) // If we still have signals to process
+            tryScheduleToExecute(); // Then we try to schedule ourselves to execute again
+        }
+      }
+    }
+
+    // This method makes sure that this `Subscription` is only running on one Thread at a time,
+    // this is important to make sure that we follow rule 1.3
+    private final void tryScheduleToExecute() {
+      if(on.compareAndSet(false, true)) {
+        try {
+          executor.execute(this);
+        } catch(Throwable t) { // If we can't run on the `Executor`, we need to fail gracefully
+          if (!cancelled) {
+            doCancel(); // First of all, this failure is not recoverable, so we need to follow rule 1.4 and 1.6
+            try {
+              terminateDueTo(new IllegalStateException("Publisher terminated due to unavailable Executor.", t));
+            } finally {
+              inboundSignals.clear(); // We're not going to need these anymore
+              // This subscription is cancelled by now, but letting it become schedulable again means
+              // that we can drain the inboundSignals queue if anything arrives after clearing
+              on.set(false);
+            }
+          }
+        }
+      }
+    }
+
+    // Our implementation of `Subscription.request` sends a signal to the Subscription that more elements are in demand
+    @Override public void request(final long n) {
+      signal(new Request(n));
+    }
+    // Our implementation of `Subscription.cancel` sends a signal to the Subscription that the `Subscriber` is not interested in any more elements
+    @Override public void cancel() {
+      signal(Cancel.Instance);
+    }
+    // The reason for the `init` method is that we want to ensure the `SubscriptionImpl`
+    // is completely constructed before it is exposed to the thread pool, therefor this
+    // method is only intended to be invoked once, and immediately after the constructor has
+    // finished.
+    void init() {
+      signal(Subscribe.Instance);
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/example/unicast/AsyncSubscriber.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.example.unicast;
+
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * AsyncSubscriber is an implementation of Reactive Streams `Subscriber`,
+ * it runs asynchronously (on an Executor), requests one element
+ * at a time, and invokes a user-defined method to process each element.
+ *
+ * NOTE: The code below uses a lot of try-catches to show the reader where exceptions can be expected, and where they are forbidden.
+ */
+public abstract class AsyncSubscriber<T> implements Subscriber<T>, Runnable {
+
+  // Signal represents the asynchronous protocol between the Publisher and Subscriber
+  private static interface Signal {}
+
+  private enum OnComplete implements Signal { Instance; }
+
+  private static class OnError implements Signal {
+    public final Throwable error;
+    public OnError(final Throwable error) { this.error = error; }
+  }
+
+  private static class OnNext<T> implements Signal {
+    public final T next;
+    public OnNext(final T next) { this.next = next; }
+  }
+
+  private static class OnSubscribe implements Signal {
+    public final Subscription subscription;
+    public OnSubscribe(final Subscription subscription) { this.subscription = subscription; }
+  }
+
+  private Subscription subscription; // Obeying rule 3.1, we make this private!
+  private boolean done; // It's useful to keep track of whether this Subscriber is done or not
+  private final Executor executor; // This is the Executor we'll use to be asynchronous, obeying rule 2.2
+
+  // Only one constructor, and it's only accessible for the subclasses
+  protected AsyncSubscriber(Executor executor) {
+    if (executor == null) throw null;
+    this.executor = executor;
+  }
+
+  // Showcases a convenience method to idempotently marking the Subscriber as "done", so we don't want to process more elements
+  // herefor we also need to cancel our `Subscription`.
+  private final void done() {
+    //On this line we could add a guard against `!done`, but since rule 3.7 says that `Subscription.cancel()` is idempotent, we don't need to.
+    done = true; // If `whenNext` throws an exception, let's consider ourselves done (not accepting more elements)
+    if (subscription != null) { // If we are bailing out before we got a `Subscription` there's little need for cancelling it.
+      try {
+        subscription.cancel(); // Cancel the subscription
+      } catch(final Throwable t) {
+        //Subscription.cancel is not allowed to throw an exception, according to rule 3.15
+        (new IllegalStateException(subscription + " violated the Reactive Streams rule 3.15 by throwing an exception from cancel.", t)).printStackTrace(System.err);
+      }
+    }
+  }
+
+  // This method is invoked when the OnNext signals arrive
+  // Returns whether more elements are desired or not, and if no more elements are desired,
+  // for convenience.
+  protected abstract boolean whenNext(final T element);
+
+  // This method is invoked when the OnComplete signal arrives
+  // override this method to implement your own custom onComplete logic.
+  protected void whenComplete() { }
+
+  // This method is invoked if the OnError signal arrives
+  // override this method to implement your own custom onError logic.
+  protected void whenError(Throwable error) { }
+
+  private final void handleOnSubscribe(final Subscription s) {
+    if (s == null) {
+      // Getting a null `Subscription` here is not valid so lets just ignore it.
+    } else if (subscription != null) { // If someone has made a mistake and added this Subscriber multiple times, let's handle it gracefully
+      try {
+        s.cancel(); // Cancel the additional subscription to follow rule 2.5
+      } catch(final Throwable t) {
+        //Subscription.cancel is not allowed to throw an exception, according to rule 3.15
+        (new IllegalStateException(s + " violated the Reactive Streams rule 3.15 by throwing an exception from cancel.", t)).printStackTrace(System.err);
+      }
+    } else {
+      // We have to assign it locally before we use it, if we want to be a synchronous `Subscriber`
+      // Because according to rule 3.10, the Subscription is allowed to call `onNext` synchronously from within `request`
+      subscription = s;
+      try {
+        // If we want elements, according to rule 2.1 we need to call `request`
+        // And, according to rule 3.2 we are allowed to call this synchronously from within the `onSubscribe` method
+        s.request(1); // Our Subscriber is unbuffered and modest, it requests one element at a time
+      } catch(final Throwable t) {
+        // Subscription.request is not allowed to throw according to rule 3.16
+        (new IllegalStateException(s + " violated the Reactive Streams rule 3.16 by throwing an exception from request.", t)).printStackTrace(System.err);
+      }
+    }
+  }
+
+  private final void handleOnNext(final T element) {
+    if (!done) { // If we aren't already done
+      if(subscription == null) { // Technically this check is not needed, since we are expecting Publishers to conform to the spec
+        // Check for spec violation of 2.1 and 1.09
+        (new IllegalStateException("Someone violated the Reactive Streams rule 1.09 and 2.1 by signalling OnNext before `Subscription.request`. (no Subscription)")).printStackTrace(System.err);
+      } else {
+        try {
+          if (whenNext(element)) {
+            try {
+              subscription.request(1); // Our Subscriber is unbuffered and modest, it requests one element at a time
+            } catch(final Throwable t) {
+              // Subscription.request is not allowed to throw according to rule 3.16
+              (new IllegalStateException(subscription + " violated the Reactive Streams rule 3.16 by throwing an exception from request.", t)).printStackTrace(System.err);
+            }
+          } else {
+            done(); // This is legal according to rule 2.6
+          }
+        } catch(final Throwable t) {
+          done();
+          try {
+            onError(t);
+          } catch(final Throwable t2) {
+            //Subscriber.onError is not allowed to throw an exception, according to rule 2.13
+            (new IllegalStateException(this + " violated the Reactive Streams rule 2.13 by throwing an exception from onError.", t2)).printStackTrace(System.err);
+          }
+        }
+      }
+    }
+  }
+
+  // Here it is important that we do not violate 2.2 and 2.3 by calling methods on the `Subscription` or `Publisher`
+  private void handleOnComplete() {
+    if (subscription == null) { // Technically this check is not needed, since we are expecting Publishers to conform to the spec
+      // Publisher is not allowed to signal onComplete before onSubscribe according to rule 1.09
+      (new IllegalStateException("Publisher violated the Reactive Streams rule 1.09 signalling onComplete prior to onSubscribe.")).printStackTrace(System.err);
+    } else {
+      done = true; // Obey rule 2.4
+      whenComplete();
+    }
+  }
+
+  // Here it is important that we do not violate 2.2 and 2.3 by calling methods on the `Subscription` or `Publisher`
+  private void handleOnError(final Throwable error) {
+    if (subscription == null) { // Technically this check is not needed, since we are expecting Publishers to conform to the spec
+      // Publisher is not allowed to signal onError before onSubscribe according to rule 1.09
+      (new IllegalStateException("Publisher violated the Reactive Streams rule 1.09 signalling onError prior to onSubscribe.")).printStackTrace(System.err);
+    } else {
+      done = true; // Obey rule 2.4
+      whenError(error);
+    }
+  }
+
+  // We implement the OnX methods on `Subscriber` to send Signals that we will process asycnhronously, but only one at a time
+
+  @Override public final void onSubscribe(final Subscription s) {
+    // As per rule 2.13, we need to throw a `java.lang.NullPointerException` if the `Subscription` is `null`
+    if (s == null) throw null;
+
+    signal(new OnSubscribe(s));
+  }
+
+  @Override public final void onNext(final T element) {
+    // As per rule 2.13, we need to throw a `java.lang.NullPointerException` if the `element` is `null`
+    if (element == null) throw null;
+
+    signal(new OnNext<T>(element));
+  }
+
+  @Override public final void onError(final Throwable t) {
+    // As per rule 2.13, we need to throw a `java.lang.NullPointerException` if the `Throwable` is `null`
+    if (t == null) throw null;
+
+    signal(new OnError(t));
+  }
+
+  @Override public final void onComplete() {
+     signal(OnComplete.Instance);
+  }
+
+  // This `ConcurrentLinkedQueue` will track signals that are sent to this `Subscriber`, like `OnComplete` and `OnNext` ,
+  // and obeying rule 2.11
+  private final ConcurrentLinkedQueue<Signal> inboundSignals = new ConcurrentLinkedQueue<Signal>();
+
+  // We are using this `AtomicBoolean` to make sure that this `Subscriber` doesn't run concurrently with itself,
+  // obeying rule 2.7 and 2.11
+  private final AtomicBoolean on = new AtomicBoolean(false);
+
+   @SuppressWarnings("unchecked")
+   @Override public final void run() {
+    if(on.get()) { // establishes a happens-before relationship with the end of the previous run
+      try {
+        final Signal s = inboundSignals.poll(); // We take a signal off the queue
+        if (!done) { // If we're done, we shouldn't process any more signals, obeying rule 2.8
+          // Below we simply unpack the `Signal`s and invoke the corresponding methods
+          if (s instanceof OnNext<?>)
+            handleOnNext(((OnNext<T>)s).next);
+          else if (s instanceof OnSubscribe)
+            handleOnSubscribe(((OnSubscribe)s).subscription);
+          else if (s instanceof OnError) // We are always able to handle OnError, obeying rule 2.10
+            handleOnError(((OnError)s).error);
+          else if (s == OnComplete.Instance) // We are always able to handle OnComplete, obeying rule 2.9
+            handleOnComplete();
+        }
+      } finally {
+        on.set(false); // establishes a happens-before relationship with the beginning of the next run
+        if(!inboundSignals.isEmpty()) // If we still have signals to process
+          tryScheduleToExecute(); // Then we try to schedule ourselves to execute again
+      }
+    }
+  }
+
+  // What `signal` does is that it sends signals to the `Subscription` asynchronously
+  private void signal(final Signal signal) {
+    if (inboundSignals.offer(signal)) // No need to null-check here as ConcurrentLinkedQueue does this for us
+      tryScheduleToExecute(); // Then we try to schedule it for execution, if it isn't already
+  }
+
+  // This method makes sure that this `Subscriber` is only executing on one Thread at a time
+  private final void tryScheduleToExecute() {
+    if(on.compareAndSet(false, true)) {
+      try {
+        executor.execute(this);
+      } catch(Throwable t) { // If we can't run on the `Executor`, we need to fail gracefully and not violate rule 2.13
+        if (!done) {
+          try {
+            done(); // First of all, this failure is not recoverable, so we need to cancel our subscription
+          } finally {
+            inboundSignals.clear(); // We're not going to need these anymore
+            // This subscription is cancelled by now, but letting the Subscriber become schedulable again means
+            // that we can drain the inboundSignals queue if anything arrives after clearing
+            on.set(false);
+          }
+        }
+      }
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/example/unicast/InfiniteIncrementNumberPublisher.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.example.unicast;
+
+import java.util.Iterator;
+import java.util.concurrent.Executor;
+
+import org.reactivestreams.Subscription;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Publisher;
+
+public class InfiniteIncrementNumberPublisher extends AsyncIterablePublisher<Integer> {
+    public InfiniteIncrementNumberPublisher(final Executor executor) {
+        super(new Iterable<Integer>() {
+          @Override public Iterator<Integer> iterator() {
+            return new Iterator<Integer>() {
+              private int at = 0;
+              @Override public boolean hasNext() { return true; }
+              @Override public Integer next() { return at++; } // Wraps around on overflow
+              @Override public void remove() { throw new UnsupportedOperationException(); }
+            };
+          }
+        }, executor);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/example/unicast/NumberIterablePublisher.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.example.unicast;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.concurrent.Executor;
+import org.reactivestreams.Subscription;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Publisher;
+
+public class NumberIterablePublisher extends AsyncIterablePublisher<Integer> {
+    public NumberIterablePublisher(final int from, final int to, final Executor executor) {
+        super(new Iterable<Integer>() {
+          { if(from > to) throw new IllegalArgumentException("from must be equal or greater than to!"); }
+          @Override public Iterator<Integer> iterator() {
+            return new Iterator<Integer>() {
+              private int at = from;
+              @Override public boolean hasNext() { return at < to; }
+              @Override public Integer next() {
+                if (!hasNext()) return Collections.<Integer>emptyList().iterator().next();
+                else return at++;
+              }
+              @Override public void remove() { throw new UnsupportedOperationException(); }
+            };
+          }
+        }, executor);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/example/unicast/RangePublisher.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.example.unicast;
+
+import org.reactivestreams.*;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * A synchronous implementation of the {@link Publisher} that can
+ * be subscribed to multiple times and each individual subscription
+ * will receive range of monotonically increasing integer values on demand.
+ */
+public final class RangePublisher implements Publisher<Integer> {
+
+    /** The starting value of the range. */
+    final int start;
+
+    /** The number of items to emit. */
+    final int count;
+
+    /**
+     * Constructs a RangePublisher instance with the given start and count values
+     * that yields a sequence of [start, start + count).
+     * @param start the starting value of the range
+     * @param count the number of items to emit
+     */
+    public RangePublisher(int start, int count) {
+        this.start = start;
+        this.count = count;
+    }
+
+    @Override
+    public void subscribe(Subscriber<? super Integer> subscriber) {
+        // As per rule 1.11, we have decided to support multiple subscribers
+        // in a unicast configuration for this `Publisher` implementation.
+
+        // As per rule 1.09, we need to throw a `java.lang.NullPointerException`
+        // if the `Subscriber` is `null`
+        if (subscriber == null) throw null;
+
+        // As per 2.13, this method must return normally (i.e. not throw).
+        try {
+            subscriber.onSubscribe(new RangeSubscription(subscriber, start, start + count));
+        } catch (Throwable ex) {
+            new IllegalStateException(subscriber + " violated the Reactive Streams rule 2.13 " +
+                    "by throwing an exception from onSubscribe.", ex)
+                    // When onSubscribe fails this way, we don't know what state the
+                    // subscriber is thus calling onError may cause more crashes.
+                    .printStackTrace();
+        }
+    }
+
+    /**
+     * A Subscription implementation that holds the current downstream
+     * requested amount and responds to the downstream's request() and
+     * cancel() calls.
+     */
+    static final class RangeSubscription
+            // We are using this `AtomicLong` to make sure that this `Subscription`
+            // doesn't run concurrently with itself, which would violate rule 1.3
+            // among others (no concurrent notifications).
+            // The atomic transition from 0L to N > 0L will ensure this.
+            extends AtomicLong implements Subscription {
+
+        private static final long serialVersionUID = -9000845542177067735L;
+
+        /** The Subscriber we are emitting integer values to. */
+        final Subscriber<? super Integer> downstream;
+
+        /** The end index (exclusive). */
+        final int end;
+
+        /**
+         * The current index and within the [start, start + count) range that
+         * will be emitted as downstream.onNext().
+         */
+        int index;
+
+        /**
+         * Indicates the emission should stop.
+         */
+        volatile boolean cancelled;
+
+        /**
+         * Holds onto the IllegalArgumentException (containing the offending stacktrace)
+         * indicating there was a non-positive request() call from the downstream.
+         */
+        volatile Throwable invalidRequest;
+
+        /**
+         * Constructs a stateful RangeSubscription that emits signals to the given
+         * downstream from an integer range of [start, end).
+         * @param downstream the Subscriber receiving the integer values and the completion signal.
+         * @param start the first integer value emitted, start of the range
+         * @param end the end of the range, exclusive
+         */
+        RangeSubscription(Subscriber<? super Integer> downstream, int start, int end) {
+            this.downstream = downstream;
+            this.index = start;
+            this.end = end;
+        }
+
+        // This method will register inbound demand from our `Subscriber` and
+        // validate it against rule 3.9 and rule 3.17
+        @Override
+        public void request(long n) {
+            // Non-positive requests should be honored with IllegalArgumentException
+            if (n <= 0L) {
+                invalidRequest = new IllegalArgumentException("§3.9: non-positive requests are not allowed!");
+                n = 1;
+            }
+            // Downstream requests are cumulative and may come from any thread
+            for (;;) {
+                long requested = get();
+                long update = requested + n;
+                // As governed by rule 3.17, when demand overflows `Long.MAX_VALUE`
+                // we treat the signalled demand as "effectively unbounded"
+                if (update < 0L) {
+                    update = Long.MAX_VALUE;
+                }
+                // atomically update the current requested amount
+                if (compareAndSet(requested, update)) {
+                    // if there was no prior request amount, we start the emission loop
+                    if (requested == 0L) {
+                        emit(update);
+                    }
+                    break;
+                }
+            }
+        }
+
+        // This handles cancellation requests, and is idempotent, thread-safe and not
+        // synchronously performing heavy computations as specified in rule 3.5
+        @Override
+        public void cancel() {
+            // Indicate to the emission loop it should stop.
+            cancelled = true;
+        }
+
+        void emit(long currentRequested) {
+            // Load fields to avoid re-reading them from memory due to volatile accesses in the loop.
+            Subscriber<? super Integer> downstream = this.downstream;
+            int index = this.index;
+            int end = this.end;
+            int emitted = 0;
+
+            try {
+                for (; ; ) {
+                    // Check if there was an invalid request and then report its exception
+                    // as mandated by rule 3.9. The stacktrace in it should
+                    // help locate the faulty logic in the Subscriber.
+                    Throwable invalidRequest = this.invalidRequest;
+                    if (invalidRequest != null) {
+                        // When we signal onError, the subscription must be considered as cancelled, as per rule 1.6
+                        cancelled = true;
+
+                        downstream.onError(invalidRequest);
+                        return;
+                    }
+
+                    // Loop while the index hasn't reached the end and we haven't
+                    // emitted all that's been requested
+                    while (index != end && emitted != currentRequested) {
+                        // to make sure that we follow rule 1.8, 3.6 and 3.7
+                        // We stop if cancellation was requested.
+                        if (cancelled) {
+                            return;
+                        }
+
+                        downstream.onNext(index);
+
+                        // Increment the index for the next possible emission.
+                        index++;
+                        // Increment the emitted count to prevent overflowing the downstream.
+                        emitted++;
+                    }
+
+                    // If the index reached the end, we complete the downstream.
+                    if (index == end) {
+                        // to make sure that we follow rule 1.8, 3.6 and 3.7
+                        // Unless cancellation was requested by the last onNext.
+                        if (!cancelled) {
+                            // We need to consider this `Subscription` as cancelled as per rule 1.6
+                            // Note, however, that this state is not observable from the outside
+                            // world and since we leave the loop with requested > 0L, any
+                            // further request() will never trigger the loop.
+                            cancelled = true;
+
+                            downstream.onComplete();
+                        }
+                        return;
+                    }
+
+                    // Did the requested amount change while we were looping?
+                    long freshRequested = get();
+                    if (freshRequested == currentRequested) {
+                        // Save where the loop has left off: the next value to be emitted
+                        this.index = index;
+                        // Atomically subtract the previously requested (also emitted) amount
+                        currentRequested = addAndGet(-currentRequested);
+                        // If there was no new request in between get() and addAndGet(), we simply quit
+                        // The next 0 to N transition in request() will trigger the next emission loop.
+                        if (currentRequested == 0L) {
+                            break;
+                        }
+                        // Looks like there were more async requests, reset the emitted count and continue.
+                        emitted = 0;
+                    } else {
+                        // Yes, avoid the atomic subtraction and resume.
+                        // emitted != currentRequest in this case and index
+                        // still points to the next value to be emitted
+                        currentRequested = freshRequested;
+                    }
+                }
+            } catch (Throwable ex) {
+                // We can only get here if `onNext`, `onError` or `onComplete` threw, and they
+                // are not allowed to according to 2.13, so we can only cancel and log here.
+                // If `onError` throws an exception, this is a spec violation according to rule 1.9,
+                // and all we can do is to log it.
+
+                // Make sure that we are cancelled, since we cannot do anything else
+                // since the `Subscriber` is faulty.
+                cancelled = true;
+
+                // We can't report the failure to onError as the Subscriber is unreliable.
+                (new IllegalStateException(downstream + " violated the Reactive Streams rule 2.13 by " +
+                        "throwing an exception from onNext, onError or onComplete.", ex))
+                        .printStackTrace();
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/example/unicast/SyncSubscriber.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.example.unicast;
+
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+
+/**
+ * SyncSubscriber is an implementation of Reactive Streams `Subscriber`,
+ * it runs synchronously (on the Publisher's thread) and requests one element
+ * at a time and invokes a user-defined method to process each element.
+ *
+ * NOTE: The code below uses a lot of try-catches to show the reader where exceptions can be expected, and where they are forbidden.
+ */
+public abstract class SyncSubscriber<T> implements Subscriber<T> {
+  private Subscription subscription; // Obeying rule 3.1, we make this private!
+  private boolean done = false;
+
+  @Override public void onSubscribe(final Subscription s) {
+    // As per rule 2.13, we need to throw a `java.lang.NullPointerException` if the `Subscription` is `null`
+    if (s == null) throw null;
+
+    if (subscription != null) { // If someone has made a mistake and added this Subscriber multiple times, let's handle it gracefully
+      try {
+        s.cancel(); // Cancel the additional subscription
+      } catch(final Throwable t) {
+        //Subscription.cancel is not allowed to throw an exception, according to rule 3.15
+        (new IllegalStateException(s + " violated the Reactive Streams rule 3.15 by throwing an exception from cancel.", t)).printStackTrace(System.err);
+      }
+    } else {
+      // We have to assign it locally before we use it, if we want to be a synchronous `Subscriber`
+      // Because according to rule 3.10, the Subscription is allowed to call `onNext` synchronously from within `request`
+      subscription = s;
+      try {
+        // If we want elements, according to rule 2.1 we need to call `request`
+        // And, according to rule 3.2 we are allowed to call this synchronously from within the `onSubscribe` method
+        s.request(1); // Our Subscriber is unbuffered and modest, it requests one element at a time
+      } catch(final Throwable t) {
+        // Subscription.request is not allowed to throw according to rule 3.16
+        (new IllegalStateException(s + " violated the Reactive Streams rule 3.16 by throwing an exception from request.", t)).printStackTrace(System.err);
+      }
+    }
+  }
+
+  @Override public void onNext(final T element) {
+    if (subscription == null) { // Technically this check is not needed, since we are expecting Publishers to conform to the spec
+      (new IllegalStateException("Publisher violated the Reactive Streams rule 1.09 signalling onNext prior to onSubscribe.")).printStackTrace(System.err);
+    } else {
+      // As per rule 2.13, we need to throw a `java.lang.NullPointerException` if the `element` is `null`
+      if (element == null) throw null;
+
+      if (!done) { // If we aren't already done
+        try {
+          if (whenNext(element)) {
+            try {
+              subscription.request(1); // Our Subscriber is unbuffered and modest, it requests one element at a time
+            } catch (final Throwable t) {
+              // Subscription.request is not allowed to throw according to rule 3.16
+              (new IllegalStateException(subscription + " violated the Reactive Streams rule 3.16 by throwing an exception from request.", t)).printStackTrace(System.err);
+            }
+          } else {
+            done();
+          }
+        } catch (final Throwable t) {
+          done();
+          try {
+            onError(t);
+          } catch (final Throwable t2) {
+            //Subscriber.onError is not allowed to throw an exception, according to rule 2.13
+            (new IllegalStateException(this + " violated the Reactive Streams rule 2.13 by throwing an exception from onError.", t2)).printStackTrace(System.err);
+          }
+        }
+      }
+    }
+  }
+
+  // Showcases a convenience method to idempotently marking the Subscriber as "done", so we don't want to process more elements
+  // herefor we also need to cancel our `Subscription`.
+  private void done() {
+    //On this line we could add a guard against `!done`, but since rule 3.7 says that `Subscription.cancel()` is idempotent, we don't need to.
+    done = true; // If we `whenNext` throws an exception, let's consider ourselves done (not accepting more elements)
+    try {
+      subscription.cancel(); // Cancel the subscription
+    } catch(final Throwable t) {
+      //Subscription.cancel is not allowed to throw an exception, according to rule 3.15
+      (new IllegalStateException(subscription + " violated the Reactive Streams rule 3.15 by throwing an exception from cancel.", t)).printStackTrace(System.err);
+    }
+  }
+
+  // This method is left as an exercise to the reader/extension point
+  // Returns whether more elements are desired or not, and if no more elements are desired
+  protected abstract boolean whenNext(final T element);
+
+  @Override public void onError(final Throwable t) {
+    if (subscription == null) { // Technically this check is not needed, since we are expecting Publishers to conform to the spec
+      (new IllegalStateException("Publisher violated the Reactive Streams rule 1.09 signalling onError prior to onSubscribe.")).printStackTrace(System.err);
+    } else {
+      // As per rule 2.13, we need to throw a `java.lang.NullPointerException` if the `Throwable` is `null`
+      if (t == null) throw null;
+      // Here we are not allowed to call any methods on the `Subscription` or the `Publisher`, as per rule 2.3
+      // And anyway, the `Subscription` is considered to be cancelled if this method gets called, as per rule 2.4
+    }
+  }
+
+  @Override public void onComplete() {
+    if (subscription == null) { // Technically this check is not needed, since we are expecting Publishers to conform to the spec
+      (new IllegalStateException("Publisher violated the Reactive Streams rule 1.09 signalling onComplete prior to onSubscribe.")).printStackTrace(System.err);
+    } else {
+      // Here we are not allowed to call any methods on the `Subscription` or the `Publisher`, as per rule 2.3
+      // And anyway, the `Subscription` is considered to be cancelled if this method gets called, as per rule 2.4
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/IdentityProcessorVerification.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,896 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.tck;
+
+import org.reactivestreams.Processor;
+import org.reactivestreams.Publisher;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+import org.reactivestreams.tck.TestEnvironment.ManualPublisher;
+import org.reactivestreams.tck.TestEnvironment.ManualSubscriber;
+import org.reactivestreams.tck.TestEnvironment.ManualSubscriberWithSubscriptionSupport;
+import org.reactivestreams.tck.TestEnvironment.Promise;
+import org.reactivestreams.tck.flow.support.Function;
+import org.reactivestreams.tck.flow.support.SubscriberWhiteboxVerificationRules;
+import org.reactivestreams.tck.flow.support.PublisherVerificationRules;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public abstract class IdentityProcessorVerification<T> extends WithHelperPublisher<T>
+  implements SubscriberWhiteboxVerificationRules, PublisherVerificationRules {
+
+  private final TestEnvironment env;
+
+  ////////////////////// DELEGATED TO SPECS //////////////////////
+
+  // for delegating tests
+  private final SubscriberWhiteboxVerification<T> subscriberVerification;
+
+  // for delegating tests
+  private final PublisherVerification<T> publisherVerification;
+
+  ////////////////// END OF DELEGATED TO SPECS //////////////////
+
+  // number of elements the processor under test must be able ot buffer,
+  // without dropping elements. Defaults to `TestEnvironment.TEST_BUFFER_SIZE`.
+  private final int processorBufferSize;
+
+  /**
+   * Test class must specify the expected time it takes for the publisher to
+   * shut itself down when the the last downstream {@code Subscription} is cancelled.
+   *
+   * The processor will be required to be able to buffer {@code TestEnvironment.TEST_BUFFER_SIZE} elements.
+   */
+  @SuppressWarnings("unused")
+  public IdentityProcessorVerification(final TestEnvironment env) {
+    this(env, PublisherVerification.envPublisherReferenceGCTimeoutMillis(), TestEnvironment.TEST_BUFFER_SIZE);
+  }
+
+  /**
+   * Test class must specify the expected time it takes for the publisher to
+   * shut itself down when the the last downstream {@code Subscription} is cancelled.
+   *
+   * The processor will be required to be able to buffer {@code TestEnvironment.TEST_BUFFER_SIZE} elements.
+   *
+   * @param publisherReferenceGCTimeoutMillis used to determine after how much time a reference to a Subscriber should be already dropped by the Publisher.
+   */
+  @SuppressWarnings("unused")
+  public IdentityProcessorVerification(final TestEnvironment env, long publisherReferenceGCTimeoutMillis) {
+    this(env, publisherReferenceGCTimeoutMillis, TestEnvironment.TEST_BUFFER_SIZE);
+  }
+
+  /**
+   * Test class must specify the expected time it takes for the publisher to
+   * shut itself down when the the last downstream {@code Subscription} is cancelled.
+   *
+   * @param publisherReferenceGCTimeoutMillis used to determine after how much time a reference to a Subscriber should be already dropped by the Publisher.
+   * @param processorBufferSize            number of elements the processor is required to be able to buffer.
+   */
+  public IdentityProcessorVerification(final TestEnvironment env, long publisherReferenceGCTimeoutMillis, int processorBufferSize) {
+    this.env = env;
+    this.processorBufferSize = processorBufferSize;
+
+    this.subscriberVerification = new SubscriberWhiteboxVerification<T>(env) {
+      @Override
+      public Subscriber<T> createSubscriber(WhiteboxSubscriberProbe<T> probe) {
+        return IdentityProcessorVerification.this.createSubscriber(probe);
+      }
+
+      @Override public T createElement(int element) {
+        return IdentityProcessorVerification.this.createElement(element);
+      }
+
+      @Override
+      public Publisher<T> createHelperPublisher(long elements) {
+        return IdentityProcessorVerification.this.createHelperPublisher(elements);
+      }
+    };
+
+    publisherVerification = new PublisherVerification<T>(env, publisherReferenceGCTimeoutMillis) {
+      @Override
+      public Publisher<T> createPublisher(long elements) {
+        return IdentityProcessorVerification.this.createPublisher(elements);
+      }
+
+      @Override
+      public Publisher<T> createFailedPublisher() {
+        return IdentityProcessorVerification.this.createFailedPublisher();
+      }
+
+      @Override
+      public long maxElementsFromPublisher() {
+        return IdentityProcessorVerification.this.maxElementsFromPublisher();
+      }
+
+      @Override
+      public long boundedDepthOfOnNextAndRequestRecursion() {
+        return IdentityProcessorVerification.this.boundedDepthOfOnNextAndRequestRecursion();
+      }
+
+      @Override
+      public boolean skipStochasticTests() {
+        return IdentityProcessorVerification.this.skipStochasticTests();
+      }
+    };
+  }
+
+  /**
+   * This is the main method you must implement in your test incarnation.
+   * It must create a {@link Processor}, which simply forwards all stream elements from its upstream
+   * to its downstream. It must be able to internally buffer the given number of elements.
+   *
+   * @param bufferSize number of elements the processor is required to be able to buffer.
+   */
+  public abstract Processor<T, T> createIdentityProcessor(int bufferSize);
+
+  /**
+   * By implementing this method, additional TCK tests concerning a "failed" publishers will be run.
+   *
+   * The expected behaviour of the {@link Publisher} returned by this method is hand out a subscription,
+   * followed by signalling {@code onError} on it, as specified by Rule 1.9.
+   *
+   * If you want to ignore these additional tests, return {@code null} from this method.
+   */
+  public abstract Publisher<T> createFailedPublisher();
+
+  /**
+   * Override and return lower value if your Publisher is only able to produce a known number of elements.
+   * For example, if it is designed to return at-most-one element, return {@code 1} from this method.
+   *
+   * Defaults to {@code Long.MAX_VALUE - 1}, meaning that the Publisher can be produce a huge but NOT an unbounded number of elements.
+   *
+   * To mark your Publisher will *never* signal an {@code onComplete} override this method and return {@code Long.MAX_VALUE},
+   * which will result in *skipping all tests which require an onComplete to be triggered* (!).
+   */
+  public long maxElementsFromPublisher() {
+    return Long.MAX_VALUE - 1;
+  }
+
+  /**
+   * In order to verify rule 3.3 of the reactive streams spec, this number will be used to check if a
+   * {@code Subscription} actually solves the "unbounded recursion" problem by not allowing the number of
+   * recursive calls to exceed the number returned by this method.
+   *
+   * @see <a href="https://github.com/reactive-streams/reactive-streams-jvm#3.3">reactive streams spec, rule 3.3</a>
+   * @see PublisherVerification#required_spec303_mustNotAllowUnboundedRecursion()
+   */
+  public long boundedDepthOfOnNextAndRequestRecursion() {
+    return 1;
+  }
+
+  /**
+   * Override and return {@code true} in order to skip executing tests marked as {@code Stochastic}.
+   * Stochastic in this case means that the Rule is impossible or infeasible to deterministically verify—
+   * usually this means that this test case can yield false positives ("be green") even if for some case,
+   * the given implementation may violate the tested behaviour.
+   */
+  public boolean skipStochasticTests() {
+    return false;
+  }
+
+  /**
+   * Describes the tested implementation in terms of how many subscribers they can support.
+   * Some tests require the {@code Publisher} under test to support multiple Subscribers,
+   * yet the spec does not require all publishers to be able to do so, thus – if an implementation
+   * supports only a limited number of subscribers (e.g. only 1 subscriber, also known as "no fanout")
+   * you MUST return that number from this method by overriding it.
+   */
+  public long maxSupportedSubscribers() {
+      return Long.MAX_VALUE;
+  }
+
+  /**
+   * Override this method and return {@code true} if the {@link Processor} returned by the
+   * {@link #createIdentityProcessor(int)} coordinates its {@link Subscriber}s
+   * request amounts and only delivers onNext signals if all Subscribers have
+   * indicated (via their Subscription#request(long)) they are ready to receive elements.
+   */
+  public boolean doesCoordinatedEmission() {
+    return false;
+  }
+
+  ////////////////////// TEST ENV CLEANUP /////////////////////////////////////
+
+  @BeforeMethod
+  public void setUp() throws Exception {
+    publisherVerification.setUp();
+    subscriberVerification.setUp();
+  }
+
+  ////////////////////// PUBLISHER RULES VERIFICATION ///////////////////////////
+
+  // A Processor
+  //   must obey all Publisher rules on its publishing side
+  public Publisher<T> createPublisher(long elements) {
+    final Processor<T, T> processor = createIdentityProcessor(processorBufferSize);
+    final Publisher<T> pub = createHelperPublisher(elements);
+    pub.subscribe(processor);
+    return processor; // we run the PublisherVerification against this
+  }
+
+  @Override @Test
+  public void required_validate_maxElementsFromPublisher() throws Exception {
+    publisherVerification.required_validate_maxElementsFromPublisher();
+  }
+
+  @Override @Test
+  public void required_validate_boundedDepthOfOnNextAndRequestRecursion() throws Exception {
+    publisherVerification.required_validate_boundedDepthOfOnNextAndRequestRecursion();
+  }
+
+  /////////////////////// DELEGATED TESTS, A PROCESSOR "IS A" PUBLISHER //////////////////////
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#4.1
+
+  @Test
+  public void required_createPublisher1MustProduceAStreamOfExactly1Element() throws Throwable {
+    publisherVerification.required_createPublisher1MustProduceAStreamOfExactly1Element();
+  }
+
+  @Test
+  public void required_createPublisher3MustProduceAStreamOfExactly3Elements() throws Throwable {
+    publisherVerification.required_createPublisher3MustProduceAStreamOfExactly3Elements();
+  }
+
+  @Override @Test
+  public void required_spec101_subscriptionRequestMustResultInTheCorrectNumberOfProducedElements() throws Throwable {
+    publisherVerification.required_spec101_subscriptionRequestMustResultInTheCorrectNumberOfProducedElements();
+  }
+
+  @Override @Test
+  public void required_spec102_maySignalLessThanRequestedAndTerminateSubscription() throws Throwable {
+    publisherVerification.required_spec102_maySignalLessThanRequestedAndTerminateSubscription();
+  }
+
+  @Override @Test
+  public void stochastic_spec103_mustSignalOnMethodsSequentially() throws Throwable {
+    publisherVerification.stochastic_spec103_mustSignalOnMethodsSequentially();
+  }
+
+  @Override @Test
+  public void optional_spec104_mustSignalOnErrorWhenFails() throws Throwable {
+    publisherVerification.optional_spec104_mustSignalOnErrorWhenFails();
+  }
+
+  @Override @Test
+  public void required_spec105_mustSignalOnCompleteWhenFiniteStreamTerminates() throws Throwable {
+    publisherVerification.required_spec105_mustSignalOnCompleteWhenFiniteStreamTerminates();
+  }
+
+  @Override @Test
+  public void optional_spec105_emptyStreamMustTerminateBySignallingOnComplete() throws Throwable {
+    publisherVerification.optional_spec105_emptyStreamMustTerminateBySignallingOnComplete();
+  }
+
+  @Override @Test
+  public void untested_spec106_mustConsiderSubscriptionCancelledAfterOnErrorOrOnCompleteHasBeenCalled() throws Throwable {
+    publisherVerification.untested_spec106_mustConsiderSubscriptionCancelledAfterOnErrorOrOnCompleteHasBeenCalled();
+  }
+
+  @Override @Test
+  public void required_spec107_mustNotEmitFurtherSignalsOnceOnCompleteHasBeenSignalled() throws Throwable {
+    publisherVerification.required_spec107_mustNotEmitFurtherSignalsOnceOnCompleteHasBeenSignalled();
+  }
+
+  @Override @Test
+  public void untested_spec107_mustNotEmitFurtherSignalsOnceOnErrorHasBeenSignalled() throws Throwable {
+    publisherVerification.untested_spec107_mustNotEmitFurtherSignalsOnceOnErrorHasBeenSignalled();
+  }
+
+  @Override @Test
+  public void untested_spec108_possiblyCanceledSubscriptionShouldNotReceiveOnErrorOrOnCompleteSignals() throws Throwable {
+    publisherVerification.untested_spec108_possiblyCanceledSubscriptionShouldNotReceiveOnErrorOrOnCompleteSignals();
+  }
+
+  @Override @Test
+  public void untested_spec109_subscribeShouldNotThrowNonFatalThrowable() throws Throwable {
+    publisherVerification.untested_spec109_subscribeShouldNotThrowNonFatalThrowable();
+  }
+
+  @Override @Test
+  public void required_spec109_subscribeThrowNPEOnNullSubscriber() throws Throwable {
+    publisherVerification.required_spec109_subscribeThrowNPEOnNullSubscriber();
+  }
+
+  @Override @Test
+  public void required_spec109_mayRejectCallsToSubscribeIfPublisherIsUnableOrUnwillingToServeThemRejectionMustTriggerOnErrorAfterOnSubscribe() throws Throwable {
+    publisherVerification.required_spec109_mayRejectCallsToSubscribeIfPublisherIsUnableOrUnwillingToServeThemRejectionMustTriggerOnErrorAfterOnSubscribe();
+  }
+
+  @Override @Test
+  public void required_spec109_mustIssueOnSubscribeForNonNullSubscriber() throws Throwable {
+    publisherVerification.required_spec109_mustIssueOnSubscribeForNonNullSubscriber();
+  }
+
+  @Override @Test
+  public void untested_spec110_rejectASubscriptionRequestIfTheSameSubscriberSubscribesTwice() throws Throwable {
+    publisherVerification.untested_spec110_rejectASubscriptionRequestIfTheSameSubscriberSubscribesTwice();
+  }
+
+  @Override @Test
+  public void optional_spec111_maySupportMultiSubscribe() throws Throwable {
+    publisherVerification.optional_spec111_maySupportMultiSubscribe();
+  }
+
+  @Override @Test
+  public void optional_spec111_registeredSubscribersMustReceiveOnNextOrOnCompleteSignals() throws Throwable {
+    publisherVerification.optional_spec111_registeredSubscribersMustReceiveOnNextOrOnCompleteSignals();
+  }
+
+  @Override @Test
+  public void optional_spec111_multicast_mustProduceTheSameElementsInTheSameSequenceToAllOfItsSubscribersWhenRequestingOneByOne() throws Throwable {
+    publisherVerification.optional_spec111_multicast_mustProduceTheSameElementsInTheSameSequenceToAllOfItsSubscribersWhenRequestingOneByOne();
+  }
+
+  @Override @Test
+  public void optional_spec111_multicast_mustProduceTheSameElementsInTheSameSequenceToAllOfItsSubscribersWhenRequestingManyUpfront() throws Throwable {
+    publisherVerification.optional_spec111_multicast_mustProduceTheSameElementsInTheSameSequenceToAllOfItsSubscribersWhenRequestingManyUpfront();
+  }
+
+  @Override @Test
+  public void optional_spec111_multicast_mustProduceTheSameElementsInTheSameSequenceToAllOfItsSubscribersWhenRequestingManyUpfrontAndCompleteAsExpected() throws Throwable {
+    publisherVerification.optional_spec111_multicast_mustProduceTheSameElementsInTheSameSequenceToAllOfItsSubscribersWhenRequestingManyUpfrontAndCompleteAsExpected();
+  }
+
+  @Override @Test
+  public void required_spec302_mustAllowSynchronousRequestCallsFromOnNextAndOnSubscribe() throws Throwable {
+    publisherVerification.required_spec302_mustAllowSynchronousRequestCallsFromOnNextAndOnSubscribe();
+  }
+
+  @Override @Test
+  public void required_spec303_mustNotAllowUnboundedRecursion() throws Throwable {
+    publisherVerification.required_spec303_mustNotAllowUnboundedRecursion();
+  }
+
+  @Override @Test
+  public void untested_spec304_requestShouldNotPerformHeavyComputations() throws Exception {
+    publisherVerification.untested_spec304_requestShouldNotPerformHeavyComputations();
+  }
+
+  @Override @Test
+  public void untested_spec305_cancelMustNotSynchronouslyPerformHeavyComputation() throws Exception {
+    publisherVerification.untested_spec305_cancelMustNotSynchronouslyPerformHeavyComputation();
+  }
+
+  @Override @Test
+  public void required_spec306_afterSubscriptionIsCancelledRequestMustBeNops() throws Throwable {
+    publisherVerification.required_spec306_afterSubscriptionIsCancelledRequestMustBeNops();
+  }
+
+  @Override @Test
+  public void required_spec307_afterSubscriptionIsCancelledAdditionalCancelationsMustBeNops() throws Throwable {
+    publisherVerification.required_spec307_afterSubscriptionIsCancelledAdditionalCancelationsMustBeNops();
+  }
+
+  @Override @Test
+  public void required_spec309_requestZeroMustSignalIllegalArgumentException() throws Throwable {
+    publisherVerification.required_spec309_requestZeroMustSignalIllegalArgumentException();
+  }
+
+  @Override @Test
+  public void required_spec309_requestNegativeNumberMustSignalIllegalArgumentException() throws Throwable {
+    publisherVerification.required_spec309_requestNegativeNumberMustSignalIllegalArgumentException();
+  }
+
+  @Override @Test
+  public void optional_spec309_requestNegativeNumberMaySignalIllegalArgumentExceptionWithSpecificMessage() throws Throwable {
+    publisherVerification.optional_spec309_requestNegativeNumberMaySignalIllegalArgumentExceptionWithSpecificMessage();
+  }
+
+  @Override @Test
+  public void required_spec312_cancelMustMakeThePublisherToEventuallyStopSignaling() throws Throwable {
+    publisherVerification.required_spec312_cancelMustMakeThePublisherToEventuallyStopSignaling();
+  }
+
+  @Override @Test
+  public void required_spec313_cancelMustMakeThePublisherEventuallyDropAllReferencesToTheSubscriber() throws Throwable {
+    publisherVerification.required_spec313_cancelMustMakeThePublisherEventuallyDropAllReferencesToTheSubscriber();
+  }
+
+  @Override @Test
+  public void required_spec317_mustSupportAPendingElementCountUpToLongMaxValue() throws Throwable {
+    publisherVerification.required_spec317_mustSupportAPendingElementCountUpToLongMaxValue();
+  }
+
+  @Override @Test
+  public void required_spec317_mustSupportACumulativePendingElementCountUpToLongMaxValue() throws Throwable {
+    publisherVerification.required_spec317_mustSupportACumulativePendingElementCountUpToLongMaxValue();
+  }
+
+  @Override @Test
+  public void required_spec317_mustNotSignalOnErrorWhenPendingAboveLongMaxValue() throws Throwable {
+    publisherVerification.required_spec317_mustNotSignalOnErrorWhenPendingAboveLongMaxValue();
+  }
+
+
+  /**
+   * Asks for a {@code Processor} that supports at least 2 {@code Subscriber}s at once and checks if two {@code Subscriber}s
+   * receive the same items and a terminal {@code Exception}.
+   * <p>
+   * If the {@code Processor} requests and/or emits items only when all of its {@code Subscriber}s have requested,
+   * override {@link #doesCoordinatedEmission()} and return {@code true} to indicate this property.
+   * <p>
+   * <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.4'>1.4</a> with multiple
+   * {@code Subscriber}s.
+   * <p>
+   * The test is not executed if {@link IdentityProcessorVerification#maxSupportedSubscribers()} is less than 2.
+   * <p>
+   * If this test fails, the following could be checked within the {@code Processor} implementation:
+   * <ul>
+   * <li>The {@code TestEnvironment} has large enough timeout specified in case the {@code Processor} has some time-delay behavior.</li>
+   * <li>The {@code Processor} is able to fulfill requests of its {@code Subscriber}s independently of each other's requests or
+   * else override {@link #doesCoordinatedEmission()} and return {@code true} to indicate the test {@code Subscriber}s
+   * both have to request first.</li>
+   * </ul>
+   */
+  @Test
+  public void required_spec104_mustCallOnErrorOnAllItsSubscribersIfItEncountersANonRecoverableError() throws Throwable {
+    optionalMultipleSubscribersTest(2, new Function<Long,TestSetup>() {
+      @Override
+      public TestSetup apply(Long aLong) throws Throwable {
+        return new TestSetup(env, processorBufferSize) {{
+          final ManualSubscriberWithErrorCollection<T> sub1 = new ManualSubscriberWithErrorCollection<T>(env);
+          env.subscribe(processor, sub1);
+
+          final ManualSubscriberWithErrorCollection<T> sub2 = new ManualSubscriberWithErrorCollection<T>(env);
+          env.subscribe(processor, sub2);
+
+          final Exception ex = new RuntimeException("Test exception");
+
+          if (doesCoordinatedEmission()) {
+            sub1.request(1);
+            sub2.request(1);
+
+            expectRequest();
+
+            final T x = sendNextTFromUpstream();
+
+            expectNextElement(sub1, x);
+            expectNextElement(sub2, x);
+
+            sub1.request(1);
+            sub2.request(1);
+          } else {
+            sub1.request(1);
+
+            expectRequest(env.defaultTimeoutMillis(),
+                    "If the Processor coordinates requests/emissions when having multiple Subscribers"
+                    + " at once, please override doesCoordinatedEmission() to return true in this "
+                    + "IdentityProcessorVerification to allow this test to pass.");
+
+            final T x = sendNextTFromUpstream();
+            expectNextElement(sub1, x,
+                    "If the Processor coordinates requests/emissions when having multiple Subscribers"
+                            + " at once, please override doesCoordinatedEmission() to return true in this "
+                            + "IdentityProcessorVerification to allow this test to pass.");
+
+            sub1.request(1);
+
+            // sub1 has received one element, and has one demand pending
+            // sub2 has not yet requested anything
+          }
+          sendError(ex);
+
+          sub1.expectError(ex);
+          sub2.expectError(ex);
+
+          env.verifyNoAsyncErrorsNoDelay();
+        }};
+      }
+    });
+  }
+
+  ////////////////////// SUBSCRIBER RULES VERIFICATION ///////////////////////////
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#4.1
+
+  // A Processor
+  //   must obey all Subscriber rules on its consuming side
+  public Subscriber<T> createSubscriber(final SubscriberWhiteboxVerification.WhiteboxSubscriberProbe<T> probe) {
+    final Processor<T, T> processor = createIdentityProcessor(processorBufferSize);
+    processor.subscribe(
+        new Subscriber<T>() {
+          private final Promise<Subscription> subs = new Promise<Subscription>(env);
+
+          @Override
+          public void onSubscribe(final Subscription subscription) {
+            if (env.debugEnabled()) {
+              env.debug(String.format("whiteboxSubscriber::onSubscribe(%s)", subscription));
+            }
+            if (subs.isCompleted()) subscription.cancel(); // the Probe must also pass subscriber verification
+
+            probe.registerOnSubscribe(new SubscriberWhiteboxVerification.SubscriberPuppet() {
+
+              @Override
+              public void triggerRequest(long elements) {
+                subscription.request(elements);
+              }
+
+              @Override
+              public void signalCancel() {
+                subscription.cancel();
+              }
+            });
+          }
+
+          @Override
+          public void onNext(T element) {
+            if (env.debugEnabled()) {
+              env.debug(String.format("whiteboxSubscriber::onNext(%s)", element));
+            }
+            probe.registerOnNext(element);
+          }
+
+          @Override
+          public void onComplete() {
+            if (env.debugEnabled()) {
+              env.debug("whiteboxSubscriber::onComplete()");
+            }
+            probe.registerOnComplete();
+          }
+
+          @Override
+          public void onError(Throwable cause) {
+            if (env.debugEnabled()) {
+              env.debug(String.format("whiteboxSubscriber::onError(%s)", cause));
+            }
+            probe.registerOnError(cause);
+          }
+        });
+
+    return processor; // we run the SubscriberVerification against this
+  }
+
+  ////////////////////// OTHER RULE VERIFICATION ///////////////////////////
+
+  // A Processor
+  //   must immediately pass on `onError` events received from its upstream to its downstream
+  @Test
+  public void mustImmediatelyPassOnOnErrorEventsReceivedFromItsUpstreamToItsDownstream() throws Exception {
+    new TestSetup(env, processorBufferSize) {{
+      final ManualSubscriberWithErrorCollection<T> sub = new ManualSubscriberWithErrorCollection<T>(env);
+      env.subscribe(processor, sub);
+
+      final Exception ex = new RuntimeException("Test exception");
+      sendError(ex);
+      sub.expectError(ex); // "immediately", i.e. without a preceding request
+
+      env.verifyNoAsyncErrorsNoDelay();
+    }};
+  }
+
+  /////////////////////// DELEGATED TESTS, A PROCESSOR "IS A" SUBSCRIBER //////////////////////
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#4.1
+
+  @Test
+  public void required_exerciseWhiteboxHappyPath() throws Throwable {
+    subscriberVerification.required_exerciseWhiteboxHappyPath();
+  }
+
+  @Override @Test
+  public void required_spec201_mustSignalDemandViaSubscriptionRequest() throws Throwable {
+    subscriberVerification.required_spec201_mustSignalDemandViaSubscriptionRequest();
+  }
+
+  @Override @Test
+  public void untested_spec202_shouldAsynchronouslyDispatch() throws Exception {
+    subscriberVerification.untested_spec202_shouldAsynchronouslyDispatch();
+  }
+
+  @Override @Test
+  public void required_spec203_mustNotCallMethodsOnSubscriptionOrPublisherInOnComplete() throws Throwable {
+    subscriberVerification.required_spec203_mustNotCallMethodsOnSubscriptionOrPublisherInOnComplete();
+  }
+
+  @Override @Test
+  public void required_spec203_mustNotCallMethodsOnSubscriptionOrPublisherInOnError() throws Throwable {
+    subscriberVerification.required_spec203_mustNotCallMethodsOnSubscriptionOrPublisherInOnError();
+  }
+
+  @Override @Test
+  public void untested_spec204_mustConsiderTheSubscriptionAsCancelledInAfterRecievingOnCompleteOrOnError() throws Exception {
+    subscriberVerification.untested_spec204_mustConsiderTheSubscriptionAsCancelledInAfterRecievingOnCompleteOrOnError();
+  }
+
+  @Override @Test
+  public void required_spec205_mustCallSubscriptionCancelIfItAlreadyHasAnSubscriptionAndReceivesAnotherOnSubscribeSignal() throws Throwable {
+    subscriberVerification.required_spec205_mustCallSubscriptionCancelIfItAlreadyHasAnSubscriptionAndReceivesAnotherOnSubscribeSignal();
+  }
+
+  @Override @Test
+  public void untested_spec206_mustCallSubscriptionCancelIfItIsNoLongerValid() throws Exception {
+    subscriberVerification.untested_spec206_mustCallSubscriptionCancelIfItIsNoLongerValid();
+  }
+
+  @Override @Test
+  public void untested_spec207_mustEnsureAllCallsOnItsSubscriptionTakePlaceFromTheSameThreadOrTakeCareOfSynchronization() throws Exception {
+    subscriberVerification.untested_spec207_mustEnsureAllCallsOnItsSubscriptionTakePlaceFromTheSameThreadOrTakeCareOfSynchronization();
+  }
+
+  @Override @Test
+  public void required_spec208_mustBePreparedToReceiveOnNextSignalsAfterHavingCalledSubscriptionCancel() throws Throwable {
+    subscriberVerification.required_spec208_mustBePreparedToReceiveOnNextSignalsAfterHavingCalledSubscriptionCancel();
+  }
+
+  @Override @Test
+  public void required_spec209_mustBePreparedToReceiveAnOnCompleteSignalWithPrecedingRequestCall() throws Throwable {
+    subscriberVerification.required_spec209_mustBePreparedToReceiveAnOnCompleteSignalWithPrecedingRequestCall();
+  }
+
+  @Override @Test
+  public void required_spec209_mustBePreparedToReceiveAnOnCompleteSignalWithoutPrecedingRequestCall() throws Throwable {
+    subscriberVerification.required_spec209_mustBePreparedToReceiveAnOnCompleteSignalWithoutPrecedingRequestCall();
+  }
+
+  @Override @Test
+  public void required_spec210_mustBePreparedToReceiveAnOnErrorSignalWithPrecedingRequestCall() throws Throwable {
+    subscriberVerification.required_spec210_mustBePreparedToReceiveAnOnErrorSignalWithPrecedingRequestCall();
+  }
+
+  @Override @Test
+  public void required_spec210_mustBePreparedToReceiveAnOnErrorSignalWithoutPrecedingRequestCall() throws Throwable {
+    subscriberVerification.required_spec210_mustBePreparedToReceiveAnOnErrorSignalWithoutPrecedingRequestCall();
+  }
+
+  @Override @Test
+  public void untested_spec211_mustMakeSureThatAllCallsOnItsMethodsHappenBeforeTheProcessingOfTheRespectiveEvents() throws Exception {
+    subscriberVerification.untested_spec211_mustMakeSureThatAllCallsOnItsMethodsHappenBeforeTheProcessingOfTheRespectiveEvents();
+  }
+
+  @Override @Test
+  public void untested_spec212_mustNotCallOnSubscribeMoreThanOnceBasedOnObjectEquality_specViolation() throws Throwable {
+    subscriberVerification.untested_spec212_mustNotCallOnSubscribeMoreThanOnceBasedOnObjectEquality_specViolation();
+  }
+
+  @Override @Test
+  public void untested_spec213_failingOnSignalInvocation() throws Exception {
+    subscriberVerification.untested_spec213_failingOnSignalInvocation();
+  }
+
+  @Override @Test
+  public void required_spec213_onSubscribe_mustThrowNullPointerExceptionWhenParametersAreNull() throws Throwable {
+    subscriberVerification.required_spec213_onSubscribe_mustThrowNullPointerExceptionWhenParametersAreNull();
+  }
+  @Override @Test
+  public void required_spec213_onNext_mustThrowNullPointerExceptionWhenParametersAreNull() throws Throwable {
+    subscriberVerification.required_spec213_onNext_mustThrowNullPointerExceptionWhenParametersAreNull();
+  }
+  @Override @Test
+  public void required_spec213_onError_mustThrowNullPointerExceptionWhenParametersAreNull() throws Throwable {
+    subscriberVerification.required_spec213_onError_mustThrowNullPointerExceptionWhenParametersAreNull();
+  }
+
+  @Override @Test
+  public void untested_spec301_mustNotBeCalledOutsideSubscriberContext() throws Exception {
+    subscriberVerification.untested_spec301_mustNotBeCalledOutsideSubscriberContext();
+  }
+
+  @Override @Test
+  public void required_spec308_requestMustRegisterGivenNumberElementsToBeProduced() throws Throwable {
+    subscriberVerification.required_spec308_requestMustRegisterGivenNumberElementsToBeProduced();
+  }
+
+  @Override @Test
+  public void untested_spec310_requestMaySynchronouslyCallOnNextOnSubscriber() throws Exception {
+    subscriberVerification.untested_spec310_requestMaySynchronouslyCallOnNextOnSubscriber();
+  }
+
+  @Override @Test
+  public void untested_spec311_requestMaySynchronouslyCallOnCompleteOrOnError() throws Exception {
+    subscriberVerification.untested_spec311_requestMaySynchronouslyCallOnCompleteOrOnError();
+  }
+
+  @Override @Test
+  public void untested_spec314_cancelMayCauseThePublisherToShutdownIfNoOtherSubscriptionExists() throws Exception {
+    subscriberVerification.untested_spec314_cancelMayCauseThePublisherToShutdownIfNoOtherSubscriptionExists();
+  }
+
+  @Override @Test
+  public void untested_spec315_cancelMustNotThrowExceptionAndMustSignalOnError() throws Exception {
+    subscriberVerification.untested_spec315_cancelMustNotThrowExceptionAndMustSignalOnError();
+  }
+
+  @Override @Test
+  public void untested_spec316_requestMustNotThrowExceptionAndMustOnErrorTheSubscriber() throws Exception {
+    subscriberVerification.untested_spec316_requestMustNotThrowExceptionAndMustOnErrorTheSubscriber();
+  }
+
+  /////////////////////// ADDITIONAL "COROLLARY" TESTS //////////////////////
+
+  /**
+   * Asks for a {@code Processor} that supports at least 2 {@code Subscriber}s at once and checks requests
+   * from {@code Subscriber}s will eventually lead to requests towards the upstream of the {@code Processor}.
+   * <p>
+   * If the {@code Processor} requests and/or emits items only when all of its {@code Subscriber}s have requested,
+   * override {@link #doesCoordinatedEmission()} and return {@code true} to indicate this property.
+   * <p>
+   * <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.4'>2.1</a> with multiple
+   * {@code Subscriber}s.
+   * <p>
+   * The test is not executed if {@link IdentityProcessorVerification#maxSupportedSubscribers()} is less than 2.
+   * <p>
+   * If this test fails, the following could be checked within the {@code Processor} implementation:
+   * <ul>
+   * <li>The {@code TestEnvironment} has large enough timeout specified in case the {@code Processor} has some time-delay behavior.</li>
+   * <li>The {@code Processor} is able to fulfill requests of its {@code Subscriber}s independently of each other's requests or
+   * else override {@link #doesCoordinatedEmission()} and return {@code true} to indicate the test {@code Subscriber}s
+   * both have to request first.</li>
+   * </ul>
+   */
+  @Test
+  public void required_mustRequestFromUpstreamForElementsThatHaveBeenRequestedLongAgo() throws Throwable {
+    optionalMultipleSubscribersTest(2, new Function<Long,TestSetup>() {
+      @Override
+      public TestSetup apply(Long subscribers) throws Throwable {
+        return new TestSetup(env, processorBufferSize) {{
+          ManualSubscriber<T> sub1 = newSubscriber();
+          sub1.request(20);
+
+          long totalRequests = expectRequest();
+          final T x = sendNextTFromUpstream();
+          expectNextElement(sub1, x);
+
+          if (totalRequests == 1) {
+            totalRequests += expectRequest();
+          }
+          final T y = sendNextTFromUpstream();
+          expectNextElement(sub1, y);
+
+          if (totalRequests == 2) {
+            totalRequests += expectRequest();
+          }
+
+          final ManualSubscriber<T> sub2 = newSubscriber();
+
+          // sub1 now has 18 pending
+          // sub2 has 0 pending
+
+          if (doesCoordinatedEmission()) {
+            sub2.expectNone(); // since sub2 hasn't requested anything yet
+
+            sub2.request(1);
+
+            final T z = sendNextTFromUpstream();
+            expectNextElement(sub1, z);
+            expectNextElement(sub2, z);
+          } else {
+            final T z = sendNextTFromUpstream();
+            expectNextElement(sub1, z,
+                    "If the Processor coordinates requests/emissions when having multiple Subscribers"
+                            + " at once, please override doesCoordinatedEmission() to return true in this "
+                            + "IdentityProcessorVerification to allow this test to pass.");
+            sub2.expectNone(); // since sub2 hasn't requested anything yet
+
+            sub2.request(1);
+            expectNextElement(sub2, z);
+          }
+          if (totalRequests == 3) {
+            expectRequest();
+          }
+
+          // to avoid error messages during test harness shutdown
+          sendCompletion();
+          sub1.expectCompletion(env.defaultTimeoutMillis());
+          sub2.expectCompletion(env.defaultTimeoutMillis());
+
+          env.verifyNoAsyncErrorsNoDelay();
+        }};
+      }
+    });
+  }
+
+  /////////////////////// TEST INFRASTRUCTURE //////////////////////
+
+  public void notVerified() {
+    publisherVerification.notVerified();
+  }
+
+  public void notVerified(String message) {
+    publisherVerification.notVerified(message);
+  }
+
+  /**
+   * Test for feature that REQUIRES multiple subscribers to be supported by Publisher.
+   */
+  public void optionalMultipleSubscribersTest(long requiredSubscribersSupport, Function<Long, TestSetup> body) throws Throwable {
+    if (requiredSubscribersSupport > maxSupportedSubscribers())
+      notVerified(String.format("The Publisher under test only supports %d subscribers, while this test requires at least %d to run.",
+                                maxSupportedSubscribers(), requiredSubscribersSupport));
+    else body.apply(requiredSubscribersSupport);
+  }
+
+  public abstract class TestSetup extends ManualPublisher<T> {
+    final private ManualSubscriber<T> tees; // gives us access to an infinite stream of T values
+    private Set<T> seenTees = new HashSet<T>();
+
+    final Processor<T, T> processor;
+
+    public TestSetup(TestEnvironment env, int testBufferSize) throws InterruptedException {
+      super(env);
+      tees = env.newManualSubscriber(createHelperPublisher(Long.MAX_VALUE));
+      processor = createIdentityProcessor(testBufferSize);
+      subscribe(processor);
+    }
+
+    public ManualSubscriber<T> newSubscriber() throws InterruptedException {
+      return env.newManualSubscriber(processor);
+    }
+
+    public T nextT() throws InterruptedException {
+      final T t = tees.requestNextElement();
+      if (seenTees.contains(t)) {
+        env.flop(String.format("Helper publisher illegally produced the same element %s twice", t));
+      }
+      seenTees.add(t);
+      return t;
+    }
+
+    public void expectNextElement(ManualSubscriber<T> sub, T expected) throws InterruptedException {
+      final T elem = sub.nextElement(String.format("timeout while awaiting %s", expected));
+      if (!elem.equals(expected)) {
+        env.flop(String.format("Received `onNext(%s)` on downstream but expected `onNext(%s)`", elem, expected));
+      }
+    }
+
+    public void expectNextElement(ManualSubscriber<T> sub, T expected, String errorMessageAddendum) throws InterruptedException {
+      final T elem = sub.nextElement(String.format("timeout while awaiting %s. %s", expected, errorMessageAddendum));
+      if (!elem.equals(expected)) {
+        env.flop(String.format("Received `onNext(%s)` on downstream but expected `onNext(%s)`", elem, expected));
+      }
+    }
+
+    public T sendNextTFromUpstream() throws InterruptedException {
+      final T x = nextT();
+      sendNext(x);
+      return x;
+    }
+  }
+
+  public class ManualSubscriberWithErrorCollection<A> extends ManualSubscriberWithSubscriptionSupport<A> {
+    Promise<Throwable> error;
+
+    public ManualSubscriberWithErrorCollection(TestEnvironment env) {
+      super(env);
+      error = new Promise<Throwable>(env);
+    }
+
+    @Override
+    public void onError(Throwable cause) {
+      error.complete(cause);
+    }
+
+    public void expectError(Throwable cause) throws InterruptedException {
+      expectError(cause, env.defaultTimeoutMillis());
+    }
+
+    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+    public void expectError(Throwable cause, long timeoutMillis) throws InterruptedException {
+      error.expectCompletion(timeoutMillis, "Did not receive expected error on downstream");
+      if (!cause.equals(error.value())) {
+        env.flop(String.format("Expected error %s but got %s", cause, error.value()));
+      }
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/PublisherVerification.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,1245 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.tck;
+
+import org.reactivestreams.Publisher;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+import org.reactivestreams.tck.TestEnvironment.BlackholeSubscriberWithSubscriptionSupport;
+import org.reactivestreams.tck.TestEnvironment.Latch;
+import org.reactivestreams.tck.TestEnvironment.ManualSubscriber;
+import org.reactivestreams.tck.TestEnvironment.ManualSubscriberWithSubscriptionSupport;
+import org.reactivestreams.tck.flow.support.Function;
+import org.reactivestreams.tck.flow.support.Optional;
+import org.reactivestreams.tck.flow.support.PublisherVerificationRules;
+import org.testng.SkipException;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.lang.Override;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Provides tests for verifying {@code Publisher} specification rules.
+ *
+ * @see org.reactivestreams.Publisher
+ */
+public abstract class PublisherVerification<T> implements PublisherVerificationRules {
+
+  private static final String PUBLISHER_REFERENCE_GC_TIMEOUT_MILLIS_ENV = "PUBLISHER_REFERENCE_GC_TIMEOUT_MILLIS";
+  private static final long DEFAULT_PUBLISHER_REFERENCE_GC_TIMEOUT_MILLIS = 300L;
+
+  private final TestEnvironment env;
+
+  /**
+   * The amount of time after which a cancelled Subscriber reference should be dropped.
+   * See Rule 3.13 for details.
+   */
+  private final long publisherReferenceGCTimeoutMillis;
+
+  /**
+   * Constructs a new verification class using the given env and configuration.
+   *
+   * @param publisherReferenceGCTimeoutMillis used to determine after how much time a reference to a Subscriber should be already dropped by the Publisher.
+   */
+  public PublisherVerification(TestEnvironment env, long publisherReferenceGCTimeoutMillis) {
+    this.env = env;
+    this.publisherReferenceGCTimeoutMillis = publisherReferenceGCTimeoutMillis;
+  }
+
+  /**
+   * Constructs a new verification class using the given env and configuration.
+   *
+   * The value for {@code publisherReferenceGCTimeoutMillis} will be obtained by using {@link PublisherVerification#envPublisherReferenceGCTimeoutMillis()}.
+   */
+  public PublisherVerification(TestEnvironment env) {
+    this.env = env;
+    this.publisherReferenceGCTimeoutMillis = envPublisherReferenceGCTimeoutMillis();
+  }
+
+  /**
+   * Tries to parse the env variable {@code PUBLISHER_REFERENCE_GC_TIMEOUT_MILLIS} as long and returns the value if present,
+   * OR its default value ({@link PublisherVerification#DEFAULT_PUBLISHER_REFERENCE_GC_TIMEOUT_MILLIS}).
+   *
+   * This value is used to determine after how much time a reference to a Subscriber should be already dropped by the Publisher.
+   *
+   * @throws java.lang.IllegalArgumentException when unable to parse the env variable
+   */
+  public static long envPublisherReferenceGCTimeoutMillis() {
+    final String envMillis = System.getenv(PUBLISHER_REFERENCE_GC_TIMEOUT_MILLIS_ENV);
+    if (envMillis == null) return DEFAULT_PUBLISHER_REFERENCE_GC_TIMEOUT_MILLIS;
+    else try {
+      return Long.parseLong(envMillis);
+    } catch (NumberFormatException ex) {
+      throw new IllegalArgumentException(String.format("Unable to parse %s env value [%s] as long!", PUBLISHER_REFERENCE_GC_TIMEOUT_MILLIS_ENV, envMillis), ex);
+    }
+  }
+
+  /**
+   * This is the main method you must implement in your test incarnation.
+   * It must create a Publisher for a stream with exactly the given number of elements.
+   * If `elements` is `Long.MAX_VALUE` the produced stream must be infinite.
+   */
+  public abstract Publisher<T> createPublisher(long elements);
+
+  /**
+   * By implementing this method, additional TCK tests concerning a "failed" publishers will be run.
+   *
+   * The expected behaviour of the {@link Publisher} returned by this method is hand out a subscription,
+   * followed by signalling {@code onError} on it, as specified by Rule 1.9.
+   *
+   * If you ignore these additional tests, return {@code null} from this method.
+   */
+  public abstract Publisher<T> createFailedPublisher();
+
+
+  /**
+   * Override and return lower value if your Publisher is only able to produce a known number of elements.
+   * For example, if it is designed to return at-most-one element, return {@code 1} from this method.
+   *
+   * Defaults to {@code Long.MAX_VALUE - 1}, meaning that the Publisher can be produce a huge but NOT an unbounded number of elements.
+   *
+   * To mark your Publisher will *never* signal an {@code onComplete} override this method and return {@code Long.MAX_VALUE},
+   * which will result in *skipping all tests which require an onComplete to be triggered* (!).
+   */
+  public long maxElementsFromPublisher() {
+    return Long.MAX_VALUE - 1;
+  }
+
+  /**
+   * Override and return {@code true} in order to skip executing tests marked as {@code Stochastic}.
+   * Stochastic in this case means that the Rule is impossible or infeasible to deterministically verify—
+   * usually this means that this test case can yield false positives ("be green") even if for some case,
+   * the given implementation may violate the tested behaviour.
+   */
+  public boolean skipStochasticTests() {
+    return false;
+  }
+
+  /**
+   * In order to verify rule 3.3 of the reactive streams spec, this number will be used to check if a
+   * {@code Subscription} actually solves the "unbounded recursion" problem by not allowing the number of
+   * recursive calls to exceed the number returned by this method.
+   *
+   * @see <a href="https://github.com/reactive-streams/reactive-streams-jvm#3.3">reactive streams spec, rule 3.3</a>
+   * @see PublisherVerification#required_spec303_mustNotAllowUnboundedRecursion()
+   */
+  public long boundedDepthOfOnNextAndRequestRecursion() {
+    return 1;
+  }
+
+  ////////////////////// TEST ENV CLEANUP /////////////////////////////////////
+
+  @BeforeMethod
+  public void setUp() throws Exception {
+    env.clearAsyncErrors();
+  }
+
+  ////////////////////// TEST SETUP VERIFICATION //////////////////////////////
+
+  @Override @Test
+  public void required_createPublisher1MustProduceAStreamOfExactly1Element() throws Throwable {
+    activePublisherTest(1, true, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws InterruptedException {
+        ManualSubscriber<T> sub = env.newManualSubscriber(pub);
+        assertTrue(requestNextElementOrEndOfStream(pub, sub).isDefined(), String.format("Publisher %s produced no elements", pub));
+        sub.requestEndOfStream();
+      }
+
+      Optional<T> requestNextElementOrEndOfStream(Publisher<T> pub, ManualSubscriber<T> sub) throws InterruptedException {
+        return sub.requestNextElementOrEndOfStream(String.format("Timeout while waiting for next element from Publisher %s", pub));
+      }
+
+    });
+  }
+
+  @Override @Test
+  public void required_createPublisher3MustProduceAStreamOfExactly3Elements() throws Throwable {
+    activePublisherTest(3, true, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws InterruptedException {
+        ManualSubscriber<T> sub = env.newManualSubscriber(pub);
+        assertTrue(requestNextElementOrEndOfStream(pub, sub).isDefined(), String.format("Publisher %s produced no elements", pub));
+        assertTrue(requestNextElementOrEndOfStream(pub, sub).isDefined(), String.format("Publisher %s produced only 1 element", pub));
+        assertTrue(requestNextElementOrEndOfStream(pub, sub).isDefined(), String.format("Publisher %s produced only 2 elements", pub));
+        sub.requestEndOfStream();
+      }
+
+      Optional<T> requestNextElementOrEndOfStream(Publisher<T> pub, ManualSubscriber<T> sub) throws InterruptedException {
+        return sub.requestNextElementOrEndOfStream(String.format("Timeout while waiting for next element from Publisher %s", pub));
+      }
+
+    });
+  }
+
+  @Override @Test
+  public void required_validate_maxElementsFromPublisher() throws Exception {
+    assertTrue(maxElementsFromPublisher() >= 0, "maxElementsFromPublisher MUST return a number >= 0");
+  }
+
+  @Override @Test
+  public void required_validate_boundedDepthOfOnNextAndRequestRecursion() throws Exception {
+    assertTrue(boundedDepthOfOnNextAndRequestRecursion() >= 1, "boundedDepthOfOnNextAndRequestRecursion must return a number >= 1");
+  }
+
+
+  ////////////////////// SPEC RULE VERIFICATION ///////////////////////////////
+
+  @Override @Test
+  public void required_spec101_subscriptionRequestMustResultInTheCorrectNumberOfProducedElements() throws Throwable {
+    activePublisherTest(5, false, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws InterruptedException {
+
+        ManualSubscriber<T> sub = env.newManualSubscriber(pub);
+        try {
+            sub.expectNone(String.format("Publisher %s produced value before the first `request`: ", pub));
+            sub.request(1);
+            sub.nextElement(String.format("Publisher %s produced no element after first `request`", pub));
+            sub.expectNone(String.format("Publisher %s produced unrequested: ", pub));
+
+            sub.request(1);
+            sub.request(2);
+            sub.nextElements(3, env.defaultTimeoutMillis(), String.format("Publisher %s produced less than 3 elements after two respective `request` calls", pub));
+
+            sub.expectNone(String.format("Publisher %sproduced unrequested ", pub));
+        } finally {
+            sub.cancel();
+        }
+      }
+    });
+  }
+
+  @Override @Test
+  public void required_spec102_maySignalLessThanRequestedAndTerminateSubscription() throws Throwable {
+    final int elements = 3;
+    final int requested = 10;
+
+    activePublisherTest(elements, true, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        final ManualSubscriber<T> sub = env.newManualSubscriber(pub);
+        sub.request(requested);
+        sub.nextElements(elements);
+        sub.expectCompletion();
+      }
+    });
+  }
+
+  @Override @Test
+  public void stochastic_spec103_mustSignalOnMethodsSequentially() throws Throwable {
+    final int iterations = 100;
+    final int elements = 10;
+
+    stochasticTest(iterations, new Function<Integer, Void>() {
+      @Override
+      public Void apply(final Integer runNumber) throws Throwable {
+        activePublisherTest(elements, true, new PublisherTestRun<T>() {
+          @Override
+          public void run(Publisher<T> pub) throws Throwable {
+            final Latch completionLatch = new Latch(env);
+
+            final AtomicInteger gotElements = new AtomicInteger(0);
+            pub.subscribe(new Subscriber<T>() {
+              private Subscription subs;
+
+              private ConcurrentAccessBarrier concurrentAccessBarrier = new ConcurrentAccessBarrier();
+
+              /**
+               * Concept wise very similar to a {@link org.reactivestreams.tck.TestEnvironment.Latch}, serves to protect
+               * a critical section from concurrent access, with the added benefit of Thread tracking and same-thread-access awareness.
+               *
+               * Since a <i>Synchronous</i> Publisher may choose to synchronously (using the same {@link Thread}) call
+               * {@code onNext} directly from either {@code subscribe} or {@code request} a plain Latch is not enough
+               * to verify concurrent access safety - one needs to track if the caller is not still using the calling thread
+               * to enter subsequent critical sections ("nesting" them effectively).
+               */
+              final class ConcurrentAccessBarrier {
+                private AtomicReference<Thread> currentlySignallingThread = new AtomicReference<Thread>(null);
+                private volatile String previousSignal = null;
+
+                public void enterSignal(String signalName) {
+                  if((!currentlySignallingThread.compareAndSet(null, Thread.currentThread())) && !isSynchronousSignal()) {
+                    env.flop(String.format(
+                      "Illegal concurrent access detected (entering critical section)! " +
+                        "%s emited %s signal, before %s finished its %s signal.",
+                        Thread.currentThread(), signalName, currentlySignallingThread.get(), previousSignal));
+                  }
+                  this.previousSignal = signalName;
+                }
+
+                public void leaveSignal(String signalName) {
+                  currentlySignallingThread.set(null);
+                  this.previousSignal = signalName;
+                }
+
+                private boolean isSynchronousSignal() {
+                  return (previousSignal != null) && Thread.currentThread().equals(currentlySignallingThread.get());
+                }
+
+              }
+
+              @Override
+              public void onSubscribe(Subscription s) {
+                final String signal = "onSubscribe()";
+                concurrentAccessBarrier.enterSignal(signal);
+
+                subs = s;
+                subs.request(1);
+
+                concurrentAccessBarrier.leaveSignal(signal);
+              }
+
+              @Override
+              public void onNext(T ignore) {
+                final String signal = String.format("onNext(%s)", ignore);
+                concurrentAccessBarrier.enterSignal(signal);
+
+                if (gotElements.incrementAndGet() <= elements) // requesting one more than we know are in the stream (some Publishers need this)
+                  subs.request(1);
+
+                concurrentAccessBarrier.leaveSignal(signal);
+              }
+
+              @Override
+              public void onError(Throwable t) {
+                final String signal = String.format("onError(%s)", t.getMessage());
+                concurrentAccessBarrier.enterSignal(signal);
+
+                // ignore value
+
+                concurrentAccessBarrier.leaveSignal(signal);
+              }
+
+              @Override
+              public void onComplete() {
+                final String signal = "onComplete()";
+                concurrentAccessBarrier.enterSignal(signal);
+
+                // entering for completeness
+
+                concurrentAccessBarrier.leaveSignal(signal);
+                completionLatch.close();
+              }
+            });
+
+            completionLatch.expectClose(
+              elements * env.defaultTimeoutMillis(),
+              String.format("Failed in iteration %d of %d. Expected completion signal after signalling %d elements (signalled %d), yet did not receive it",
+                            runNumber, iterations, elements, gotElements.get()));
+          }
+        });
+        return null;
+      }
+    });
+  }
+
+  @Override @Test
+  public void optional_spec104_mustSignalOnErrorWhenFails() throws Throwable {
+    try {
+      whenHasErrorPublisherTest(new PublisherTestRun<T>() {
+        @Override
+        public void run(final Publisher<T> pub) throws InterruptedException {
+          final Latch onErrorlatch = new Latch(env);
+          final Latch onSubscribeLatch = new Latch(env);
+          pub.subscribe(new TestEnvironment.TestSubscriber<T>(env) {
+            @Override
+            public void onSubscribe(Subscription subs) {
+              onSubscribeLatch.assertOpen("Only one onSubscribe call expected");
+              onSubscribeLatch.close();
+            }
+            @Override
+            public void onError(Throwable cause) {
+              onSubscribeLatch.assertClosed("onSubscribe should be called prior to onError always");
+              onErrorlatch.assertOpen(String.format("Error-state Publisher %s called `onError` twice on new Subscriber", pub));
+              onErrorlatch.close();
+            }
+          });
+
+          onSubscribeLatch.expectClose("Should have received onSubscribe");
+          onErrorlatch.expectClose(String.format("Error-state Publisher %s did not call `onError` on new Subscriber", pub));
+
+          env.verifyNoAsyncErrors();
+          }
+      });
+    } catch (SkipException se) {
+      throw se;
+    } catch (Throwable ex) {
+      // we also want to catch AssertionErrors and anything the publisher may have thrown inside subscribe
+      // which was wrong of him - he should have signalled on error using onError
+      throw new RuntimeException(String.format("Publisher threw exception (%s) instead of signalling error via onError!", ex.getMessage()), ex);
+    }
+  }
+
+  @Override @Test
+  public void required_spec105_mustSignalOnCompleteWhenFiniteStreamTerminates() throws Throwable {
+    activePublisherTest(3, true, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        ManualSubscriber<T> sub = env.newManualSubscriber(pub);
+        sub.requestNextElement();
+        sub.requestNextElement();
+        sub.requestNextElement();
+        sub.requestEndOfStream();
+        sub.expectNone();
+      }
+    });
+  }
+
+  @Override @Test
+  public void optional_spec105_emptyStreamMustTerminateBySignallingOnComplete() throws Throwable {
+    optionalActivePublisherTest(0, true, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        ManualSubscriber<T> sub = env.newManualSubscriber(pub);
+        sub.request(1);
+        sub.expectCompletion();
+        sub.expectNone();
+      }
+    });
+  }
+
+  @Override @Test
+  public void untested_spec106_mustConsiderSubscriptionCancelledAfterOnErrorOrOnCompleteHasBeenCalled() throws Throwable {
+    notVerified(); // not really testable without more control over the Publisher
+  }
+
+  @Override @Test
+  public void required_spec107_mustNotEmitFurtherSignalsOnceOnCompleteHasBeenSignalled() throws Throwable {
+    activePublisherTest(1, true, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        ManualSubscriber<T> sub = env.newManualSubscriber(pub);
+        sub.request(10);
+        sub.nextElement();
+        sub.expectCompletion();
+
+        sub.request(10);
+        sub.expectNone();
+      }
+    });
+  }
+
+  @Override @Test
+  public void untested_spec107_mustNotEmitFurtherSignalsOnceOnErrorHasBeenSignalled() throws Throwable {
+    notVerified(); // can we meaningfully test this, without more control over the publisher?
+  }
+
+  @Override @Test
+  public void untested_spec108_possiblyCanceledSubscriptionShouldNotReceiveOnErrorOrOnCompleteSignals() throws Throwable {
+    notVerified(); // can we meaningfully test this?
+  }
+
+  @Override @Test
+  public void untested_spec109_subscribeShouldNotThrowNonFatalThrowable() throws Throwable {
+    notVerified(); // can we meaningfully test this?
+  }
+
+  @Override @Test
+  public void required_spec109_subscribeThrowNPEOnNullSubscriber() throws Throwable {
+    activePublisherTest(0, false, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        try {
+            pub.subscribe(null);
+            env.flop("Publisher did not throw a NullPointerException when given a null Subscribe in subscribe");
+        } catch (NullPointerException ignored) {
+          // valid behaviour
+        }
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+  @Override @Test
+  public void required_spec109_mustIssueOnSubscribeForNonNullSubscriber() throws Throwable {
+    activePublisherTest(0, false, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        final Latch onSubscribeLatch = new Latch(env);
+        final AtomicReference<Subscription> cancel = new AtomicReference<Subscription>();
+        try {
+          pub.subscribe(new Subscriber<T>() {
+            @Override
+            public void onError(Throwable cause) {
+              onSubscribeLatch.assertClosed("onSubscribe should be called prior to onError always");
+            }
+
+            @Override
+            public void onSubscribe(Subscription subs) {
+              cancel.set(subs);
+              onSubscribeLatch.assertOpen("Only one onSubscribe call expected");
+              onSubscribeLatch.close();
+            }
+
+            @Override
+            public void onNext(T elem) {
+              onSubscribeLatch.assertClosed("onSubscribe should be called prior to onNext always");
+            }
+
+            @Override
+            public void onComplete() {
+              onSubscribeLatch.assertClosed("onSubscribe should be called prior to onComplete always");
+            }
+          });
+          onSubscribeLatch.expectClose("Should have received onSubscribe");
+          env.verifyNoAsyncErrorsNoDelay();
+        } finally {
+          Subscription s = cancel.getAndSet(null);
+          if (s != null) {
+            s.cancel();
+          }
+        }
+      }
+    });
+  }
+
+  @Override @Test
+  public void required_spec109_mayRejectCallsToSubscribeIfPublisherIsUnableOrUnwillingToServeThemRejectionMustTriggerOnErrorAfterOnSubscribe() throws Throwable {
+    whenHasErrorPublisherTest(new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        final Latch onErrorLatch = new Latch(env);
+        final Latch onSubscribeLatch = new Latch(env);
+        ManualSubscriberWithSubscriptionSupport<T> sub = new ManualSubscriberWithSubscriptionSupport<T>(env) {
+          @Override
+          public void onError(Throwable cause) {
+            onSubscribeLatch.assertClosed("onSubscribe should be called prior to onError always");
+            onErrorLatch.assertOpen("Only one onError call expected");
+            onErrorLatch.close();
+          }
+
+          @Override
+          public void onSubscribe(Subscription subs) {
+            onSubscribeLatch.assertOpen("Only one onSubscribe call expected");
+            onSubscribeLatch.close();
+          }
+        };
+        pub.subscribe(sub);
+        onSubscribeLatch.expectClose("Should have received onSubscribe");
+        onErrorLatch.expectClose("Should have received onError");
+
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+  @Override @Test
+  public void untested_spec110_rejectASubscriptionRequestIfTheSameSubscriberSubscribesTwice() throws Throwable {
+    notVerified(); // can we meaningfully test this?
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#1.11
+  @Override @Test
+  public void optional_spec111_maySupportMultiSubscribe() throws Throwable {
+    optionalActivePublisherTest(1, false, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        ManualSubscriber<T> sub1 = env.newManualSubscriber(pub);
+        ManualSubscriber<T> sub2 = env.newManualSubscriber(pub);
+
+        try {
+          env.verifyNoAsyncErrors();
+        } finally {
+          try {
+            sub1.cancel();
+          } finally {
+            sub2.cancel();
+          }
+        }
+      }
+    });
+  }
+
+  @Override @Test
+  public void optional_spec111_registeredSubscribersMustReceiveOnNextOrOnCompleteSignals() throws Throwable {
+    optionalActivePublisherTest(1, false, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        ManualSubscriber<T> sub1 = env.newManualSubscriber(pub);
+        ManualSubscriber<T> sub2 = env.newManualSubscriber(pub);
+        // Since we're testing the case when the Publisher DOES support the optional multi-subscribers scenario,
+        // and decides if it handles them uni-cast or multi-cast, we don't know which subscriber will receive an
+        // onNext (and optional onComplete) signal(s) and which just onComplete signal.
+        // Plus, even if subscription assumed to be unicast, it's implementation choice, which one will be signalled
+        // with onNext.
+        sub1.requestNextElementOrEndOfStream();
+        sub2.requestNextElementOrEndOfStream();
+        try {
+            env.verifyNoAsyncErrors();
+        } finally {
+            try {
+                sub1.cancel();
+            } finally {
+                sub2.cancel();
+            }
+        }
+      }
+    });
+  }
+
+  @Override @Test
+  public void optional_spec111_multicast_mustProduceTheSameElementsInTheSameSequenceToAllOfItsSubscribersWhenRequestingOneByOne() throws Throwable {
+    optionalActivePublisherTest(5, true, new PublisherTestRun<T>() { // This test is skipped if the publisher is unbounded (never sends onComplete)
+      @Override
+      public void run(Publisher<T> pub) throws InterruptedException {
+        ManualSubscriber<T> sub1 = env.newManualSubscriber(pub);
+        ManualSubscriber<T> sub2 = env.newManualSubscriber(pub);
+        ManualSubscriber<T> sub3 = env.newManualSubscriber(pub);
+
+        sub1.request(1);
+        T x1 = sub1.nextElement(String.format("Publisher %s did not produce the requested 1 element on 1st subscriber", pub));
+        sub2.request(2);
+        List<T> y1 = sub2.nextElements(2, String.format("Publisher %s did not produce the requested 2 elements on 2nd subscriber", pub));
+        sub1.request(1);
+        T x2 = sub1.nextElement(String.format("Publisher %s did not produce the requested 1 element on 1st subscriber", pub));
+        sub3.request(3);
+        List<T> z1 = sub3.nextElements(3, String.format("Publisher %s did not produce the requested 3 elements on 3rd subscriber", pub));
+        sub3.request(1);
+        T z2 = sub3.nextElement(String.format("Publisher %s did not produce the requested 1 element on 3rd subscriber", pub));
+        sub3.request(1);
+        T z3 = sub3.nextElement(String.format("Publisher %s did not produce the requested 1 element on 3rd subscriber", pub));
+        sub3.requestEndOfStream(String.format("Publisher %s did not complete the stream as expected on 3rd subscriber", pub));
+        sub2.request(3);
+        List<T> y2 = sub2.nextElements(3, String.format("Publisher %s did not produce the requested 3 elements on 2nd subscriber", pub));
+        sub2.requestEndOfStream(String.format("Publisher %s did not complete the stream as expected on 2nd subscriber", pub));
+        sub1.request(2);
+        List<T> x3 = sub1.nextElements(2, String.format("Publisher %s did not produce the requested 2 elements on 1st subscriber", pub));
+        sub1.request(1);
+        T x4 = sub1.nextElement(String.format("Publisher %s did not produce the requested 1 element on 1st subscriber", pub));
+        sub1.requestEndOfStream(String.format("Publisher %s did not complete the stream as expected on 1st subscriber", pub));
+
+        @SuppressWarnings("unchecked")
+        List<T> r = new ArrayList<T>(Arrays.asList(x1, x2));
+        r.addAll(x3);
+        r.addAll(Collections.singleton(x4));
+
+        List<T> check1 = new ArrayList<T>(y1);
+        check1.addAll(y2);
+
+        //noinspection unchecked
+        List<T> check2 = new ArrayList<T>(z1);
+        check2.add(z2);
+        check2.add(z3);
+
+        assertEquals(r, check1, String.format("Publisher %s did not produce the same element sequence for subscribers 1 and 2", pub));
+        assertEquals(r, check2, String.format("Publisher %s did not produce the same element sequence for subscribers 1 and 3", pub));
+      }
+    });
+  }
+
+  @Override @Test
+  public void optional_spec111_multicast_mustProduceTheSameElementsInTheSameSequenceToAllOfItsSubscribersWhenRequestingManyUpfront() throws Throwable {
+    optionalActivePublisherTest(3, false, new PublisherTestRun<T>() { // This test is skipped if the publisher cannot produce enough elements
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        ManualSubscriber<T> sub1 = env.newManualSubscriber(pub);
+        ManualSubscriber<T> sub2 = env.newManualSubscriber(pub);
+        ManualSubscriber<T> sub3 = env.newManualSubscriber(pub);
+
+        List<T> received1 = new ArrayList<T>();
+        List<T> received2 = new ArrayList<T>();
+        List<T> received3 = new ArrayList<T>();
+
+        // if the publisher must touch it's source to notice it's been drained, the OnComplete won't come until we ask for more than it actually contains...
+        // edgy edge case?
+        sub1.request(4);
+        sub2.request(4);
+        sub3.request(4);
+
+        received1.addAll(sub1.nextElements(3));
+        received2.addAll(sub2.nextElements(3));
+        received3.addAll(sub3.nextElements(3));
+
+        // NOTE: can't check completion, the Publisher may not be able to signal it
+        //       a similar test *with* completion checking is implemented
+
+        assertEquals(received1, received2, String.format("Expected elements to be signaled in the same sequence to 1st and 2nd subscribers"));
+        assertEquals(received2, received3, String.format("Expected elements to be signaled in the same sequence to 2nd and 3rd subscribers"));
+      }
+    });
+  }
+
+  @Override @Test
+  public void optional_spec111_multicast_mustProduceTheSameElementsInTheSameSequenceToAllOfItsSubscribersWhenRequestingManyUpfrontAndCompleteAsExpected() throws Throwable {
+    optionalActivePublisherTest(3, true, new PublisherTestRun<T>() { // This test is skipped if the publisher is unbounded (never sends onComplete)
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        ManualSubscriber<T> sub1 = env.newManualSubscriber(pub);
+        ManualSubscriber<T> sub2 = env.newManualSubscriber(pub);
+        ManualSubscriber<T> sub3 = env.newManualSubscriber(pub);
+
+        List<T> received1 = new ArrayList<T>();
+        List<T> received2 = new ArrayList<T>();
+        List<T> received3 = new ArrayList<T>();
+
+        // if the publisher must touch it's source to notice it's been drained, the OnComplete won't come until we ask for more than it actually contains...
+        // edgy edge case?
+        sub1.request(4);
+        sub2.request(4);
+        sub3.request(4);
+
+        received1.addAll(sub1.nextElements(3));
+        received2.addAll(sub2.nextElements(3));
+        received3.addAll(sub3.nextElements(3));
+
+        sub1.expectCompletion();
+        sub2.expectCompletion();
+        sub3.expectCompletion();
+
+        assertEquals(received1, received2, String.format("Expected elements to be signaled in the same sequence to 1st and 2nd subscribers"));
+        assertEquals(received2, received3, String.format("Expected elements to be signaled in the same sequence to 2nd and 3rd subscribers"));
+      }
+    });
+  }
+
+  ///////////////////// SUBSCRIPTION TESTS //////////////////////////////////
+
+  @Override @Test
+  public void required_spec302_mustAllowSynchronousRequestCallsFromOnNextAndOnSubscribe() throws Throwable {
+    activePublisherTest(6, false, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        ManualSubscriber<T> sub = new ManualSubscriber<T>(env) {
+          @Override
+          public void onSubscribe(Subscription subs) {
+            this.subscription.completeImmediatly(subs);
+
+            subs.request(1);
+            subs.request(1);
+            subs.request(1);
+          }
+
+          @Override
+          public void onNext(T element) {
+            Subscription subs = this.subscription.value();
+            subs.request(1);
+          }
+        };
+
+        env.subscribe(pub, sub);
+
+        env.verifyNoAsyncErrors();
+      }
+    });
+  }
+
+  @Override @Test
+  public void required_spec303_mustNotAllowUnboundedRecursion() throws Throwable {
+    final long oneMoreThanBoundedLimit = boundedDepthOfOnNextAndRequestRecursion() + 1;
+
+    activePublisherTest(oneMoreThanBoundedLimit, false, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        final ThreadLocal<Long> stackDepthCounter = new ThreadLocal<Long>() {
+          @Override
+          protected Long initialValue() {
+            return 0L;
+          }
+        };
+
+        final Latch runCompleted = new Latch(env);
+
+        final ManualSubscriber<T> sub = new ManualSubscriberWithSubscriptionSupport<T>(env) {
+          // counts the number of signals received, used to break out from possibly infinite request/onNext loops
+          long signalsReceived = 0L;
+
+          @Override
+          public void onNext(T element) {
+            // NOT calling super.onNext as this test only cares about stack depths, not the actual values of elements
+            // which also simplifies this test as we do not have to drain the test buffer, which would otherwise be in danger of overflowing
+
+            signalsReceived += 1;
+            stackDepthCounter.set(stackDepthCounter.get() + 1);
+            if (env.debugEnabled()) {
+              env.debug(String.format("%s(recursion depth: %d)::onNext(%s)", this, stackDepthCounter.get(), element));
+            }
+
+            final long callsUntilNow = stackDepthCounter.get();
+            if (callsUntilNow > boundedDepthOfOnNextAndRequestRecursion()) {
+              env.flop(String.format("Got %d onNext calls within thread: %s, yet expected recursive bound was %d",
+                                     callsUntilNow, Thread.currentThread(), boundedDepthOfOnNextAndRequestRecursion()));
+
+              // stop the recursive call chain
+              runCompleted.close();
+              return;
+            } else if (signalsReceived >= oneMoreThanBoundedLimit) {
+              // since max number of signals reached, and recursion depth not exceeded, we judge this as a success and
+              // stop the recursive call chain
+              runCompleted.close();
+              return;
+            }
+
+            // request more right away, the Publisher must break the recursion
+            subscription.value().request(1);
+
+            stackDepthCounter.set(stackDepthCounter.get() - 1);
+          }
+
+          @Override
+          public void onComplete() {
+            super.onComplete();
+            runCompleted.close();
+          }
+
+          @Override
+          public void onError(Throwable cause) {
+            super.onError(cause);
+            runCompleted.close();
+          }
+        };
+
+        try {
+          env.subscribe(pub, sub);
+
+          sub.request(1); // kick-off the `request -> onNext -> request -> onNext -> ...`
+
+          final String msg = String.format("Unable to validate call stack depth safety, " +
+                                               "awaited at-most %s signals (`maxOnNextSignalsInRecursionTest()`) or completion",
+                                           oneMoreThanBoundedLimit);
+          runCompleted.expectClose(env.defaultTimeoutMillis(), msg);
+          env.verifyNoAsyncErrorsNoDelay();
+        } finally {
+          // since the request/onNext recursive calls may keep the publisher running "forever",
+          // we MUST cancel it manually before exiting this test case
+          sub.cancel();
+        }
+      }
+    });
+  }
+
+  @Override @Test
+  public void untested_spec304_requestShouldNotPerformHeavyComputations() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  @Override @Test
+  public void untested_spec305_cancelMustNotSynchronouslyPerformHeavyComputation() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  @Override @Test
+  public void required_spec306_afterSubscriptionIsCancelledRequestMustBeNops() throws Throwable {
+    activePublisherTest(3, false, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+
+        // override ManualSubscriberWithSubscriptionSupport#cancel because by default a ManualSubscriber will drop the
+        // subscription once it's cancelled (as expected).
+        // In this test however it must keep the cancelled Subscription and keep issuing `request(long)` to it.
+        ManualSubscriber<T> sub = new ManualSubscriberWithSubscriptionSupport<T>(env) {
+          @Override
+          public void cancel() {
+            if (subscription.isCompleted()) {
+              subscription.value().cancel();
+            } else {
+              env.flop("Cannot cancel a subscription before having received it");
+            }
+          }
+        };
+
+        env.subscribe(pub, sub);
+
+        sub.cancel();
+        sub.request(1);
+        sub.request(1);
+        sub.request(1);
+
+        sub.expectNone();
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+  @Override @Test
+  public void required_spec307_afterSubscriptionIsCancelledAdditionalCancelationsMustBeNops() throws Throwable {
+    activePublisherTest(1, false, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        final ManualSubscriber<T> sub = env.newManualSubscriber(pub);
+
+        // leak the Subscription
+        final Subscription subs = sub.subscription.value();
+
+        subs.cancel();
+        subs.cancel();
+        subs.cancel();
+
+        sub.expectNone();
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+  @Override @Test
+  public void required_spec309_requestZeroMustSignalIllegalArgumentException() throws Throwable {
+    activePublisherTest(10, false, new PublisherTestRun<T>() {
+      @Override public void run(Publisher<T> pub) throws Throwable {
+        final ManualSubscriber<T> sub = env.newManualSubscriber(pub);
+        sub.request(0);
+        sub.expectError(IllegalArgumentException.class);
+      }
+    });
+  }
+
+  @Override @Test
+  public void required_spec309_requestNegativeNumberMustSignalIllegalArgumentException() throws Throwable {
+    activePublisherTest(10, false, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        final ManualSubscriber<T> sub = env.newManualSubscriber(pub);
+        final Random r = new Random();
+        sub.request(-r.nextInt(Integer.MAX_VALUE) - 1);
+        // we do require implementations to mention the rule number at the very least, or mentioning that the non-negative request is the problem
+        sub.expectError(IllegalArgumentException.class);
+      }
+    });
+  }
+
+  @Override @Test
+  public void optional_spec309_requestNegativeNumberMaySignalIllegalArgumentExceptionWithSpecificMessage() throws Throwable {
+    optionalActivePublisherTest(10, false, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        final ManualSubscriber<T> sub = env.newManualSubscriber(pub);
+        final Random r = new Random();
+        sub.request(-r.nextInt(Integer.MAX_VALUE) - 1);
+        // we do require implementations to mention the rule number at the very least, or mentioning that the non-negative request is the problem
+        sub.expectErrorWithMessage(IllegalArgumentException.class, Arrays.asList("3.9", "non-positive subscription request", "negative subscription request"));
+      }
+    });
+  }
+
+  @Override @Test
+  public void required_spec312_cancelMustMakeThePublisherToEventuallyStopSignaling() throws Throwable {
+    // the publisher is able to signal more elements than the subscriber will be requesting in total
+    final int publisherElements = 20;
+
+    final int demand1 = 10;
+    final int demand2 = 5;
+    final int totalDemand = demand1 + demand2;
+
+    activePublisherTest(publisherElements, false, new PublisherTestRun<T>() {
+      @Override @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+      public void run(Publisher<T> pub) throws Throwable {
+        final ManualSubscriber<T> sub = env.newManualSubscriber(pub);
+
+        sub.request(demand1);
+        sub.request(demand2);
+
+        /*
+          NOTE: The order of the nextElement/cancel calls below is very important (!)
+
+          If this ordering was reversed, given an asynchronous publisher,
+          the following scenario would be *legal* and would break this test:
+
+          > AsyncPublisher receives request(10) - it does not emit data right away, it's asynchronous
+          > AsyncPublisher receives request(5) - demand is now 15
+          ! AsyncPublisher didn't emit any onNext yet (!)
+          > AsyncPublisher receives cancel() - handles it right away, by "stopping itself" for example
+          ! cancel was handled hefore the AsyncPublisher ever got the chance to emit data
+          ! the subscriber ends up never receiving even one element - the test is stuck (and fails, even on valid Publisher)
+
+          Which is why we must first expect an element, and then cancel, once the producing is "running".
+         */
+        sub.nextElement();
+        sub.cancel();
+
+        int onNextsSignalled = 1;
+
+        boolean stillBeingSignalled;
+        do {
+          // put asyncError if onNext signal received
+          sub.expectNone();
+          Throwable error = env.dropAsyncError();
+
+          if (error == null) {
+            stillBeingSignalled = false;
+          } else {
+            onNextsSignalled += 1;
+            stillBeingSignalled = true;
+          }
+
+          // if the Publisher tries to emit more elements than was requested (and/or ignores cancellation) this will throw
+          assertTrue(onNextsSignalled <= totalDemand,
+                     String.format("Publisher signalled [%d] elements, which is more than the signalled demand: %d",
+                                   onNextsSignalled, totalDemand));
+
+        } while (stillBeingSignalled);
+      }
+    });
+
+    env.verifyNoAsyncErrorsNoDelay();
+  }
+
+  @Override @Test
+  public void required_spec313_cancelMustMakeThePublisherEventuallyDropAllReferencesToTheSubscriber() throws Throwable {
+    final ReferenceQueue<ManualSubscriber<T>> queue = new ReferenceQueue<ManualSubscriber<T>>();
+
+    final Function<Publisher<T>, WeakReference<ManualSubscriber<T>>> run = new Function<Publisher<T>, WeakReference<ManualSubscriber<T>>>() {
+      @Override
+      public WeakReference<ManualSubscriber<T>> apply(Publisher<T> pub) throws Exception {
+        final ManualSubscriber<T> sub = env.newManualSubscriber(pub);
+        final WeakReference<ManualSubscriber<T>> ref = new WeakReference<ManualSubscriber<T>>(sub, queue);
+
+        sub.request(1);
+        sub.nextElement();
+        sub.cancel();
+
+        return ref;
+      }
+    };
+
+    activePublisherTest(3, false, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        final WeakReference<ManualSubscriber<T>> ref = run.apply(pub);
+
+        // cancel may be run asynchronously so we add a sleep before running the GC
+        // to "resolve" the race
+        Thread.sleep(publisherReferenceGCTimeoutMillis);
+        System.gc();
+
+        if (!ref.equals(queue.remove(100))) {
+          env.flop(String.format("Publisher %s did not drop reference to test subscriber after subscription cancellation", pub));
+        }
+
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+  @Override @Test
+  public void required_spec317_mustSupportAPendingElementCountUpToLongMaxValue() throws Throwable {
+    final int totalElements = 3;
+
+    activePublisherTest(totalElements, true, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        ManualSubscriber<T> sub = env.newManualSubscriber(pub);
+        sub.request(Long.MAX_VALUE);
+
+        sub.nextElements(totalElements);
+        sub.expectCompletion();
+
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+  @Override @Test
+  public void required_spec317_mustSupportACumulativePendingElementCountUpToLongMaxValue() throws Throwable {
+    final int totalElements = 3;
+
+    activePublisherTest(totalElements, true, new PublisherTestRun<T>() {
+      @Override
+      public void run(Publisher<T> pub) throws Throwable {
+        final ManualSubscriber<T> sub = env.newManualSubscriber(pub);
+        sub.request(Long.MAX_VALUE / 2); // pending = Long.MAX_VALUE / 2
+        sub.request(Long.MAX_VALUE / 2); // pending = Long.MAX_VALUE - 1
+        sub.request(1); // pending = Long.MAX_VALUE
+
+        sub.nextElements(totalElements);
+        sub.expectCompletion();
+
+        try {
+          env.verifyNoAsyncErrorsNoDelay();
+        } finally {
+          sub.cancel();
+        }
+
+      }
+    });
+  }
+
+  @Override @Test
+  public void required_spec317_mustNotSignalOnErrorWhenPendingAboveLongMaxValue() throws Throwable {
+    activePublisherTest(Integer.MAX_VALUE, false, new PublisherTestRun<T>() {
+      @Override public void run(Publisher<T> pub) throws Throwable {
+        final ManualSubscriberWithSubscriptionSupport<T> sub = new BlackholeSubscriberWithSubscriptionSupport<T>(env) {
+           // arbitrarily set limit on nuber of request calls signalled, we expect overflow after already 2 calls,
+           // so 10 is relatively high and safe even if arbitrarily chosen
+          int callsCounter = 10;
+
+          @Override
+          public void onNext(T element) {
+            if (env.debugEnabled()) {
+              env.debug(String.format("%s::onNext(%s)", this, element));
+            }
+            if (subscription.isCompleted()) {
+              if (callsCounter > 0) {
+                subscription.value().request(Long.MAX_VALUE - 1);
+                callsCounter--;
+              } else {
+                  subscription.value().cancel();
+              }
+            } else {
+              env.flop(String.format("Subscriber::onNext(%s) called before Subscriber::onSubscribe", element));
+            }
+          }
+        };
+        env.subscribe(pub, sub, env.defaultTimeoutMillis());
+
+        // eventually triggers `onNext`, which will then trigger up to `callsCounter` times `request(Long.MAX_VALUE - 1)`
+        // we're pretty sure to overflow from those
+        sub.request(1);
+
+        // no onError should be signalled
+        try {
+          env.verifyNoAsyncErrors();
+        } finally {
+          sub.cancel();
+        }
+      }
+    });
+  }
+
+  ///////////////////// ADDITIONAL "COROLLARY" TESTS ////////////////////////
+
+  ///////////////////// TEST INFRASTRUCTURE /////////////////////////////////
+
+  public interface PublisherTestRun<T> {
+    public void run(Publisher<T> pub) throws Throwable;
+  }
+
+  /**
+   * Test for feature that SHOULD/MUST be implemented, using a live publisher.
+   *
+   * @param elements the number of elements the Publisher under test  must be able to emit to run this test
+   * @param completionSignalRequired true if an {@code onComplete} signal is required by this test to run.
+   *                                 If the tested Publisher is unable to signal completion, tests requireing onComplete signals will be skipped.
+   *                                 To signal if your Publisher is able to signal completion see {@link PublisherVerification#maxElementsFromPublisher()}.
+   */
+  public void activePublisherTest(long elements, boolean completionSignalRequired, PublisherTestRun<T> body) throws Throwable {
+    if (elements > maxElementsFromPublisher()) {
+      throw new SkipException(String.format("Unable to run this test, as required elements nr: %d is higher than supported by given producer: %d", elements, maxElementsFromPublisher()));
+    } else if (completionSignalRequired && maxElementsFromPublisher() == Long.MAX_VALUE) {
+      throw new SkipException("Unable to run this test, as it requires an onComplete signal, " +
+                                "which this Publisher is unable to provide (as signalled by returning Long.MAX_VALUE from `maxElementsFromPublisher()`)");
+    } else {
+      Publisher<T> pub = createPublisher(elements);
+      body.run(pub);
+      env.verifyNoAsyncErrorsNoDelay();
+    }
+  }
+
+  /**
+   * Test for feature that MAY be implemented. This test will be marked as SKIPPED if it fails.
+   *
+   * @param elements the number of elements the Publisher under test  must be able to emit to run this test
+   * @param completionSignalRequired true if an {@code onComplete} signal is required by this test to run.
+   *                                 If the tested Publisher is unable to signal completion, tests requireing onComplete signals will be skipped.
+   *                                 To signal if your Publisher is able to signal completion see {@link PublisherVerification#maxElementsFromPublisher()}.
+   */
+  public void optionalActivePublisherTest(long elements, boolean completionSignalRequired, PublisherTestRun<T> body) throws Throwable {
+    if (elements > maxElementsFromPublisher()) {
+      throw new SkipException(String.format("Unable to run this test, as required elements nr: %d is higher than supported by given producer: %d", elements, maxElementsFromPublisher()));
+    } else if (completionSignalRequired && maxElementsFromPublisher() == Long.MAX_VALUE) {
+      throw new SkipException("Unable to run this test, as it requires an onComplete signal, " +
+                                "which this Publisher is unable to provide (as signalled by returning Long.MAX_VALUE from `maxElementsFromPublisher()`)");
+    } else {
+
+      final Publisher<T> pub = createPublisher(elements);
+      final String skipMessage = "Skipped because tested publisher does NOT implement this OPTIONAL requirement.";
+
+      try {
+        potentiallyPendingTest(pub, body);
+      } catch (Exception ex) {
+        notVerified(skipMessage);
+      } catch (AssertionError ex) {
+        notVerified(skipMessage + " Reason for skipping was: " + ex.getMessage());
+      }
+    }
+  }
+
+  public static final String SKIPPING_NO_ERROR_PUBLISHER_AVAILABLE =
+    "Skipping because no error state Publisher provided, and the test requires it. " +
+          "Please implement PublisherVerification#createFailedPublisher to run this test.";
+
+  public static final String SKIPPING_OPTIONAL_TEST_FAILED =
+    "Skipping, because provided Publisher does not pass this *additional* verification.";
+  /**
+   * Additional test for Publisher in error state
+   */
+  public void whenHasErrorPublisherTest(PublisherTestRun<T> body) throws Throwable {
+    potentiallyPendingTest(createFailedPublisher(), body, SKIPPING_NO_ERROR_PUBLISHER_AVAILABLE);
+  }
+
+  public void potentiallyPendingTest(Publisher<T> pub, PublisherTestRun<T> body) throws Throwable {
+    potentiallyPendingTest(pub, body, SKIPPING_OPTIONAL_TEST_FAILED);
+  }
+
+  public void potentiallyPendingTest(Publisher<T> pub, PublisherTestRun<T> body, String message) throws Throwable {
+    if (pub != null) {
+      body.run(pub);
+    } else {
+      throw new SkipException(message);
+    }
+  }
+
+  /**
+   * Executes a given test body {@code n} times.
+   * All the test runs must pass in order for the stochastic test to pass.
+   */
+  public void stochasticTest(int n, Function<Integer, Void> body) throws Throwable {
+    if (skipStochasticTests()) {
+      notVerified("Skipping @Stochastic test because `skipStochasticTests()` returned `true`!");
+    }
+
+    for (int i = 0; i < n; i++) {
+      body.apply(i);
+    }
+  }
+
+  public void notVerified() {
+    throw new SkipException("Not verified by this TCK.");
+  }
+
+  /**
+   * Return this value from {@link PublisherVerification#maxElementsFromPublisher()} to mark that the given {@link org.reactivestreams.Publisher},
+   * is not able to signal completion. For example it is strictly a time-bound or unbounded source of data.
+   *
+   * <b>Returning this value from {@link PublisherVerification#maxElementsFromPublisher()} will result in skipping all TCK tests which require onComplete signals!</b>
+   */
+  public long publisherUnableToSignalOnComplete() {
+    return Long.MAX_VALUE;
+  }
+
+  public void notVerified(String message) {
+    throw new SkipException(message);
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/SubscriberBlackboxVerification.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.tck;
+
+import org.reactivestreams.Publisher;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+import org.reactivestreams.tck.TestEnvironment.ManualPublisher;
+import org.reactivestreams.tck.TestEnvironment.ManualSubscriber;
+import org.reactivestreams.tck.flow.support.Optional;
+import org.reactivestreams.tck.flow.support.SubscriberBlackboxVerificationRules;
+import org.reactivestreams.tck.flow.support.TestException;
+import org.testng.SkipException;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static org.reactivestreams.tck.SubscriberWhiteboxVerification.BlackboxSubscriberProxy;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Provides tests for verifying {@link org.reactivestreams.Subscriber} and {@link org.reactivestreams.Subscription}
+ * specification rules, without any modifications to the tested implementation (also known as "Black Box" testing).
+ *
+ * This verification is NOT able to check many of the rules of the spec, and if you want more
+ * verification of your implementation you'll have to implement {@code org.reactivestreams.tck.SubscriberWhiteboxVerification}
+ * instead.
+ *
+ * @see org.reactivestreams.Subscriber
+ * @see org.reactivestreams.Subscription
+ */
+public abstract class SubscriberBlackboxVerification<T> extends WithHelperPublisher<T>
+  implements SubscriberBlackboxVerificationRules {
+
+  protected final TestEnvironment env;
+
+  protected SubscriberBlackboxVerification(TestEnvironment env) {
+    this.env = env;
+  }
+
+  // USER API
+
+  /**
+   * This is the main method you must implement in your test incarnation.
+   * It must create a new {@link org.reactivestreams.Subscriber} instance to be subjected to the testing logic.
+   */
+  public abstract Subscriber<T> createSubscriber();
+
+  /**
+   * Override this method if the Subscriber implementation you are verifying
+   * needs an external signal before it signals demand to its Publisher.
+   *
+   * By default this method does nothing.
+   */
+  public void triggerRequest(final Subscriber<? super T> subscriber) {
+    // this method is intentionally left blank
+  }
+
+  // ENV SETUP
+
+  /**
+   * Executor service used by the default provided asynchronous Publisher.
+   * @see #createHelperPublisher(long)
+   */
+  private ExecutorService publisherExecutor;
+  @BeforeClass public void startPublisherExecutorService() { publisherExecutor = Executors.newFixedThreadPool(4); }
+  @AfterClass public void shutdownPublisherExecutorService() { if (publisherExecutor != null) publisherExecutor.shutdown(); }
+  @Override public ExecutorService publisherExecutorService() { return publisherExecutor; }
+
+  ////////////////////// TEST ENV CLEANUP /////////////////////////////////////
+
+  @BeforeMethod
+  public void setUp() throws Exception {
+    env.clearAsyncErrors();
+  }
+
+  ////////////////////// SPEC RULE VERIFICATION ///////////////////////////////
+
+  @Override @Test
+  public void required_spec201_blackbox_mustSignalDemandViaSubscriptionRequest() throws Throwable {
+    blackboxSubscriberTest(new BlackboxTestStageTestRun() {
+      @Override
+      public void run(BlackboxTestStage stage) throws InterruptedException {
+        triggerRequest(stage.subProxy().sub());
+        final long requested = stage.expectRequest();// assuming subscriber wants to consume elements...
+        final long signalsToEmit = Math.min(requested, 512); // protecting against Subscriber which sends ridiculous large demand
+
+        // should cope with up to requested number of elements
+        for (int i = 0; i < signalsToEmit && sampleIsCancelled(stage, i, 10); i++)
+          stage.signalNext();
+
+        // we complete after `signalsToEmit` (which can be less than `requested`),
+        // which is legal under https://github.com/reactive-streams/reactive-streams-jvm#1.2
+        stage.sendCompletion();
+      }
+
+      /**
+       * In order to allow some "skid" and not check state on each iteration,
+       * only check {@code stage.isCancelled} every {@code checkInterval}'th iteration.
+       */
+      private boolean sampleIsCancelled(BlackboxTestStage stage, int i, int checkInterval) throws InterruptedException {
+        if (i % checkInterval == 0) return stage.isCancelled();
+        else return false;
+      }
+    });
+  }
+
+  @Override @Test
+  public void untested_spec202_blackbox_shouldAsynchronouslyDispatch() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  @Override @Test
+  public void required_spec203_blackbox_mustNotCallMethodsOnSubscriptionOrPublisherInOnComplete() throws Throwable {
+    blackboxSubscriberWithoutSetupTest(new BlackboxTestStageTestRun() {
+      @Override
+      public void run(BlackboxTestStage stage) throws Throwable {
+        final Subscription subs = new Subscription() {
+          @Override
+          public void request(long n) {
+            final Optional<StackTraceElement> onCompleteStackTraceElement = env.findCallerMethodInStackTrace("onComplete");
+            if (onCompleteStackTraceElement.isDefined()) {
+              final StackTraceElement stackElem = onCompleteStackTraceElement.get();
+              env.flop(String.format("Subscription::request MUST NOT be called from Subscriber::onComplete (Rule 2.3)! (Caller: %s::%s line %d)",
+                                     stackElem.getClassName(), stackElem.getMethodName(), stackElem.getLineNumber()));
+            }
+          }
+
+          @Override
+          public void cancel() {
+            final Optional<StackTraceElement> onCompleteStackElement = env.findCallerMethodInStackTrace("onComplete");
+            if (onCompleteStackElement.isDefined()) {
+              final StackTraceElement stackElem = onCompleteStackElement.get();
+              env.flop(String.format("Subscription::cancel MUST NOT be called from Subscriber::onComplete (Rule 2.3)! (Caller: %s::%s line %d)",
+                                     stackElem.getClassName(), stackElem.getMethodName(), stackElem.getLineNumber()));
+            }
+          }
+        };
+
+        final Subscriber<T> sub = createSubscriber();
+        sub.onSubscribe(subs);
+        sub.onComplete();
+
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+  @Override @Test
+  public void required_spec203_blackbox_mustNotCallMethodsOnSubscriptionOrPublisherInOnError() throws Throwable {
+    blackboxSubscriberWithoutSetupTest(new BlackboxTestStageTestRun() {
+      @Override
+      public void run(BlackboxTestStage stage) throws Throwable {
+        final Subscription subs = new Subscription() {
+          @Override
+          public void request(long n) {
+            Throwable thr = new Throwable();
+            for (StackTraceElement stackElem : thr.getStackTrace()) {
+              if (stackElem.getMethodName().equals("onError")) {
+                env.flop(String.format("Subscription::request MUST NOT be called from Subscriber::onError (Rule 2.3)! (Caller: %s::%s line %d)",
+                                       stackElem.getClassName(), stackElem.getMethodName(), stackElem.getLineNumber()));
+              }
+            }
+          }
+
+          @Override
+          public void cancel() {
+            Throwable thr = new Throwable();
+            for (StackTraceElement stackElem : thr.getStackTrace()) {
+              if (stackElem.getMethodName().equals("onError")) {
+                env.flop(String.format("Subscription::cancel MUST NOT be called from Subscriber::onError (Rule 2.3)! (Caller: %s::%s line %d)",
+                                       stackElem.getClassName(), stackElem.getMethodName(), stackElem.getLineNumber()));
+              }
+            }
+          }
+        };
+
+        final Subscriber<T> sub = createSubscriber();
+        sub.onSubscribe(subs);
+        sub.onError(new TestException());
+
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+  @Override @Test
+  public void untested_spec204_blackbox_mustConsiderTheSubscriptionAsCancelledInAfterRecievingOnCompleteOrOnError() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  @Override @Test
+  public void required_spec205_blackbox_mustCallSubscriptionCancelIfItAlreadyHasAnSubscriptionAndReceivesAnotherOnSubscribeSignal() throws Exception {
+    new BlackboxTestStage(env) {{
+      // try to subscribe another time, if the subscriber calls `probe.registerOnSubscribe` the test will fail
+      final TestEnvironment.Latch secondSubscriptionCancelled = new TestEnvironment.Latch(env);
+      sub().onSubscribe(
+          new Subscription() {
+            @Override
+            public void request(long elements) {
+              env.flop(String.format("Subscriber %s illegally called `subscription.request(%s)`!", sub(), elements));
+            }
+
+            @Override
+            public void cancel() {
+              secondSubscriptionCancelled.close();
+            }
+
+            @Override
+            public String toString() {
+              return "SecondSubscription(should get cancelled)";
+            }
+          });
+
+      secondSubscriptionCancelled.expectClose("Expected SecondSubscription given to subscriber to be cancelled, but `Subscription.cancel()` was not called.");
+      env.verifyNoAsyncErrorsNoDelay();
+      sendCompletion(); // we're done, complete the subscriber under test
+    }};
+  }
+
+  @Override @Test
+  public void untested_spec206_blackbox_mustCallSubscriptionCancelIfItIsNoLongerValid() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  @Override @Test
+  public void untested_spec207_blackbox_mustEnsureAllCallsOnItsSubscriptionTakePlaceFromTheSameThreadOrTakeCareOfSynchronization() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+    // the same thread part of the clause can be verified but that is not very useful, or is it?
+  }
+
+  @Override @Test
+  public void untested_spec208_blackbox_mustBePreparedToReceiveOnNextSignalsAfterHavingCalledSubscriptionCancel() throws Throwable {
+    notVerified(); // cannot be meaningfully tested as black box, or can it?
+  }
+
+  @Override @Test
+  public void required_spec209_blackbox_mustBePreparedToReceiveAnOnCompleteSignalWithPrecedingRequestCall() throws Throwable {
+    blackboxSubscriberTest(new BlackboxTestStageTestRun() {
+      @Override @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+      public void run(BlackboxTestStage stage) throws Throwable {
+        triggerRequest(stage.subProxy().sub());
+        final long notUsed = stage.expectRequest(); // received request signal
+        stage.sub().onComplete();
+        stage.subProxy().expectCompletion();
+      }
+    });
+  }
+
+  @Override @Test
+  public void required_spec209_blackbox_mustBePreparedToReceiveAnOnCompleteSignalWithoutPrecedingRequestCall() throws Throwable {
+    blackboxSubscriberTest(new BlackboxTestStageTestRun() {
+      @Override @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+      public void run(BlackboxTestStage stage) throws Throwable {
+        final Subscriber<? super T> sub = stage.sub();
+        sub.onComplete();
+        stage.subProxy().expectCompletion();
+      }
+    });
+  }
+
+  @Override @Test
+  public void required_spec210_blackbox_mustBePreparedToReceiveAnOnErrorSignalWithPrecedingRequestCall() throws Throwable {
+    blackboxSubscriberTest(new BlackboxTestStageTestRun() {
+      @Override @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+      public void run(BlackboxTestStage stage) throws Throwable {
+        triggerRequest(stage.subProxy().sub());
+        final long notUsed = stage.expectRequest(); // received request signal
+        stage.sub().onError(new TestException()); // in response to that, we fail
+        stage.subProxy().expectError(Throwable.class);
+      }
+    });
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.10
+  @Override @Test
+  public void required_spec210_blackbox_mustBePreparedToReceiveAnOnErrorSignalWithoutPrecedingRequestCall() throws Throwable {
+    blackboxSubscriberTest(new BlackboxTestStageTestRun() {
+      @Override @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+      public void run(BlackboxTestStage stage) throws Throwable {
+
+        stage.sub().onError(new TestException());
+        stage.subProxy().expectError(Throwable.class);
+      }
+    });
+  }
+
+  @Override @Test
+  public void untested_spec211_blackbox_mustMakeSureThatAllCallsOnItsMethodsHappenBeforeTheProcessingOfTheRespectiveEvents() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  @Override @Test
+  public void untested_spec212_blackbox_mustNotCallOnSubscribeMoreThanOnceBasedOnObjectEquality() throws Throwable {
+    notVerified(); // cannot be meaningfully tested as black box, or can it?
+  }
+
+  @Override @Test
+  public void untested_spec213_blackbox_failingOnSignalInvocation() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  @Override @Test
+  public void required_spec213_blackbox_onSubscribe_mustThrowNullPointerExceptionWhenParametersAreNull() throws Throwable {
+    blackboxSubscriberWithoutSetupTest(new BlackboxTestStageTestRun() {
+      @Override
+      public void run(BlackboxTestStage stage) throws Throwable {
+
+        {
+          final Subscriber<T> sub = createSubscriber();
+          boolean gotNPE = false;
+          try {
+            sub.onSubscribe(null);
+          } catch(final NullPointerException expected) {
+            gotNPE = true;
+          }
+          assertTrue(gotNPE, "onSubscribe(null) did not throw NullPointerException");
+        }
+
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+  @Override @Test
+  public void required_spec213_blackbox_onNext_mustThrowNullPointerExceptionWhenParametersAreNull() throws Throwable {
+    blackboxSubscriberWithoutSetupTest(new BlackboxTestStageTestRun() {
+      @Override
+      public void run(BlackboxTestStage stage) throws Throwable {
+        final Subscription subscription = new Subscription() {
+          @Override public void request(final long elements) {}
+          @Override public void cancel() {}
+        };
+
+        {
+          final Subscriber<T> sub = createSubscriber();
+          boolean gotNPE = false;
+          sub.onSubscribe(subscription);
+          try {
+            sub.onNext(null);
+          } catch(final NullPointerException expected) {
+            gotNPE = true;
+          }
+          assertTrue(gotNPE, "onNext(null) did not throw NullPointerException");
+        }
+
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+  @Override @Test
+  public void required_spec213_blackbox_onError_mustThrowNullPointerExceptionWhenParametersAreNull() throws Throwable {
+    blackboxSubscriberWithoutSetupTest(new BlackboxTestStageTestRun() {
+      @Override
+      public void run(BlackboxTestStage stage) throws Throwable {
+        final Subscription subscription = new Subscription() {
+          @Override public void request(final long elements) {}
+          @Override public void cancel() {}
+        };
+
+        {
+          final Subscriber<T> sub = createSubscriber();
+          boolean gotNPE = false;
+          sub.onSubscribe(subscription);
+          try {
+            sub.onError(null);
+          } catch(final NullPointerException expected) {
+            gotNPE = true;
+          }
+          assertTrue(gotNPE, "onError(null) did not throw NullPointerException");
+        }
+
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+  ////////////////////// SUBSCRIPTION SPEC RULE VERIFICATION //////////////////
+
+  @Override @Test
+  public void untested_spec301_blackbox_mustNotBeCalledOutsideSubscriberContext() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  @Override @Test
+  public void untested_spec308_blackbox_requestMustRegisterGivenNumberElementsToBeProduced() throws Throwable {
+    notVerified(); // cannot be meaningfully tested as black box, or can it?
+  }
+
+  @Override @Test
+  public void untested_spec310_blackbox_requestMaySynchronouslyCallOnNextOnSubscriber() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  @Override @Test
+  public void untested_spec311_blackbox_requestMaySynchronouslyCallOnCompleteOrOnError() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  @Override @Test
+  public void untested_spec314_blackbox_cancelMayCauseThePublisherToShutdownIfNoOtherSubscriptionExists() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  @Override @Test
+  public void untested_spec315_blackbox_cancelMustNotThrowExceptionAndMustSignalOnError() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  @Override @Test
+  public void untested_spec316_blackbox_requestMustNotThrowExceptionAndMustOnErrorTheSubscriber() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  /////////////////////// ADDITIONAL "COROLLARY" TESTS ////////////////////////
+
+  /////////////////////// TEST INFRASTRUCTURE /////////////////////////////////
+
+  abstract class BlackboxTestStageTestRun {
+    public abstract void run(BlackboxTestStage stage) throws Throwable;
+  }
+
+  public void blackboxSubscriberTest(BlackboxTestStageTestRun body) throws Throwable {
+    BlackboxTestStage stage = new BlackboxTestStage(env, true);
+    body.run(stage);
+  }
+
+  public void blackboxSubscriberWithoutSetupTest(BlackboxTestStageTestRun body) throws Throwable {
+    BlackboxTestStage stage = new BlackboxTestStage(env, false);
+    body.run(stage);
+  }
+
+  public class BlackboxTestStage extends ManualPublisher<T> {
+    public Publisher<T> pub;
+    public ManualSubscriber<T> tees; // gives us access to an infinite stream of T values
+
+    public T lastT = null;
+    private Optional<BlackboxSubscriberProxy<T>> subProxy = Optional.empty();
+
+    public BlackboxTestStage(TestEnvironment env) throws InterruptedException {
+      this(env, true);
+    }
+
+    public BlackboxTestStage(TestEnvironment env, boolean runDefaultInit) throws InterruptedException {
+      super(env);
+      if (runDefaultInit) {
+        pub = this.createHelperPublisher(Long.MAX_VALUE);
+        tees = env.newManualSubscriber(pub);
+        Subscriber<T> sub = createSubscriber();
+        subProxy = Optional.of(createBlackboxSubscriberProxy(env, sub));
+        subscribe(subProxy.get());
+      }
+    }
+
+    public Subscriber<? super T> sub() {
+      return subscriber.value();
+    }
+
+    /**
+     * Proxy for the {@link #sub()} {@code Subscriber}, providing certain assertions on methods being called on the Subscriber.
+     */
+    public BlackboxSubscriberProxy<T> subProxy() {
+      return subProxy.get();
+    }
+
+    public Publisher<T> createHelperPublisher(long elements) {
+      return SubscriberBlackboxVerification.this.createHelperPublisher(elements);
+    }
+
+    public BlackboxSubscriberProxy<T> createBlackboxSubscriberProxy(TestEnvironment env, Subscriber<T> sub) {
+      return new BlackboxSubscriberProxy<T>(env, sub);
+    }
+
+    public T signalNext() throws InterruptedException {
+      T element = nextT();
+      sendNext(element);
+      return element;
+    }
+
+    public T nextT() throws InterruptedException {
+      lastT = tees.requestNextElement();
+      return lastT;
+    }
+
+  }
+
+  public void notVerified() {
+    throw new SkipException("Not verified using this TCK.");
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/SubscriberWhiteboxVerification.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,843 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.tck;
+
+import org.reactivestreams.Publisher;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+import org.reactivestreams.tck.TestEnvironment.*;
+import org.reactivestreams.tck.flow.support.Optional;
+import org.reactivestreams.tck.flow.support.SubscriberWhiteboxVerificationRules;
+import org.reactivestreams.tck.flow.support.TestException;
+import org.testng.SkipException;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Provides whitebox style tests for verifying {@link org.reactivestreams.Subscriber}
+ * and {@link org.reactivestreams.Subscription} specification rules.
+ *
+ * @see org.reactivestreams.Subscriber
+ * @see org.reactivestreams.Subscription
+ */
+public abstract class SubscriberWhiteboxVerification<T> extends WithHelperPublisher<T>
+  implements SubscriberWhiteboxVerificationRules {
+
+  private final TestEnvironment env;
+
+  protected SubscriberWhiteboxVerification(TestEnvironment env) {
+    this.env = env;
+  }
+
+  // USER API
+
+  /**
+   * This is the main method you must implement in your test incarnation.
+   * It must create a new {@link org.reactivestreams.Subscriber} instance to be subjected to the testing logic.
+   *
+   * In order to be meaningfully testable your Subscriber must inform the given
+   * `WhiteboxSubscriberProbe` of the respective events having been received.
+   */
+  public abstract Subscriber<T> createSubscriber(WhiteboxSubscriberProbe<T> probe);
+
+  // ENV SETUP
+
+  /**
+   * Executor service used by the default provided asynchronous Publisher.
+   * @see #createHelperPublisher(long)
+   */
+  private ExecutorService publisherExecutor;
+  @BeforeClass public void startPublisherExecutorService() { publisherExecutor = Executors.newFixedThreadPool(4); }
+  @AfterClass public void shutdownPublisherExecutorService() { if (publisherExecutor != null) publisherExecutor.shutdown(); }
+  @Override public ExecutorService publisherExecutorService() { return publisherExecutor; }
+
+  ////////////////////// TEST ENV CLEANUP /////////////////////////////////////
+
+  @BeforeMethod
+  public void setUp() throws Exception {
+    env.clearAsyncErrors();
+  }
+
+  ////////////////////// TEST SETUP VERIFICATION //////////////////////////////
+
+  @Test
+  public void required_exerciseWhiteboxHappyPath() throws Throwable {
+    subscriberTest(new TestStageTestRun() {
+      @Override
+      public void run(WhiteboxTestStage stage) throws InterruptedException {
+        stage.puppet().triggerRequest(1);
+        stage.puppet().triggerRequest(1);
+
+        long receivedRequests = stage.expectRequest();
+
+        stage.signalNext();
+        stage.probe.expectNext(stage.lastT);
+
+        stage.puppet().triggerRequest(1);
+        if (receivedRequests == 1) {
+          stage.expectRequest();
+        }
+
+        stage.signalNext();
+        stage.probe.expectNext(stage.lastT);
+
+        stage.puppet().signalCancel();
+        stage.expectCancelling();
+
+        stage.verifyNoAsyncErrors();
+      }
+    });
+  }
+
+  ////////////////////// SPEC RULE VERIFICATION ///////////////////////////////
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.1
+  @Override @Test
+  public void required_spec201_mustSignalDemandViaSubscriptionRequest() throws Throwable {
+    subscriberTest(new TestStageTestRun() {
+      @Override
+      public void run(WhiteboxTestStage stage) throws InterruptedException {
+        stage.puppet().triggerRequest(1);
+        stage.expectRequest();
+
+        stage.signalNext();
+      }
+    });
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.2
+  @Override @Test
+  public void untested_spec202_shouldAsynchronouslyDispatch() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.3
+  @Override @Test
+  public void required_spec203_mustNotCallMethodsOnSubscriptionOrPublisherInOnComplete() throws Throwable {
+    subscriberTestWithoutSetup(new TestStageTestRun() {
+      @Override
+      public void run(WhiteboxTestStage stage) throws Throwable {
+        final Subscription subs = new Subscription() {
+          @Override
+          public void request(long n) {
+            final Optional<StackTraceElement> onCompleteStackTraceElement = env.findCallerMethodInStackTrace("onComplete");
+            if (onCompleteStackTraceElement.isDefined()) {
+              final StackTraceElement stackElem = onCompleteStackTraceElement.get();
+              env.flop(String.format("Subscription::request MUST NOT be called from Subscriber::onComplete (Rule 2.3)! (Caller: %s::%s line %d)",
+                                     stackElem.getClassName(), stackElem.getMethodName(), stackElem.getLineNumber()));
+            }
+          }
+
+          @Override
+          public void cancel() {
+            final Optional<StackTraceElement> onCompleteStackElement = env.findCallerMethodInStackTrace("onComplete");
+            if (onCompleteStackElement.isDefined()) {
+              final StackTraceElement stackElem = onCompleteStackElement.get();
+              env.flop(String.format("Subscription::cancel MUST NOT be called from Subscriber::onComplete (Rule 2.3)! (Caller: %s::%s line %d)",
+                                     stackElem.getClassName(), stackElem.getMethodName(), stackElem.getLineNumber()));
+            }
+          }
+        };
+
+        stage.probe = stage.createWhiteboxSubscriberProbe(env);
+        final Subscriber<T> sub = createSubscriber(stage.probe);
+
+        sub.onSubscribe(subs);
+        sub.onComplete();
+
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.3
+  @Override @Test
+  public void required_spec203_mustNotCallMethodsOnSubscriptionOrPublisherInOnError() throws Throwable {
+    subscriberTestWithoutSetup(new TestStageTestRun() {
+      @Override
+      public void run(WhiteboxTestStage stage) throws Throwable {
+        final Subscription subs = new Subscription() {
+          @Override
+          public void request(long n) {
+            Throwable thr = new Throwable();
+            for (StackTraceElement stackElem : thr.getStackTrace()) {
+              if (stackElem.getMethodName().equals("onError")) {
+                env.flop(String.format("Subscription::request MUST NOT be called from Subscriber::onError (Rule 2.3)! (Caller: %s::%s line %d)",
+                                       stackElem.getClassName(), stackElem.getMethodName(), stackElem.getLineNumber()));
+              }
+            }
+          }
+
+          @Override
+          public void cancel() {
+            Throwable thr = new Throwable();
+            for (StackTraceElement stackElem : thr.getStackTrace()) {
+              if (stackElem.getMethodName().equals("onError")) {
+                env.flop(String.format("Subscription::cancel MUST NOT be called from Subscriber::onError (Rule 2.3)! (Caller: %s::%s line %d)",
+                                       stackElem.getClassName(), stackElem.getMethodName(), stackElem.getLineNumber()));
+              }
+            }
+          }
+        };
+
+        stage.probe = stage.createWhiteboxSubscriberProbe(env);
+        final Subscriber<T> sub = createSubscriber(stage.probe);
+
+        sub.onSubscribe(subs);
+        sub.onError(new TestException());
+
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.4
+  @Override @Test
+  public void untested_spec204_mustConsiderTheSubscriptionAsCancelledInAfterRecievingOnCompleteOrOnError() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.5
+  @Override @Test
+  public void required_spec205_mustCallSubscriptionCancelIfItAlreadyHasAnSubscriptionAndReceivesAnotherOnSubscribeSignal() throws Throwable {
+    subscriberTest(new TestStageTestRun() {
+      @Override
+      public void run(WhiteboxTestStage stage) throws Throwable {
+        // try to subscribe another time, if the subscriber calls `probe.registerOnSubscribe` the test will fail
+        final Latch secondSubscriptionCancelled = new Latch(env);
+        final Subscriber<? super T> sub = stage.sub();
+        final Subscription subscription = new Subscription() {
+          @Override
+          public void request(long elements) {
+            // ignore...
+          }
+
+          @Override
+          public void cancel() {
+            secondSubscriptionCancelled.close();
+          }
+
+          @Override
+          public String toString() {
+            return "SecondSubscription(should get cancelled)";
+          }
+        };
+        sub.onSubscribe(subscription);
+
+        secondSubscriptionCancelled.expectClose("Expected 2nd Subscription given to subscriber to be cancelled, but `Subscription.cancel()` was not called");
+        env.verifyNoAsyncErrors();
+      }
+    });
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.6
+  @Override @Test
+  public void untested_spec206_mustCallSubscriptionCancelIfItIsNoLongerValid() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.7
+  @Override @Test
+  public void untested_spec207_mustEnsureAllCallsOnItsSubscriptionTakePlaceFromTheSameThreadOrTakeCareOfSynchronization() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+    // the same thread part of the clause can be verified but that is not very useful, or is it?
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.8
+  @Override @Test
+  public void required_spec208_mustBePreparedToReceiveOnNextSignalsAfterHavingCalledSubscriptionCancel() throws Throwable {
+    subscriberTest(new TestStageTestRun() {
+      @Override
+      public void run(WhiteboxTestStage stage) throws InterruptedException {
+        stage.puppet().triggerRequest(1);
+        stage.puppet().signalCancel();
+        stage.expectRequest();
+        stage.signalNext();
+
+        stage.puppet().triggerRequest(1);
+        stage.puppet().triggerRequest(1);
+
+        stage.verifyNoAsyncErrors();
+      }
+    });
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.9
+  @Override @Test
+  public void required_spec209_mustBePreparedToReceiveAnOnCompleteSignalWithPrecedingRequestCall() throws Throwable {
+    subscriberTest(new TestStageTestRun() {
+      @Override
+      public void run(WhiteboxTestStage stage) throws InterruptedException {
+        stage.puppet().triggerRequest(1);
+        stage.sendCompletion();
+        stage.probe.expectCompletion();
+
+        stage.verifyNoAsyncErrors();
+      }
+    });
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.9
+  @Override @Test
+  public void required_spec209_mustBePreparedToReceiveAnOnCompleteSignalWithoutPrecedingRequestCall() throws Throwable {
+    subscriberTest(new TestStageTestRun() {
+      @Override
+      public void run(WhiteboxTestStage stage) throws InterruptedException {
+        stage.sendCompletion();
+        stage.probe.expectCompletion();
+
+        stage.verifyNoAsyncErrors();
+      }
+    });
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.10
+  @Override @Test
+  public void required_spec210_mustBePreparedToReceiveAnOnErrorSignalWithPrecedingRequestCall() throws Throwable {
+    subscriberTest(new TestStageTestRun() {
+      @Override
+      public void run(WhiteboxTestStage stage) throws InterruptedException {
+        stage.puppet().triggerRequest(1);
+        stage.puppet().triggerRequest(1);
+
+        Exception ex = new TestException();
+        stage.sendError(ex);
+        stage.probe.expectError(ex);
+
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.10
+  @Override @Test
+  public void required_spec210_mustBePreparedToReceiveAnOnErrorSignalWithoutPrecedingRequestCall() throws Throwable {
+    subscriberTest(new TestStageTestRun() {
+      @Override
+      public void run(WhiteboxTestStage stage) throws InterruptedException {
+        Exception ex = new TestException();
+        stage.sendError(ex);
+        stage.probe.expectError(ex);
+
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.11
+  @Override @Test
+  public void untested_spec211_mustMakeSureThatAllCallsOnItsMethodsHappenBeforeTheProcessingOfTheRespectiveEvents() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.12
+  @Override @Test
+  public void untested_spec212_mustNotCallOnSubscribeMoreThanOnceBasedOnObjectEquality_specViolation() throws Throwable {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.13
+  @Override @Test
+  public void untested_spec213_failingOnSignalInvocation() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.13
+  @Override @Test
+  public void required_spec213_onSubscribe_mustThrowNullPointerExceptionWhenParametersAreNull() throws Throwable {
+    subscriberTest(new TestStageTestRun() {
+      @Override
+      public void run(WhiteboxTestStage stage) throws Throwable {
+
+        final Subscriber<? super T> sub = stage.sub();
+        boolean gotNPE = false;
+        try {
+          sub.onSubscribe(null);
+        } catch (final NullPointerException expected) {
+          gotNPE = true;
+        }
+
+        assertTrue(gotNPE, "onSubscribe(null) did not throw NullPointerException");
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.13
+  @Override @Test
+  public void required_spec213_onNext_mustThrowNullPointerExceptionWhenParametersAreNull() throws Throwable {
+    subscriberTest(new TestStageTestRun() {
+      @Override
+      public void run(WhiteboxTestStage stage) throws Throwable {
+
+        final Subscriber<? super T> sub = stage.sub();
+        boolean gotNPE = false;
+        try {
+          sub.onNext(null);
+        } catch (final NullPointerException expected) {
+          gotNPE = true;
+        }
+
+        assertTrue(gotNPE, "onNext(null) did not throw NullPointerException");
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#2.13
+  @Override @Test
+  public void required_spec213_onError_mustThrowNullPointerExceptionWhenParametersAreNull() throws Throwable {
+    subscriberTest(new TestStageTestRun() {
+      @Override
+      public void run(WhiteboxTestStage stage) throws Throwable {
+
+          final Subscriber<? super T> sub = stage.sub();
+          boolean gotNPE = false;
+          try {
+            sub.onError(null);
+          } catch (final NullPointerException expected) {
+            gotNPE = true;
+          } finally {
+            assertTrue(gotNPE, "onError(null) did not throw NullPointerException");
+          }
+
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    });
+  }
+
+
+  ////////////////////// SUBSCRIPTION SPEC RULE VERIFICATION //////////////////
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#3.1
+  @Override @Test
+  public void untested_spec301_mustNotBeCalledOutsideSubscriberContext() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#3.8
+  @Override @Test
+  public void required_spec308_requestMustRegisterGivenNumberElementsToBeProduced() throws Throwable {
+    subscriberTest(new TestStageTestRun() {
+      @Override
+      public void run(WhiteboxTestStage stage) throws InterruptedException {
+        stage.puppet().triggerRequest(2);
+        long requestedElements = stage.expectRequest();
+        stage.probe.expectNext(stage.signalNext());
+        // Some subscribers may only request one element at a time.
+        if (requestedElements < 2) {
+          stage.expectRequest();
+        }
+        stage.probe.expectNext(stage.signalNext());
+
+        stage.probe.expectNone();
+        stage.puppet().triggerRequest(3);
+
+        stage.verifyNoAsyncErrors();
+      }
+    });
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#3.10
+  @Override @Test
+  public void untested_spec310_requestMaySynchronouslyCallOnNextOnSubscriber() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#3.11
+  @Override @Test
+  public void untested_spec311_requestMaySynchronouslyCallOnCompleteOrOnError() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#3.14
+  @Override @Test
+  public void untested_spec314_cancelMayCauseThePublisherToShutdownIfNoOtherSubscriptionExists() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#3.15
+  @Override @Test
+  public void untested_spec315_cancelMustNotThrowExceptionAndMustSignalOnError() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#3.16
+  @Override @Test
+  public void untested_spec316_requestMustNotThrowExceptionAndMustOnErrorTheSubscriber() throws Exception {
+    notVerified(); // cannot be meaningfully tested, or can it?
+  }
+
+  /////////////////////// ADDITIONAL "COROLLARY" TESTS ////////////////////////
+
+  /////////////////////// TEST INFRASTRUCTURE /////////////////////////////////
+
+  abstract class TestStageTestRun {
+    public abstract void run(WhiteboxTestStage stage) throws Throwable;
+  }
+
+  /**
+   * Prepares subscriber and publisher pair (by subscribing the first to the latter),
+   * and then hands over the tests {@link WhiteboxTestStage} over to the test.
+   *
+   * The test stage is, like in a puppet show, used to orchestrate what each participant should do.
+   * Since this is a whitebox test, this allows the stage to completely control when and how to signal / expect signals.
+   */
+  public void subscriberTest(TestStageTestRun body) throws Throwable {
+    WhiteboxTestStage stage = new WhiteboxTestStage(env, true);
+    body.run(stage);
+  }
+
+  /**
+   * Provides a {@link WhiteboxTestStage} without performing any additional setup,
+   * like the {@link #subscriberTest(SubscriberWhiteboxVerification.TestStageTestRun)} would.
+   *
+   * Use this method to write tests in which you need full control over when and how the initial {@code subscribe} is signalled.
+   */
+  public void subscriberTestWithoutSetup(TestStageTestRun body) throws Throwable {
+    WhiteboxTestStage stage = new WhiteboxTestStage(env, false);
+    body.run(stage);
+  }
+
+  /**
+   * Test for feature that MAY be implemented. This test will be marked as SKIPPED if it fails.
+   */
+  public void optionalSubscriberTestWithoutSetup(TestStageTestRun body) throws Throwable {
+    try {
+      subscriberTestWithoutSetup(body);
+    } catch (Exception ex) {
+      notVerified("Skipped because tested publisher does NOT implement this OPTIONAL requirement.");
+    }
+  }
+
+  public class WhiteboxTestStage extends ManualPublisher<T> {
+    public Publisher<T> pub;
+    public ManualSubscriber<T> tees; // gives us access to a stream T values
+    public WhiteboxSubscriberProbe<T> probe;
+
+    public T lastT = null;
+
+    public WhiteboxTestStage(TestEnvironment env) throws InterruptedException {
+      this(env, true);
+    }
+
+    public WhiteboxTestStage(TestEnvironment env, boolean runDefaultInit) throws InterruptedException {
+      super(env);
+      if (runDefaultInit) {
+        pub = this.createHelperPublisher(Long.MAX_VALUE);
+        tees = env.newManualSubscriber(pub);
+        probe = new WhiteboxSubscriberProbe<T>(env, subscriber);
+        subscribe(createSubscriber(probe));
+        probe.puppet.expectCompletion(env.defaultTimeoutMillis(), String.format("Subscriber %s did not `registerOnSubscribe`", sub()));
+        env.verifyNoAsyncErrorsNoDelay();
+      }
+    }
+
+    public Subscriber<? super T> sub() {
+      return subscriber.value();
+    }
+
+    public SubscriberPuppet puppet() {
+      return probe.puppet();
+    }
+
+    public WhiteboxSubscriberProbe<T> probe() {
+      return probe;
+    }
+
+    public Publisher<T> createHelperPublisher(long elements) {
+      return SubscriberWhiteboxVerification.this.createHelperPublisher(elements);
+    }
+
+    public WhiteboxSubscriberProbe<T> createWhiteboxSubscriberProbe(TestEnvironment env) {
+      return new WhiteboxSubscriberProbe<T>(env, subscriber);
+    }
+
+    public T signalNext() throws InterruptedException {
+      return signalNext(nextT());
+    }
+
+    private T signalNext(T element) throws InterruptedException {
+      sendNext(element);
+      return element;
+    }
+
+    public T nextT() throws InterruptedException {
+      lastT = tees.requestNextElement();
+      return lastT;
+    }
+
+    public void verifyNoAsyncErrors() {
+      env.verifyNoAsyncErrors();
+    }
+  }
+
+  /**
+   * This class is intented to be used as {@code Subscriber} decorator and should be used in {@code pub.subscriber(...)} calls,
+   * in order to allow intercepting calls on the underlying {@code Subscriber}.
+   * This delegation allows the proxy to implement {@link BlackboxProbe} assertions.
+   */
+  public static class BlackboxSubscriberProxy<T> extends BlackboxProbe<T> implements Subscriber<T> {
+
+    public BlackboxSubscriberProxy(TestEnvironment env, Subscriber<T> subscriber) {
+      super(env, Promise.<Subscriber<? super T>>completed(env, subscriber));
+    }
+
+    @Override
+    public void onSubscribe(Subscription s) {
+      sub().onSubscribe(s);
+    }
+
+    @Override
+    public void onNext(T t) {
+      registerOnNext(t);
+      sub().onNext(t);
+    }
+
+    @Override
+    public void onError(Throwable cause) {
+      registerOnError(cause);
+      sub().onError(cause);
+    }
+
+    @Override
+    public void onComplete() {
+      registerOnComplete();
+      sub().onComplete();
+    }
+  }
+
+  public static class BlackboxProbe<T> implements SubscriberProbe<T> {
+    protected final TestEnvironment env;
+    protected final Promise<Subscriber<? super T>> subscriber;
+
+    protected final Receptacle<T> elements;
+    protected final Promise<Throwable> error;
+
+    public BlackboxProbe(TestEnvironment env, Promise<Subscriber<? super T>> subscriber) {
+      this.env = env;
+      this.subscriber = subscriber;
+      elements = new Receptacle<T>(env);
+      error = new Promise<Throwable>(env);
+    }
+
+    @Override
+    public void registerOnNext(T element) {
+      elements.add(element);
+    }
+
+    @Override
+    public void registerOnComplete() {
+      try {
+        elements.complete();
+      } catch (IllegalStateException ex) {
+        // "Queue full", onComplete was already called
+        env.flop("subscriber::onComplete was called a second time, which is illegal according to Rule 1.7");
+      }
+    }
+
+    @Override
+    public void registerOnError(Throwable cause) {
+      try {
+        error.complete(cause);
+      } catch (IllegalStateException ex) {
+        // "Queue full", onError was already called
+        env.flop("subscriber::onError was called a second time, which is illegal according to Rule 1.7");
+      }
+    }
+
+    public T expectNext() throws InterruptedException {
+      return elements.next(env.defaultTimeoutMillis(), String.format("Subscriber %s did not call `registerOnNext(_)`", sub()));
+    }
+
+    public void expectNext(T expected) throws InterruptedException {
+      expectNext(expected, env.defaultTimeoutMillis());
+    }
+
+    public void expectNext(T expected, long timeoutMillis) throws InterruptedException {
+      T received = elements.next(timeoutMillis, String.format("Subscriber %s did not call `registerOnNext(%s)`", sub(), expected));
+      if (!received.equals(expected)) {
+        env.flop(String.format("Subscriber %s called `registerOnNext(%s)` rather than `registerOnNext(%s)`", sub(), received, expected));
+      }
+    }
+
+    public Subscriber<? super T> sub() {
+      return subscriber.value();
+    }
+
+    public void expectCompletion() throws InterruptedException {
+      expectCompletion(env.defaultTimeoutMillis());
+    }
+
+    public void expectCompletion(long timeoutMillis) throws InterruptedException {
+      expectCompletion(timeoutMillis, String.format("Subscriber %s did not call `registerOnComplete()`", sub()));
+    }
+
+    public void expectCompletion(long timeoutMillis, String msg) throws InterruptedException {
+      elements.expectCompletion(timeoutMillis, msg);
+    }
+
+    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+    public <E extends Throwable> void expectErrorWithMessage(Class<E> expected, String requiredMessagePart) throws InterruptedException {
+      final E err = expectError(expected);
+      String message = err.getMessage();
+      assertTrue(message.contains(requiredMessagePart),
+        String.format("Got expected exception %s but missing message [%s], was: %s", err.getClass(), requiredMessagePart, expected));
+    }
+
+    public <E extends Throwable> E expectError(Class<E> expected) throws InterruptedException {
+      return expectError(expected, env.defaultTimeoutMillis());
+    }
+
+    @SuppressWarnings({"unchecked", "ThrowableResultOfMethodCallIgnored"})
+    public <E extends Throwable> E expectError(Class<E> expected, long timeoutMillis) throws InterruptedException {
+      error.expectCompletion(timeoutMillis, String.format("Subscriber %s did not call `registerOnError(%s)`", sub(), expected));
+      if (error.value() == null) {
+        return env.flopAndFail(String.format("Subscriber %s did not call `registerOnError(%s)`", sub(), expected));
+      } else if (expected.isInstance(error.value())) {
+        return (E) error.value();
+      } else {
+        return env.flopAndFail(String.format("Subscriber %s called `registerOnError(%s)` rather than `registerOnError(%s)`", sub(), error.value(), expected));
+      }
+    }
+
+    public void expectError(Throwable expected) throws InterruptedException {
+      expectError(expected, env.defaultTimeoutMillis());
+    }
+
+    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+    public void expectError(Throwable expected, long timeoutMillis) throws InterruptedException {
+      error.expectCompletion(timeoutMillis, String.format("Subscriber %s did not call `registerOnError(%s)`", sub(), expected));
+      if (error.value() != expected) {
+        env.flop(String.format("Subscriber %s called `registerOnError(%s)` rather than `registerOnError(%s)`", sub(), error.value(), expected));
+      }
+    }
+
+    public void expectNone() throws InterruptedException {
+      expectNone(env.defaultNoSignalsTimeoutMillis());
+    }
+
+    public void expectNone(long withinMillis) throws InterruptedException {
+      elements.expectNone(withinMillis, "Expected nothing");
+    }
+
+  }
+
+  public static class WhiteboxSubscriberProbe<T> extends BlackboxProbe<T> implements SubscriberPuppeteer {
+    protected Promise<SubscriberPuppet> puppet;
+
+    public WhiteboxSubscriberProbe(TestEnvironment env, Promise<Subscriber<? super T>> subscriber) {
+      super(env, subscriber);
+      puppet = new Promise<SubscriberPuppet>(env);
+    }
+
+    private SubscriberPuppet puppet() {
+      return puppet.value();
+    }
+
+    @Override
+    public void registerOnSubscribe(SubscriberPuppet p) {
+      if (!puppet.isCompleted()) {
+        puppet.complete(p);
+      }
+    }
+
+  }
+
+  public interface SubscriberPuppeteer {
+
+    /**
+     * Must be called by the test subscriber when it has successfully registered a subscription
+     * inside the `onSubscribe` method.
+     */
+    void registerOnSubscribe(SubscriberPuppet puppet);
+  }
+
+  public interface SubscriberProbe<T> {
+
+    /**
+     * Must be called by the test subscriber when it has received an`onNext` event.
+     */
+    void registerOnNext(T element);
+
+    /**
+     * Must be called by the test subscriber when it has received an `onComplete` event.
+     */
+    void registerOnComplete();
+
+    /**
+     * Must be called by the test subscriber when it has received an `onError` event.
+     */
+    void registerOnError(Throwable cause);
+
+  }
+
+  /**
+   * Implement this puppet in your Whitebox style tests.
+   * The test suite will invoke the specific trigger/signal methods requesting you to execute the specific action.
+   * Since this is a whitebox style test, you're allowed and expected to use knowladge about your implementation to
+   * make implement these calls.
+   */
+  public interface SubscriberPuppet {
+
+    /**
+     * Ensure that at least {@code elements} are eventually requested by your {@link Subscriber}, if it hasn't already
+     * requested that many elements.
+     * <p>
+     * This does not necessarily have to correlate 1:1 with a {@code Subscription.request(elements)} call, but the sum
+     * of the elements requested by your {@code Subscriber} must eventually be at least the the sum of the elements
+     * triggered to be requested by all the invocations of this method.
+     * <p>
+     * Additionally, subscribers are permitted to delay requesting elements until previous requests for elements have
+     * been fulfilled. For example, a subscriber that only requests one element at a time may fulfill the request made
+     * by this method by requesting one element {@code elements} times, waiting for each element to arrive before the
+     * next request is made.
+     * <p>
+     * Before sending any element to the subscriber, the TCK must wait for the subscriber to request that element, and
+     * must be prepared for the subscriber to only request one element at a time, it is not enough for the TCK to
+     * simply invoke this method before sending elements.
+     */
+    void triggerRequest(long elements);
+
+    /**
+     * Trigger {@code cancel()} on your {@link Subscriber}
+     */
+    void signalCancel();
+  }
+
+  public void notVerified() {
+    throw new SkipException("Not verified using this TCK.");
+  }
+
+  public void notVerified(String msg) {
+    throw new SkipException(msg);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/TestEnvironment.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,1168 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.tck;
+
+import org.reactivestreams.Publisher;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+import org.reactivestreams.tck.flow.support.Optional;
+import org.reactivestreams.tck.flow.support.SubscriberBufferOverflowException;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+public class TestEnvironment {
+  public static final int TEST_BUFFER_SIZE = 16;
+
+  private static final String DEFAULT_TIMEOUT_MILLIS_ENV = "DEFAULT_TIMEOUT_MILLIS";
+  private static final long DEFAULT_TIMEOUT_MILLIS = 100;
+
+  private static final String DEFAULT_NO_SIGNALS_TIMEOUT_MILLIS_ENV = "DEFAULT_NO_SIGNALS_TIMEOUT_MILLIS";
+  private static final String DEFAULT_POLL_TIMEOUT_MILLIS_ENV = "DEFAULT_POLL_TIMEOUT_MILLIS_ENV";
+
+  private final long defaultTimeoutMillis;
+  private final long defaultPollTimeoutMillis;
+  private final long defaultNoSignalsTimeoutMillis;
+  private final boolean printlnDebug;
+
+  private CopyOnWriteArrayList<Throwable> asyncErrors = new CopyOnWriteArrayList<Throwable>();
+
+  /**
+   * Tests must specify the timeout for expected outcome of asynchronous
+   * interactions. Longer timeout does not invalidate the correctness of
+   * the implementation, but can in some cases result in longer time to
+   * run the tests.
+   * @param defaultTimeoutMillis default timeout to be used in all expect* methods
+   * @param defaultNoSignalsTimeoutMillis default timeout to be used when no further signals are expected anymore
+   * @param defaultPollTimeoutMillis default amount of time to poll for events if {@code defaultTimeoutMillis} isn't
+    *                                preempted by an asynchronous event.
+   * @param printlnDebug         if true, signals such as OnNext / Request / OnComplete etc will be printed to standard output,
+   */
+  public TestEnvironment(long defaultTimeoutMillis, long defaultNoSignalsTimeoutMillis, long defaultPollTimeoutMillis,
+                         boolean printlnDebug) {
+    this.defaultTimeoutMillis = defaultTimeoutMillis;
+    this.defaultPollTimeoutMillis = defaultPollTimeoutMillis;
+    this.defaultNoSignalsTimeoutMillis = defaultNoSignalsTimeoutMillis;
+    this.printlnDebug = printlnDebug;
+  }
+
+  /**
+   * Tests must specify the timeout for expected outcome of asynchronous
+   * interactions. Longer timeout does not invalidate the correctness of
+   * the implementation, but can in some cases result in longer time to
+   * run the tests.
+   * @param defaultTimeoutMillis default timeout to be used in all expect* methods
+   * @param defaultNoSignalsTimeoutMillis default timeout to be used when no further signals are expected anymore
+   * @param printlnDebug         if true, signals such as OnNext / Request / OnComplete etc will be printed to standard output,
+   */
+  public TestEnvironment(long defaultTimeoutMillis, long defaultNoSignalsTimeoutMillis, boolean printlnDebug) {
+    this(defaultTimeoutMillis, defaultNoSignalsTimeoutMillis, defaultTimeoutMillis, printlnDebug);
+  }
+
+  /**
+   * Tests must specify the timeout for expected outcome of asynchronous
+   * interactions. Longer timeout does not invalidate the correctness of
+   * the implementation, but can in some cases result in longer time to
+   * run the tests.
+   *
+   * @param defaultTimeoutMillis default timeout to be used in all expect* methods
+   * @param defaultNoSignalsTimeoutMillis default timeout to be used when no further signals are expected anymore
+   * @param defaultPollTimeoutMillis default amount of time to poll for events if {@code defaultTimeoutMillis} isn't
+   *                                 preempted by an asynchronous event.
+   */
+  public TestEnvironment(long defaultTimeoutMillis, long defaultNoSignalsTimeoutMillis, long defaultPollTimeoutMillis) {
+      this(defaultTimeoutMillis, defaultNoSignalsTimeoutMillis, defaultPollTimeoutMillis, false);
+  }
+
+  /**
+   * Tests must specify the timeout for expected outcome of asynchronous
+   * interactions. Longer timeout does not invalidate the correctness of
+   * the implementation, but can in some cases result in longer time to
+   * run the tests.
+   *
+   * @param defaultTimeoutMillis default timeout to be used in all expect* methods
+   * @param defaultNoSignalsTimeoutMillis default timeout to be used when no further signals are expected anymore
+   */
+  public TestEnvironment(long defaultTimeoutMillis, long defaultNoSignalsTimeoutMillis) {
+    this(defaultTimeoutMillis, defaultTimeoutMillis, defaultNoSignalsTimeoutMillis);
+  }
+
+  /**
+   * Tests must specify the timeout for expected outcome of asynchronous
+   * interactions. Longer timeout does not invalidate the correctness of
+   * the implementation, but can in some cases result in longer time to
+   * run the tests.
+   *
+   * @param defaultTimeoutMillis default timeout to be used in all expect* methods
+   */
+  public TestEnvironment(long defaultTimeoutMillis) {
+    this(defaultTimeoutMillis, defaultTimeoutMillis, defaultTimeoutMillis);
+  }
+
+  /**
+   * Tests must specify the timeout for expected outcome of asynchronous
+   * interactions. Longer timeout does not invalidate the correctness of
+   * the implementation, but can in some cases result in longer time to
+   * run the tests.
+   *
+   * The default timeout for all expect* methods will be obtained by either the env variable {@code DEFAULT_TIMEOUT_MILLIS}
+   * or the default value ({@link TestEnvironment#DEFAULT_TIMEOUT_MILLIS}) will be used.
+   *
+   * @param printlnDebug if true, signals such as OnNext / Request / OnComplete etc will be printed to standard output,
+   *                     often helpful to pinpoint simple race conditions etc.
+   */
+  public TestEnvironment(boolean printlnDebug) {
+    this(envDefaultTimeoutMillis(), envDefaultNoSignalsTimeoutMillis(), envDefaultPollTimeoutMillis(), printlnDebug);
+  }
+
+  /**
+   * Tests must specify the timeout for expected outcome of asynchronous
+   * interactions. Longer timeout does not invalidate the correctness of
+   * the implementation, but can in some cases result in longer time to
+   * run the tests.
+   *
+   * The default timeout for all expect* methods will be obtained by either the env variable {@code DEFAULT_TIMEOUT_MILLIS}
+   * or the default value ({@link TestEnvironment#DEFAULT_TIMEOUT_MILLIS}) will be used.
+   */
+  public TestEnvironment() {
+    this(envDefaultTimeoutMillis(), envDefaultNoSignalsTimeoutMillis());
+  }
+
+  /** This timeout is used when waiting for a signal to arrive. */
+  public long defaultTimeoutMillis() {
+    return defaultTimeoutMillis;
+  }
+
+  /**
+   * This timeout is used when asserting that no further signals are emitted.
+   * Note that this timeout default
+   */
+  public long defaultNoSignalsTimeoutMillis() {
+    return defaultNoSignalsTimeoutMillis;
+  }
+
+  /**
+   * The default amount of time to poll for events if {@code defaultTimeoutMillis} isn't preempted by an asynchronous
+   * event.
+   */
+  public long defaultPollTimeoutMillis() {
+    return defaultPollTimeoutMillis;
+  }
+
+  /**
+   * Tries to parse the env variable {@code DEFAULT_TIMEOUT_MILLIS} as long and returns the value if present OR its default value.
+   *
+   * @throws java.lang.IllegalArgumentException when unable to parse the env variable
+   */
+  public static long envDefaultTimeoutMillis() {
+    final String envMillis = System.getenv(DEFAULT_TIMEOUT_MILLIS_ENV);
+    if (envMillis == null) return DEFAULT_TIMEOUT_MILLIS;
+    else try {
+      return Long.parseLong(envMillis);
+    } catch (NumberFormatException ex) {
+      throw new IllegalArgumentException(String.format("Unable to parse %s env value [%s] as long!", DEFAULT_TIMEOUT_MILLIS_ENV, envMillis), ex);
+    }
+  }
+
+  /**
+   * Tries to parse the env variable {@code DEFAULT_NO_SIGNALS_TIMEOUT_MILLIS} as long and returns the value if present OR its default value.
+   *
+   * @throws java.lang.IllegalArgumentException when unable to parse the env variable
+   */
+  public static long envDefaultNoSignalsTimeoutMillis() {
+    final String envMillis = System.getenv(DEFAULT_NO_SIGNALS_TIMEOUT_MILLIS_ENV);
+    if (envMillis == null) return envDefaultTimeoutMillis();
+    else try {
+      return Long.parseLong(envMillis);
+    } catch (NumberFormatException ex) {
+      throw new IllegalArgumentException(String.format("Unable to parse %s env value [%s] as long!", DEFAULT_NO_SIGNALS_TIMEOUT_MILLIS_ENV, envMillis), ex);
+    }
+  }
+
+  /**
+   * Tries to parse the env variable {@code DEFAULT_POLL_TIMEOUT_MILLIS_ENV} as long and returns the value if present OR its default value.
+   *
+   * @throws java.lang.IllegalArgumentException when unable to parse the env variable
+   */
+  public static long envDefaultPollTimeoutMillis() {
+    final String envMillis = System.getenv(DEFAULT_POLL_TIMEOUT_MILLIS_ENV);
+    if (envMillis == null) return envDefaultTimeoutMillis();
+    else try {
+      return Long.parseLong(envMillis);
+    } catch (NumberFormatException ex) {
+      throw new IllegalArgumentException(String.format("Unable to parse %s env value [%s] as long!", DEFAULT_POLL_TIMEOUT_MILLIS_ENV, envMillis), ex);
+    }
+  }
+
+  /**
+   * To flop means to "fail asynchronously", either by onErroring or by failing some TCK check triggered asynchronously.
+   * This method does *NOT* fail the test - it's up to inspections of the error to fail the test if required.
+   *
+   * Use {@code env.verifyNoAsyncErrorsNoDelay()} at the end of your TCK tests to verify there no flops called during it's execution.
+   * To check investigate asyncErrors more closely you can use {@code expectError} methods or collect the error directly
+   * from the environment using {@code env.dropAsyncError()}.
+   *
+   * To clear asyncErrors you can call {@link org.reactivestreams.tck.TestEnvironment#clearAsyncErrors()}
+   */
+  public void flop(String msg) {
+    try {
+      fail(msg);
+    } catch (Throwable t) {
+      asyncErrors.add(t);
+    }
+  }
+
+  /**
+   * To flop means to "fail asynchronously", either by onErroring or by failing some TCK check triggered asynchronously.
+   * This method does *NOT* fail the test - it's up to inspections of the error to fail the test if required.
+   *
+   * This overload keeps the passed in throwable as the asyncError, instead of creating an AssertionError for this.
+   *
+   * Use {@code env.verifyNoAsyncErrorsNoDelay()} at the end of your TCK tests to verify there no flops called during it's execution.
+   * To check investigate asyncErrors more closely you can use {@code expectError} methods or collect the error directly
+   * from the environment using {@code env.dropAsyncError()}.
+   *
+   * To clear asyncErrors you can call {@link org.reactivestreams.tck.TestEnvironment#clearAsyncErrors()}
+   */
+  public void flop(Throwable thr, String msg) {
+    try {
+      fail(msg, thr);
+    } catch (Throwable t) {
+      asyncErrors.add(thr);
+    }
+  }
+
+  /**
+   * To flop means to "fail asynchronously", either by onErroring or by failing some TCK check triggered asynchronously.
+   * This method does *NOT* fail the test - it's up to inspections of the error to fail the test if required.
+   *
+   * This overload keeps the passed in throwable as the asyncError, instead of creating an AssertionError for this.
+   *
+   * Use {@code env.verifyNoAsyncErrorsNoDelay()} at the end of your TCK tests to verify there no flops called during it's execution.
+   * To check investigate asyncErrors more closely you can use {@code expectError} methods or collect the error directly
+   * from the environment using {@code env.dropAsyncError()}.
+   *
+   * To clear asyncErrors you can call {@link org.reactivestreams.tck.TestEnvironment#clearAsyncErrors()}
+   */
+  public void flop(Throwable thr) {
+    try {
+      fail(thr.getMessage(), thr);
+    } catch (Throwable t) {
+      asyncErrors.add(thr);
+    }
+  }
+
+  /**
+   * To flop means to "fail asynchronously", either by onErroring or by failing some TCK check triggered asynchronously.
+   *
+   * This method DOES fail the test right away (it tries to, by throwing an AssertionException),
+   * in such it is different from {@link org.reactivestreams.tck.TestEnvironment#flop} which only records the error.
+   *
+   * Use {@code env.verifyNoAsyncErrorsNoDelay()} at the end of your TCK tests to verify there no flops called during it's execution.
+   * To check investigate asyncErrors more closely you can use {@code expectError} methods or collect the error directly
+   * from the environment using {@code env.dropAsyncError()}.
+   *
+   * To clear asyncErrors you can call {@link org.reactivestreams.tck.TestEnvironment#clearAsyncErrors()}
+   */
+  public <T> T flopAndFail(String msg) {
+    try {
+      fail(msg);
+    } catch (Throwable t) {
+      asyncErrors.add(t);
+      fail(msg, t);
+    }
+    return null; // unreachable, the previous block will always exit by throwing
+  }
+
+
+
+  public <T> void subscribe(Publisher<T> pub, TestSubscriber<T> sub) throws InterruptedException {
+    subscribe(pub, sub, defaultTimeoutMillis);
+  }
+
+  public <T> void subscribe(Publisher<T> pub, TestSubscriber<T> sub, long timeoutMillis) throws InterruptedException {
+    pub.subscribe(sub);
+    sub.subscription.expectCompletion(timeoutMillis, String.format("Could not subscribe %s to Publisher %s", sub, pub));
+    verifyNoAsyncErrorsNoDelay();
+  }
+
+  public <T> ManualSubscriber<T> newBlackholeSubscriber(Publisher<T> pub) throws InterruptedException {
+    ManualSubscriberWithSubscriptionSupport<T> sub = new BlackholeSubscriberWithSubscriptionSupport<T>(this);
+    subscribe(pub, sub, defaultTimeoutMillis());
+    return sub;
+  }
+
+  public <T> ManualSubscriber<T> newManualSubscriber(Publisher<T> pub) throws InterruptedException {
+    return newManualSubscriber(pub, defaultTimeoutMillis());
+  }
+
+  public <T> ManualSubscriber<T> newManualSubscriber(Publisher<T> pub, long timeoutMillis) throws InterruptedException {
+    ManualSubscriberWithSubscriptionSupport<T> sub = new ManualSubscriberWithSubscriptionSupport<T>(this);
+    subscribe(pub, sub, timeoutMillis);
+    return sub;
+  }
+
+  public void clearAsyncErrors() {
+    asyncErrors.clear();
+  }
+
+  public Throwable dropAsyncError() {
+    try {
+      return asyncErrors.remove(0);
+    } catch (IndexOutOfBoundsException ex) {
+      return null;
+    }
+  }
+
+  /**
+   * Waits for {@link TestEnvironment#defaultNoSignalsTimeoutMillis()} and then verifies that no asynchronous errors
+   * were signalled pior to, or during that time (by calling {@code flop()}).
+   */
+  public void verifyNoAsyncErrors() {
+    verifyNoAsyncErrors(defaultNoSignalsTimeoutMillis());
+  }
+
+  /**
+   * This version of {@code verifyNoAsyncErrors} should be used when errors still could be signalled
+   * asynchronously during {@link TestEnvironment#defaultTimeoutMillis()} time.
+   * <p></p>
+   * It will immediatly check if any async errors were signaled (using {@link TestEnvironment#flop(String)},
+   * and if no errors encountered wait for another default timeout as the errors may yet be signalled.
+   * The initial check is performed in order to fail-fast in case of an already failed test.
+   */
+  public void verifyNoAsyncErrors(long delay) {
+    try {
+      verifyNoAsyncErrorsNoDelay();
+
+      Thread.sleep(delay);
+      verifyNoAsyncErrorsNoDelay();
+    } catch (InterruptedException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  /**
+   * Verifies that no asynchronous errors were signalled pior to calling this method (by calling {@code flop()}).
+   * This version of verifyNoAsyncError <b>does not wait before checking for asynchronous errors</b>, and is to be used
+   * for example in tight loops etc.
+   */
+  public void verifyNoAsyncErrorsNoDelay() {
+    for (Throwable e : asyncErrors) {
+      if (e instanceof AssertionError) {
+        throw (AssertionError) e;
+      } else {
+        fail(String.format("Async error during test execution: %s", e.getMessage()), e);
+      }
+    }
+  }
+
+  /** If {@code TestEnvironment#printlnDebug} is true, print debug message to std out. */
+  public void debug(String msg) {
+    if (debugEnabled()) {
+      System.out.printf("[TCK-DEBUG] %s%n", msg);
+    }
+  }
+
+  public final boolean debugEnabled() {
+    return printlnDebug;
+  }
+
+  /**
+   * Looks for given {@code method} method in stack trace.
+   * Can be used to answer questions like "was this method called from onComplete?".
+   *
+   * @return the caller's StackTraceElement at which he the looked for method was found in the call stack, EMPTY otherwise
+   */
+  public Optional<StackTraceElement> findCallerMethodInStackTrace(String method) {
+    final Throwable thr = new Throwable(); // gets the stacktrace
+
+    for (StackTraceElement stackElement : thr.getStackTrace()) {
+      if (stackElement.getMethodName().equals(method)) {
+        return Optional.of(stackElement);
+      }
+    }
+    return Optional.empty();
+  }
+
+  // ---- classes ----
+
+  /**
+   * {@link Subscriber} implementation which can be steered by test code and asserted on.
+   */
+  public static class ManualSubscriber<T> extends TestSubscriber<T> {
+    Receptacle<T> received;
+
+    public ManualSubscriber(TestEnvironment env) {
+      super(env);
+      received = new Receptacle<T>(this.env);
+    }
+
+    @Override
+    public void onNext(T element) {
+      try {
+        received.add(element);
+      } catch (IllegalStateException ex) {
+          // error message refinement
+          throw new SubscriberBufferOverflowException(
+            String.format("Received more than bufferSize (%d) onNext signals. " +
+                            "The Publisher probably emited more signals than expected!",
+                          received.QUEUE_SIZE), ex);
+      }
+    }
+
+    @Override
+    public void onComplete() {
+      received.complete();
+    }
+
+    public void request(long elements) {
+      subscription.value().request(elements);
+    }
+
+    public T requestNextElement() throws InterruptedException {
+      return requestNextElement(env.defaultTimeoutMillis());
+    }
+
+    public T requestNextElement(long timeoutMillis) throws InterruptedException {
+      return requestNextElement(timeoutMillis, "Did not receive expected element");
+    }
+
+    public T requestNextElement(String errorMsg) throws InterruptedException {
+      return requestNextElement(env.defaultTimeoutMillis(), errorMsg);
+    }
+
+    public T requestNextElement(long timeoutMillis, String errorMsg) throws InterruptedException {
+      request(1);
+      return nextElement(timeoutMillis, errorMsg);
+    }
+
+    public Optional<T> requestNextElementOrEndOfStream() throws InterruptedException {
+      return requestNextElementOrEndOfStream(env.defaultTimeoutMillis(), "Did not receive expected stream completion");
+    }
+
+    public Optional<T> requestNextElementOrEndOfStream(String errorMsg) throws InterruptedException {
+      return requestNextElementOrEndOfStream(env.defaultTimeoutMillis(), errorMsg);
+    }
+
+    public Optional<T> requestNextElementOrEndOfStream(long timeoutMillis) throws InterruptedException {
+      return requestNextElementOrEndOfStream(timeoutMillis, "Did not receive expected stream completion");
+    }
+
+    public Optional<T> requestNextElementOrEndOfStream(long timeoutMillis, String errorMsg) throws InterruptedException {
+      request(1);
+      return nextElementOrEndOfStream(timeoutMillis, errorMsg);
+    }
+
+    public void requestEndOfStream() throws InterruptedException {
+      requestEndOfStream(env.defaultTimeoutMillis(), "Did not receive expected stream completion");
+    }
+
+    public void requestEndOfStream(long timeoutMillis) throws InterruptedException {
+      requestEndOfStream(timeoutMillis, "Did not receive expected stream completion");
+    }
+
+    public void requestEndOfStream(String errorMsg) throws InterruptedException {
+      requestEndOfStream(env.defaultTimeoutMillis(), errorMsg);
+    }
+
+    public void requestEndOfStream(long timeoutMillis, String errorMsg) throws InterruptedException {
+      request(1);
+      expectCompletion(timeoutMillis, errorMsg);
+    }
+
+    public List<T> requestNextElements(long elements) throws InterruptedException {
+      request(elements);
+      return nextElements(elements, env.defaultTimeoutMillis());
+    }
+
+    public List<T> requestNextElements(long elements, long timeoutMillis) throws InterruptedException {
+      request(elements);
+      return nextElements(elements, timeoutMillis, String.format("Did not receive %d expected elements", elements));
+    }
+
+    public List<T> requestNextElements(long elements, long timeoutMillis, String errorMsg) throws InterruptedException {
+      request(elements);
+      return nextElements(elements, timeoutMillis, errorMsg);
+    }
+
+    public T nextElement() throws InterruptedException {
+      return nextElement(env.defaultTimeoutMillis());
+    }
+
+    public T nextElement(long timeoutMillis) throws InterruptedException {
+      return nextElement(timeoutMillis, "Did not receive expected element");
+    }
+
+    public T nextElement(String errorMsg) throws InterruptedException {
+      return nextElement(env.defaultTimeoutMillis(), errorMsg);
+    }
+
+    public T nextElement(long timeoutMillis, String errorMsg) throws InterruptedException {
+      return received.next(timeoutMillis, errorMsg);
+    }
+
+    public Optional<T> nextElementOrEndOfStream() throws InterruptedException {
+      return nextElementOrEndOfStream(env.defaultTimeoutMillis(), "Did not receive expected stream completion");
+    }
+
+    public Optional<T> nextElementOrEndOfStream(long timeoutMillis) throws InterruptedException {
+      return nextElementOrEndOfStream(timeoutMillis, "Did not receive expected stream completion");
+    }
+
+    public Optional<T> nextElementOrEndOfStream(long timeoutMillis, String errorMsg) throws InterruptedException {
+      return received.nextOrEndOfStream(timeoutMillis, errorMsg);
+    }
+
+    public List<T> nextElements(long elements) throws InterruptedException {
+      return nextElements(elements, env.defaultTimeoutMillis(), "Did not receive expected element or completion");
+    }
+
+    public List<T> nextElements(long elements, String errorMsg) throws InterruptedException {
+      return nextElements(elements, env.defaultTimeoutMillis(), errorMsg);
+    }
+
+    public List<T> nextElements(long elements, long timeoutMillis) throws InterruptedException {
+      return nextElements(elements, timeoutMillis, "Did not receive expected element or completion");
+    }
+
+    public List<T> nextElements(long elements, long timeoutMillis, String errorMsg) throws InterruptedException {
+      return received.nextN(elements, timeoutMillis, errorMsg);
+    }
+
+    public void expectNext(T expected) throws InterruptedException {
+      expectNext(expected, env.defaultTimeoutMillis());
+    }
+
+    public void expectNext(T expected, long timeoutMillis) throws InterruptedException {
+      T received = nextElement(timeoutMillis, "Did not receive expected element on downstream");
+      if (!received.equals(expected)) {
+        env.flop(String.format("Expected element %s on downstream but received %s", expected, received));
+      }
+    }
+
+    public void expectCompletion() throws InterruptedException {
+      expectCompletion(env.defaultTimeoutMillis(), "Did not receive expected stream completion");
+    }
+
+    public void expectCompletion(long timeoutMillis) throws InterruptedException {
+      expectCompletion(timeoutMillis, "Did not receive expected stream completion");
+    }
+
+    public void expectCompletion(String errorMsg) throws InterruptedException {
+      expectCompletion(env.defaultTimeoutMillis(), errorMsg);
+    }
+
+    public void expectCompletion(long timeoutMillis, String errorMsg) throws InterruptedException {
+      received.expectCompletion(timeoutMillis, errorMsg);
+    }
+
+    public <E extends Throwable> void expectErrorWithMessage(Class<E> expected, String requiredMessagePart) throws Exception {
+      expectErrorWithMessage(expected, Collections.singletonList(requiredMessagePart), env.defaultTimeoutMillis(), env.defaultPollTimeoutMillis());
+    }
+    public <E extends Throwable> void expectErrorWithMessage(Class<E> expected, List<String> requiredMessagePartAlternatives) throws Exception {
+      expectErrorWithMessage(expected, requiredMessagePartAlternatives, env.defaultTimeoutMillis(), env.defaultPollTimeoutMillis());
+    }
+
+    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+    public <E extends Throwable> void expectErrorWithMessage(Class<E> expected, String requiredMessagePart, long timeoutMillis) throws Exception {
+      expectErrorWithMessage(expected, Collections.singletonList(requiredMessagePart), timeoutMillis);
+    }
+
+    public <E extends Throwable> void expectErrorWithMessage(Class<E> expected, List<String> requiredMessagePartAlternatives, long timeoutMillis) throws Exception {
+      expectErrorWithMessage(expected, requiredMessagePartAlternatives, timeoutMillis, timeoutMillis);
+    }
+
+    public <E extends Throwable> void expectErrorWithMessage(Class<E> expected, List<String> requiredMessagePartAlternatives,
+                                                             long totalTimeoutMillis, long pollTimeoutMillis) throws Exception {
+      final E err = expectError(expected, totalTimeoutMillis, pollTimeoutMillis);
+      final String message = err.getMessage();
+
+      boolean contains = false;
+      for (String requiredMessagePart : requiredMessagePartAlternatives)
+        if (message.contains(requiredMessagePart)) contains = true; // not short-circuting loop, it is expected to
+      assertTrue(contains,
+              String.format("Got expected exception [%s] but missing message part [%s], was: %s",
+                      err.getClass(), "anyOf: " + requiredMessagePartAlternatives, err.getMessage()));
+    }
+
+    public <E extends Throwable> E expectError(Class<E> expected) throws Exception {
+      return expectError(expected, env.defaultTimeoutMillis());
+    }
+
+    public <E extends Throwable> E expectError(Class<E> expected, long timeoutMillis) throws Exception {
+      return expectError(expected, timeoutMillis, env.defaultPollTimeoutMillis());
+    }
+
+    public <E extends Throwable> E expectError(Class<E> expected, String errorMsg) throws Exception {
+      return expectError(expected, env.defaultTimeoutMillis(), errorMsg);
+    }
+
+    public <E extends Throwable> E expectError(Class<E> expected, long timeoutMillis, String errorMsg) throws Exception {
+      return expectError(expected, timeoutMillis, env.defaultPollTimeoutMillis(), errorMsg);
+    }
+
+    public <E extends Throwable> E expectError(Class<E> expected, long totalTimeoutMillis, long pollTimeoutMillis) throws Exception {
+      return expectError(expected, totalTimeoutMillis, pollTimeoutMillis, String.format("Expected onError(%s)", expected.getName()));
+    }
+
+    public <E extends Throwable> E expectError(Class<E> expected, long totalTimeoutMillis, long pollTimeoutMillis,
+                                               String errorMsg) throws Exception {
+      return received.expectError(expected, totalTimeoutMillis, pollTimeoutMillis, errorMsg);
+    }
+
+    public void expectNone() throws InterruptedException {
+      expectNone(env.defaultNoSignalsTimeoutMillis());
+    }
+
+    public void expectNone(String errMsgPrefix) throws InterruptedException {
+      expectNone(env.defaultNoSignalsTimeoutMillis(), errMsgPrefix);
+    }
+
+    public void expectNone(long withinMillis) throws InterruptedException {
+      expectNone(withinMillis, "Did not expect an element but got element");
+    }
+
+    public void expectNone(long withinMillis, String errMsgPrefix) throws InterruptedException {
+      received.expectNone(withinMillis, errMsgPrefix);
+    }
+
+  }
+
+  public static class ManualSubscriberWithSubscriptionSupport<T> extends ManualSubscriber<T> {
+
+    public ManualSubscriberWithSubscriptionSupport(TestEnvironment env) {
+      super(env);
+    }
+
+    @Override
+    public void onNext(T element) {
+      if (env.debugEnabled()) {
+        env.debug(String.format("%s::onNext(%s)", this, element));
+      }
+      if (subscription.isCompleted()) {
+        super.onNext(element);
+      } else {
+        env.flop(String.format("Subscriber::onNext(%s) called before Subscriber::onSubscribe", element));
+      }
+    }
+
+    @Override
+    public void onComplete() {
+      if (env.debugEnabled()) {
+        env.debug(this + "::onComplete()");
+      }
+      if (subscription.isCompleted()) {
+        super.onComplete();
+      } else {
+        env.flop("Subscriber::onComplete() called before Subscriber::onSubscribe");
+      }
+    }
+
+    @Override
+    public void onSubscribe(Subscription s) {
+      if (env.debugEnabled()) {
+        env.debug(String.format("%s::onSubscribe(%s)", this, s));
+      }
+      if (!subscription.isCompleted()) {
+        subscription.complete(s);
+      } else {
+        env.flop("Subscriber::onSubscribe called on an already-subscribed Subscriber");
+      }
+    }
+
+    @Override
+    public void onError(Throwable cause) {
+      if (env.debugEnabled()) {
+        env.debug(String.format("%s::onError(%s)", this, cause));
+      }
+      if (subscription.isCompleted()) {
+        super.onError(cause);
+      } else {
+        env.flop(cause, String.format("Subscriber::onError(%s) called before Subscriber::onSubscribe", cause));
+      }
+    }
+  }
+
+  /**
+   * Similar to {@link org.reactivestreams.tck.TestEnvironment.ManualSubscriberWithSubscriptionSupport}
+   * but does not accumulate values signalled via <code>onNext</code>, thus it can not be used to assert
+   * values signalled to this subscriber. Instead it may be used to quickly drain a given publisher.
+   */
+  public static class BlackholeSubscriberWithSubscriptionSupport<T>
+    extends ManualSubscriberWithSubscriptionSupport<T> {
+
+    public BlackholeSubscriberWithSubscriptionSupport(TestEnvironment env) {
+      super(env);
+    }
+
+    @Override
+    public void onNext(T element) {
+      if (env.debugEnabled()) {
+        env.debug(String.format("%s::onNext(%s)", this, element));
+      }
+      if (!subscription.isCompleted()) {
+        env.flop(String.format("Subscriber::onNext(%s) called before Subscriber::onSubscribe", element));
+      }
+    }
+
+    @Override
+    public T nextElement(long timeoutMillis, String errorMsg) throws InterruptedException {
+      throw new RuntimeException("Can not expect elements from BlackholeSubscriber, use ManualSubscriber instead!");
+    }
+
+    @Override
+    public List<T> nextElements(long elements, long timeoutMillis, String errorMsg) throws InterruptedException {
+      throw new RuntimeException("Can not expect elements from BlackholeSubscriber, use ManualSubscriber instead!");
+    }
+  }
+
+  public static class TestSubscriber<T> implements Subscriber<T> {
+    final Promise<Subscription> subscription;
+
+    protected final TestEnvironment env;
+
+    public TestSubscriber(TestEnvironment env) {
+      this.env = env;
+      subscription = new Promise<Subscription>(env);
+    }
+
+    @Override
+    public void onError(Throwable cause) {
+      env.flop(cause, String.format("Unexpected Subscriber::onError(%s)", cause));
+    }
+
+    @Override
+    public void onComplete() {
+      env.flop("Unexpected Subscriber::onComplete()");
+    }
+
+    @Override
+    public void onNext(T element) {
+      env.flop(String.format("Unexpected Subscriber::onNext(%s)", element));
+    }
+
+    @Override
+    public void onSubscribe(Subscription subscription) {
+      env.flop(String.format("Unexpected Subscriber::onSubscribe(%s)", subscription));
+    }
+
+    public void cancel() {
+      if (subscription.isCompleted()) {
+        subscription.value().cancel();
+      } else {
+        env.flop("Cannot cancel a subscription before having received it");
+      }
+    }
+  }
+
+  public static class ManualPublisher<T> implements Publisher<T> {
+    protected final TestEnvironment env;
+
+    protected long pendingDemand = 0L;
+    protected Promise<Subscriber<? super T>> subscriber;
+
+    protected final Receptacle<Long> requests;
+
+    protected final Latch cancelled;
+
+    public ManualPublisher(TestEnvironment env) {
+      this.env = env;
+      requests = new Receptacle<Long>(env);
+      cancelled = new Latch(env);
+      subscriber = new Promise<Subscriber<? super T>>(this.env);
+    }
+
+    @Override
+    public void subscribe(Subscriber<? super T> s) {
+      if (!subscriber.isCompleted()) {
+        subscriber.completeImmediatly(s);
+
+        Subscription subs = new Subscription() {
+          @Override
+          public void request(long elements) {
+            requests.add(elements);
+          }
+
+          @Override
+          public void cancel() {
+            cancelled.close();
+          }
+        };
+        s.onSubscribe(subs);
+
+      } else {
+        env.flop("TestPublisher doesn't support more than one Subscriber");
+      }
+    }
+
+    public void sendNext(T element) {
+      if (subscriber.isCompleted()) {
+        subscriber.value().onNext(element);
+      } else {
+        env.flop("Cannot sendNext before having a Subscriber");
+      }
+    }
+
+    public void sendCompletion() {
+      if (subscriber.isCompleted()) {
+        subscriber.value().onComplete();
+      } else {
+        env.flop("Cannot sendCompletion before having a Subscriber");
+      }
+    }
+
+    public void sendError(Throwable cause) {
+      if (subscriber.isCompleted()) {
+        subscriber.value().onError(cause);
+      } else {
+        env.flop("Cannot sendError before having a Subscriber");
+      }
+    }
+
+    public long expectRequest() throws InterruptedException {
+      return expectRequest(env.defaultTimeoutMillis());
+    }
+
+    public long expectRequest(long timeoutMillis) throws InterruptedException {
+      long requested = requests.next(timeoutMillis, "Did not receive expected `request` call");
+      if (requested <= 0) {
+        return env.<Long>flopAndFail(String.format("Requests cannot be zero or negative but received request(%s)", requested));
+      } else {
+        pendingDemand += requested;
+        return requested;
+      }
+    }
+
+
+    public long expectRequest(long timeoutMillis, String errorMessageAddendum) throws InterruptedException {
+      long requested = requests.next(timeoutMillis, String.format("Did not receive expected `request` call. %s", errorMessageAddendum));
+      if (requested <= 0) {
+        return env.<Long>flopAndFail(String.format("Requests cannot be zero or negative but received request(%s)", requested));
+      } else {
+        pendingDemand += requested;
+        return requested;
+      }
+    }
+
+    public void expectExactRequest(long expected) throws InterruptedException {
+      expectExactRequest(expected, env.defaultTimeoutMillis());
+    }
+
+    public void expectExactRequest(long expected, long timeoutMillis) throws InterruptedException {
+      long requested = expectRequest(timeoutMillis);
+      if (requested != expected) {
+        env.flop(String.format("Received `request(%d)` on upstream but expected `request(%d)`", requested, expected));
+      }
+      pendingDemand += requested;
+    }
+
+    public void expectNoRequest() throws InterruptedException {
+      expectNoRequest(env.defaultTimeoutMillis());
+    }
+
+    public void expectNoRequest(long timeoutMillis) throws InterruptedException {
+      requests.expectNone(timeoutMillis, "Received an unexpected call to: request: ");
+    }
+
+    public void expectCancelling() throws InterruptedException {
+      expectCancelling(env.defaultTimeoutMillis());
+    }
+
+    public void expectCancelling(long timeoutMillis) throws InterruptedException {
+      cancelled.expectClose(timeoutMillis, "Did not receive expected cancelling of upstream subscription");
+    }
+
+    public boolean isCancelled() throws InterruptedException {
+      return cancelled.isClosed();
+    }
+  }
+
+  /**
+   * Like a CountDownLatch, but resettable and with some convenience methods
+   */
+  public static class Latch {
+    private final TestEnvironment env;
+    volatile private CountDownLatch countDownLatch = new CountDownLatch(1);
+
+    public Latch(TestEnvironment env) {
+      this.env = env;
+    }
+
+    public void reOpen() {
+      countDownLatch = new CountDownLatch(1);
+    }
+
+    public boolean isClosed() {
+      return countDownLatch.getCount() == 0;
+    }
+
+    public void close() {
+      countDownLatch.countDown();
+    }
+
+    public void assertClosed(String openErrorMsg) {
+      if (!isClosed()) {
+        env.flop(new ExpectedClosedLatchException(openErrorMsg));
+      }
+    }
+
+    public void assertOpen(String closedErrorMsg) {
+      if (isClosed()) {
+        env.flop(new ExpectedOpenLatchException(closedErrorMsg));
+      }
+    }
+
+    public void expectClose(String notClosedErrorMsg) throws InterruptedException {
+      expectClose(env.defaultTimeoutMillis(), notClosedErrorMsg);
+    }
+
+    public void expectClose(long timeoutMillis, String notClosedErrorMsg) throws InterruptedException {
+      countDownLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);
+      if (countDownLatch.getCount() > 0) {
+        env.flop(String.format("%s within %d ms", notClosedErrorMsg, timeoutMillis));
+      }
+    }
+
+    static final class ExpectedOpenLatchException extends RuntimeException {
+      public ExpectedOpenLatchException(String message) {
+        super(message);
+      }
+    }
+
+    static final class ExpectedClosedLatchException extends RuntimeException {
+      public ExpectedClosedLatchException(String message) {
+        super(message);
+      }
+    }
+
+  }
+
+  // simple promise for *one* value, which cannot be reset
+  public static class Promise<T> {
+    private final TestEnvironment env;
+
+    public static <T> Promise<T> completed(TestEnvironment env, T value) {
+      Promise<T> promise = new Promise<T>(env);
+      promise.completeImmediatly(value);
+      return promise;
+    }
+
+    public Promise(TestEnvironment env) {
+      this.env = env;
+    }
+
+    private ArrayBlockingQueue<T> abq = new ArrayBlockingQueue<T>(1);
+    private AtomicReference<T> _value = new AtomicReference<T>();
+
+    public T value() {
+      final T value = _value.get();
+      if (value != null) {
+        return value;
+      } else {
+        env.flop("Cannot access promise value before completion");
+        return null;
+      }
+    }
+
+    public boolean isCompleted() {
+      return _value.get() != null;
+    }
+
+    /**
+     * Allows using expectCompletion to await for completion of the value and complete it _then_
+     */
+    public void complete(T value) {
+      if (_value.compareAndSet(null, value)) {
+        // we add the value to the queue such to wake up any expectCompletion which was triggered before complete() was called
+        abq.add(value);
+      } else {
+        env.flop(String.format("Cannot complete a promise more than once! Present value: %s, attempted to set: %s", _value.get(), value));
+      }
+    }
+
+    /**
+     * Same as complete.
+     *
+     * Keeping this method for binary compatibility.
+     */
+    public void completeImmediatly(T value) {
+      complete(value);
+    }
+
+    public void expectCompletion(long timeoutMillis, String errorMsg) throws InterruptedException {
+      if (!isCompleted()) {
+        T val = abq.poll(timeoutMillis, TimeUnit.MILLISECONDS);
+
+        if (val == null) {
+          env.flop(String.format("%s within %d ms", errorMsg, timeoutMillis));
+        }
+      }
+    }
+  }
+
+  // a "Promise" for multiple values, which also supports "end-of-stream reached"
+  public static class Receptacle<T> {
+    final int QUEUE_SIZE = 2 * TEST_BUFFER_SIZE;
+    private final TestEnvironment env;
+
+    private final ArrayBlockingQueue<Optional<T>> abq = new ArrayBlockingQueue<Optional<T>>(QUEUE_SIZE);
+
+    private final Latch completedLatch;
+
+    Receptacle(TestEnvironment env) {
+      this.env = env;
+      this.completedLatch = new Latch(env);
+    }
+
+    public void add(T value) {
+      completedLatch.assertOpen(String.format("Unexpected element %s received after stream completed", value));
+
+      abq.add(Optional.of(value));
+    }
+
+    public void complete() {
+      completedLatch.assertOpen("Unexpected additional complete signal received!");
+      completedLatch.close();
+
+      abq.add(Optional.<T>empty());
+    }
+
+    public T next(long timeoutMillis, String errorMsg) throws InterruptedException {
+      Optional<T> value = abq.poll(timeoutMillis, TimeUnit.MILLISECONDS);
+
+      if (value == null) {
+        return env.flopAndFail(String.format("%s within %d ms", errorMsg, timeoutMillis));
+      } else if (value.isDefined()) {
+        return value.get();
+      } else {
+        return env.flopAndFail("Expected element but got end-of-stream");
+      }
+    }
+
+    public Optional<T> nextOrEndOfStream(long timeoutMillis, String errorMsg) throws InterruptedException {
+      Optional<T> value = abq.poll(timeoutMillis, TimeUnit.MILLISECONDS);
+
+      if (value == null) {
+        env.flop(String.format("%s within %d ms", errorMsg, timeoutMillis));
+        return Optional.empty();
+      }
+
+      return value;
+    }
+
+    /**
+     * @param timeoutMillis total timeout time for awaiting all {@code elements} number of elements
+     */
+    public List<T> nextN(long elements, long timeoutMillis, String errorMsg) throws InterruptedException {
+      List<T> result = new LinkedList<T>();
+      long remaining = elements;
+      long deadline = System.currentTimeMillis() + timeoutMillis;
+      while (remaining > 0) {
+        long remainingMillis = deadline - System.currentTimeMillis();
+
+        result.add(next(remainingMillis, errorMsg));
+        remaining--;
+      }
+
+      return result;
+    }
+
+
+    public void expectCompletion(long timeoutMillis, String errorMsg) throws InterruptedException {
+      Optional<T> value = abq.poll(timeoutMillis, TimeUnit.MILLISECONDS);
+
+      if (value == null) {
+        env.flop(String.format("%s within %d ms", errorMsg, timeoutMillis));
+      } else if (value.isDefined()) {
+        env.flop(String.format("Expected end-of-stream but got element [%s]", value.get()));
+      } // else, ok
+    }
+
+    /**
+     * @deprecated Deprecated in favor of {@link #expectError(Class, long, long, String)}.
+     */
+    @Deprecated
+    public <E extends Throwable> E expectError(Class<E> clazz, long timeoutMillis, String errorMsg) throws Exception {
+      return expectError(clazz, timeoutMillis, timeoutMillis, errorMsg);
+    }
+
+    @SuppressWarnings("unchecked")
+    final <E extends Throwable> E expectError(Class<E> clazz, final long totalTimeoutMillis,
+                                              long pollTimeoutMillis,
+                                              String errorMsg) throws Exception {
+      long totalTimeoutRemainingNs = MILLISECONDS.toNanos(totalTimeoutMillis);
+      long timeStampANs = System.nanoTime();
+      long timeStampBNs;
+
+      for (;;) {
+        Thread.sleep(Math.min(pollTimeoutMillis, NANOSECONDS.toMillis(totalTimeoutRemainingNs)));
+
+        if (env.asyncErrors.isEmpty()) {
+          timeStampBNs = System.nanoTime();
+          totalTimeoutRemainingNs =- timeStampBNs - timeStampANs;
+          timeStampANs = timeStampBNs;
+
+          if (totalTimeoutRemainingNs <= 0) {
+            return env.flopAndFail(String.format("%s within %d ms", errorMsg, totalTimeoutMillis));
+          }
+        } else {
+          // ok, there was an expected error
+          Throwable thrown = env.asyncErrors.remove(0);
+
+          if (clazz.isInstance(thrown)) {
+            return (E) thrown;
+          } else {
+
+            return env.flopAndFail(String.format("%s within %d ms; Got %s but expected %s",
+                    errorMsg, totalTimeoutMillis, thrown.getClass().getCanonicalName(), clazz.getCanonicalName()));
+          }
+        }
+      }
+    }
+
+    public void expectNone(long withinMillis, String errorMsgPrefix) throws InterruptedException {
+      Thread.sleep(withinMillis);
+      Optional<T> value = abq.poll();
+
+      if (value == null) {
+        // ok
+      } else if (value.isDefined()) {
+        env.flop(String.format("%s [%s]", errorMsgPrefix, value.get()));
+      } else {
+        env.flop("Expected no element but got end-of-stream");
+      }
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/WithHelperPublisher.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.tck;
+
+import org.reactivestreams.Publisher;
+import org.reactivestreams.tck.flow.support.Function;
+import org.reactivestreams.tck.flow.support.HelperPublisher;
+import org.reactivestreams.tck.flow.support.InfiniteHelperPublisher;
+
+import java.util.concurrent.ExecutorService;
+
+/**
+ * Type which is able to create elements based on a seed {@code id} value.
+ * <p>
+ * Simplest implementations will simply return the incoming id as the element.
+ *
+ * @param <T> type of element to be delivered to the Subscriber
+ */
+public abstract class WithHelperPublisher<T> {
+
+  /** ExecutorService to be used by the provided helper {@link org.reactivestreams.Publisher} */
+  public abstract ExecutorService publisherExecutorService();
+
+  /**
+   * Implement this method to match your expected element type.
+   * In case of implementing a simple Subscriber which is able to consume any kind of element simply return the
+   * incoming {@code element} element.
+   * <p>
+   * Sometimes the Subscriber may be limited in what type of element it is able to consume, this you may have to implement
+   * this method such that the emitted element matches the Subscribers requirements. Simplest implementations would be
+   * to simply pass in the {@code element} as payload of your custom element, such as appending it to a String or other identifier.
+   * <p>
+   * <b>Warning:</b> This method may be called concurrently by the helper publisher, thus it should be implemented in a
+   * thread-safe manner.
+   *
+   * @return element of the matching type {@code T} that will be delivered to the tested Subscriber
+   */
+  public abstract T createElement(int element);
+
+  /**
+   * Helper method required for creating the Publisher to which the tested Subscriber will be subscribed and tested against.
+   * <p>
+   * By default an <b>asynchronously signalling Publisher</b> is provided, which will use {@link #createElement(int)}
+   * to generate elements type your Subscriber is able to consume.
+   * <p>
+   * Sometimes you may want to implement your own custom custom helper Publisher - to validate behaviour of a Subscriber
+   * when facing a synchronous Publisher for example. If you do, it MUST emit the exact number of elements asked for
+   * (via the {@code elements} parameter) and MUST also must treat the following numbers of elements in these specific ways:
+   * <ul>
+   *   <li>
+   *     If {@code elements} is {@code Long.MAX_VALUE} the produced stream must be infinite.
+   *   </li>
+   *   <li>
+   *     If {@code elements} is {@code 0} the {@code Publisher} should signal {@code onComplete} immediatly.
+   *     In other words, it should represent a "completed stream".
+   *   </li>
+   * </ul>
+   */
+  @SuppressWarnings("unchecked")
+  public Publisher<T> createHelperPublisher(long elements) {
+    final Function<Integer, T> mkElement = new Function<Integer, T>() {
+      @Override public T apply(Integer id) throws Throwable {
+        return createElement(id);
+      }
+    };
+
+    if (elements > Integer.MAX_VALUE) return new InfiniteHelperPublisher(mkElement, publisherExecutorService());
+    else return new HelperPublisher(0, (int) elements, mkElement, publisherExecutorService());
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/FlowPublisherVerification.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.tck.flow;
+
+import org.reactivestreams.Publisher;
+import org.reactivestreams.FlowAdapters;
+import org.reactivestreams.tck.PublisherVerification;
+import org.reactivestreams.tck.TestEnvironment;
+
+import java.util.concurrent.Flow;
+
+/**
+ * Provides tests for verifying a Java 9+ {@link java.util.concurrent.Flow.Publisher} specification rules.
+ *
+ * @see java.util.concurrent.Flow.Publisher
+ */
+public abstract class FlowPublisherVerification<T> extends PublisherVerification<T> {
+
+  public FlowPublisherVerification(TestEnvironment env, long publisherReferenceGCTimeoutMillis) {
+    super(env, publisherReferenceGCTimeoutMillis);
+  }
+
+  public FlowPublisherVerification(TestEnvironment env) {
+    super(env);
+  }
+
+  @Override
+  final public Publisher<T> createPublisher(long elements) {
+    final Flow.Publisher<T> flowPublisher = createFlowPublisher(elements);
+    return FlowAdapters.toPublisher(flowPublisher);
+  }
+  /**
+   * This is the main method you must implement in your test incarnation.
+   * It must create a Publisher for a stream with exactly the given number of elements.
+   * If `elements` is `Long.MAX_VALUE` the produced stream must be infinite.
+   */
+  public abstract Flow.Publisher<T> createFlowPublisher(long elements);
+
+  @Override
+  final public Publisher<T> createFailedPublisher() {
+    final Flow.Publisher<T> failed = createFailedFlowPublisher();
+    if (failed == null) return null; // because `null` means "SKIP" in createFailedPublisher
+    else return FlowAdapters.toPublisher(failed);
+  }
+  /**
+   * By implementing this method, additional TCK tests concerning a "failed" publishers will be run.
+   *
+   * The expected behaviour of the {@link Flow.Publisher} returned by this method is hand out a subscription,
+   * followed by signalling {@code onError} on it, as specified by Rule 1.9.
+   *
+   * If you ignore these additional tests, return {@code null} from this method.
+   */
+  public abstract Flow.Publisher<T> createFailedFlowPublisher();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/FlowSubscriberBlackboxVerification.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.tck.flow;
+
+import org.reactivestreams.FlowAdapters;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+import org.reactivestreams.tck.SubscriberBlackboxVerification;
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.support.SubscriberBlackboxVerificationRules;
+
+import java.util.concurrent.Flow;
+
+/**
+ * Provides tests for verifying {@link java.util.concurrent.Flow.Subscriber} and {@link java.util.concurrent.Flow.Subscription}
+ * specification rules, without any modifications to the tested implementation (also known as "Black Box" testing).
+ *
+ * This verification is NOT able to check many of the rules of the spec, and if you want more
+ * verification of your implementation you'll have to implement {@code org.reactivestreams.tck.SubscriberWhiteboxVerification}
+ * instead.
+ *
+ * @see java.util.concurrent.Flow.Subscriber
+ * @see java.util.concurrent.Flow.Subscription
+ */
+public abstract class FlowSubscriberBlackboxVerification<T> extends SubscriberBlackboxVerification<T>
+  implements SubscriberBlackboxVerificationRules {
+
+  protected FlowSubscriberBlackboxVerification(TestEnvironment env) {
+    super(env);
+  }
+
+  @Override
+  public final void triggerRequest(Subscriber<? super T> subscriber) {
+    triggerFlowRequest(FlowAdapters.toFlowSubscriber(subscriber));
+  }
+  /**
+   * Override this method if the {@link java.util.concurrent.Flow.Subscriber} implementation you are verifying
+   * needs an external signal before it signals demand to its Publisher.
+   *
+   * By default this method does nothing.
+   */
+  public void triggerFlowRequest(Flow.Subscriber<? super T> subscriber) {
+    // this method is intentionally left blank
+  }
+
+  @Override
+  public final Subscriber<T> createSubscriber() {
+    return FlowAdapters.<T>toSubscriber(createFlowSubscriber());
+  }
+  /**
+   * This is the main method you must implement in your test incarnation.
+   * It must create a new {@link Flow.Subscriber} instance to be subjected to the testing logic.
+   */
+  abstract public Flow.Subscriber<T> createFlowSubscriber();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/FlowSubscriberWhiteboxVerification.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.tck.flow;
+
+import org.reactivestreams.FlowAdapters;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.tck.SubscriberWhiteboxVerification;
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.support.SubscriberWhiteboxVerificationRules;
+
+import java.util.concurrent.Flow;
+
+/**
+ * Provides whitebox style tests for verifying {@link java.util.concurrent.Flow.Subscriber}
+ * and {@link java.util.concurrent.Flow.Subscription} specification rules.
+ *
+ * @see java.util.concurrent.Flow.Subscriber
+ * @see java.util.concurrent.Flow.Subscription
+ */
+public abstract class FlowSubscriberWhiteboxVerification<T> extends SubscriberWhiteboxVerification<T>
+  implements SubscriberWhiteboxVerificationRules {
+
+  protected FlowSubscriberWhiteboxVerification(TestEnvironment env) {
+    super(env);
+  }
+
+  @Override
+  final public Subscriber<T> createSubscriber(WhiteboxSubscriberProbe<T> probe) {
+    return FlowAdapters.toSubscriber(createFlowSubscriber(probe));
+  }
+  /**
+   * This is the main method you must implement in your test incarnation.
+   * It must create a new {@link org.reactivestreams.Subscriber} instance to be subjected to the testing logic.
+   *
+   * In order to be meaningfully testable your Subscriber must inform the given
+   * `WhiteboxSubscriberProbe` of the respective events having been received.
+   */
+  protected abstract Flow.Subscriber<T> createFlowSubscriber(WhiteboxSubscriberProbe<T> probe);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/IdentityFlowProcessorVerification.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.tck.flow;
+
+import org.reactivestreams.*;
+import org.reactivestreams.tck.IdentityProcessorVerification;
+import org.reactivestreams.tck.TestEnvironment;
+import org.reactivestreams.tck.flow.support.SubscriberWhiteboxVerificationRules;
+import org.reactivestreams.tck.flow.support.PublisherVerificationRules;
+
+import java.util.concurrent.Flow;
+
+public abstract class IdentityFlowProcessorVerification<T> extends IdentityProcessorVerification<T>
+  implements SubscriberWhiteboxVerificationRules, PublisherVerificationRules {
+
+  public IdentityFlowProcessorVerification(TestEnvironment env) {
+    super(env);
+  }
+
+  public IdentityFlowProcessorVerification(TestEnvironment env, long publisherReferenceGCTimeoutMillis) {
+    super(env, publisherReferenceGCTimeoutMillis);
+  }
+
+  public IdentityFlowProcessorVerification(TestEnvironment env, long publisherReferenceGCTimeoutMillis, int processorBufferSize) {
+    super(env, publisherReferenceGCTimeoutMillis, processorBufferSize);
+  }
+
+  /**
+   * By implementing this method, additional TCK tests concerning a "failed" Flow publishers will be run.
+   *
+   * The expected behaviour of the {@link Flow.Publisher} returned by this method is hand out a subscription,
+   * followed by signalling {@code onError} on it, as specified by Rule 1.9.
+   *
+   * If you want to ignore these additional tests, return {@code null} from this method.
+   */
+  protected abstract Flow.Publisher<T> createFailedFlowPublisher();
+
+  /**
+   * This is the main method you must implement in your test incarnation.
+   * It must create a {@link Flow.Processor}, which simply forwards all stream elements from its upstream
+   * to its downstream. It must be able to internally buffer the given number of elements.
+   *
+   * @param bufferSize number of elements the processor is required to be able to buffer.
+   */
+  protected abstract Flow.Processor<T,T> createIdentityFlowProcessor(int bufferSize);
+
+  @Override
+  public final Processor<T, T> createIdentityProcessor(int bufferSize) {
+    return FlowAdapters.toProcessor(createIdentityFlowProcessor(bufferSize));
+  }
+
+  @Override
+  public final Publisher<T> createFailedPublisher() {
+    Flow.Publisher<T> failed = createFailedFlowPublisher();
+    if (failed == null) return null; // because `null` means "SKIP" in createFailedPublisher
+    else return FlowAdapters.toPublisher(failed);
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/Function.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.tck.flow.support;
+
+public interface Function<In, Out> {
+  public Out apply(In in) throws Throwable;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/HelperPublisher.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.tck.flow.support;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.concurrent.Executor;
+
+import org.reactivestreams.example.unicast.AsyncIterablePublisher;
+
+public class HelperPublisher<T> extends AsyncIterablePublisher<T> {
+
+    public HelperPublisher(final int from, final int to, final Function<Integer, T> create, final Executor executor) {
+        super(new Iterable<T>() {
+          { if(from > to) throw new IllegalArgumentException("from must be equal or greater than to!"); }
+          @Override public Iterator<T> iterator() {
+            return new Iterator<T>() {
+              private int at = from;
+              @Override public boolean hasNext() { return at < to; }
+              @Override public T next() {
+                if (!hasNext()) return Collections.<T>emptyList().iterator().next();
+                else try {
+                  return create.apply(at++);
+                } catch (Throwable t) {
+                  throw new IllegalStateException(String.format("Failed to create element for id %d!", at - 1), t);
+                }
+              }
+              @Override public void remove() { throw new UnsupportedOperationException(); }
+            };
+          }
+        }, executor);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/InfiniteHelperPublisher.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.tck.flow.support;
+
+import org.reactivestreams.example.unicast.AsyncIterablePublisher;
+
+import java.util.Iterator;
+import java.util.concurrent.Executor;
+
+public class InfiniteHelperPublisher<T> extends AsyncIterablePublisher<T> {
+
+    public InfiniteHelperPublisher(final Function<Integer, T> create, final Executor executor) {
+        super(new Iterable<T>() {
+          @Override public Iterator<T> iterator() {
+            return new Iterator<T>() {
+              private int at = 0;
+
+              @Override public boolean hasNext() { return true; }
+              @Override public T next() {
+                try {
+                  return create.apply(at++); // Wraps around on overflow
+                } catch (Throwable t) {
+                  throw new IllegalStateException(
+                    String.format("Failed to create element in %s for id %s!", getClass().getSimpleName(), at - 1), t);
+                }
+              }
+              @Override public void remove() { throw new UnsupportedOperationException(); }
+            };
+          }
+        }, executor);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/NonFatal.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.tck.flow.support;
+
+
+/**
+ * Copy of scala.control.util.NonFatal in order to not depend on scala-library
+ */
+public class NonFatal {
+  private NonFatal() {
+    // no instances, please.
+  }
+
+  /**
+   * Returns true if the provided `Throwable` is to be considered non-fatal, or false if it is to be considered fatal
+   *
+   * @param t throwable to be matched for fatal-ness
+   * @return true if is a non-fatal throwable, false otherwise
+   */
+  public static boolean isNonFatal(Throwable t) {
+    if (t instanceof StackOverflowError) {
+      // StackOverflowError ok even though it is a VirtualMachineError
+      return true;
+    } else if (t instanceof VirtualMachineError ||
+        t instanceof ThreadDeath ||
+        t instanceof InterruptedException ||
+        t instanceof LinkageError) {
+      // VirtualMachineError includes OutOfMemoryError and other fatal errors
+      return false;
+    } else {
+      return true;
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/Optional.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.tck.flow.support;
+
+import java.util.NoSuchElementException;
+
+// simplest possible version of Scala's Option type
+public abstract class Optional<T> {
+
+  private static final Optional<Object> NONE = new Optional<Object>() {
+    @Override
+    public Object get() {
+      throw new NoSuchElementException(".get call on None!");
+    }
+
+    @Override
+    public boolean isEmpty() {
+      return true;
+    }
+  };
+
+  private Optional() {
+  }
+
+  @SuppressWarnings("unchecked")
+  public static <T> Optional<T> empty() {
+    return (Optional<T>) NONE;
+  }
+
+  @SuppressWarnings("unchecked")
+  public static <T> Optional<T> of(T it) {
+    if (it == null) return (Optional<T>) Optional.NONE;
+    else return new Some(it);
+  }
+
+  public abstract T get();
+
+  public abstract boolean isEmpty();
+
+  public boolean isDefined() {
+    return !isEmpty();
+  }
+
+  public static class Some<T> extends Optional<T> {
+    private final T value;
+
+    Some(T value) {
+      this.value = value;
+    }
+
+    @Override
+    public T get() {
+      return value;
+    }
+
+    @Override
+    public boolean isEmpty() {
+      return false;
+    }
+
+    @Override
+    public String toString() {
+      return String.format("Some(%s)", value);
+    }
+  }
+
+  @Override
+  public String toString() {
+    return "None";
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/PublisherVerificationRules.java	Tue Jul 02 13:25:51 2019 +0100
@@ -0,0 +1,658 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package org.reactivestreams.tck.flow.support;
+
+/**
+ * Internal TCK use only.
+ * Add / Remove tests for PublisherVerification here to make sure that they arre added/removed in the other places.
+ */
+public interface PublisherVerificationRules {
+  /**
+   * Validates that the override of {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()}
+   * returns a non-negative value.
+   */
+  void required_validate_maxElementsFromPublisher() throws Exception;
+  /**
+   * Validates that the override of {@link org.reactivestreams.tck.PublisherVerification#boundedDepthOfOnNextAndRequestRecursion()}
+   * returns a positive value.
+   */
+  void required_validate_boundedDepthOfOnNextAndRequestRecursion() throws Exception;
+  /**
+   * Asks for a {@code Publisher} that should emit exactly one item and complete (both within a
+   * timeout specified by {@link org.reactivestreams.tck.TestEnvironment#defaultTimeoutMillis()})
+   * in response to a request(1).
+   * <p>
+   * The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} returns zero.
+   * If this test fails, the following could be checked within the {@code Publisher} implementation:
+   * <ul>
+   * <li>the {@code Publisher.subscribe(Subscriber)} method has actual implementation,</li>
+   * <li>in the {@code Publisher.subscribe(Subscriber)} method, if there is an upstream {@code Publisher},
+   * that {@code Publisher} is actually subscribed to,</li>
+   * <li>if the {@code Publisher} is part of a chain, all elements actually issue a {@code request()} call
+   * in response to the test subscriber or by default to their upstream,</li>
+   * <li>in the {@code Publisher.subscribe(Subscriber)} method, the {@code Subscriber.onSubscribe} is called
+   * as part of the preparation process (usually before subscribing to other {@code Publisher}s),</li>
+   * <li>if the {@code Publisher} implementation works for a consumer that calls {@code request(1)},</li>
+   * <li>if the {@code Publisher} implementation is able to emit an {@code onComplete} without requests,</li>
+   * <li>that the {@code Publisher} implementation does not emit more than the allowed elements (exactly one).</li>
+   * </ul>
+   */
+  void required_createPublisher1MustProduceAStreamOfExactly1Element() throws Throwable;
+  /**
+   * Asks for a {@code Publisher} that should emit exactly three items and complete (all within a
+   * timeout specified by {@link org.reactivestreams.tck.TestEnvironment#defaultTimeoutMillis()}).
+   * <p>
+   * The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 3.
+   * <p>
+   * The tests requests one-by-one and verifies each single response item arrives in time.
+   * <p>
+   * If this test fails, the following could be checked within the {@code Publisher} implementation:
+   * <ul>
+   * <li>the {@code Publisher.subscribe(Subscriber)} method has actual implementation,</li>
+   * <li>in the {@code Publisher.subscribe(Subscriber)} method, if there is an upstream {@code Publisher},
+   * that {@code Publisher} is actually subscribed to,</li>
+   * <li>if the {@code Publisher} is part of a chain, all elements actually issue a {@code request()} call
+   * in response to the test subscriber or by default to their upstream,</li>
+   * <li>in the {@code Publisher.subscribe(Subscriber)} method, the {@code Subscriber.onSubscribe} is called
+   * as part of the preparation process (usually before subscribing to other {@code Publisher}s),</li>
+   * <li>if the {@code Publisher} implementation works for a subscriber that calls {@code request(1)} after consuming an item,</li>
+   * <li>if the {@code Publisher} implementation is able to emit an {@code onComplete} without requests.</li>
+   * </ul>
+   */
+  void required_createPublisher3MustProduceAStreamOfExactly3Elements() throws Throwable;
+  /**
+   * Asks for a {@code Publisher} that responds to a request pattern of 0 (not requesting upfront), 1, 1 and 2
+   * in a timely manner.
+   * <p>
+   * <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.1'>1.1</a>
+   * <p>
+   * The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 5.
+   * <p>
+   * This test ensures that the {@code Publisher} implementation correctly responds to {@code request()} calls that in
+   * total are less than the number of elements this {@code Publisher} could emit (thus the completion event won't be emitted).
+   * <p>
+   * If this test fails, the following could be checked within the {@code Publisher} implementation:
+   * <ul>
+   * <li>the {@code TestEnvironment} has large enough timeout specified in case the {@code Publisher} has some time-delay behavior,</li>
+   * <li>make sure the {@link #required_createPublisher1MustProduceAStreamOfExactly1Element()} and {@link #required_createPublisher3MustProduceAStreamOfExactly3Elements()} tests pass,</li>
+   * <li>if the {@code Publisher} implementation considers the cumulative request amount it receives,</li>
+   * <li>if the {@code Publisher} doesn't lose any {@code request()} signal and the state transition from idle -&gt; emitting or emitting -&gt; keep emitting works properly.</li>
+   * </ul>
+   */
+  void required_spec101_subscriptionRequestMustResultInTheCorrectNumberOfProducedElements() throws Throwable;
+  /**
+   * Asks for a short {@code Publisher} and verifies that requesting once and with more than the length (but bounded) results in the
+   * correct number of items to be emitted (i.e., length 3 and request 10) followed by an {@code onComplete} signal.
+   * <p>
+   * <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.2'>1.2</a>
+   * <p>
+   * The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 3.
+   * <p>
+   * This test ensures that the {@code Publisher} implementation can deal with larger requests than the number of items it can produce.
+   * <p>
+   * If this test fails, the following could be checked within the {@code Publisher} implementation:
+   * <ul>
+   * <li>the {@code TestEnvironment} has large enough timeout specified in case the {@code Publisher} has some time-delay behavior,</li>
+   * <li>make sure the {@link #required_createPublisher1MustProduceAStreamOfExactly1Element()} and {@link #required_createPublisher3MustProduceAStreamOfExactly3Elements()} tests pass.</li>
+   * </ul>
+   */
+  void required_spec102_maySignalLessThanRequestedAndTerminateSubscription() throws Throwable;
+  /**
+   * Asks for a short {@code Publisher} (i.e., length 10), repeatedly subscribes to this {@code Publisher}, requests items
+   * one by one and verifies the {@code Publisher} calls the {@code onXXX} methods non-overlappingly.
+   * <p>
+   * <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.3'>1.3</a>
+   * <p>
+   * The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 10.
+   * <p>
+   * Note that this test is probabilistic, that is, may not capture any concurrent invocation in a {code Publisher} implementation.
+   * Note also that this test is sensitive to cases when a {@code request()} call in {@code onSubscribe()} triggers an asynchronous
+   * call to the other {@code onXXX} methods. In contrast, the test allows synchronous call chain of
+   * {@code onSubscribe -> request -> onNext}.
+   * <p>
+   * If this test fails, the following could be checked within the {@code Publisher} implementation:
+   * <ul>
+   * <li>the {@code TestEnvironment} has large enough timeout specified in case the {@code Publisher} has some time-delay behavior,</li>
+   * <li>make sure the {@link #required_createPublisher1MustProduceAStreamOfExactly1Element()} and {@link #required_createPublisher3MustProduceAStreamOfExactly3Elements()} tests pass,</li>
+   * <li>if a {@code request()} call from {@code onSubscribe()} could trigger an asynchronous call to {@code onNext()} and if so, make sure
+   * such {@code request()} calls are deferred until the call to {@code onSubscribe()} returns normally.</li>
+   * </ul>
+   */
+  void stochastic_spec103_mustSignalOnMethodsSequentially() throws Throwable;
+  /**
+   * Asks for an error {@code Publisher} that should call {@code onSubscribe} exactly once
+   * followed by a single call to {@code onError()} without receiving any requests and otherwise
+   * not throwing any exception.
+   * <p>
+   * <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.4'>1.4</a>
+   * <p>
+   * The test is not executed if {@code PublisherVerification.createErrorPublisher()} returns null.
+   * <p>
+   * If this test fails, the following could be checked within the error {@code Publisher} implementation:
+   * <ul>
+   * <li>the {@code Publisher.subscribe(Subscriber)} method has actual implementation,</li>
+   * <li>in the {@code Publisher.subscribe(Subscriber)} method, if there is an upstream {@code Publisher},
+   * that {@code Publisher} is actually subscribed to,</li>
+   * <li>if the {@code Publisher} implementation does signal an {@code onSubscribe} before signalling {@code onError},</li>
+   * <li>if the {@code Publisher} implementation is able to emit an {@code onError} without requests,</li>
+   * <li>if the {@code Publisher} is non-empty as this test requires a {@code Publisher} to signal an
+   * {@code onError} eagerly.</li>
+   * </ul>
+   */
+  void optional_spec104_mustSignalOnErrorWhenFails() throws Throwable;
+  /**
+   * Asks for a short {@code Publisher} (i.e., length 3) and verifies, after requesting one by one, the sequence
+   * completes normally.
+   * <p>
+   * <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.5'>1.5</a>
+   * <p>
+   * The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 3.
+   * <p>
+   * Note that the tests requests 1 after the items have been received and before expecting an {@code onComplete} signal.
+   * <p>
+   * If this test fails, the following could be checked within the {@code Publisher} implementation:
+   * <ul>
+   * <li>the {@code TestEnvironment} has large enough timeout specified in case the {@code Publisher} has some time-delay behavior,</li>
+   * <li>make sure the {@link #required_createPublisher1MustProduceAStreamOfExactly1Element()} and {@link #required_createPublisher3MustProduceAStreamOfExactly3Elements()} tests pass,</li>
+   * </ul>
+   */
+  void required_spec105_mustSignalOnCompleteWhenFiniteStreamTerminates() throws Throwable;
+  /**
+   * Asks for an empty {@code Publisher} (i.e., length 0) and verifies it completes in a timely manner.
+   * <p>
+   * <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.5'>1.5</a>
+   * <p>
+   * Note that the tests requests 1 before expecting an {@code onComplete} signal.
+   * <p>
+   * If this test fails, the following could be checked within the {@code Publisher} implementation:
+   * <ul>
+   * <li>the {@code TestEnvironment} has large enough timeout specified in case the {@code Publisher} has some time-delay behavior,</li>
+   * <li>if the {@code Publisher} is non-empty as this test requires a {@code Publisher} without items.</li>
+   * </ul>
+   */
+  void optional_spec105_emptyStreamMustTerminateBySignallingOnComplete() throws Throwable;
+  /**
+   * Currently, this test is skipped because it is unclear this rule can be effectively checked
+   * on a {@code Publisher} instance without looking into or hooking into the implementation of it.
+   * <p>
+   * <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.6'>1.6</a>
+   */
+  void untested_spec106_mustConsiderSubscriptionCancelledAfterOnErrorOrOnCompleteHasBeenCalled() throws Throwable;
+  /**
+   * Asks for a single-element {@code Publisher} and checks if requesting after the terminal event doesn't
+   * lead to more items or terminal signals to be emitted.
+   * <p>
+   * <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.7'>1.7</a>
+   * <p>
+   * The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 1.
+   * <p>
+   * The tests requests more items than the expected {@code Publisher} length upfront and some more items after its completion.
+   * <p>
+   * If this test fails, the following could be checked within the {@code Publisher} implementation:
+   * <ul>
+   * <li>the {@code TestEnvironment} has large enough timeout specified in case the {@code Publisher} has some time-delay behavior,</li>
+   * <li>the indication for the terminal state is properly persisted and a request call can't trigger emission of more items or another
+   * terminal signal.</li>
+   * </ul>
+   */
+  void required_spec107_mustNotEmitFurtherSignalsOnceOnCompleteHasBeenSignalled() throws Throwable;
+  /**
+   * Currently, this test is skipped, although it is possible to validate an error {@code Publisher} along
+   * the same lines as {@link #required_spec107_mustNotEmitFurtherSignalsOnceOnCompleteHasBeenSignalled()}.
+   * <p>
+   * <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.7'>1.7</a>
+   */
+  void untested_spec107_mustNotEmitFurtherSignalsOnceOnErrorHasBeenSignalled() throws Throwable;
+  /**
+   * Currently, this test is skipped because there was no agreement on how to verify its "eventually" requirement.
+   * <p>
+   * <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.8'>1.8</a>
+   */
+  void untested_spec108_possiblyCanceledSubscriptionShouldNotReceiveOnErrorOrOnCompleteSignals() throws Throwable;
+  /**
+   * Asks for an empty {@code Publisher} and verifies if {@code onSubscribe} signal was emitted before
+   * any other {@code onNext}, {@code onError} or {@code onComplete} signal.
+   * <p>
+   * <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.9'>1.9</a>
+   * <p>
+   * Note that this test doesn't request anything, however, an {@code onNext} is not considered as a failure.
+   * <p>
+   * If this test fails, the following could be checked within the {@code Publisher} implementation:
+   * <ul>
+   * <li>the {@code TestEnvironment} has large enough timeout specified in case the {@code Publisher} has some time-delay behavior,</li>
+   * <li>the {@code Publisher.subscribe(Subscriber)} method has actual implementation,</li>
+   * <li>in the {@code Publisher.subscribe(Subscriber)} method, if there is an upstream {@code Publisher},
+   * that {@code Publisher} is actually subscribed to,</li>
+   * <li>in the {@code Publisher.subscribe(Subscriber)} method, the {@code Subscriber.onSubscribe} is called
+   * as part of the preparation process (usually before subscribing to other {@code Publisher}s).</li>
+   * </ul>
+   */
+  void required_spec109_mustIssueOnSubscribeForNonNullSubscriber() throws Throwable;
+  /**
+   * Currently, this test is skipped because there is no common agreement on what is to be considered a fatal exception and
+   * besides, {@code Publisher.subscribe} is only allowed throw a {@code NullPointerException} and any other
+   * exception would require looking into or hooking into the implementation of the {@code Publisher}.
+   * <p>
+   * <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.9'>1.9</a>
+   */
+  void untested_spec109_subscribeShouldNotThrowNonFatalThrowable() throws Throwable;
+  /**
+   * Asks for an empty {@code Publisher} and calls {@code subscribe} on it with {@code null} that should result in
+   * a {@code NullPointerException} to be thrown.
+   * <p>
+   * <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.9'>1.9</a>
+   * <p>
+   * If this test fails, check if the {@code subscribe()} implementation has an explicit null check (or a method dereference
+   * on the {@code Subscriber}), especially if the incoming {@code Subscriber} is wrapped or stored to be used later.
+   */
+  void required_spec109_subscribeThrowNPEOnNullSubscriber() throws Throwable;
+  /**
+   * Asks for an error {@code Publisher} that should call {@code onSubscribe} exactly once