8297645: Drop the test/jdk/java/net/httpclient/reactivestreams-tck-tests/TckDriver.java test

Reviewed-by: alanb, jpai, michaelm
This commit is contained in:
Daniel Fuchs 2022-12-02 10:44:29 +00:00
parent 687fd714bb
commit 6d0fbb2c49
58 changed files with 0 additions and 10085 deletions
test/jdk/java/net/httpclient
reactivestreams-tck-tests
reactivestreams-tck/org/reactivestreams

@ -1,92 +0,0 @@
/*
* Copyright (c) 2020, 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.BodyPublisher;
import java.net.http.HttpRequest.BodyPublishers;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Flow.Publisher;
/* See TckDriver.java for more information */
public class BodyPublishersConcat
extends FlowPublisherVerification<ByteBuffer> {
private static final int ELEMENT_SIZE = 16 * 1024;
public BodyPublishersConcat() {
super(new TestEnvironment(450L));
}
private static BodyPublisher ofByteArrays(int n, byte[] bytes) {
return BodyPublishers.ofByteArrays(Collections.nCopies((int) n, bytes));
}
@Override
public Publisher<ByteBuffer> createFlowPublisher(long nElements) {
System.out.println("BodyPublishersConcat: %d elements requested"
.formatted(nElements));
byte[] bytes = S.arrayOfNRandomBytes(ELEMENT_SIZE);
if (nElements == 0) {
System.out.println("BodyPublishersConcat: empty publisher");
return BodyPublishers.concat();
} else if (nElements == 1) {
System.out.println("BodyPublishersConcat: singleton publisher");
return BodyPublishers.concat(ofByteArrays(1, bytes));
} else if (nElements < 4) {
int left = (int)nElements/2;
int right = (int)nElements - left;
System.out.println("BodyPublishersConcat: dual publisher (%d, %d)".formatted(left, right));
return BodyPublishers.concat(ofByteArrays(left, bytes),
ofByteArrays(right, bytes));
} else {
List<BodyPublisher> publishers = new ArrayList<>();
List<Integer> sizes = new ArrayList<>();
long remaining = nElements;
int max = (int) Math.min((long)Integer.MAX_VALUE, nElements/2L);
while (remaining > 0) {
int length = S.randomIntUpTo(max);
if (length == 0) length = 1;
sizes.add(length);
if (remaining > length) {
publishers.add(ofByteArrays(length, bytes));
remaining = remaining - length;
} else {
publishers.add(ofByteArrays((int)remaining, bytes));
remaining = 0;
}
}
System.out.println("BodyPublishersConcat: multi publisher " + sizes);
return BodyPublishers.concat(publishers.toArray(BodyPublisher[]::new));
}
}
@Override
public Publisher<ByteBuffer> createFailedFlowPublisher() {
return null;
}
}

@ -1,53 +0,0 @@
/*
* 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());
}
}

@ -1,53 +0,0 @@
/*
* 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;
}
}

@ -1,56 +0,0 @@
/*
* 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;
}
}

@ -1,53 +0,0 @@
/*
* 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;
}
}

@ -1,72 +0,0 @@
/*
* 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;
}
}

@ -1,51 +0,0 @@
/*
* 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;
}
}

@ -1,60 +0,0 @@
/*
* 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;
}
}

@ -1,50 +0,0 @@
/*
* 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);
}
}

@ -1,49 +0,0 @@
/*
* 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);
}
}

@ -1,51 +0,0 @@
/*
* 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));
}
}

@ -1,51 +0,0 @@
/*
* 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));
}
}

@ -1,50 +0,0 @@
/*
* 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);
}
}

@ -1,49 +0,0 @@
/*
* 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);
}
}

@ -1,49 +0,0 @@
/*
* 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);
}
}

@ -1,50 +0,0 @@
/*
* 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);
}
}

@ -1,49 +0,0 @@
/*
* 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);
}
}

@ -1,51 +0,0 @@
/*
* 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));
}
}

@ -1,79 +0,0 @@
/*
* 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);
}
}

@ -1,84 +0,0 @@
/*
* 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);
}
}

@ -1,67 +0,0 @@
/*
* 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;
}
}

@ -1,51 +0,0 @@
/*
* 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));
}
}

@ -1,50 +0,0 @@
/*
* 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);
}
}

@ -1,277 +0,0 @@
/*
* 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;
}
}
}

@ -1,49 +0,0 @@
/*
* 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;
}
}

@ -1,119 +0,0 @@
/*
* 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);
}
}

@ -1,158 +0,0 @@
/*
* 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 BodyPublishersConcat.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 BodyPublishersConcat
*
* @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.
*/
}

@ -1,389 +0,0 @@
/*
* 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));
}
}
}

@ -1,34 +0,0 @@
/*
* 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 stagewhich 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> {
}

@ -1,52 +0,0 @@
/*
* 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);
}

@ -1,78 +0,0 @@
/*
* 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();
}

@ -1,56 +0,0 @@
/*
* 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 neededbut 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();
}

@ -1,281 +0,0 @@
/*
* 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);
}
};
}

@ -1,261 +0,0 @@
/*
* 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);
}
}
}
}
}
}

@ -1,46 +0,0 @@
/*
* 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);
}
}

@ -1,50 +0,0 @@
/*
* 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);
}
}

@ -1,254 +0,0 @@
/*
* 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();
}
}
}
}

@ -1,134 +0,0 @@
/*
* 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
}
}
}

@ -1,896 +0,0 @@
/*
* Copyright (c) 2019, 2022, 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 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 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 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()));
}
}
}
}

@ -1,516 +0,0 @@
/*
* 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.");
}
}

@ -1,850 +0,0 @@
/*
* Copyright (c) 2019, 2022, 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.expectRequest();
stage.puppet().signalCancel();
stage.expectCancelling();
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 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.
* <p>
* An invocation of {@link #signalCancel()} may be coalesced into any elements that have not yet been requested,
* such that only a cancel signal is emitted.
*/
void triggerRequest(long elements);
/**
* Trigger {@code cancel()} on your {@link Subscriber}.
* <p>
* An invocation of this method may be coalesced into any outstanding requests, as requested by
*{@link #triggerRequest(long)}, such that only a cancel signal is emitted.
*/
void signalCancel();
}
public void notVerified() {
throw new SkipException("Not verified using this TCK.");
}
public void notVerified(String msg) {
throw new SkipException(msg);
}
}

@ -1,92 +0,0 @@
/*
* 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());
}
}

@ -1,75 +0,0 @@
/*
* 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();
}

@ -1,77 +0,0 @@
/*
* 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();
}

@ -1,60 +0,0 @@
/*
* 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);
}

@ -1,80 +0,0 @@
/*
* 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);
}
}

@ -1,28 +0,0 @@
/*
* 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;
}

@ -1,54 +0,0 @@
/*
* 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);
}
}

@ -1,53 +0,0 @@
/*
* 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);
}
}

@ -1,55 +0,0 @@
/*
* 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;
}
}
}

@ -1,92 +0,0 @@
/*
* 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";
}
}

@ -1,658 +0,0 @@
/*
* 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
* followed by a single call to {@code onError()} without receiving any requests.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.9'>1.9</a>
* <p>
* The test is not executed if {@code PublisherVerification.createErrorPublisher()} returns null.
* <p>
* The difference between this test and {@link #optional_spec104_mustSignalOnErrorWhenFails()} is that there is
* no explicit verification if exceptions were thrown in addition to the regular {@code onSubscribe+onError} signal pair.
* <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 is able to emit an {@code onError} without requests,</li>
* <li>if the {@code Publisher} is non-empty as this test expects a {@code Publisher} without items.</li>
* </ul>
*/
void required_spec109_mayRejectCallsToSubscribeIfPublisherIsUnableOrUnwillingToServeThemRejectionMustTriggerOnErrorAfterOnSubscribe() throws Throwable;
/**
* Currently, this test is skipped because enforcing rule §1.10 requires unlimited retention and reference-equal checks on
* all incoming {@code Subscriber} which is generally infeasible, plus reusing the same {@code Subscriber} instance is
* better detected (or ignored) inside {@code Subscriber.onSubscribe} when the method is called multiple times.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.10'>1.10</a>
*/
void untested_spec110_rejectASubscriptionRequestIfTheSameSubscriberSubscribesTwice() throws Throwable;
/**
* Asks for a single-element {@code Publisher} and subscribes to it twice, without consuming with either
* {@code Subscriber} instance
* (i.e., no requests are issued).
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.11'>1.11</a>
* <p>
* The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 1.
* <p>
* Note that this test ignores what signals the {@code Publisher} emits. Any exception thrown through non-regular
* means will indicate a skipped test.
*/
void optional_spec111_maySupportMultiSubscribe() throws Throwable;
/**
* Asks for a single-element {@code Publisher} and subscribes to it twice.
* Each {@code Subscriber} requests for 1 element and checks if onNext or onComplete signals was received.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.11'>1.11</a>,
* and depends on valid implementation of rule <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.5'>1.5</a>
* in order to verify this.
* <p>
* The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 1.
* <p>
* Any exception thrown through non-regular means will indicate a skipped test.
*/
void optional_spec111_registeredSubscribersMustReceiveOnNextOrOnCompleteSignals() throws Throwable;
/**
* Asks for a short {@code Publisher} (length 5), subscribes 3 {@code Subscriber}s to it, requests with different
* patterns and checks if all 3 received the same events in the same order.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.11'>1.11</a>
* <p>
* The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 5.
* <p>
* The request pattern for the first {@code Subscriber} is (1, 1, 2, 1); for the second is (2, 3) and for the third is (3, 1, 1).
* <p>
* Note that this test requires a {@code Publisher} that always emits the same signals to any {@code Subscriber}, regardless of
* when they subscribe and how they request elements. I.e., a "live" {@code Publisher} emitting the current time would not pass this test.
* <p>
* Note that this test is optional and may appear skipped even if the behavior should be actually supported by the {@code Publisher},
* see the skip message for an indication of this.
* <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 optional_spec111_multicast_mustProduceTheSameElementsInTheSameSequenceToAllOfItsSubscribersWhenRequestingOneByOne() throws Throwable;
/**
* Asks for a short {@code Publisher} (length 3), subscribes 3 {@code Subscriber}s to it, requests more than the length items
* upfront with each and verifies they all received the same items in the same order (but does not verify they all complete).
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.11'>1.11</a>
* <p>
* The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 3.
* <p>
* Note that this test requires a {@code Publisher} that always emits the same signals to any {@code Subscriber}, regardless of
* when they subscribe and how they request elements. I.e., a "live" {@code Publisher} emitting the current time would not pass this test.
* <p>
* Note that this test is optional and may appear skipped even if the behavior should be actually supported by the {@code Publisher},
* see the skip message for an indication of this.
* <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 optional_spec111_multicast_mustProduceTheSameElementsInTheSameSequenceToAllOfItsSubscribersWhenRequestingManyUpfront() throws Throwable;
/**
* Asks for a short {@code Publisher} (length 3), subscribes 3 {@code Subscriber}s to it, requests more than the length items
* upfront with each and verifies they all received the same items in the same order followed by an {@code onComplete} signal.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.11'>1.11</a>
* <p>
* The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 3.
* <p>
* Note that this test requires a {@code Publisher} that always emits the same signals to any {@code Subscriber}, regardless of
* when they subscribe and how they request elements. I.e., a "live" {@code Publisher} emitting the current time would not pass this test.
* <p>
* Note that this test is optional and may appear skipped even if the behavior should be actually supported by the {@code Publisher},
* see the skip message for an indication of this.
* <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 optional_spec111_multicast_mustProduceTheSameElementsInTheSameSequenceToAllOfItsSubscribersWhenRequestingManyUpfrontAndCompleteAsExpected() throws Throwable;
/**
* Asks for a short {@code Publisher} (length 6), requests several times from within {@code onSubscribe} and then requests
* one-by-one from {@code onNext}.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.2'>3.2</a>
* <p>
* The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 6.
* <p>
* The request pattern is 3 x 1 from within {@code onSubscribe} and one from within each {@code onNext} invocation.
* <p>
* The test consumes the {@code Publisher} but otherwise doesn't verify the {@code Publisher} completes (however, it checks
* for errors).
* <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_spec302_mustAllowSynchronousRequestCallsFromOnNextAndOnSubscribe() throws Throwable;
/**
* Asks for a {@code Publisher} with length equal to the value returned by {@link #required_validate_boundedDepthOfOnNextAndRequestRecursion()} plus 1,
* calls {@code request(1)} externally and then from within {@code onNext} and checks if the stack depth did not increase beyond the
* amount permitted by {@link #required_validate_boundedDepthOfOnNextAndRequestRecursion()}.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.3'>3.3</a>
* <p>
* The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than
* {@link #required_validate_boundedDepthOfOnNextAndRequestRecursion()} plus 1.
* <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>the implementation doesn't allow unbounded recursion when {@code request()} is called from within {@code onNext}, i.e., the lack of
* reentrant-safe state machine around the request amount (such as a for loop with a bound on the parameter {@code n} that calls {@code onNext}).
* </ul>
*/
void required_spec303_mustNotAllowUnboundedRecursion() throws Throwable;
/**
* Currently, this test is skipped because a {@code request} could enter into a synchronous computation via {@code onNext}
* legally and otherwise there is no common agreement how to detect such heavy computation reliably.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.4'>3.4</a>
*/
void untested_spec304_requestShouldNotPerformHeavyComputations() throws Exception;
/**
* Currently, this test is skipped because there is no reliable agreed upon way to detect a heavy computation.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.5'>3.5</a>
*/
void untested_spec305_cancelMustNotSynchronouslyPerformHeavyComputation() throws Exception;
/**
* Asks for a short {@code Publisher} (length 3) and verifies that cancelling without requesting anything, then requesting
* items should result in no signals to be emitted.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.6'>3.6</a>
* <p>
* The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 3.
* <p>
* The post-cancellation request pattern is (1, 1, 1).
* <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>the cancellation indicator flag is properly persisted (may require volatile) and checked as part of the signal emission process.</li>
* </ul>
*/
void required_spec306_afterSubscriptionIsCancelledRequestMustBeNops() throws Throwable;
/**
* Asks for a single-element {@code Publisher} and verifies that without requesting anything, cancelling the sequence
* multiple times should result in no signals to be emitted and should result in an thrown exception.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.7'>3.7</a>
* <p>
* The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 1.
* <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>the cancellation indicator flag is properly persisted (may require volatile) and checked as part of the signal emission process.</li>
* </ul>
*/
void required_spec307_afterSubscriptionIsCancelledAdditionalCancelationsMustBeNops() throws Throwable;
/**
* Asks for a short {@code Publisher} (length 10) and issues a {@code request(0)} which should trigger an {@code onError} call
* with an {@code IllegalArgumentException}.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.9'>3.9</a>
* <p>
* The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 10.
* <p>
* Note that this test expects the {@code IllegalArgumentException} being signalled through {@code onError}, not by
* throwing from {@code request()} (which is also forbidden) or signalling the error by any other means (i.e., through the
* {@code Thread.currentThread().getUncaughtExceptionHandler()} for example).
* <p>
* Note also that requesting and emission may happen concurrently and honoring this rule may require extra coordination within
* the {@code Publisher}.
* <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>the {@code Publisher} can emit an {@code onError} in this particular case, even if there was no prior and legal
* {@code request} call and even if the {@code Publisher} would like to emit items first before emitting an {@code onError}
* in general.
* </ul>
*/
void required_spec309_requestZeroMustSignalIllegalArgumentException() throws Throwable;
/**
* Asks for a short {@code Publisher} (length 10) and issues a random, negative {@code request()} call which should
* trigger an {@code onError} call with an {@code IllegalArgumentException}.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.9'>3.9</a>
* <p>
* The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 10.
* <p>
* Note that this test expects the {@code IllegalArgumentException} being signalled through {@code onError}, not by
* throwing from {@code request()} (which is also forbidden) or signalling the error by any other means (i.e., through the
* {@code Thread.currentThread().getUncaughtExceptionHandler()} for example).
* <p>
* Note also that requesting and emission may happen concurrently and honoring this rule may require extra coordination within
* the {@code Publisher}.
* <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>the {@code Publisher} can emit an {@code onError} in this particular case, even if there was no prior and legal
* {@code request} call and even if the {@code Publisher} would like to emit items first before emitting an {@code onError}
* in general.
* </ul>
*/
void required_spec309_requestNegativeNumberMustSignalIllegalArgumentException() throws Throwable;
/**
* Asks for a short {@code Publisher} (length 10) and issues a random, negative {@code request()} call which should
* trigger an {@code onError} call with an {@code IllegalArgumentException}.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.9'>3.9</a>
* <p>
* The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 10.
* <p>
* Note that this test expects the {@code IllegalArgumentException} being signalled through {@code onError}, not by
* throwing from {@code request()} (which is also forbidden) or signalling the error by any other means (i.e., through the
* {@code Thread.currentThread().getUncaughtExceptionHandler()} for example).
* <p>
* Note also that requesting and emission may happen concurrently and honoring this rule may require extra coordination within
* the {@code Publisher}.
* <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>the {@code Publisher} can emit an {@code onError} in this particular case, even if there was no prior and legal
* {@code request} call and even if the {@code Publisher} would like to emit items first before emitting an {@code onError}
* in general.
* </ul>
*/
void optional_spec309_requestNegativeNumberMaySignalIllegalArgumentExceptionWithSpecificMessage() throws Throwable;
/**
* Asks for a short {@code Publisher} (length 20), requests some items (less than the length), consumes one item then
* cancels the sequence and verifies the publisher emitted at most the requested amount and stopped emitting (or terminated).
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.12'>3.12</a>
* <p>
* The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 20.
* <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>the cancellation indicator flag is properly persisted (may require volatile) and checked as part of the signal emission process.</li>
* </ul>
*/
void required_spec312_cancelMustMakeThePublisherToEventuallyStopSignaling() throws Throwable;
/**
* Asks for a short {@code Publisher} (length 3) requests and consumes one element from it, cancels the {@code Subscription}
* , calls {@code System.gc()} and then checks if all references to the test {@code Subscriber} has been dropped (by checking
* the {@code WeakReference} has been emptied).
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.13'>3.13</a>
* <p>
* The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 3.
* <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>the cancellation indicator flag is properly persisted (may require volatile) and checked as part of the signal emission process.</li>
* <li>the {@code Publisher} stores the {@code Subscriber} reference somewhere which is then not cleaned up when the {@code Subscriber} is cancelled.
* Note that this may happen on many code paths in a {@code Publisher}, for example in an emission loop that terminates because of the
* {@code cancel} signal or because reaching a terminal state. Note also that eagerly nulling {@code Subscriber} references may not be necessary
* for this test to pass in case there is a self-contained chain of them (i.e., {@code Publisher.subscribe()} creates a chain of fresh
* {@code Subscriber} instances where each of them only references their downstream {@code Subscriber} thus the chain can get GC'd
* when the reference to the final {@code Subscriber} is dropped).
* </ul>
*/
void required_spec313_cancelMustMakeThePublisherEventuallyDropAllReferencesToTheSubscriber() throws Throwable;
/**
* Asks for a short {@code Publisher} (length 3) and requests {@code Long.MAX_VALUE} from it, verifying that the
* {@code Publisher} emits all of its items and completes normally
* and does not keep spinning attempting to fulfill the {@code Long.MAX_VALUE} demand by some means.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.17'>3.17</a>
* <p>
* The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 3.
* <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_spec317_mustSupportAPendingElementCountUpToLongMaxValue() throws Throwable;
/**
* Asks for a short {@code Publisher} (length 3) and requests {@code Long.MAX_VALUE} from it in total (split across
* two {@code Long.MAX_VALUE / 2} and one {@code request(1)}), verifying that the
* {@code Publisher} emits all of its items and completes normally.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.17'>3.17</a>
* <p>
* The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than 3.
* <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} implements adding individual request amounts together properly (not overflowing into zero or negative pending request amounts)
* or not properly deducing the number of emitted items from the pending amount,</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_spec317_mustSupportACumulativePendingElementCountUpToLongMaxValue() throws Throwable;
/**
* Asks for a very long {@code Publisher} (up to {@code Integer.MAX_VALUE}), requests {@code Long.MAX_VALUE - 1} after
* each received item and expects no failure due to a potential overflow in the pending emission count while consuming
* 10 items and cancelling the sequence.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.17'>3.17</a>
* <p>
* The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} is less than {@code Integer.MAX_VALUE}.
* <p>
* The request pattern is one {@code request(1)} upfront and ten {@code request(Long.MAX_VALUE - 1)} after.
* <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} implements adding individual request amounts together properly (not overflowing into zero or negative pending request amounts)
* or not properly deducing the number of emitted items from the pending amount,</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_spec317_mustNotSignalOnErrorWhenPendingAboveLongMaxValue() throws Throwable;
}

@ -1,395 +0,0 @@
/*
* 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.tck.SubscriberBlackboxVerification;
/**
* Internal TCK use only.
* Add / Remove tests for SubscriberBlackboxVerification here to make sure that they arre added/removed in the other places.
*/
public interface SubscriberBlackboxVerificationRules {
/**
* Asks for a {@code Subscriber} instance, expects it to call {@code request()} in
* a timely manner and signals as many {@code onNext} items as the very first request
* amount specified by the {@code Subscriber}.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.1'>2.1</a>
* <p>
* Notes:
* <ul>
* <li>This test emits the number of items requested thus the {@code Subscriber} implementation
* should not request too much.</li>
* <li>Only the very first {@code request} amount is considered.</li>
* <li>This test doesn't signal {@code onComplete} after the first set of {@code onNext} signals
* has been emitted and may cause resource leak in
* {@code Subscriber}s that expect a finite {@code Publisher}.</li>
* <li>The test ignores cancellation from the {@code Subscriber} and emits the requested amount regardless.</li>
* </ul>
* <p>
* If this test fails, the following could be checked within the {@code Subscriber} implementation:
* <ul>
* <li>if the {@code Subscriber} requires external stimulus to begin requesting; override the
* {@link SubscriberBlackboxVerification#triggerRequest(org.reactivestreams.Subscriber)} method
* in this case,</li>
* <li>the {@code TestEnvironment} has large enough timeout specified in case the {@code Subscriber} has some time-delay behavior,</li>
* <li>if the {@code Subscriber} requests zero or a negative value in some circumstances,</li>
* <li>if the {@code Subscriber} throws an unchecked exception from its {@code onSubscribe} or
* {@code onNext} methods.
* </ul>
*/
void required_spec201_blackbox_mustSignalDemandViaSubscriptionRequest() throws Throwable;
/**
* Currently, this test is skipped because there is no agreed upon approach how
* to detect if the {@code Subscriber} really goes async or just responds in
* a timely manner.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.2'>2.2</a>
*/
void untested_spec202_blackbox_shouldAsynchronouslyDispatch() throws Exception;
/**
* Asks for a {@code Subscriber}, signals an {@code onSubscribe} followed by an {@code onComplete} synchronously,
* and checks if neither {@code request} nor {@code cancel} was called from within the {@code Subscriber}'s
* {@code onComplete} implementation.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.3'>2.3</a>
* <p>
* Notes:
* <ul>
* <li>The test checks for the presensce of method named "onComplete" in the current stacktrace when handling
* the {@code request} or {@code cancel} calls in the test's own {@code Subscription}.
* </ul>
* <p>
* If this test fails, the following could be checked within the {@code Subscriber} implementation:
* <ul>
* <li>no calls happen to {@code request} or {@code cancel} in response to an {@code onComplete}
* directly or indirectly,</li>
* <li>if the {@code Subscriber} throws an unchecked exception from its {@code onSubscribe} or
* {@code onComplete} methods.
* </ul>
*/
void required_spec203_blackbox_mustNotCallMethodsOnSubscriptionOrPublisherInOnComplete() throws Throwable;
/**
* Asks for a {@code Subscriber}, signals an {@code onSubscribe} followed by an {@code onError} synchronously,
* and checks if neither {@code request} nor {@code cancel} was called from within the {@code Subscriber}'s
* {@code onComplete} implementation.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.3'>2.3</a>
* <p>
* Notes:
* <ul>
* <li>The test checks for the presensce of method named "onError" in the current stacktrace when handling
* the {@code request} or {@code cancel} calls in the test's own {@code Subscription}.
* </ul>
* <p>
* If this test fails, the following could be checked within the {@code Subscriber} implementation:
* <ul>
* <li>no calls happen to {@code request} or {@code cancel} in response to an {@code onError}
* directly or indirectly,</li>
* <li>if the {@code Subscriber} throws an unchecked exception from its {@code onSubscribe} or
* {@code onError} methods.
* </ul>
*/
void required_spec203_blackbox_mustNotCallMethodsOnSubscriptionOrPublisherInOnError() throws Throwable;
/**
* Currently, this test is skipped because there is no way to check what the {@code Subscriber} "considers"
* since rule §2.3 forbids interaction from within the {@code onError} and {@code onComplete} methods.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.4'>2.4</a>
* <p>
* Notes:
* <ul>
* <li>It would be possible to check if there was an async interaction with the test's {@code Subscription}
* within a grace period but such check is still not generally decisive.</li>
* </ul>
*/
void untested_spec204_blackbox_mustConsiderTheSubscriptionAsCancelledInAfterRecievingOnCompleteOrOnError() throws Exception;
/**
* Asks for a {@code Subscriber}, signals {@code onSubscribe} twice synchronously and expects the second {@code Subscription} gets
* cancelled in a timely manner and without any calls to its {@code request} method.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.5'>2.5</a>
* <p>
* Notes:
* <ul>
* <li>The test doesn't signal any other events than {@code onSubscribe} and may cause resource leak in
* {@code Subscriber}s that expect a finite {@code Publisher}.
* </ul>
* <p>
* If this test fails, the following could be checked within the {@code Subscriber} implementation:
* <ul>
* <li>if the {@code Subscribe.onSubscribe} implementation actually tries to detect multiple calls to it,</li>
* <li>if the second {@code Subscription} is cancelled asynchronously and that takes longer time than
* the {@code TestEnvironment}'s timeout permits.</li>
* </ul>
*/
void required_spec205_blackbox_mustCallSubscriptionCancelIfItAlreadyHasAnSubscriptionAndReceivesAnotherOnSubscribeSignal() throws Exception;
/**
* Currently, this test is skipped because it requires more control over the {@code Subscriber} implementation
* to make it cancel the {@code Subscription} for some external condition.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.6'>2.6</a>
*/
void untested_spec206_blackbox_mustCallSubscriptionCancelIfItIsNoLongerValid() throws Exception;
/**
* Currently, this test is skipped because it requires more control over the {@code Subscriber} implementation
* to issue requests based on external stimulus.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.7'>2.7</a>
*/
void untested_spec207_blackbox_mustEnsureAllCallsOnItsSubscriptionTakePlaceFromTheSameThreadOrTakeCareOfSynchronization() throws Exception;
/**
* Currently, this test is skipped because there is no way to make the {@code Subscriber} implementation
* cancel the test's {@code Subscription} and check the outcome of sending {@code onNext}s after such
* cancel.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.8'>2.8</a>
*/
void untested_spec208_blackbox_mustBePreparedToReceiveOnNextSignalsAfterHavingCalledSubscriptionCancel() throws Throwable;
/**
* Asks for a {@code Subscriber}, expects it to request some amount and in turn be able to receive an {@code onComplete}
* synchronously from the {@code request} call without any {@code onNext} signals before that.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.9'>2.9</a>
* <p>
* Notes:
* <ul>
* <li>The test ignores cancellation from the {@code Subscriber}.</li>
* <li>Invalid request amounts are ignored by this test.</li>
* <li>Concurrent calls to the test's {@code Subscription.request()} must be externally synchronized, otherwise
* such case results probabilistically in multiple {@code onComplete} calls by the test.</li>
* </ul>
* <p>
* If this test fails, the following could be checked within the {@code Subscriber} implementation:
* <ul>
* <li>if the {@code Subscriber} throws an unchecked exception from its {@code onSubscribe} or
* {@code onComplete} methods.
* <li>if the {@code Subscriber} requires external stimulus to begin requesting; override the
* {@link SubscriberBlackboxVerification#triggerRequest(org.reactivestreams.Subscriber)} method
* in this case,</li>
* </ul>
*/
void required_spec209_blackbox_mustBePreparedToReceiveAnOnCompleteSignalWithPrecedingRequestCall() throws Throwable;
/**
* Asks for a {@code Subscriber} and expects it to handle {@code onComplete} independent of whether the {@code Subscriber}
* requests items or not.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.9'>2.9</a>
* <p>
* Notes:
* <ul>
* <li>Currently, the test doesn't call {@code onSubscribe} on the {@code Subscriber} which violates §1.9.
* </ul>
* <p>
* If this test fails, the following could be checked within the {@code Subscriber} implementation:
* <ul>
* <li>if the {@code Subscriber} throws an unchecked exception from its {@code onSubscribe} or
* {@code onComplete} methods.
* </ul>
*/
void required_spec209_blackbox_mustBePreparedToReceiveAnOnCompleteSignalWithoutPrecedingRequestCall() throws Throwable;
/**
* Asks for a {@code Subscriber}, signals {@code onSubscribe} followed by an {@code onError} synchronously.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.10'>2.10</a>
* <p>
* Notes:
* <ul>
* <li>Despite the method name, the test doesn't expect a request signal from {@code Subscriber} and emits the
* {@code onError} signal anyway.
* </ul>
* <p>
* If this test fails, the following could be checked within the {@code Subscriber} implementation:
* <ul>
* <li>if the {@code Subscriber} throws an unchecked exception from its {@code onSubscribe} or
* {@code onError} methods.
* </ul>
*/
void required_spec210_blackbox_mustBePreparedToReceiveAnOnErrorSignalWithPrecedingRequestCall() throws Throwable;
/**
* Asks for a {@code Subscriber}, signals {@code onSubscribe} followed by an {@code onError} synchronously.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.10'>2.10</a>
* <p>
* If this test fails, the following could be checked within the {@code Subscriber} implementation:
* <ul>
* <li>if the {@code Subscriber} throws an unchecked exception from its {@code onSubscribe} or
* {@code onError} methods.
* </ul>
*/
void required_spec210_blackbox_mustBePreparedToReceiveAnOnErrorSignalWithoutPrecedingRequestCall() throws Throwable;
/**
* Currently, this test is skipped because it would require analyzing what the {@code Subscriber} implementation
* does.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.11'>2.11</a>
*/
void untested_spec211_blackbox_mustMakeSureThatAllCallsOnItsMethodsHappenBeforeTheProcessingOfTheRespectiveEvents() throws Exception;
/**
* Currently, this test is skipped because the test for
* {@link #required_spec205_blackbox_mustCallSubscriptionCancelIfItAlreadyHasAnSubscriptionAndReceivesAnotherOnSubscribeSignal §2.5}
* is in a better position to test for handling the reuse of the same {@code Subscriber}.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.12'>2.12</a>
* <p>
* Notes:
* <ul>
* <li>In addition to §2.5, this rule could be better verified when testing a {@code Publisher}'s subscription behavior.
* </ul>
*/
void untested_spec212_blackbox_mustNotCallOnSubscribeMoreThanOnceBasedOnObjectEquality() throws Throwable;
/**
* Currently, this test is skipped because it would require more control over the {@code Subscriber} to
* fail internally in response to a set of legal event emissions, not throw any exception from the {@code Subscriber}
* methods and have it cancel the {@code Subscription}.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.13'>2.13</a>
*/
void untested_spec213_blackbox_failingOnSignalInvocation() throws Exception;
/**
* Asks for a {@code Subscriber} and signals an {@code onSubscribe} event with {@code null} as a parameter and
* expects an immediate {@code NullPointerException} to be thrown by the {@code Subscriber.onSubscribe} method.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.13'>2.13</a>
* <p>
* If this test fails, the following could be checked within the {@code Subscriber} implementation:
* <ul>
* <li>if the {@code Subscriber} throws a {@code NullPointerException} from its {@code onSubscribe} method
* in response to a {@code null} parameter and not some other unchecked exception or no exception at all.
* </ul>
*/
void required_spec213_blackbox_onSubscribe_mustThrowNullPointerExceptionWhenParametersAreNull() throws Throwable;
/**
* Asks for a {@code Subscriber}, signals an {@code onSubscribe} event followed by a
* {@code onNext} with {@code null} as a parameter and
* expects an immediate {@code NullPointerException} to be thrown by the {@code Subscriber.onNext} method.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.13'>2.13</a>
* <p>
* Notes:
* <ul>
* <li>The test ignores cancellation and requests from the {@code Subscriber} and emits the {@code onNext}
* signal with a {@code null} parameter anyway.</li>
* </ul>
* <p>
* If this test fails, the following could be checked within the {@code Subscriber} implementation:
* <ul>
* <li>if the {@code Subscriber} throws a {@code NullPointerException} from its {@code onNext} method
* in response to a {@code null} parameter and not some other unchecked exception or no exception at all.
* </ul>
*/
void required_spec213_blackbox_onNext_mustThrowNullPointerExceptionWhenParametersAreNull() throws Throwable;
/**
* Asks for a {@code Subscriber}, signals an {@code onSubscribe} event followed by a
* {@code onError} with {@code null} as a parameter and
* expects an immediate {@code NullPointerException} to be thrown by the {@code Subscriber.onError} method.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#2.13'>2.13</a>
* <p>
* Notes:
* <ul>
* <li>The test ignores cancellation from the {@code Subscriber} and emits the {@code onError}
* signal with a {@code null} parameter anyway.</li>
* </ul>
* <p>
* If this test fails, the following could be checked within the {@code Subscriber} implementation:
* <ul>
* <li>if the {@code Subscriber} throws a {@code NullPointerException} from its {@code onNext} method
* in response to a {@code null} parameter and not some other unchecked exception or no exception at all.
* </ul>
*/
void required_spec213_blackbox_onError_mustThrowNullPointerExceptionWhenParametersAreNull() throws Throwable;
/**
* Currently, this test is skipped because there is no agreed upon way for specifying, enforcing and testing
* a {@code Subscriber} with an arbitrary context.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.1'>3.1</a>
*/
void untested_spec301_blackbox_mustNotBeCalledOutsideSubscriberContext() throws Exception;
/**
* Currently, this test is skipped because element production is the responsibility of the {@code Publisher} and
* a {@code Subscription} is not expected to be the active element in an established subscription.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.8'>3.8</a>
*/
void untested_spec308_blackbox_requestMustRegisterGivenNumberElementsToBeProduced() throws Throwable;
/**
* Currently, this test is skipped because element production is the responsibility of the {@code Publisher} and
* a {@code Subscription} is not expected to be the active element in an established subscription.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.10'>3.10</a>
* <p>
* Notes:
* <ul>
* <li>This could be tested with a synchronous source currently not available within the TCK.</li>
* </ul>
*/
void untested_spec310_blackbox_requestMaySynchronouslyCallOnNextOnSubscriber() throws Exception;
/**
* Currently, this test is skipped because signal production is the responsibility of the {@code Publisher} and
* a {@code Subscription} is not expected to be the active element in an established subscription.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.11'>3.11</a>
* <p>
* Notes:
* <ul>
* <li>Tests {@link #required_spec209_blackbox_mustBePreparedToReceiveAnOnCompleteSignalWithPrecedingRequestCall() §2.9}
* and {@link #required_spec210_blackbox_mustBePreparedToReceiveAnOnErrorSignalWithPrecedingRequestCall() §2.10} are
* supposed to cover this case from the {@code Subscriber's} perspective.</li>
* </ul>
*/
void untested_spec311_blackbox_requestMaySynchronouslyCallOnCompleteOrOnError() throws Exception;
/**
* Currently, this test is skipped because it is the responsibility of the {@code Publisher} deal with the case
* that all subscribers have cancelled their subscription.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.14'>3.14</a>
* <p>
* Notes:
* <ul>
* <li>The specification lists this as an optional behavior because only some {@code Publisher} implementations
* (most likely {@code Processor}s) would coordinate with multiple {@code Subscriber}s.</li>
* </ul>
*/
void untested_spec314_blackbox_cancelMayCauseThePublisherToShutdownIfNoOtherSubscriptionExists() throws Exception;
/**
* Currently, this test is skipped because it requires more control over the {@code Subscriber} implementation
* thus there is no way to detect that the {@code Subscriber} called its own {@code onError} method in response
* to an exception thrown from {@code Subscription.cancel}.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.15'>3.15</a>
*/
void untested_spec315_blackbox_cancelMustNotThrowExceptionAndMustSignalOnError() throws Exception;
/**
* Currently, this test is skipped because it requires more control over the {@code Subscriber} implementation
* thus there is no way to detect that the {@code Subscriber} called its own {@code onError} method in response
* to an exception thrown from {@code Subscription.request}.
* <p>
* <b>Verifies rule:</b> <a href='https://github.com/reactive-streams/reactive-streams-jvm#3.16'>3.16</a>
*/
void untested_spec316_blackbox_requestMustNotThrowExceptionAndMustOnErrorTheSubscriber() throws Exception;
}

@ -1,41 +0,0 @@
/*
* 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 final class SubscriberBufferOverflowException extends RuntimeException {
public SubscriberBufferOverflowException() {
}
public SubscriberBufferOverflowException(String message) {
super(message);
}
public SubscriberBufferOverflowException(String message, Throwable cause) {
super(message, cause);
}
public SubscriberBufferOverflowException(Throwable cause) {
super(cause);
}
}

@ -1,58 +0,0 @@
/*
* 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 PublisherVerificaSubscriberWhiteboxVerification here to make sure that they arre added/removed in the other places.
*/
public interface SubscriberWhiteboxVerificationRules {
void required_exerciseWhiteboxHappyPath() throws Throwable;
void required_spec201_mustSignalDemandViaSubscriptionRequest() throws Throwable;
void untested_spec202_shouldAsynchronouslyDispatch() throws Exception;
void required_spec203_mustNotCallMethodsOnSubscriptionOrPublisherInOnComplete() throws Throwable;
void required_spec203_mustNotCallMethodsOnSubscriptionOrPublisherInOnError() throws Throwable;
void untested_spec204_mustConsiderTheSubscriptionAsCancelledInAfterRecievingOnCompleteOrOnError() throws Exception;
void required_spec205_mustCallSubscriptionCancelIfItAlreadyHasAnSubscriptionAndReceivesAnotherOnSubscribeSignal() throws Throwable;
void untested_spec206_mustCallSubscriptionCancelIfItIsNoLongerValid() throws Exception;
void untested_spec207_mustEnsureAllCallsOnItsSubscriptionTakePlaceFromTheSameThreadOrTakeCareOfSynchronization() throws Exception;
void required_spec208_mustBePreparedToReceiveOnNextSignalsAfterHavingCalledSubscriptionCancel() throws Throwable;
void required_spec209_mustBePreparedToReceiveAnOnCompleteSignalWithPrecedingRequestCall() throws Throwable;
void required_spec209_mustBePreparedToReceiveAnOnCompleteSignalWithoutPrecedingRequestCall() throws Throwable;
void required_spec210_mustBePreparedToReceiveAnOnErrorSignalWithPrecedingRequestCall() throws Throwable;
void required_spec210_mustBePreparedToReceiveAnOnErrorSignalWithoutPrecedingRequestCall() throws Throwable;
void untested_spec211_mustMakeSureThatAllCallsOnItsMethodsHappenBeforeTheProcessingOfTheRespectiveEvents() throws Exception;
void untested_spec212_mustNotCallOnSubscribeMoreThanOnceBasedOnObjectEquality_specViolation() throws Throwable;
void untested_spec213_failingOnSignalInvocation() throws Exception;
void required_spec213_onSubscribe_mustThrowNullPointerExceptionWhenParametersAreNull() throws Throwable;
void required_spec213_onNext_mustThrowNullPointerExceptionWhenParametersAreNull() throws Throwable;
void required_spec213_onError_mustThrowNullPointerExceptionWhenParametersAreNull() throws Throwable;
void untested_spec301_mustNotBeCalledOutsideSubscriberContext() throws Exception;
void required_spec308_requestMustRegisterGivenNumberElementsToBeProduced() throws Throwable;
void untested_spec310_requestMaySynchronouslyCallOnNextOnSubscriber() throws Exception;
void untested_spec311_requestMaySynchronouslyCallOnCompleteOrOnError() throws Exception;
void untested_spec314_cancelMayCauseThePublisherToShutdownIfNoOtherSubscriptionExists() throws Exception;
void untested_spec315_cancelMustNotThrowExceptionAndMustSignalOnError() throws Exception;
void untested_spec316_requestMustNotThrowExceptionAndMustOnErrorTheSubscriber() throws Exception;
}

@ -1,34 +0,0 @@
/*
* 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;
/**
* Exception used by the TCK to signal failures.
* May be thrown or signalled through {@link org.reactivestreams.Subscriber#onError(Throwable)}.
*/
public final class TestException extends RuntimeException {
public TestException() {
super("Test Exception: Boom!");
}
}