Search in sources :

Example 1 with ManualSubscriber

use of org.reactivestreams.tck.TestEnvironment.ManualSubscriber in project reactive-streams-jvm by reactive-streams.

the class PublisherVerification method required_spec303_mustNotAllowUnboundedRecursion.

@Override
@Test
public void required_spec303_mustNotAllowUnboundedRecursion() throws Throwable {
    final long oneMoreThanBoundedLimit = boundedDepthOfOnNextAndRequestRecursion() + 1;
    activePublisherTest(oneMoreThanBoundedLimit, false, new PublisherTestRun<T>() {

        @Override
        public void run(Publisher<T> pub) throws Throwable {
            final ThreadLocal<Long> stackDepthCounter = new ThreadLocal<Long>() {

                @Override
                protected Long initialValue() {
                    return 0L;
                }
            };
            final Latch runCompleted = new Latch(env);
            final ManualSubscriber<T> sub = new ManualSubscriberWithSubscriptionSupport<T>(env) {

                // counts the number of signals received, used to break out from possibly infinite request/onNext loops
                long signalsReceived = 0L;

                @Override
                public void onNext(T element) {
                    // NOT calling super.onNext as this test only cares about stack depths, not the actual values of elements
                    // which also simplifies this test as we do not have to drain the test buffer, which would otherwise be in danger of overflowing
                    signalsReceived += 1;
                    stackDepthCounter.set(stackDepthCounter.get() + 1);
                    if (env.debugEnabled()) {
                        env.debug(String.format("%s(recursion depth: %d)::onNext(%s)", this, stackDepthCounter.get(), element));
                    }
                    final long callsUntilNow = stackDepthCounter.get();
                    if (callsUntilNow > boundedDepthOfOnNextAndRequestRecursion()) {
                        env.flop(String.format("Got %d onNext calls within thread: %s, yet expected recursive bound was %d", callsUntilNow, Thread.currentThread(), boundedDepthOfOnNextAndRequestRecursion()));
                        // stop the recursive call chain
                        runCompleted.close();
                        return;
                    } else if (signalsReceived >= oneMoreThanBoundedLimit) {
                        // since max number of signals reached, and recursion depth not exceeded, we judge this as a success and
                        // stop the recursive call chain
                        runCompleted.close();
                        return;
                    }
                    // request more right away, the Publisher must break the recursion
                    subscription.value().request(1);
                    stackDepthCounter.set(stackDepthCounter.get() - 1);
                }

                @Override
                public void onComplete() {
                    super.onComplete();
                    runCompleted.close();
                }

                @Override
                public void onError(Throwable cause) {
                    super.onError(cause);
                    runCompleted.close();
                }
            };
            try {
                env.subscribe(pub, sub);
                // kick-off the `request -> onNext -> request -> onNext -> ...`
                sub.request(1);
                final String msg = String.format("Unable to validate call stack depth safety, " + "awaited at-most %s signals (`maxOnNextSignalsInRecursionTest()`) or completion", oneMoreThanBoundedLimit);
                runCompleted.expectClose(env.defaultTimeoutMillis(), msg);
                env.verifyNoAsyncErrorsNoDelay();
            } finally {
                // since the request/onNext recursive calls may keep the publisher running "forever",
                // we MUST cancel it manually before exiting this test case
                sub.cancel();
            }
        }
    });
}
Also used : ManualSubscriber(org.reactivestreams.tck.TestEnvironment.ManualSubscriber) Latch(org.reactivestreams.tck.TestEnvironment.Latch) Override(java.lang.Override) Test(org.testng.annotations.Test) Override(java.lang.Override)

Aggregations

Override (java.lang.Override)1 Latch (org.reactivestreams.tck.TestEnvironment.Latch)1 ManualSubscriber (org.reactivestreams.tck.TestEnvironment.ManualSubscriber)1 Test (org.testng.annotations.Test)1