8297645: Drop the test/jdk/java/net/httpclient/reactivestreams-tck-tests/TckDriver.java test
Reviewed-by: alanb, jpai, michaelm
This commit is contained in:
parent
687fd714bb
commit
6d0fbb2c49
test/jdk/java/net/httpclient
reactivestreams-tck-tests
BodyPublishersConcat.javaBodyPublishersFromPublisher.javaBodyPublishersNoBody.javaBodyPublishersOfByteArray.javaBodyPublishersOfByteArrays.javaBodyPublishersOfFile.javaBodyPublishersOfInputStream.javaBodyPublishersOfSubByteArray.javaBodySubscribersBuffering.javaBodySubscribersDiscarding.javaBodySubscribersFromLineSubscriber.javaBodySubscribersFromSubscriber.javaBodySubscribersMapping.javaBodySubscribersOfByteArray.javaBodySubscribersOfByteArrayConsumer.javaBodySubscribersOfFile.javaBodySubscribersOfInputStream.javaBodySubscribersOfLines.javaBodySubscribersOfPublisher.javaBodySubscribersOfPublisher1.javaBodySubscribersOfPublisherPublisher.javaBodySubscribersOfString.javaBodySubscribersReplacing.javaS.javaSPublisherOfStream.javaSTest.javaTckDriver.java
reactivestreams-tck/org/reactivestreams
FlowAdapters.javaProcessor.javaPublisher.javaSubscriber.javaSubscription.java
example/unicast
AsyncIterablePublisher.javaAsyncSubscriber.javaInfiniteIncrementNumberPublisher.javaNumberIterablePublisher.javaRangePublisher.javaSyncSubscriber.java
tck
IdentityProcessorVerification.javaPublisherVerification.javaSubscriberBlackboxVerification.javaSubscriberWhiteboxVerification.javaTestEnvironment.javaWithHelperPublisher.java
flow
@ -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 stage—which is both a {@link Subscriber}
|
||||
* and a {@link Publisher} and obeys the contracts of both.
|
||||
*
|
||||
* @param <T> the type of element signaled to the {@link Subscriber}
|
||||
* @param <R> the type of element signaled by the {@link Publisher}
|
||||
*/
|
||||
public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
|
||||
}
|
@ -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 needed—but if the outstanding cumulative demand ever becomes Long.MAX_VALUE or more,
|
||||
* it may be treated by the {@link Publisher} as "effectively unbounded".
|
||||
* <p>
|
||||
* Whatever has been requested can be sent by the {@link Publisher} so only signal demand for what can be safely handled.
|
||||
* <p>
|
||||
* A {@link Publisher} can send less than is requested if the stream ends but
|
||||
* then must emit either {@link Subscriber#onError(Throwable)} or {@link Subscriber#onComplete()}.
|
||||
*
|
||||
* @param n the strictly positive number of elements to requests to the upstream {@link Publisher}
|
||||
*/
|
||||
public void request(long n);
|
||||
|
||||
/**
|
||||
* Request the {@link Publisher} to stop sending data and clean up resources.
|
||||
* <p>
|
||||
* Data may still be sent to meet previously signalled demand after calling cancel.
|
||||
*/
|
||||
public void cancel();
|
||||
}
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1245
test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/PublisherVerification.java
1245
test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/PublisherVerification.java
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
92
test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/WithHelperPublisher.java
92
test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/WithHelperPublisher.java
@ -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);
|
||||
}
|
||||
|
||||
}
|
28
test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/Function.java
28
test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/Function.java
@ -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);
|
||||
}
|
||||
}
|
55
test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/NonFatal.java
55
test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/NonFatal.java
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
92
test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/Optional.java
92
test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/flow/support/Optional.java
@ -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 -> emitting or emitting -> 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 -> emitting or emitting -> 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 -> emitting or emitting -> 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 -> emitting or emitting -> 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 -> emitting or emitting -> 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 -> emitting or emitting -> 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 -> emitting or emitting -> 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 -> emitting or emitting -> 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!");
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user