package com.typesafe.netty; import io.netty.channel.*; import io.netty.util.concurrent.DefaultPromise; import io.netty.util.concurrent.Promise; import org.reactivestreams.Subscriber; import org.reactivestreams.tck.SubscriberWhiteboxVerification; import org.reactivestreams.tck.TestEnvironment; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; public class HandlerSubscriberWhiteboxVerificationTest extends SubscriberWhiteboxVerification<Long> { private boolean workAroundIssue277; public HandlerSubscriberWhiteboxVerificationTest() { super(new TestEnvironment()); } private DefaultEventLoopGroup eventLoop; // I tried making this before/after class, but encountered a strange error where after 32 publishers were created, // the following tests complained about the executor being shut down when I registered the channel. Though, it // doesn't happen if you create 32 publishers in a single test. @BeforeMethod public void startEventLoop() { workAroundIssue277 = false; eventLoop = new DefaultEventLoopGroup(); } @AfterMethod public void stopEventLoop() { eventLoop.shutdownGracefully(); eventLoop = null; } @Override public Subscriber<Long> createSubscriber(WhiteboxSubscriberProbe<Long> probe) { final ClosedLoopChannel channel = new ClosedLoopChannel(); channel.config().setAutoRead(false); ChannelFuture registered = eventLoop.register(channel); final HandlerSubscriber<Long> subscriber = new HandlerSubscriber<>(registered.channel().eventLoop(), 2, 4); final ProbeHandler<Long> probeHandler = new ProbeHandler<>(probe, Long.class); final Promise<Void> handlersInPlace = new DefaultPromise<>(eventLoop.next()); registered.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { channel.pipeline().addLast("probe", probeHandler); channel.pipeline().addLast("subscriber", subscriber); handlersInPlace.setSuccess(null); // Channel needs to be active before the subscriber starts responding to demand channel.pipeline().fireChannelActive(); } }); if (workAroundIssue277) { try { // Wait for the pipeline to be setup, so we're ready to receive elements even if they aren't requested, // because https://github.com/reactive-streams/reactive-streams-jvm/issues/277 handlersInPlace.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } } return probeHandler.wrap(subscriber); } @Override public void required_spec208_mustBePreparedToReceiveOnNextSignalsAfterHavingCalledSubscriptionCancel() throws Throwable { // See https://github.com/reactive-streams/reactive-streams-jvm/issues/277 workAroundIssue277 = true; super.required_spec208_mustBePreparedToReceiveOnNextSignalsAfterHavingCalledSubscriptionCancel(); } @Override public void required_spec308_requestMustRegisterGivenNumberElementsToBeProduced() throws Throwable { workAroundIssue277 = true; super.required_spec308_requestMustRegisterGivenNumberElementsToBeProduced(); } @Override public Long createElement(int element) { return (long) element; } }