package com.tacitknowledge.slowlight.proxyserver.handler; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import io.netty.util.concurrent.EventExecutor; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; /** * @author Alexandr Donciu (adonciu@tacitknowledge.com) */ @RunWith(MockitoJUnitRunner.class) public class WriteDataFragmentsTaskTest { @Mock private Channel channel; @Mock private ChannelHandlerContext ctx; @Mock private ChannelPromise promise; @Mock private ChannelFuture future; @Mock private EventExecutor executor; @Test public void taskShouldDoNothingIfNoReadableBytes() throws Exception { final ByteBuf msg = mock(ByteBuf.class); doReturn(0).when(msg).readableBytes(); final WriteDataFragmentsTask writeDataFragmentsTask = new WriteDataFragmentsTask(ctx, msg, promise, 3, 0); writeDataFragmentsTask.run(); verifyNoMoreInteractions(ctx); } @Test public void taskShouldWriteMessageInFragments() throws Exception { final ByteBuf msg = Unpooled.wrappedBuffer("test message".getBytes()); doReturn(future).when(ctx).writeAndFlush(any()); final WriteDataFragmentsTask writeDataFragmentsTask = new WriteDataFragmentsTask(ctx, msg, promise, 3, 0); writeDataFragmentsTask.run(); final ArgumentCaptor<ByteBuf> msgFragmentCaptor = ArgumentCaptor.forClass(ByteBuf.class); verify(ctx).writeAndFlush(msgFragmentCaptor.capture()); final ArgumentCaptor<ChannelFutureListener> futureListenerCaptor = ArgumentCaptor.forClass(ChannelFutureListener.class); verify(future).addListener(futureListenerCaptor.capture()); final ByteBuf msgFragment = msgFragmentCaptor.getValue(); assertThat(msgFragment.readableBytes(), is(equalTo(3))); } @Test public void taskShouldWriteOnlyReadableBytesWhenDataSizeIsGreater() throws Exception { final ByteBuf msg = Unpooled.wrappedBuffer("test".getBytes()); doReturn(future).when(ctx).writeAndFlush(any()); final WriteDataFragmentsTask writeDataFragmentsTask = new WriteDataFragmentsTask(ctx, msg, promise, 10, 0); writeDataFragmentsTask.run(); final ArgumentCaptor<ByteBuf> msgFragmentCaptor = ArgumentCaptor.forClass(ByteBuf.class); verify(ctx).writeAndFlush(msgFragmentCaptor.capture()); final ByteBuf msgFragment = msgFragmentCaptor.getValue(); assertThat(msgFragment.readableBytes(), is(equalTo(4))); } @Test public void taskShouldWriteAllReadableBytesWhenDataSizeSetToZero() throws Exception { final ByteBuf msg = Unpooled.wrappedBuffer("test message".getBytes()); doReturn(future).when(ctx).writeAndFlush(any()); final WriteDataFragmentsTask writeDataFragmentsTask = new WriteDataFragmentsTask(ctx, msg, promise, 0, 0); writeDataFragmentsTask.run(); final ArgumentCaptor<ByteBuf> msgFragmentCaptor = ArgumentCaptor.forClass(ByteBuf.class); verify(ctx).writeAndFlush(msgFragmentCaptor.capture()); final ByteBuf msgFragment = msgFragmentCaptor.getValue(); assertThat(msgFragment.readableBytes(), is(equalTo(12))); } @Test public void listenerShouldScheduleNextMessageFragmentIfAvailable() throws Exception { final ByteBuf msg = mock(ByteBuf.class); final WriteDataFragmentsTask writeDataFragmentsTask = new WriteDataFragmentsTask(ctx, msg, promise, 3, 100); final WriteDataFragmentsTask.WriteDataListener writeDataListener = writeDataFragmentsTask.new WriteDataListener(); doReturn(true).when(future).isSuccess(); doReturn(10).when(msg).readableBytes(); doReturn(executor).when(ctx).executor(); writeDataListener.operationComplete(future); verify(executor).schedule(writeDataFragmentsTask, 100, TimeUnit.MILLISECONDS); } @Test public void listenerShouldChannelPromiseToSuccessIfNoMoreDataToRead() throws Exception { final ByteBuf msg = mock(ByteBuf.class); final WriteDataFragmentsTask writeDataFragmentsTask = new WriteDataFragmentsTask(ctx, msg, promise, 3, 100); final WriteDataFragmentsTask.WriteDataListener writeDataListener = writeDataFragmentsTask.new WriteDataListener(); doReturn(true).when(future).isSuccess(); doReturn(0).when(msg).readableBytes(); doReturn(executor).when(ctx).executor(); writeDataListener.operationComplete(future); verify(promise).setSuccess(); } @Test public void listenerShouldCloseChannelIfWriteFutureCompletedUnsuccessfully() throws Exception { final ByteBuf msg = mock(ByteBuf.class); final WriteDataFragmentsTask writeDataFragmentsTask = new WriteDataFragmentsTask(ctx, msg, promise, 3, 100); final WriteDataFragmentsTask.WriteDataListener writeDataListener = writeDataFragmentsTask.new WriteDataListener(); doReturn(false).when(future).isSuccess(); doReturn(channel).when(future).channel(); writeDataListener.operationComplete(future); verify(channel).close(); } }