Skip to content

Add Javadoc explanation to the TCK test methods about what they do #356

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 28, 2017

Conversation

akarnokd
Copy link
Contributor

@akarnokd akarnokd commented Apr 4, 2017

This PR adds some explanation about what each PublisherVerificationRules methods do in respect to their expectation and request/emission pattern that could help tracking down the errors in the Publisher under test and tries to hint about what could be wrong in that Publisher.

I run into situations when there were errors in my Publishers but the error response didn't help much and had to look into what the test actually did (in C# it was even harder due to lack of nicely linked TCK sources) and single-stepping through is cumbersome because of the several layers of test equipment within the TCK.

Let me address some concerns upfront:

Why PublisherVerificationRules and not PublisherVerification?

Mainly because the Javadoc would need duplication for IdentityProcessorVerification which is double maintenance work.

Why exposing the implementation details?

Knowing the request pattern or the expected emission pattern without digging into the TCK sources helps identify mistakes in the Publisher much faster, if not allow separate unit tests to be built with the same properties and debug the Publisher through it.

What about SubscriberWhiteboxVerificationRules?

If there is interest for such kind of documentation enhacements, I can do that after this.

@smaldini
Copy link
Contributor

Nice suff , wdyt @reactive-streams/contributors

@DougLea
Copy link
Contributor

DougLea commented Apr 11, 2017

Looks OK to me.

Copy link
Contributor

@viktorklang viktorklang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the documentation, @akarnokd!

I trust you have generated the docs to verify the formatting etc of the outputted html?

* Asks for a {@code Publisher} that should emit exaclty one item and complete (both within a
* timeout specified by {@link org.reactivestreams.tck.TestEnvironment#defaultTimeoutMillis()}).
* <p>The test is not executed if {@link org.reactivestreams.tck.PublisherVerification#maxElementsFromPublisher()} returns zero.
* <p>This test mostly ensures that the {@code Publisher} implementation is actually operational.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence is very vague, don't you agree?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sentence is totally fine. If the non-empty Publisher fails this test, there is a fundamental mistake in its implementation. See the hints below this javadoc line.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant was that "mostly ensures" and "actually operational" are vague. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed those two sentences.

void required_createPublisher1MustProduceAStreamOfExactly1Element() throws Throwable;
/**
* Asks for a {@code Publisher} that should emit exaclty three items and complete (all within a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exactly

* Asks for a {@code Publisher} that should emit exaclty 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>This test mostly ensures that the {@code Publisher} implementation is actually operational.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too vague IMO. (Doesn't inspire confidence)

void optional_spec105_emptyStreamMustTerminateBySignallingOnComplete() throws Throwable;
/**
* Currently, this test is skipped because it is unclear this rule can be effectively checked
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great explanation, thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

/**
* Asks for an empty {@code Publisher} and calls and verifies if {@code onSubscribe} was called before any calls
* to the other {@code onXXX} methods.
* <p>Note that this test doesn't request anything yet an {@code onNext} is not considered as a failure.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence reads weirdly, clarify?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is supposed to mean that in case the Publisher doesn't respect backpressure, this test will still pass. Otherwise the implementation is odd because without requesting, there is no point in verifying onNext has been called after an onSubscribe.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is the "anything yet an" part, is there a comma or something missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll rephrase it with "anything, however, an ".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

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 on what constitutes as heavy computation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a definition for "heavy computation" part of the spec now in the glossary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about rewording as "and otherwise there is no common agreemend how to detect such heavy computation reliably."

* <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
* Thread.currentThread().getUncaughtExceptionHandler() for example).
* <p>Note also that requesting and emission may happen concurrently and honoring this rule may require extra serialization within
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"serialization" already has a bit of definition baggage in the JDK. I'd use "coordination" instead.

@akarnokd
Copy link
Contributor Author

I trust you have generated the docs to verify the formatting etc of the outputted html?

No, just looked at the result from the Eclipse's Javadoc view.

@akarnokd
Copy link
Contributor Author

Fixed spelling errors, clarified a couple of sentences.

@viktorklang
Copy link
Contributor

@ktoso Yay or nay?

@ktoso
Copy link
Contributor

ktoso commented Apr 24, 2017

Moved continents in the last weeks, should be online this week and back to reviewing / contributing, thanks for your patience. Reading this one.

@viktorklang
Copy link
Contributor

@ktoso Thanks Konrad, it's not the end of the world if we need to postpone the 1.0.1 another week.

@ktoso
Copy link
Contributor

ktoso commented Apr 25, 2017

Started reviewing, I think not all comments are accurate, I will have to really focus on this one - review should come up during my day today I hope.

@ktoso
Copy link
Contributor

ktoso commented Apr 26, 2017

More comments coming now in a batch, however I think since if we're adding such comments, we should also transform the // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#1.1 comments into those javadoc comments so they link back to the specification.

How about adding that as first sentence in all of those javadoc comments (then a

)?

@akarnokd
Copy link
Contributor Author

How about adding that as first sentence in all of those javadoc comments

I find those rule explanations not really helpful when considering the concrete unit tests. Perhaps we can link them as a second or later sentence.

@ktoso
Copy link
Contributor

ktoso commented Apr 26, 2017

It is the spec after all, the source of truth - we should link to it. People should not just rely on the TCK comments, but get an understanding of the spec in which these comments can (and will) help :-)

You're right about them not being the most important thing though when reading the comments - how about we add them as last sentence - with a

so it's nicely separated etc.

// finishing review now, overall very good - some nitpicks

Copy link
Contributor

@ktoso ktoso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments, but in general I think this will be an useful addition. Have to call it a night now - will be back tomorrow reviewing the other things, thanks!

* 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 (likely with a timeout error), the following could be checked within the {@code Publisher} implementation:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's avoid "likely with a timeout" and other phrasings like that IMHO, we simply provide hints of what to check - it could fail for different reasons, just being completely wrong and emitting values not asked for etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay.

* <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>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The points are good, but focusing on the timeout case. Please add a point about "that it does not emit more than allowed elements (exactly one)". Trivial, but since we're adding such text, it should be included (many of the points are "trivial", so if we list them, we should list all that matter).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, but my experience is that it is rarely the case of emitting more than the requested amount but instead failing to emit or losing signal somewhere in the chain.

/**
* 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the same style of

in javadoc as the rest of the repo, so https://github.com/reactive-streams/reactive-streams-jvm/blob/master/api/src/main/java/org/reactivestreams/Subscriber.java#L18 on an empty like like that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay

* <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)} after consuming an item,</li>
* <li>if the {@code Publisher} implementation is able to emit an {@code onComplete} without requests.</li>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hint is not correct. There is a request(1) issued before expecting the completion

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was in an odd position because it was declared by one of you that onComplete must happen without requesting yet this test requests. Thus two options here: 1) leave this and fix the test or 2) change the wording and have a rule inconsistency.

* <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>
* </ul>
* @throws Throwable allow arbitrary exceptions to be thrown by the implementation of this test
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this adds much useful information, smells like documenting "what a getter is" to me, omit on all methods? It's a pretty known pattern to allow test methods to throw whatever they want

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay.

void untested_spec304_requestShouldNotPerformHeavyComputations() throws Exception;
/**
* Currently, this test is skipped because there is no common agreement on what constitutes as heavy computation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the phrasing of "no common agreement on what ... is heavy", isn't it more about "because there is no reliable agreed upon way to detect a heavy computation"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we could actually make that length smaller - not that it matters much in practice I guess...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I proposed that many tests should actually try multiple lengths and there is an example PR of this proposal. In practice, a chain of operators may behave differently when the Publisher is known to be of length 0 or 1. 2+ is handled with the same algorithm but some peculiar concurrency bugs may appear above 10-20 because of buffers spanning multiple cache lines...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will review that one next then

* <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
* Thread.currentThread().getUncaughtExceptionHandler() for example).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{@code Thread.....}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay.

* in general.
* </ul>
* @throws Throwable allow arbitrary exceptions to be thrown by the implementation of this test
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good hints on this one

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"and does not keep spinning attempting to fulfil the MAX_VALUE demand by some means"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay.

* <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
* Thread.currentThread().getUncaughtExceptionHandler() for example).
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added {@code } here as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good

@akarnokd
Copy link
Contributor Author

Changed the javadoc for most of the feedback from @ktoso.

@ktoso
Copy link
Contributor

ktoso commented Apr 27, 2017 via email

* <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>
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please do add links to the spec in all tests as I mentioned previously.
We can then also delete the // Verifies rule: https://github.com/reactive-streams/reactive-streams-jvm#3.2 inline comments from the impls

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Copy link
Contributor

@ktoso ktoso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM now, thanks.
Will you do the same for the Subscriber side as well so we have consistency in the docs?

@akarnokd
Copy link
Contributor Author

Yes.

@viktorklang viktorklang merged commit 241dbda into reactive-streams:master Apr 28, 2017
@viktorklang
Copy link
Contributor

Merged! Massive thanks @akarnokd! 👍

@viktorklang viktorklang added this to the 1.0.1 milestone Jun 26, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants