netty5/transport/src/test/java/io/netty/channel/CoalescingBufferQueueTest.java

319 lines
11 KiB
Java
Raw Normal View History

/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you 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.netty.channel;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Tests for {@link CoalescingBufferQueue}.
*/
public class CoalescingBufferQueueTest {
private ByteBuf cat;
private ByteBuf mouse;
private ChannelPromise catPromise, emptyPromise;
private ChannelPromise voidPromise;
private ChannelFutureListener mouseListener;
private boolean mouseDone;
private boolean mouseSuccess;
private EmbeddedChannel channel;
private CoalescingBufferQueue writeQueue;
@Before
public void setup() {
mouseDone = false;
mouseSuccess = false;
channel = new EmbeddedChannel();
writeQueue = new CoalescingBufferQueue(channel, 16, true);
catPromise = newPromise();
mouseListener = new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
mouseDone = true;
mouseSuccess = future.isSuccess();
}
};
emptyPromise = newPromise();
voidPromise = channel.voidPromise();
cat = Unpooled.wrappedBuffer("cat".getBytes(CharsetUtil.US_ASCII));
mouse = Unpooled.wrappedBuffer("mouse".getBytes(CharsetUtil.US_ASCII));
}
@After
public void finish() {
assertFalse(channel.finish());
}
@Test
public void testAddFirstPromiseRetained() {
writeQueue.add(cat, catPromise);
assertQueueSize(3, false);
writeQueue.add(mouse, mouseListener);
assertQueueSize(8, false);
ChannelPromise aggregatePromise = newPromise();
assertEquals("catmous", dequeue(7, aggregatePromise));
ByteBuf remainder = Unpooled.wrappedBuffer("mous".getBytes(CharsetUtil.US_ASCII));
writeQueue.addFirst(remainder, aggregatePromise);
ChannelPromise aggregatePromise2 = newPromise();
assertEquals("mouse", dequeue(5, aggregatePromise2));
aggregatePromise2.setSuccess();
assertTrue(catPromise.isSuccess());
assertTrue(mouseSuccess);
assertEquals(0, cat.refCnt());
assertEquals(0, mouse.refCnt());
}
@Test
public void testAddFirstVoidPromise() {
writeQueue.add(cat, catPromise);
assertQueueSize(3, false);
writeQueue.add(mouse, mouseListener);
assertQueueSize(8, false);
ChannelPromise aggregatePromise = newPromise();
assertEquals("catmous", dequeue(7, aggregatePromise));
ByteBuf remainder = Unpooled.wrappedBuffer("mous".getBytes(CharsetUtil.US_ASCII));
writeQueue.addFirst(remainder, voidPromise);
ChannelPromise aggregatePromise2 = newPromise();
assertEquals("mouse", dequeue(5, aggregatePromise2));
aggregatePromise2.setSuccess();
// Because we used a void promise above, we shouldn't complete catPromise until aggregatePromise is completed.
assertFalse(catPromise.isSuccess());
assertTrue(mouseSuccess);
aggregatePromise.setSuccess();
assertTrue(catPromise.isSuccess());
assertTrue(mouseSuccess);
assertEquals(0, cat.refCnt());
assertEquals(0, mouse.refCnt());
}
@Test
public void testAggregateWithFullRead() {
writeQueue.add(cat, catPromise);
assertQueueSize(3, false);
writeQueue.add(mouse, mouseListener);
assertQueueSize(8, false);
ChannelPromise aggregatePromise = newPromise();
assertEquals("catmouse", dequeue(8, aggregatePromise));
assertQueueSize(0, true);
assertFalse(catPromise.isSuccess());
assertFalse(mouseDone);
aggregatePromise.setSuccess();
assertTrue(catPromise.isSuccess());
assertTrue(mouseSuccess);
assertEquals(0, cat.refCnt());
assertEquals(0, mouse.refCnt());
}
@Test
public void testWithVoidPromise() {
writeQueue.add(cat, voidPromise);
writeQueue.add(mouse, voidPromise);
assertQueueSize(8, false);
assertEquals("catm", dequeue(4, newPromise()));
assertQueueSize(4, false);
assertEquals("ouse", dequeue(4, newPromise()));
assertQueueSize(0, true);
assertEquals(0, cat.refCnt());
assertEquals(0, mouse.refCnt());
}
@Test
public void testAggregateWithPartialRead() {
writeQueue.add(cat, catPromise);
writeQueue.add(mouse, mouseListener);
ChannelPromise aggregatePromise = newPromise();
assertEquals("catm", dequeue(4, aggregatePromise));
assertQueueSize(4, false);
assertFalse(catPromise.isSuccess());
assertFalse(mouseDone);
aggregatePromise.setSuccess();
assertTrue(catPromise.isSuccess());
assertFalse(mouseDone);
aggregatePromise = newPromise();
assertEquals("ouse", dequeue(Integer.MAX_VALUE, aggregatePromise));
assertQueueSize(0, true);
assertFalse(mouseDone);
aggregatePromise.setSuccess();
assertTrue(mouseSuccess);
assertEquals(0, cat.refCnt());
assertEquals(0, mouse.refCnt());
}
@Test
public void testReadExactAddedBufferSizeReturnsOriginal() {
writeQueue.add(cat, catPromise);
writeQueue.add(mouse, mouseListener);
ChannelPromise aggregatePromise = newPromise();
assertSame(cat, writeQueue.remove(3, aggregatePromise));
assertFalse(catPromise.isSuccess());
aggregatePromise.setSuccess();
assertTrue(catPromise.isSuccess());
assertEquals(1, cat.refCnt());
cat.release();
aggregatePromise = newPromise();
assertSame(mouse, writeQueue.remove(5, aggregatePromise));
assertFalse(mouseDone);
aggregatePromise.setSuccess();
assertTrue(mouseSuccess);
assertEquals(1, mouse.refCnt());
mouse.release();
}
@Test
public void testReadEmptyQueueReturnsEmptyBuffer() {
// Not used in this test.
cat.release();
mouse.release();
assertQueueSize(0, true);
ChannelPromise aggregatePromise = newPromise();
assertEquals("", dequeue(Integer.MAX_VALUE, aggregatePromise));
assertQueueSize(0, true);
}
@Test
public void testReleaseAndFailAll() {
writeQueue.add(cat, catPromise);
writeQueue.add(mouse, mouseListener);
RuntimeException cause = new RuntimeException("ooops");
writeQueue.releaseAndFailAll(cause);
ChannelPromise aggregatePromise = newPromise();
assertQueueSize(0, true);
assertEquals(0, cat.refCnt());
assertEquals(0, mouse.refCnt());
assertSame(cause, catPromise.cause());
assertEquals("", dequeue(Integer.MAX_VALUE, aggregatePromise));
assertQueueSize(0, true);
}
@Test
public void testEmptyBuffersAreCoalesced() {
ByteBuf empty = Unpooled.buffer(0, 1);
assertQueueSize(0, true);
writeQueue.add(cat, catPromise);
writeQueue.add(empty, emptyPromise);
assertQueueSize(3, false);
ChannelPromise aggregatePromise = newPromise();
assertEquals("cat", dequeue(3, aggregatePromise));
assertQueueSize(0, true);
assertFalse(catPromise.isSuccess());
assertFalse(emptyPromise.isSuccess());
aggregatePromise.setSuccess();
assertTrue(catPromise.isSuccess());
assertTrue(emptyPromise.isSuccess());
assertEquals(0, cat.refCnt());
assertEquals(0, empty.refCnt());
}
@Test
public void testMerge() {
writeQueue.add(cat, catPromise);
CoalescingBufferQueue otherQueue = new CoalescingBufferQueue(channel);
otherQueue.add(mouse, mouseListener);
otherQueue.copyTo(writeQueue);
assertQueueSize(8, false);
ChannelPromise aggregatePromise = newPromise();
assertEquals("catmouse", dequeue(8, aggregatePromise));
assertQueueSize(0, true);
assertFalse(catPromise.isSuccess());
assertFalse(mouseDone);
aggregatePromise.setSuccess();
assertTrue(catPromise.isSuccess());
assertTrue(mouseSuccess);
assertEquals(0, cat.refCnt());
assertEquals(0, mouse.refCnt());
}
@Test
public void testWritabilityChanged() {
testWritabilityChanged0(false);
}
@Test
public void testWritabilityChangedFailAll() {
testWritabilityChanged0(true);
}
private void testWritabilityChanged0(boolean fail) {
channel.config().setWriteBufferWaterMark(new WriteBufferWaterMark(3, 4));
assertTrue(channel.isWritable());
writeQueue.add(Unpooled.wrappedBuffer(new byte[] {1 , 2, 3}));
assertTrue(channel.isWritable());
writeQueue.add(Unpooled.wrappedBuffer(new byte[] {4, 5}));
assertFalse(channel.isWritable());
assertEquals(5, writeQueue.readableBytes());
if (fail) {
writeQueue.releaseAndFailAll(new IllegalStateException());
} else {
ByteBuf buffer = writeQueue.removeFirst(voidPromise);
assertEquals(1, buffer.readByte());
assertEquals(2, buffer.readByte());
assertEquals(3, buffer.readByte());
assertFalse(buffer.isReadable());
buffer.release();
assertTrue(channel.isWritable());
buffer = writeQueue.removeFirst(voidPromise);
assertEquals(4, buffer.readByte());
assertEquals(5, buffer.readByte());
assertFalse(buffer.isReadable());
buffer.release();
}
assertTrue(channel.isWritable());
assertTrue(writeQueue.isEmpty());
}
private ChannelPromise newPromise() {
return channel.newPromise();
}
private void assertQueueSize(int size, boolean isEmpty) {
assertEquals(size, writeQueue.readableBytes());
if (isEmpty) {
assertTrue(writeQueue.isEmpty());
} else {
assertFalse(writeQueue.isEmpty());
}
}
private String dequeue(int numBytes, ChannelPromise aggregatePromise) {
ByteBuf removed = writeQueue.remove(numBytes, aggregatePromise);
String result = removed.toString(CharsetUtil.US_ASCII);
ReferenceCountUtil.safeRelease(removed);
return result;
}
}