/************************************************************************ * Licensed under Public Domain (CC0) * * * * To the extent possible under law, the person who associated CC0 with * * this code has waived all copyright and related or neighboring * * rights to this code. * * * * You should have received a copy of the CC0 legalcode along with this * * work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.* ************************************************************************/ package org.reactivestreams.tck; import org.reactivestreams.Processor; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.reactivestreams.tck.support.TCKVerificationSupport; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Validates that the TCK's {@link IdentityProcessorVerification} fails with nice human readable errors. * <b>Important: Please note that all Processors implemented in this file are *wrong*!</b> */ public class IdentityProcessorVerificationTest extends TCKVerificationSupport { static final long DEFAULT_TIMEOUT_MILLIS = TestEnvironment.envDefaultTimeoutMillis(); static final long DEFAULT_NO_SIGNALS_TIMEOUT_MILLIS = TestEnvironment.envDefaultNoSignalsTimeoutMillis(); private ExecutorService ex; @BeforeClass void before() { ex = Executors.newFixedThreadPool(4); } @AfterClass void after() { if (ex != null) ex.shutdown(); } @Test public void required_spec104_mustCallOnErrorOnAllItsSubscribersIfItEncountersANonRecoverableError_shouldBeIgnored() throws Throwable { requireTestSkip(new ThrowingRunnable() { @Override public void run() throws Throwable { new IdentityProcessorVerification<Integer>(newTestEnvironment(), DEFAULT_TIMEOUT_MILLIS){ @Override public Processor<Integer, Integer> createIdentityProcessor(int bufferSize) { return new NoopProcessor(); } @Override public ExecutorService publisherExecutorService() { return ex; } @Override public Integer createElement(int element) { return element; } @Override public Publisher<Integer> createHelperPublisher(long elements) { return SKIP; } @Override public Publisher<Integer> createFailedPublisher() { return SKIP; } @Override public long maxSupportedSubscribers() { return 1; // can only support 1 subscribe => unable to run this test } }.required_spec104_mustCallOnErrorOnAllItsSubscribersIfItEncountersANonRecoverableError(); } }, "The Publisher under test only supports 1 subscribers, while this test requires at least 2 to run"); } @Test public void required_spec104_mustCallOnErrorOnAllItsSubscribersIfItEncountersANonRecoverableError_shouldFailWhileWaitingForOnError() throws Throwable { requireTestFailure(new ThrowingRunnable() { @Override public void run() throws Throwable { new IdentityProcessorVerification<Integer>(newTestEnvironment(), DEFAULT_TIMEOUT_MILLIS) { @Override public Processor<Integer, Integer> createIdentityProcessor(int bufferSize) { return new Processor<Integer, Integer>() { @Override public void subscribe(final Subscriber<? super Integer> s) { s.onSubscribe(new Subscription() { @Override public void request(long n) { s.onNext(0); } @Override public void cancel() { } }); } @Override public void onSubscribe(Subscription s) { s.request(1); } @Override public void onNext(Integer integer) { // noop } @Override public void onError(Throwable t) { // noop } @Override public void onComplete() { // noop } }; } @Override public ExecutorService publisherExecutorService() { return ex; } @Override public Integer createElement(int element) { return element; } @Override public Publisher<Integer> createHelperPublisher(long elements) { return new Publisher<Integer>() { @Override public void subscribe(final Subscriber<? super Integer> s) { s.onSubscribe(new NoopSubscription() { @Override public void request(long n) { for (int i = 0; i < 10; i++) { s.onNext(i); } } }); } }; } @Override public Publisher<Integer> createFailedPublisher() { return SKIP; } }.required_spec104_mustCallOnErrorOnAllItsSubscribersIfItEncountersANonRecoverableError(); } }, "Did not receive expected error on downstream within " + DEFAULT_TIMEOUT_MILLIS); } // FAILING IMPLEMENTATIONS // final Publisher<Integer> SKIP = null; /** Subscription which does nothing. */ static class NoopSubscription implements Subscription { @Override public void request(long n) { // noop } @Override public void cancel() { // noop } } static class NoopProcessor implements Processor<Integer, Integer> { @Override public void subscribe(Subscriber<? super Integer> s) { s.onSubscribe(new NoopSubscription()); } @Override public void onSubscribe(Subscription s) { // noop } @Override public void onNext(Integer integer) { // noop } @Override public void onError(Throwable t) { // noop } @Override public void onComplete() { // noop } } private TestEnvironment newTestEnvironment() { return new TestEnvironment(DEFAULT_TIMEOUT_MILLIS, DEFAULT_NO_SIGNALS_TIMEOUT_MILLIS); } }