Skip to content

Commit a955a94

Browse files
committed
Review followups and rebase
* Make spec rules linkable (#2.13) and use links in TCK * Verified 1-element-publishers are testable with TCK (skips unsupported tests)
1 parent 1369af3 commit a955a94

File tree

9 files changed

+304
-291
lines changed

9 files changed

+304
-291
lines changed

README.md

+54-54
Large diffs are not rendered by default.

api/src/examples/java/org/reactivestreams/example/multicast/NeverEndingStockStream.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public static synchronized void addHandler(Handler handler) {
3535

3636
public static synchronized void removeHandler(Handler handler) {
3737
// too lazy to do the array handling
38-
HashSet<Handler> set = new HashSet<>(Arrays.asList(INSTANCE.handlers));
38+
HashSet<Handler> set = new HashSet<Handler>(Arrays.asList(INSTANCE.handlers));
3939
set.remove(handler);
4040
INSTANCE.handlers = set.toArray(new Handler[set.size()]);
4141
}

api/src/examples/java/org/reactivestreams/example/multicast/StockPriceSubscriber.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class StockPriceSubscriber implements Subscriber<Stock> {
1313
private final int take;
1414

1515
public StockPriceSubscriber(int bufferSize, int delayPerStock, int take) {
16-
this.buffer = new ArrayBlockingQueue<>(bufferSize);
16+
this.buffer = new ArrayBlockingQueue<Stock>(bufferSize);
1717
this.delayPerStock = delayPerStock;
1818
this.take = take;
1919
}

api/src/examples/java/org/reactivestreams/example/unicast/NumberSubscriberThatHopsThreads.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
class NumberSubscriberThatHopsThreads implements Subscriber<Integer> {
99

1010
final int BUFFER_SIZE = 10;
11-
private final ArrayBlockingQueue<Integer> buffer = new ArrayBlockingQueue<>(BUFFER_SIZE);
11+
private final ArrayBlockingQueue<Integer> buffer = new ArrayBlockingQueue<Integer>(BUFFER_SIZE);
1212
private volatile boolean terminated = false;
1313
private final String token;
1414

tck/src/main/java/org/reactivestreams/tck/Annotations.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ private Annotations() {}
1717
static @interface NotVerified {
1818
}
1919

20+
/**
21+
* Used to mark stochastic tests which MAY yield false positives (pass) can violate the tested rule in some specific scenario.
22+
*/
23+
@Target(ElementType.METHOD)
24+
@Retention(RetentionPolicy.SOURCE)
25+
static @interface Stochastic {
26+
27+
}
28+
2029
/**
2130
* Used to mark tests that MUST pass in all (even very restricted types of) Publishers / Subscribers.
2231
*/
@@ -31,7 +40,7 @@ private Annotations() {}
3140
*/
3241
@Target(ElementType.METHOD)
3342
@Retention(RetentionPolicy.SOURCE)
34-
@interface Additional {
43+
static @interface Additional {
3544
/** Description of situation when it's OK to not pass this test */
3645
String value() default "";
3746

tck/src/main/java/org/reactivestreams/tck/IdentityProcessorVerification.java

+62-43
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
import org.reactivestreams.Publisher;
55
import org.reactivestreams.Subscriber;
66
import org.reactivestreams.Subscription;
7-
import org.reactivestreams.tck.Annotations.NotVerified;
8-
import org.reactivestreams.tck.Annotations.Required;
97
import org.reactivestreams.tck.TestEnvironment.ManualPublisher;
108
import org.reactivestreams.tck.TestEnvironment.ManualSubscriber;
119
import org.reactivestreams.tck.TestEnvironment.ManualSubscriberWithSubscriptionSupport;
@@ -30,21 +28,25 @@ public abstract class IdentityProcessorVerification<T> {
3028

3129
////////////////// END OF DELEGATED TO SPECS //////////////////
3230

33-
34-
private final int testBufferSize;
31+
//
32+
private final int processorBufferSize;
3533

3634
/**
3735
* Test class must specify the expected time it takes for the publisher to
38-
* shut itself down when the the last downstream Subscription is cancelled.
39-
* Used by `publisherSubscribeWhenInShutDownStateMustTriggerOnErrorAndNotOnSubscribe`.
36+
* shut itself down when the the last downstream {@code Subscription} is cancelled.
4037
*/
38+
@SuppressWarnings("unused")
4139
public IdentityProcessorVerification(TestEnvironment env, long publisherShutdownTimeoutMillis) {
4240
this(env, publisherShutdownTimeoutMillis, TestEnvironment.TEST_BUFFER_SIZE);
4341
}
4442

45-
public IdentityProcessorVerification(final TestEnvironment env, long publisherShutdownTimeoutMillis, int testBufferSize) {
43+
/**
44+
* Test class must specify the expected time it takes for the publisher to
45+
* shut itself down when the the last downstream {@code Subscription} is cancelled.
46+
*/
47+
public IdentityProcessorVerification(final TestEnvironment env, long publisherShutdownTimeoutMillis, int processorBufferSize) {
4648
this.env = env;
47-
this.testBufferSize = testBufferSize;
49+
this.processorBufferSize = processorBufferSize;
4850

4951
this.subscriberVerification = new SubscriberVerification<T>(env) {
5052
@Override
@@ -53,14 +55,14 @@ public Subscriber<T> createSubscriber(SubscriberProbe<T> probe) {
5355
}
5456

5557
@Override
56-
public Publisher<T> createHelperPublisher(int elements) {
58+
public Publisher<T> createHelperPublisher(long elements) {
5759
return IdentityProcessorVerification.this.createHelperPublisher(elements);
5860
}
5961
};
6062

6163
publisherVerification = new PublisherVerification<T>(env, publisherShutdownTimeoutMillis) {
6264
@Override
63-
public Publisher<T> createPublisher(int elements) {
65+
public Publisher<T> createPublisher(long elements) {
6466
return IdentityProcessorVerification.this.createPublisher(elements);
6567
}
6668

@@ -73,24 +75,31 @@ public Publisher<T> createCompletedStatePublisher() {
7375
public Publisher<T> createErrorStatePublisher() {
7476
return IdentityProcessorVerification.this.createErrorStatePublisher();
7577
}
78+
79+
@Override
80+
public long maxElementsFromPublisher() {
81+
return IdentityProcessorVerification.this.maxElementsFromPublisher();
82+
}
7683
};
7784
}
7885

7986
/**
8087
* This is the main method you must implement in your test incarnation.
8188
* It must create a Publisher, which simply forwards all stream elements from its upstream
8289
* to its downstream. It must be able to internally buffer the given number of elements.
90+
*
91+
* @param bufferSize number of elements the processor is required to be able to buffer
8392
*/
8493
public abstract Processor<T, T> createIdentityProcessor(int bufferSize);
8594

8695
/**
8796
* Helper method required for running the Publisher rules against a Publisher.
8897
* It must create a Publisher for a stream with exactly the given number of elements.
89-
* If `elements` is zero the produced stream must be infinite.
98+
* If {@code elements} is {@code Long.MAX_VALUE} the produced stream must be infinite.
9099
* The stream must not produce the same element twice (in case of an infinite stream this requirement
91100
* is relaxed to only apply to the elements that are actually requested during all tests).
92101
*/
93-
public abstract Publisher<T> createHelperPublisher(int elements);
102+
public abstract Publisher<T> createHelperPublisher(long elements);
94103

95104
/**
96105
* Return a Publisher in {@code completed} state in order to run additional tests on it,
@@ -104,30 +113,38 @@ public Publisher<T> createErrorStatePublisher() {
104113
*/
105114
public abstract Publisher<T> createErrorStatePublisher();
106115

116+
/**
117+
* Override and return lower value if your Publisher is only able to produce a set number of elements.
118+
* For example, if it is designed to return at-most-one element, return {@code 1} from this method.
119+
*/
120+
public long maxElementsFromPublisher() {
121+
// general idea is to skip tests that we are unable to run on a given publisher (if it can signal less than we need for a test)
122+
// see: https://github.com/reactive-streams/reactive-streams/issues/87 for details
123+
return Long.MAX_VALUE;
124+
}
125+
107126
////////////////////// TEST ENV CLEANUP /////////////////////////////////////
108127

109128
@BeforeMethod
110129
public void setUp() throws Exception {
111-
env.clearAsyncErrors();
130+
publisherVerification.setUp();
131+
subscriberVerification.setUp();
112132
}
113133

114134
////////////////////// PUBLISHER RULES VERIFICATION ///////////////////////////
115-
// 4.1
116-
// A Processor represents a processing stage—which is both a Subscriber and a Publisher
117-
// It MUST obey the contracts of both [1]
118-
119135

120-
// A Publisher
121-
// must obey all Publisher rules on its producing side
122-
public Publisher<T> createPublisher(int elements) {
123-
Processor<T, T> processor = createIdentityProcessor(testBufferSize);
136+
// A Processor
137+
// must obey all Publisher rules on its publishing side
138+
public Publisher<T> createPublisher(long elements) {
139+
Processor<T, T> processor = createIdentityProcessor(processorBufferSize);
124140
Publisher<T> pub = createHelperPublisher(elements);
125141
pub.subscribe(processor);
126142
return processor; // we run the PublisherVerification against this
127143
}
128144

129145
/////////////////////// DELEGATED TESTS, A PROCESSOR "IS A" PUBLISHER //////////////////////
130-
146+
// Verifies rule: https://github.com/reactive-streams/reactive-streams#4.1
147+
131148
@Test
132149
public void createPublisher3MustProduceAStreamOfExactly3Elements() throws Throwable {
133150
publisherVerification.createPublisher3MustProduceAStreamOfExactly3Elements();
@@ -144,7 +161,7 @@ public void spec102_maySignalLessThanRequestedAndTerminateSubscription() throws
144161
}
145162

146163
@Test
147-
public void spec103_mustSignalOnMethodsSequentially() throws Exception {
164+
public void spec103_mustSignalOnMethodsSequentially() throws Throwable {
148165
publisherVerification.spec103_mustSignalOnMethodsSequentially();
149166
}
150167

@@ -213,11 +230,11 @@ public void spec313_cancelMustMakeThePublisherEventuallyDropAllReferencesToTheSu
213230
publisherVerification.spec313_cancelMustMakeThePublisherEventuallyDropAllReferencesToTheSubscriber();
214231
}
215232

216-
// A Processor
217-
// must call `onError` on all its subscribers if it encounters a non-recoverable error
233+
// Verifies rule: https://github.com/reactive-streams/reactive-streams#1.4
234+
// for multiple subscribers
218235
@Test
219236
public void spec104_mustCallOnErrorOnAllItsSubscribersIfItEncountersANonRecoverableError() throws Exception {
220-
new TestSetup(env, testBufferSize) {{
237+
new TestSetup(env, processorBufferSize) {{
221238
ManualSubscriberWithErrorCollection<T> sub1 = new ManualSubscriberWithErrorCollection<T>(env);
222239
env.subscribe(processor, sub1);
223240
ManualSubscriberWithErrorCollection<T> sub2 = new ManualSubscriberWithErrorCollection<T>(env);
@@ -242,11 +259,12 @@ public void spec104_mustCallOnErrorOnAllItsSubscribersIfItEncountersANonRecovera
242259
}
243260

244261
////////////////////// SUBSCRIBER RULES VERIFICATION ///////////////////////////
262+
// Verifies rule: https://github.com/reactive-streams/reactive-streams#4.1
245263

246264
// A Processor
247265
// must obey all Subscriber rules on its consuming side
248266
public Subscriber<T> createSubscriber(final SubscriberVerification.SubscriberProbe<T> probe) {
249-
Processor<T, T> processor = createIdentityProcessor(testBufferSize);
267+
Processor<T, T> processor = createIdentityProcessor(processorBufferSize);
250268
processor.subscribe(
251269
new Subscriber<T>() {
252270
public void onSubscribe(final Subscription subscription) {
@@ -288,7 +306,7 @@ public void onError(Throwable cause) {
288306
// must cancel its upstream Subscription if its last downstream Subscription has been cancelled
289307
@Test
290308
public void mustCancelItsUpstreamSubscriptionIfItsLastDownstreamSubscriptionHasBeenCancelled() throws Exception {
291-
new TestSetup(env, testBufferSize) {{
309+
new TestSetup(env, processorBufferSize) {{
292310
ManualSubscriber<T> sub = newSubscriber();
293311
sub.cancel();
294312
expectCancelling();
@@ -301,7 +319,7 @@ public void mustCancelItsUpstreamSubscriptionIfItsLastDownstreamSubscriptionHasB
301319
// must immediately pass on `onError` events received from its upstream to its downstream
302320
@Test
303321
public void mustImmediatelyPassOnOnErrorEventsReceivedFromItsUpstreamToItsDownstream() throws Exception {
304-
new TestSetup(env, testBufferSize) {{
322+
new TestSetup(env, processorBufferSize) {{
305323
ManualSubscriberWithErrorCollection<T> sub = new ManualSubscriberWithErrorCollection<T>(env);
306324
env.subscribe(processor, sub);
307325

@@ -317,12 +335,12 @@ public void mustImmediatelyPassOnOnErrorEventsReceivedFromItsUpstreamToItsDownst
317335
// must be prepared to receive incoming elements from its upstream even if a downstream subscriber has not requested anything yet
318336
@Test
319337
public void mustBePreparedToReceiveIncomingElementsFromItsUpstreamEvenIfADownstreamSubscriberHasNotRequestedYet() throws Exception {
320-
new TestSetup(env, testBufferSize) {{
338+
new TestSetup(env, processorBufferSize) {{
321339
ManualSubscriber<T> sub = newSubscriber();
322340
final T x = sendNextTFromUpstream();
323341
sub.expectNone(50);
324342
final T y = sendNextTFromUpstream();
325-
sub.expectNone(50);
343+
sub.expectNone(50);
326344

327345
sub.request(2);
328346
sub.expectNext(x);
@@ -337,6 +355,7 @@ public void mustBePreparedToReceiveIncomingElementsFromItsUpstreamEvenIfADownstr
337355
}
338356

339357
/////////////////////// DELEGATED TESTS, A PROCESSOR "IS A" SUBSCRIBER //////////////////////
358+
// Verifies rule: https://github.com/reactive-streams/reactive-streams#4.1
340359

341360
@Test
342361
public void exerciseHappyPath() throws Throwable {
@@ -520,10 +539,11 @@ public void spec317_mustSupportAPendingElementCountUpToLongMaxValue() throws Thr
520539

521540
/////////////////////// ADDITIONAL "COROLLARY" TESTS //////////////////////
522541

523-
// trigger `requestFromUpstream` for elements that have been requested 'long ago'
542+
// A Processor
543+
// must trigger `requestFromUpstream` for elements that have been requested 'long ago'
524544
@Test
525545
public void mustRequestFromUpstreamForElementsThatHaveBeenRequestedLongAgo() throws Exception {
526-
new TestSetup(env, testBufferSize) {{
546+
new TestSetup(env, processorBufferSize) {{
527547
ManualSubscriber<T> sub1 = newSubscriber();
528548
sub1.request(20);
529549

@@ -566,19 +586,20 @@ public void mustRequestFromUpstreamForElementsThatHaveBeenRequestedLongAgo() thr
566586
}};
567587
}
568588

569-
// unblock the stream if a 'blocking' subscription has been cancelled
589+
// A Processor
590+
// must unblock the stream if a 'blocking' subscription has been cancelled
570591
@Test
571592
@SuppressWarnings("unchecked")
572593
public void mustUnblockTheStreamIfABlockingSubscriptionHasBeenCancelled() throws InterruptedException {
573-
new TestSetup(env, testBufferSize) {{
594+
new TestSetup(env, processorBufferSize) {{
574595
ManualSubscriber<T> sub1 = newSubscriber();
575596
ManualSubscriber<T> sub2 = newSubscriber();
576597

577-
sub1.request(testBufferSize + 1);
598+
sub1.request(processorBufferSize + 1);
578599
long pending = 0;
579600
int sent = 0;
580-
final T[] tees = (T[]) new Object[testBufferSize];
581-
while (sent < testBufferSize) {
601+
final T[] tees = (T[]) new Object[processorBufferSize];
602+
while (sent < processorBufferSize) {
582603
if (pending == 0) {
583604
pending = expectRequest();
584605
}
@@ -588,7 +609,7 @@ public void mustUnblockTheStreamIfABlockingSubscriptionHasBeenCancelled() throws
588609
pending -= 1;
589610
}
590611

591-
expectNoRequest(); // because we only have buffer size testBufferSize and sub2 hasn't seen the first value yet
612+
expectNoRequest(); // because we only have buffer size processorBufferSize and sub2 hasn't seen the first value yet
592613
sub2.cancel(); // must "unblock"
593614

594615
expectRequest();
@@ -606,16 +627,14 @@ public void mustUnblockTheStreamIfABlockingSubscriptionHasBeenCancelled() throws
606627
/////////////////////// TEST INFRASTRUCTURE //////////////////////
607628

608629
public abstract class TestSetup extends ManualPublisher<T> {
609-
private ManualSubscriber<T> tees; // gives us access to an infinite stream of T values
630+
final private ManualSubscriber<T> tees; // gives us access to an infinite stream of T values
610631
private Set<T> seenTees = new HashSet<T>();
611632

612633
final Processor<T, T> processor;
613-
final int testBufferSize;
614634

615635
public TestSetup(TestEnvironment env, int testBufferSize) throws InterruptedException {
616636
super(env);
617-
this.testBufferSize = testBufferSize;
618-
tees = env.newManualSubscriber(createHelperPublisher(0));
637+
tees = env.newManualSubscriber(createHelperPublisher(Long.MAX_VALUE));
619638
processor = createIdentityProcessor(testBufferSize);
620639
subscribe(processor);
621640
}

0 commit comments

Comments
 (0)