/*
* Copyright 2015 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.reactivex.netty.channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.logging.LoggingHandler;
import io.reactivex.netty.channel.BackpressureManagingHandler.BytesWriteInterceptor;
import io.reactivex.netty.channel.BackpressureManagingHandler.WriteStreamSubscriber;
import io.reactivex.netty.test.util.MockProducer;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExternalResource;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import java.io.IOException;
import java.util.Queue;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
public class WriteStreamSubscriberTest {
@Rule
public final SubscriberRule subscriberRule = new SubscriberRule();
@Test(timeout = 60000)
public void testOnStart() throws Exception {
assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(false));
subscriberRule.start();
assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(false));
assertThat("Unexpected request made to the producer.", subscriberRule.mockProducer.getRequested(),
is(subscriberRule.defaultRequestN()));
}
@Test(timeout = 60000)
public void testUnsubscribeOnPromiseCancel() throws Exception {
subscriberRule.start();
assertThat("Subsriber isn't subscribed.", subscriberRule.subscriber.isUnsubscribed(), is(false));
subscriberRule.channelPromise.cancel(false);
assertThat("Promise not cancelled.", subscriberRule.channelPromise.isCancelled(), is(true));
assertThat("Subsriber isn't unsubscribed.", subscriberRule.subscriber.isUnsubscribed(), is(true));
}
@Test(timeout = 60000)
public void testWriteCompleteBeforeStream() throws Exception {
subscriberRule.start();
String msg1 = "msg1";
subscriberRule.writeAndFlushMessages(msg1);
subscriberRule.assertMessagesWritten(msg1);
assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(false));
subscriberRule.subscriber.onCompleted();
assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(true));
assertThat("Unexpected promise result.", subscriberRule.channelPromise.isSuccess(), is(true));
}
@Test(timeout = 60000)
public void testWriteCompleteAfterStream() throws Exception {
subscriberRule.start();
String msg1 = "msg1";
subscriberRule.writeMessages(msg1);
assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(false));
subscriberRule.subscriber.onCompleted();
/*Complete when write completes.*/
assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(false));
subscriberRule.channel.flush(); /*Completes write*/
subscriberRule.assertMessagesWritten(msg1);
assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(true));
assertThat("Unexpected promise result.", subscriberRule.channelPromise.isSuccess(), is(true));
}
@Test(timeout = 60000)
public void testMultiWrite() throws Exception {
subscriberRule.start();
String msg1 = "msg1";
String msg2 = "msg2";
subscriberRule.writeMessages(msg1, msg2);
assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(false));
subscriberRule.subscriber.onCompleted();
/*Complete when write completes.*/
assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(false));
subscriberRule.channel.flush(); /*Completes write*/
subscriberRule.assertMessagesWritten(msg1, msg2);
assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(true));
assertThat("Unexpected promise result.", subscriberRule.channelPromise.isSuccess(), is(true));
}
@Test(timeout = 60000)
public void testWriteFailed() throws Exception {
subscriberRule.start();
String msg1 = "msg1";
subscriberRule.writeMessages(msg1);
assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(false));
subscriberRule.channel.close();
assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(true));
assertThat("Unexpected promise result.", subscriberRule.channelPromise.isSuccess(), is(false));
}
@Test(timeout = 60000)
public void testStreamError() throws Exception {
subscriberRule.start();
subscriberRule.sendMessagesAndAssert(1);
subscriberRule.subscriber.onError(new IOException());
assertThat("Unexpected promise completion state.", subscriberRule.channelPromise.isDone(), is(true));
assertThat("Unexpected promise result.", subscriberRule.channelPromise.isSuccess(), is(false));
}
@Test(timeout = 60000)
public void testRequestMoreNotRequired() throws Exception {
subscriberRule.init(4);
subscriberRule.start();
assertThat("Unexpected request made to the producer.", subscriberRule.mockProducer.getRequested(),
is(subscriberRule.defaultRequestN()));
subscriberRule.sendMessagesAndAssert(2); // Pending: 4 - 2 : low water mark: 4/2
subscriberRule.subscriber.requestMoreIfNeeded(subscriberRule.defaultRequestN);
assertThat("Unexpected request made to the producer.", subscriberRule.mockProducer.getRequested(),
is(subscriberRule.defaultRequestN()));
}
@Test(timeout = 60000)
public void testRequestMoreRequired() throws Exception {
subscriberRule.init(4);
subscriberRule.start();
assertThat("Unexpected request made to the producer.", subscriberRule.mockProducer.getRequested(),
is(subscriberRule.defaultRequestN()));
subscriberRule.sendMessagesAndAssert(3); // Pending: 4 - 3 : low water mark: 4/2
subscriberRule.subscriber.requestMoreIfNeeded(subscriberRule.defaultRequestN);
assertThat("Unexpected request made to the producer.", subscriberRule.mockProducer.getRequested(),
is(7L));// request: 4 + 4 - (4 - 3)
}
@Test(timeout = 60000)
public void testLowerMaxBufferSize() throws Exception {
subscriberRule.init(4);
subscriberRule.start();
subscriberRule.subscriber.requestMoreIfNeeded(2);
assertThat("Unexpected request made to the producer.", subscriberRule.mockProducer.getRequested(),
is(subscriberRule.defaultRequestN()));
}
@Test(timeout = 60000)
public void testLowerMaxBufferSizeAndThenMore() throws Exception {
subscriberRule.init(8);
subscriberRule.start();
subscriberRule.subscriber.requestMoreIfNeeded(6);
subscriberRule.sendMessagesAndAssert(6); // Pending: 8 - 6 : low water mark: 6/2
subscriberRule.subscriber.requestMoreIfNeeded(6);
assertThat("Unexpected request made to the producer.", subscriberRule.mockProducer.getRequested(),
is(12L)); // requestN: 8 + 6 - (8 - 6)
}
@Test(timeout = 60000)
public void testHigherMaxBufferSize() throws Exception {
subscriberRule.init(4);
subscriberRule.start();
subscriberRule.subscriber.requestMoreIfNeeded(6);
assertThat("Unexpected request made to the producer.", subscriberRule.mockProducer.getRequested(),
is(6L)); // requestN: 4 + 6 - 4
}
public static class SubscriberRule extends ExternalResource {
private WriteStreamSubscriber subscriber;
private ChannelPromise channelPromise;
private EmbeddedChannel channel;
private MockProducer mockProducer;
private int defaultRequestN;
@Override
public Statement apply(final Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
init(BytesWriteInterceptor.MAX_PER_SUBSCRIBER_REQUEST);
base.evaluate();
}
};
}
protected void init(int defaultRequestN) {
this.defaultRequestN = defaultRequestN;
channel = new EmbeddedChannel(new LoggingHandler());
channelPromise = channel.newPromise();
ChannelHandlerContext ctx = channel.pipeline().firstContext();
subscriber = new WriteStreamSubscriber(ctx, channelPromise, defaultRequestN().intValue());
mockProducer = new MockProducer();
}
public void start() {
subscriber.onStart(); /*So that setProducer does not request Long.MAX_VALUE*/
subscriber.setProducer(mockProducer);
mockProducer.assertBackpressureRequested();
mockProducer.assertIllegalRequest();
}
public void writeAndFlushMessages(Object... msgs) {
writeMessages(msgs);
channel.flush();
}
public void writeMessages(Object... msgs) {
for (Object msg : msgs) {
subscriber.onNext(msg);
}
}
public void assertMessagesWritten(Object... msgs) {
Queue<Object> outboundMessages = channel.outboundMessages();
if (null == msgs || msgs.length == 0) {
assertThat("Unexpected number of messages written on the channel.", outboundMessages, is(empty()));
return;
}
assertThat("Unexpected number of messages written on the channel.", outboundMessages, hasSize(msgs.length));
assertThat("Unexpected messages written on the channel.", outboundMessages, contains(msgs));
}
protected void sendMessagesAndAssert(int count) {
String[] msgs = new String[count];
for (int i = 0; i < count; i++) {
msgs[i] = "msg" + i;
}
writeAndFlushMessages(msgs);
assertThat("Unexpected promise completion state.", channelPromise.isDone(), is(false));
assertMessagesWritten(msgs);
}
public Long defaultRequestN() {
return Long.valueOf(defaultRequestN);
}
}
}