2015-06-30 21:31:08 +02:00
|
|
|
/*
|
|
|
|
* 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;
|
2015-07-16 00:04:22 +02:00
|
|
|
import io.netty.util.CharsetUtil;
|
2015-06-30 21:31:08 +02:00
|
|
|
import io.netty.util.ReferenceCountUtil;
|
2017-10-21 14:30:16 +02:00
|
|
|
import org.junit.After;
|
2015-06-30 21:31:08 +02:00
|
|
|
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;
|
|
|
|
|
2017-10-21 14:30:16 +02:00
|
|
|
private EmbeddedChannel channel;
|
|
|
|
private CoalescingBufferQueue writeQueue;
|
2015-06-30 21:31:08 +02:00
|
|
|
|
|
|
|
@Before
|
|
|
|
public void setup() {
|
2017-10-21 14:30:16 +02:00
|
|
|
mouseDone = false;
|
|
|
|
mouseSuccess = false;
|
|
|
|
channel = new EmbeddedChannel();
|
|
|
|
writeQueue = new CoalescingBufferQueue(channel, 16, true);
|
2015-06-30 21:31:08 +02:00
|
|
|
catPromise = newPromise();
|
|
|
|
mouseListener = new ChannelFutureListener() {
|
|
|
|
@Override
|
|
|
|
public void operationComplete(ChannelFuture future) throws Exception {
|
|
|
|
mouseDone = true;
|
|
|
|
mouseSuccess = future.isSuccess();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
emptyPromise = newPromise();
|
|
|
|
voidPromise = channel.voidPromise();
|
|
|
|
|
2015-07-16 00:04:22 +02:00
|
|
|
cat = Unpooled.wrappedBuffer("cat".getBytes(CharsetUtil.US_ASCII));
|
|
|
|
mouse = Unpooled.wrappedBuffer("mouse".getBytes(CharsetUtil.US_ASCII));
|
2015-06-30 21:31:08 +02:00
|
|
|
}
|
|
|
|
|
2017-10-21 14:30:16 +02:00
|
|
|
@After
|
|
|
|
public void finish() {
|
|
|
|
assertFalse(channel.finish());
|
|
|
|
}
|
|
|
|
|
2017-11-07 18:54:17 +01:00
|
|
|
@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());
|
|
|
|
}
|
|
|
|
|
2015-06-30 21:31:08 +02:00
|
|
|
@Test
|
|
|
|
public void testAggregateWithFullRead() {
|
|
|
|
writeQueue.add(cat, catPromise);
|
|
|
|
assertQueueSize(3, false);
|
|
|
|
writeQueue.add(mouse, mouseListener);
|
|
|
|
assertQueueSize(8, false);
|
2017-10-21 14:30:16 +02:00
|
|
|
ChannelPromise aggregatePromise = newPromise();
|
2015-07-16 00:04:22 +02:00
|
|
|
assertEquals("catmouse", dequeue(8, aggregatePromise));
|
2015-06-30 21:31:08 +02:00
|
|
|
assertQueueSize(0, true);
|
|
|
|
assertFalse(catPromise.isSuccess());
|
|
|
|
assertFalse(mouseDone);
|
2015-11-25 15:50:30 +01:00
|
|
|
aggregatePromise.setSuccess();
|
2015-06-30 21:31:08 +02:00
|
|
|
assertTrue(catPromise.isSuccess());
|
|
|
|
assertTrue(mouseSuccess);
|
2015-11-25 15:50:30 +01:00
|
|
|
assertEquals(0, cat.refCnt());
|
|
|
|
assertEquals(0, mouse.refCnt());
|
2015-06-30 21:31:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testWithVoidPromise() {
|
|
|
|
writeQueue.add(cat, voidPromise);
|
|
|
|
writeQueue.add(mouse, voidPromise);
|
|
|
|
assertQueueSize(8, false);
|
2015-07-16 00:04:22 +02:00
|
|
|
assertEquals("catm", dequeue(4, newPromise()));
|
2015-06-30 21:31:08 +02:00
|
|
|
assertQueueSize(4, false);
|
2015-07-16 00:04:22 +02:00
|
|
|
assertEquals("ouse", dequeue(4, newPromise()));
|
2015-06-30 21:31:08 +02:00
|
|
|
assertQueueSize(0, true);
|
2015-11-25 15:50:30 +01:00
|
|
|
assertEquals(0, cat.refCnt());
|
|
|
|
assertEquals(0, mouse.refCnt());
|
2015-06-30 21:31:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testAggregateWithPartialRead() {
|
|
|
|
writeQueue.add(cat, catPromise);
|
|
|
|
writeQueue.add(mouse, mouseListener);
|
2017-10-21 14:30:16 +02:00
|
|
|
ChannelPromise aggregatePromise = newPromise();
|
2015-07-16 00:04:22 +02:00
|
|
|
assertEquals("catm", dequeue(4, aggregatePromise));
|
2015-06-30 21:31:08 +02:00
|
|
|
assertQueueSize(4, false);
|
|
|
|
assertFalse(catPromise.isSuccess());
|
|
|
|
assertFalse(mouseDone);
|
2015-11-25 15:50:30 +01:00
|
|
|
aggregatePromise.setSuccess();
|
2015-06-30 21:31:08 +02:00
|
|
|
assertTrue(catPromise.isSuccess());
|
|
|
|
assertFalse(mouseDone);
|
|
|
|
|
|
|
|
aggregatePromise = newPromise();
|
2015-07-16 00:04:22 +02:00
|
|
|
assertEquals("ouse", dequeue(Integer.MAX_VALUE, aggregatePromise));
|
2015-06-30 21:31:08 +02:00
|
|
|
assertQueueSize(0, true);
|
|
|
|
assertFalse(mouseDone);
|
2015-11-25 15:50:30 +01:00
|
|
|
aggregatePromise.setSuccess();
|
2015-06-30 21:31:08 +02:00
|
|
|
assertTrue(mouseSuccess);
|
2015-11-25 15:50:30 +01:00
|
|
|
assertEquals(0, cat.refCnt());
|
|
|
|
assertEquals(0, mouse.refCnt());
|
2015-06-30 21:31:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testReadExactAddedBufferSizeReturnsOriginal() {
|
|
|
|
writeQueue.add(cat, catPromise);
|
|
|
|
writeQueue.add(mouse, mouseListener);
|
2015-11-25 15:50:30 +01:00
|
|
|
|
2017-10-21 14:30:16 +02:00
|
|
|
ChannelPromise aggregatePromise = newPromise();
|
2015-06-30 21:31:08 +02:00
|
|
|
assertSame(cat, writeQueue.remove(3, aggregatePromise));
|
|
|
|
assertFalse(catPromise.isSuccess());
|
2015-11-25 15:50:30 +01:00
|
|
|
aggregatePromise.setSuccess();
|
2015-06-30 21:31:08 +02:00
|
|
|
assertTrue(catPromise.isSuccess());
|
2015-11-25 15:50:30 +01:00
|
|
|
assertEquals(1, cat.refCnt());
|
|
|
|
cat.release();
|
2015-06-30 21:31:08 +02:00
|
|
|
|
|
|
|
aggregatePromise = newPromise();
|
|
|
|
assertSame(mouse, writeQueue.remove(5, aggregatePromise));
|
|
|
|
assertFalse(mouseDone);
|
2015-11-25 15:50:30 +01:00
|
|
|
aggregatePromise.setSuccess();
|
2015-06-30 21:31:08 +02:00
|
|
|
assertTrue(mouseSuccess);
|
2015-11-25 15:50:30 +01:00
|
|
|
assertEquals(1, mouse.refCnt());
|
|
|
|
mouse.release();
|
2015-06-30 21:31:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testReadEmptyQueueReturnsEmptyBuffer() {
|
2015-11-25 15:50:30 +01:00
|
|
|
// Not used in this test.
|
|
|
|
cat.release();
|
|
|
|
mouse.release();
|
|
|
|
|
2015-06-30 21:31:08 +02:00
|
|
|
assertQueueSize(0, true);
|
2017-10-21 14:30:16 +02:00
|
|
|
ChannelPromise aggregatePromise = newPromise();
|
2015-07-16 00:04:22 +02:00
|
|
|
assertEquals("", dequeue(Integer.MAX_VALUE, aggregatePromise));
|
2015-06-30 21:31:08 +02:00
|
|
|
assertQueueSize(0, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testReleaseAndFailAll() {
|
|
|
|
writeQueue.add(cat, catPromise);
|
|
|
|
writeQueue.add(mouse, mouseListener);
|
|
|
|
RuntimeException cause = new RuntimeException("ooops");
|
|
|
|
writeQueue.releaseAndFailAll(cause);
|
2017-10-21 14:30:16 +02:00
|
|
|
ChannelPromise aggregatePromise = newPromise();
|
2015-06-30 21:31:08 +02:00
|
|
|
assertQueueSize(0, true);
|
|
|
|
assertEquals(0, cat.refCnt());
|
|
|
|
assertEquals(0, mouse.refCnt());
|
|
|
|
assertSame(cause, catPromise.cause());
|
2015-07-16 00:04:22 +02:00
|
|
|
assertEquals("", dequeue(Integer.MAX_VALUE, aggregatePromise));
|
2015-06-30 21:31:08 +02:00
|
|
|
assertQueueSize(0, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testEmptyBuffersAreCoalesced() {
|
2015-07-16 00:04:22 +02:00
|
|
|
ByteBuf empty = Unpooled.buffer(0, 1);
|
2015-06-30 21:31:08 +02:00
|
|
|
assertQueueSize(0, true);
|
|
|
|
writeQueue.add(cat, catPromise);
|
|
|
|
writeQueue.add(empty, emptyPromise);
|
|
|
|
assertQueueSize(3, false);
|
2017-10-21 14:30:16 +02:00
|
|
|
ChannelPromise aggregatePromise = newPromise();
|
2015-07-16 00:04:22 +02:00
|
|
|
assertEquals("cat", dequeue(3, aggregatePromise));
|
2015-06-30 21:31:08 +02:00
|
|
|
assertQueueSize(0, true);
|
|
|
|
assertFalse(catPromise.isSuccess());
|
|
|
|
assertFalse(emptyPromise.isSuccess());
|
2015-11-25 15:50:30 +01:00
|
|
|
aggregatePromise.setSuccess();
|
2015-06-30 21:31:08 +02:00
|
|
|
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);
|
2017-10-21 14:30:16 +02:00
|
|
|
ChannelPromise aggregatePromise = newPromise();
|
2015-07-16 00:04:22 +02:00
|
|
|
assertEquals("catmouse", dequeue(8, aggregatePromise));
|
2015-06-30 21:31:08 +02:00
|
|
|
assertQueueSize(0, true);
|
|
|
|
assertFalse(catPromise.isSuccess());
|
|
|
|
assertFalse(mouseDone);
|
2015-11-25 15:50:30 +01:00
|
|
|
aggregatePromise.setSuccess();
|
2015-06-30 21:31:08 +02:00
|
|
|
assertTrue(catPromise.isSuccess());
|
|
|
|
assertTrue(mouseSuccess);
|
2015-11-25 15:50:30 +01:00
|
|
|
assertEquals(0, cat.refCnt());
|
|
|
|
assertEquals(0, mouse.refCnt());
|
2015-06-30 21:31:08 +02:00
|
|
|
}
|
|
|
|
|
2017-10-21 14:30:16 +02:00
|
|
|
@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();
|
2015-06-30 21:31:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void assertQueueSize(int size, boolean isEmpty) {
|
|
|
|
assertEquals(size, writeQueue.readableBytes());
|
|
|
|
if (isEmpty) {
|
|
|
|
assertTrue(writeQueue.isEmpty());
|
|
|
|
} else {
|
|
|
|
assertFalse(writeQueue.isEmpty());
|
|
|
|
}
|
|
|
|
}
|
2015-07-16 00:04:22 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2015-06-30 21:31:08 +02:00
|
|
|
}
|