/* * Copyright 2016 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.WriteBufferWaterMark; import io.netty.channel.embedded.EmbeddedChannel; 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 static io.reactivex.netty.channel.BackpressureManagingHandler.BytesWriteInterceptor.MAX_PER_SUBSCRIBER_REQUEST; import static io.reactivex.netty.channel.BytesWriteInterceptorTest.InspectorRule.defaultRequestN; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; public class BytesWriteInterceptorTest { @Rule public final InspectorRule inspectorRule = new InspectorRule(); @Test(timeout = 60000) public void testAddSubscriber() throws Exception { WriteStreamSubscriber sub1 = inspectorRule.newSubscriber(); assertThat("Subscriber not added.", inspectorRule.interceptor.getSubscribers(), hasSize(1)); assertThat("Subscriber not added.", inspectorRule.interceptor.getSubscribers(), contains(sub1)); sub1.unsubscribe(); inspectorRule.channel.runPendingTasks(); assertThat("Subscriber not removed post unsubscribe", inspectorRule.interceptor.getSubscribers(), is(empty())); } @Test(timeout = 60000) public void testRequestMore() throws Exception { WriteStreamSubscriber sub1 = inspectorRule.newSubscriber(); MockProducer mockProducer = inspectorRule.setupSubscriberAndValidate(sub1, 1); assertThat("Unexpected items requested from producer.", mockProducer.getRequested(), is(defaultRequestN())); inspectorRule.sendMessages(1); assertThat("Channel not writable post write.", inspectorRule.channel.isWritable(), is(true)); assertThat("Unexpected items requested.", mockProducer.getRequested(), is(defaultRequestN())); } @Test(timeout = 60000) public void testRequestMorePostFlush() throws Exception { WriteStreamSubscriber sub1 = inspectorRule.newSubscriber(); MockProducer mockProducer = inspectorRule.setupSubscriberAndValidate(sub1, 1); assertThat("Unexpected items requested from producer.", mockProducer.getRequested(), is(defaultRequestN())); inspectorRule.channel.config().setWriteBufferWaterMark(new WriteBufferWaterMark(1, 2)); /*Make sure that the channel is not writable on writing.*/ String msg = "Hello"; inspectorRule.channel.write(msg); assertThat("Channel still writable.", inspectorRule.channel.isWritable(), is(false)); assertThat("More items requested when channel is not writable.", mockProducer.getRequested(), is(defaultRequestN())); inspectorRule.channel.flush(); assertThat("Channel not writable post flush.", inspectorRule.channel.isWritable(), is(true)); assertThat("Unexpected items requested.", mockProducer.getRequested(), is(defaultRequestN())); } @Test(timeout = 60000) public void testMultiSubscribers() throws Exception { WriteStreamSubscriber sub1 = inspectorRule.newSubscriber(); MockProducer producer1 = inspectorRule.setupSubscriberAndValidate(sub1, 1); WriteStreamSubscriber sub2 = inspectorRule.newSubscriber(); MockProducer producer2 = inspectorRule.setupSubscriberAndValidate(sub2, 2); inspectorRule.sendMessages(1); assertThat("Channel not writable post write.", inspectorRule.channel.isWritable(), is(true)); assertThat("Unexpected items requested from first subscriber.", producer1.getRequested(), is(defaultRequestN())); assertThat("Unexpected items requested from second subscriber.", producer2.getRequested(), is(defaultRequestN() / 2)); } @Test(timeout = 10000) public void testOneLongWriteAndManySmallWrites() throws Exception { WriteStreamSubscriber sub1 = inspectorRule.newSubscriber(); MockProducer producer1 = inspectorRule.setupSubscriberAndValidate(sub1, 1); assertThat("Unexpected items requested from producer.", producer1.getRequested(), is(defaultRequestN())); inspectorRule.setupNewSubscriberAndComplete(2, true); inspectorRule.setupNewSubscriberAndComplete(2, true); inspectorRule.sendMessages(sub1, 33); assertThat("Unexpected items requested.", producer1.getRequested(), is(97L)); } @Test(timeout = 10000) public void testBatchedSubscriberRemoves() throws Exception { WriteStreamSubscriber sub1 = inspectorRule.newSubscriber(); MockProducer producer1 = inspectorRule.setupSubscriberAndValidate(sub1, 1); assertThat("Unexpected items requested from producer.", producer1.getRequested(), is(defaultRequestN())); for (int i=1; i < 5; i++) { inspectorRule.setupNewSubscriberAndComplete(i+1, false); } inspectorRule.channel.runPendingTasks(); inspectorRule.sendMessages(sub1, 35); assertThat("Unexpected items requested.", producer1.getRequested(), is(95L)); } @Test(timeout = 10000) public void testMinRequestN() throws Exception { for (int i=1; i < 66; i++) { inspectorRule.setupNewSubscriberAndComplete(i, false); } WriteStreamSubscriber sub1 = inspectorRule.newSubscriber(); MockProducer producer1 = inspectorRule.setupSubscriberAndValidate(sub1, 66); assertThat("Unexpected items requested from producer.", producer1.getRequested(), is(1L)); inspectorRule.channel.runPendingTasks(); inspectorRule.sendMessages(sub1, 35); assertThat("Unexpected items requested.", producer1.getRequested(), greaterThan(1L)); } public static class InspectorRule extends ExternalResource { private BytesWriteInterceptor interceptor; private EmbeddedChannel channel; @Override public Statement apply(final Statement base, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { interceptor = new BytesWriteInterceptor("foo"); channel = new EmbeddedChannel(new WriteTransformer(), interceptor); base.evaluate(); } }; } WriteStreamSubscriber newSubscriber() { return interceptor.newSubscriber(channel.pipeline().lastContext(), channel.newPromise()); } private MockProducer setupSubscriberAndValidate(WriteStreamSubscriber sub, int expectedSubCount) { MockProducer mockProducer = setupSubscriber(sub); assertThat("Subscriber not added.", interceptor.getSubscribers(), hasSize(expectedSubCount)); assertThat("Subscriber not added.", interceptor.getSubscribers().get(expectedSubCount - 1), equalTo(sub)); return mockProducer; } private static MockProducer setupSubscriber(WriteStreamSubscriber sub) { sub.onStart(); MockProducer mockProducer = new MockProducer(); sub.setProducer(mockProducer); return mockProducer; } public static Long defaultRequestN() { return Long.valueOf(MAX_PER_SUBSCRIBER_REQUEST); } public void sendMessages(WriteStreamSubscriber subscriber, int msgCount) { for(int i=0; i < msgCount; i++) { subscriber.onNext("Hello"); channel.write("Hello"); } channel.flush(); } public void sendMessages(int msgCount) { for(int i=0; i < msgCount; i++) { channel.write("Hello"); } channel.flush(); } public void setupNewSubscriberAndComplete(int expectedSubCount, boolean runPendingTasks) { WriteStreamSubscriber sub2 = newSubscriber(); MockProducer producer2 = setupSubscriberAndValidate(sub2, expectedSubCount); assertThat("Unexpected items requested from producer.", producer2.getRequested(), lessThanOrEqualTo(Math.max(1, defaultRequestN()/expectedSubCount))); sub2.onCompleted(); sub2.unsubscribe(); if (runPendingTasks) { channel.runPendingTasks(); } } } }