Skip to content

Commit 7030855

Browse files
committed
=tck general tck/readme.md cleanup so it matches current code / spec
Resolves reactive-streams#99 Depends on reactive-streams#241
1 parent bca27f5 commit 7030855

File tree

2 files changed

+95
-44
lines changed

2 files changed

+95
-44
lines changed

tck/README.md

+92-42
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ The TCK is implemented using **plain Java (1.6)** and **TestNG** tests, and shou
1010
The TCK aims to cover all rules defined in the Specification, however for some rules outlined in the Specification it is
1111
not possible (or viable) to construct automated tests, thus the TCK does not claim to completely verify an implementation, however it is very helpful and is able to validate the most important rules.
1212

13-
The TCK is split up into 4 files JUnit 4 test classes which should be extended by implementers, providing their `Publisher` / `Subscriber` implementations for the test harness to validate them. The tests are split in the following way:
13+
The TCK is split up into 4 TestNG test classes which should be extended by implementers, providing their `Publisher` / `Subscriber` implementations for the test harness to validate them. The tests are split in the following way:
1414

1515
* `PublisherVerification`
16-
* `SubscriberBlackboxVerification`
1716
* `SubscriberWhiteboxVerification`
17+
* `SubscriberBlackboxVerification`
1818
* `IdentityProcessorVerification`
1919

2020
The next sections include examples on how these can be used and describe the various configuration options.
@@ -32,53 +32,65 @@ The TCK is provided as binary artifact on [Maven Central](http://search.maven.or
3232

3333
Please refer to the [Reactive Streams Specification](https://github.com/reactive-streams/reactive-streams) for the current latest version number. Make sure that the API and TCK dependency versions are equal.
3434

35-
### Types of tests
35+
### Test method naming convention
3636

3737
Since the TCK is aimed at Reactive Stream implementers, looking into the sources of the TCK is well expected,
3838
and should help during a libraries implementation cycle.
3939

4040
In order to make mapping between test cases and Specification rules easier, each test case covering a specific
41-
Specification rule abides the following naming convention: `spec###_DESC` where:
41+
Specification rule abides the following naming convention: `TYPE_spec###_DESC` where:
4242

43+
* `TYPE` is one of: [#type-required](required), [#type-optional](optional), [#type-stochastic](stochastic) or [#type-untested](untested) which describe if this test is covering a Rule that MUST or SHOULD be implemented. The specific words are explained in detail below.
4344
* `###` is the Rule number (`1.xx` Rules are about Publishers, `2.xx` Rules are about Subscribers etc.)
4445
* `DESC` is a short explanation of what exactly is being tested in this test case, as sometimes one Rule may have multiple test cases in order to cover the entire Rule.
4546

47+
Here is an example test method signature:
48+
4649
```java
4750
// Verifies rule: https://github.com/reactive-streams/reactive-streams#1.1
48-
@Test public void required_spec101_subscriptionRequestMustResultInTheCorrectNumberOfProducedElements() throws Throwable
51+
@Test public void required_spec101_subscriptionRequestMustResultInTheCorrectNumberOfProducedElements() throws Throwable {
4952
// ...
5053
}
5154
```
5255

53-
The prefixes of the names of the test methods are used in order to signify the character of the test. For example, these are the kinds of prefixes you may find:
54-
"`required_`", "`optional_`", "`stochastic_`", "`untested_`".
55-
56-
Explanations:
56+
#### Test types explained:
5757

5858
```java
5959
@Test public void required_spec101_subscriptionRequestMustResultInTheCorrectNumberOfProducedElements() throws Throwable
6060
```
6161

62-
... means that this test case is a hard requirement, it covers a *MUST* or *MUST NOT* Rule of the Specification.
62+
<a name="type-required"></a>
63+
The `required_` means that this test case is a hard requirement, it covers a *MUST* or *MUST NOT* Rule of the Specification.
6364

6465

6566
```java
6667
@Test public void optional_spec104_mustSignalOnErrorWhenFails() throws Throwable
6768
```
6869

69-
... means that this test case is optional, it covers a *MAY* or *SHOULD* Rule of the Specification.
70+
<a name="type-optional"></a>
71+
The `optional_` means that this test case is optional, it covers a *MAY* or *SHOULD* Rule of the Specification.
72+
This prefix is also used if more configuration is needed in order to run it, e.g.
73+
`@Additional(implement = "createFailedPublisher") @Test` signals the implementer that in order to run this test
74+
one has to implement the `Publisher<T> createFailedPublisher()` method.
7075

7176
```java
7277
@Test public void stochastic_spec103_mustSignalOnMethodsSequentially() throws Throwable
7378
```
7479

75-
... means that the Rule is either racy, and/or inherently hard to verify without heavy modification of the tested implementation. 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.
80+
<a name="type-stochastic"></a>
81+
The `stochastic_` means that the Rule is either racy, and/or inherently hard to verify without heavy modification of the tested implementation.
82+
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.
7683

7784
```java
7885
@Test public void untested_spec106_mustConsiderSubscriptionCancelledAfterOnErrorOrOnCompleteHasBeenCalled() throws Throwable
7986
```
8087

81-
... means that the test case is not implemented, either because it is inherently hard to verify (e.g. Rules which use the wording "*SHOULD consider X as Y*") or have not been implemented yet (though we hope we have implemented all we could!). Such tests will show up in your test runs as `SKIPPED`, with a message pointing out that the TCK is unable to validate this Rule. We would be delighted if you can figure out a way to deterministically test Rules, which have been marked with this prefix – pull requests are very welcome!
88+
<a name="type-untested"></a>
89+
The `untested_` means that the test case is not implemented, either because it is inherently hard to verify (e.g. Rules which use
90+
the wording "*SHOULD consider X as Y*") or have not been implemented yet (though we hope we have implemented all we
91+
could!). Such tests will show up in your test runs as `SKIPPED`, with a message pointing out that the TCK is unable to
92+
validate this Rule. We would be delighted if you can figure out a way to deterministically test Rules, which have been
93+
marked with this prefix – pull requests are very welcome!
8294

8395
### Test isolation
8496

@@ -188,14 +200,17 @@ public class RangePublisherTest extends PublisherVerification<Integer> {
188200

189201
Notable configuration options include:
190202

191-
* `maxElementsFromPublisher` – which should only be overridden in case the Publisher under test is not able to provide arbitrary length streams, e.g. it's wrapping a `Future<T>` and thus can only publish up to 1 element. In such case you should return `1` from this method. It will cause all tests which require more elements in order to validate a certain Rule to be skipped,
192-
* `boundedDepthOfOnNextAndRequestRecursion` – which should only be overridden in case of synchronous Publishers. This number will be used to validate if a
193-
`Subscription` actually solves the "unbounded recursion" problem (Rule 3.3).
203+
* `maxElementsFromPublisher` – which should only be overridden in case the Publisher under test is not able to provide
204+
arbitrary length streams, e.g. it's wrapping a `Future<T>` and thus can only publish up to 1 element. In such case you
205+
should return `1` from this method. It will cause all tests which require more elements in order to validate a certain
206+
Rule to be skipped,
207+
* `boundedDepthOfOnNextAndRequestRecursion` – which should only be overridden in case of synchronous Publishers.
208+
This number will be used to validate if a `Subscription` actually solves the "unbounded recursion" problem (Rule 3.3).
194209

195210
### Timeout configuration
196211
Publisher tests make use of two kinds of timeouts, one is the `defaultTimeoutMillis` which corresponds to all methods used
197212
within the TCK which await for something to happen. The other timeout is `publisherReferenceGCTimeoutMillis` which is only used in order to verify
198-
[Rule 3.13](https://github.com/reactive-streams/reactive-streams#3.13) which defines that subscriber references MUST be dropped
213+
[Rule 3.13](https://github.com/reactive-streams/reactive-streams#3.13) which defines that subscriber references dropped
199214
by the Publisher.
200215

201216
In order to configure these timeouts (for example when running on a slow continious integtation machine), you can either:
@@ -260,7 +275,7 @@ The `createElement` method MAY be called from multiple
260275
threads, so in case of more complicated implementations, please be aware of this fact.
261276

262277
**Very Advanced**: While we do not expect many implementations having to do so, it is possible to take full control of the `Publisher`
263-
which will be driving the TCKs test. You can do this by implementing the `createHelperPublisher` method in which you can implement your
278+
which will be driving the TCKs test. This can be achieved by implementing the `createHelperPublisher` method in which you can implement your
264279
own Publisher which will then be used by the TCK to drive your Subscriber tests:
265280

266281
```java
@@ -309,8 +324,7 @@ Based on experiences so far implementing the `SubscriberPuppet` is non-trivial a
309324
We keep the whitebox verification, as it is tremendously useful in the `ProcessorVerification`, where the Puppet is implemented within the TCK and injected to the tests.
310325
We do not expect all implementations to make use of the plain `SubscriberWhiteboxVerification`, using the `SubscriberBlackboxVerification` instead.
311326

312-
For the simplest possible (and most common) `Subscriber` implementation using the whitebox verification boils down to
313-
exteding (or delegating to) your implementation with additionally signalling and registering the test probe, as shown in the below example:
327+
Instrumenting a simple synchronous `Subscriber` should look similar to following example, in which we extend the implementation we want to test, and inject the required `probe` triggering where needed:
314328

315329
```java
316330
package com.example.streams;
@@ -327,24 +341,21 @@ public class MySubscriberWhiteboxVerificationTest extends SubscriberWhiteboxVeri
327341
super(new TestEnvironment());
328342
}
329343

330-
// The implementation under test is "SyncSubscriber":
331-
// class SyncSubscriber<T> extends Subscriber<T> { /* ... */ }
332-
333344
@Override
334345
public Subscriber<Integer> createSubscriber(final WhiteboxSubscriberProbe<Integer> probe) {
335-
// in order to test the SyncSubscriber we must instrument it by extending it,
336-
// and calling the WhiteboxSubscriberProbe in all of the Subscribers methods:
337-
return new SyncSubscriber<Integer>() {
346+
347+
// return YOUR subscriber under-test, with additional WhiteboxSubscriberProbe instrumentation
348+
return new Subscriber<Integer>() {
349+
338350
@Override
339351
public void onSubscribe(final Subscription s) {
340-
super.onSubscribe(s);
341-
342-
// register a successful subscription, and create a Puppet,
343-
// for the WhiteboxVerification to be able to drive its tests:
352+
// in addition to normal Subscriber work that you're testing,
353+
// register a SubscriberPuppet, to give the TCK control over demand generation and cancelling
344354
probe.registerOnSubscribe(new SubscriberPuppet() {
355+
345356
@Override
346-
public void triggerRequest(long elements) {
347-
s.request(elements);
357+
public void triggerRequest(long n) {
358+
s.request(n);
348359
}
349360

350361
@Override
@@ -356,18 +367,21 @@ public class MySubscriberWhiteboxVerificationTest extends SubscriberWhiteboxVeri
356367

357368
@Override
358369
public void onNext(Integer element) {
370+
// in addition to normal Subscriber work that you're testing, register onNext with the probe
359371
super.onNext(element);
360372
probe.registerOnNext(element);
361373
}
362374

363375
@Override
364376
public void onError(Throwable cause) {
377+
// in addition to normal Subscriber work that you're testing, register onError with the probe
365378
super.onError(cause);
366379
probe.registerOnError(cause);
367380
}
368381

369382
@Override
370383
public void onComplete() {
384+
// in addition to normal Subscriber work that you're testing, register onComplete with the probe
371385
super.onComplete();
372386
probe.registerOnComplete();
373387
}
@@ -400,7 +414,7 @@ public class MySubscriberTest extends BlackboxSubscriberVerification<Integer> {
400414
public static final long DEFAULT_TIMEOUT_MILLIS = 300L;
401415

402416
public RangePublisherTest() {
403-
super(new MySubscriberTest(DEFAULT_TIMEOUT_MILLIS));
417+
super(new TestEnvironment(DEFAULT_TIMEOUT_MILLIS));
404418
}
405419

406420
// ...
@@ -453,10 +467,13 @@ public class MyIdentityProcessorVerificationTest extends IdentityProcessorVerifi
453467

454468
@Override
455469
public Publisher<Integer> createFailedPublisher() {
470+
// return Publisher that just signals onError instead of null to run additional tests
471+
// see this methods JavaDocs for more details on how the returned Publisher should work.
456472
return null;
457473
}
458474

459475
// OPTIONAL CONFIGURATION OVERRIDES
476+
// override these only if you understand why you'd need to do so for your impl.
460477

461478
@Override
462479
public long maxElementsFromPublisher() {
@@ -482,30 +499,63 @@ to skip tests inherited from the TCK's base classes:
482499
```java
483500
package com.example.streams;
484501

502+
import org.reactivestreams.Processor;
503+
import org.reactivestreams.Publisher;
504+
import org.reactivestreams.Subscriber;
505+
import org.reactivestreams.Subscription;
485506
import org.reactivestreams.tck.IdentityProcessorVerification;
507+
import org.reactivestreams.tck.TestEnvironment;
508+
import org.testng.annotations.AfterClass;
509+
import org.testng.annotations.BeforeClass;
510+
511+
import java.util.concurrent.ExecutorService;
512+
import java.util.concurrent.Executors;
486513

487-
public class SkippingIdentityProcessorTest extends IdentityProcessorVerification<Integer> {
514+
public class MyIdentityProcessorTest extends IdentityProcessorVerification<Integer> {
515+
516+
private ExecutorService e;
517+
518+
@BeforeClass
519+
public void before() { e = Executors.newFixedThreadPool(4); }
520+
521+
@AfterClass
522+
public void after() { if (e != null) e.shutdown(); }
488523

489524
public SkippingIdentityProcessorTest() {
490-
super(new TestEnvironment(500, true), 1000);
525+
super(new TestEnvironment());
526+
}
527+
528+
@Override
529+
public ExecutorService publisherExecutorService() {
530+
return e;
531+
}
532+
533+
@Override
534+
public Integer createElement(int element) {
535+
return element;
491536
}
492537

493538
@Override
494539
public Processor<Integer, Integer> createIdentityProcessor(int bufferSize) {
495-
return /* ... */;
540+
return new MyProcessor<Integer, Integer>(buffer Size); // return your implementation
496541
}
497542

498-
@Override // override the test method, and provide a reason on why you're doing so in the notVerified() message
499-
public void spec999_mustDoVeryCrazyThings() throws Throwable {
500-
notVerified("Unable to implement test because ...");
543+
@Override
544+
public Publisher<Integer> createFailedPublisher() {
545+
return null; // returning null means that the tests validating a failed publisher will be skipped
501546
}
502547

503548
}
504549
```
505550

506-
## Upgrade story
551+
## Upgrading the TCK to newer versions
552+
While we do not expect the Reactive Streams specification to change in the forseeable future,
553+
it *may happen* that some semantics may need to change at some point. In this case you should expect test
554+
methods being phased out in terms of deprecation or removal, new tests may also be added over time.
507555

508-
**TODO** - What is our story about updating the TCK? How do we make sure that implementations don't accidentally miss some change in the spec, if the TCK is unable to fail verify the new behavior? Comments are very welcome, discussion about this is under-way in [Issue #99 – TCK Upgrade Story](https://github.com/reactive-streams/reactive-streams/issues/99).
556+
In general this should not be of much concern, unless overriding test methods in your test suite.
557+
We ask implementers who find the need of overriding provided test methods to reach out via opening tickets
558+
on the `reactive-streams/reactive-streams-jvm` project, so we can discuss the use case and, most likely, improve the TCK.
509559

510560
## Using the TCK from other languages
511561

@@ -542,4 +592,4 @@ class IterablePublisherTest(env: TestEnvironment, publisherShutdownTimeout: Long
542592

543593
Contributions to this document are very welcome!
544594

545-
If you're implementing reactive streams using the TCK in some language, please feel free to share an example on how to best use it from your language of choice.
595+
When implementing Reactive Streams using the TCK in some language, please feel free to share an example on how to best use it from your language of choice.

tck/src/test/java/org/reactivestreams/tck/IdentityProcessorVerificationDelegationTest.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package org.reactivestreams.tck;
22

3-
import org.testng.AssertJUnit;
43
import org.testng.annotations.Test;
54

65
import java.lang.reflect.Method;
76
import java.util.ArrayList;
87
import java.util.List;
98

9+
import static org.testng.AssertJUnit.assertTrue;
10+
1011
/**
1112
* The {@link org.reactivestreams.tck.IdentityProcessorVerification} must also run all tests from
1213
* {@link org.reactivestreams.tck.PublisherVerification} and {@link org.reactivestreams.tck.SubscriberWhiteboxVerification}.
@@ -52,7 +53,7 @@ private void assertSuiteDelegatedAllTests(Class<?> delegatingFrom, List<String>
5253
delegatingFrom,
5354
targetTest, targetClass.getSimpleName(), targetTest);
5455

55-
AssertJUnit.assertTrue(msg, testsInclude(allTests, targetTest));
56+
assertTrue(msg, testsInclude(allTests, targetTest));
5657
}
5758
}
5859

0 commit comments

Comments
 (0)