package org.limewire.rudp; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.util.Random; import java.util.concurrent.Executor; import org.limewire.concurrent.ExecutorsHelper; import org.limewire.listener.AsynchronousMulticasterImpl; import org.limewire.listener.EventListenerList; import org.limewire.nio.observer.TransportListener; import org.limewire.rudp.messages.SynMessage.Role; import org.limewire.util.BaseTestCase; import junit.framework.Test; public class UDPSocketChannelTest extends BaseTestCase { public UDPSocketChannelTest(String name) { super(name); } public static Test suite() { return buildTestSuite(UDPSocketChannelTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } public void testRead() throws Exception { StubProcessor stub = new StubProcessor(); DataWindow window = new DataWindow(8, 1); stub.setReadWindow(window); UDPSocketChannel channel = new UDPSocketChannel(stub, Role.UNDEFINED); ByteBuffer buffer = ByteBuffer.allocate(1000); assertEquals(0, channel.read(buffer)); Random rnd = new Random(); // Normal read. byte[] data = new byte[400]; rnd.nextBytes(data); window.addData(MessageHelper.createDataMessage((byte)0, 1, data, data.length)); assertEquals(400, channel.read(buffer)); assertEquals(data, buffer.array(), 0, 400); assertFalse(stub.isSentKeepAlive()); } public void testReadDataOverBufferLength() throws Exception { StubProcessor stub = new StubProcessor(); DataWindow window = new DataWindow(8, 1); stub.setReadWindow(window); UDPSocketChannel channel = new UDPSocketChannel(stub, Role.UNDEFINED); ByteBuffer buffer = ByteBuffer.allocate(1000); assertEquals(0, channel.read(buffer)); Random rnd = new Random(); byte[] data = new byte[1500]; rnd.nextBytes(data); buffer.clear(); window.addData(MessageHelper.createDataMessage((byte)0, 1, data, data.length)); assertEquals(1000, channel.read(buffer)); assertEquals(data, 0, 1000, buffer.array()); buffer.clear(); assertEquals(500, channel.read(buffer)); assertEquals(data, 1000, 500, buffer.array(), 0, 500); assertFalse(stub.isSentKeepAlive()); } public void testReadDataInRightOrder() throws Exception { StubProcessor stub = new StubProcessor(); DataWindow window = new DataWindow(8, 1); stub.setReadWindow(window); UDPSocketChannel channel = new UDPSocketChannel(stub, Role.UNDEFINED); ByteBuffer buffer = ByteBuffer.allocate(1000); assertEquals(0, channel.read(buffer)); Random rnd = new Random(); byte[] data = new byte[103]; byte[] data2 = new byte[105]; rnd.nextBytes(data); rnd.nextBytes(data2); buffer.clear(); window.addData(MessageHelper.createDataMessage((byte)0, 2, data2, data2.length)); window.addData(MessageHelper.createDataMessage((byte)0, 1, data, data.length)); assertEquals(208, channel.read(buffer)); assertEquals(data, buffer.array(), 0, 103); assertEquals(data2, buffer.array(), 103, 105); assertFalse(stub.isSentKeepAlive()); } public void testReadEOSWhenClosed() throws Exception { StubProcessor stub = new StubProcessor(); DataWindow window = new DataWindow(8, 1); stub.setReadWindow(window); UDPSocketChannel channel = new UDPSocketChannel(stub, Role.UNDEFINED); ByteBuffer buffer = ByteBuffer.allocate(1000); assertEquals(0, channel.read(buffer)); Random rnd = new Random(); byte[] data = new byte[101]; rnd.nextBytes(data); buffer.clear(); window.addData(MessageHelper.createDataMessage((byte)0, 1, data, data.length)); stub.close(); assertEquals(101, channel.read(buffer)); assertEquals(data, buffer.array(), 0, 101); assertEquals(-1, channel.read(buffer)); channel.close(); try { channel.read(buffer); fail("shouldn't have read!"); } catch(ClosedChannelException expected) {} assertFalse(stub.isSentKeepAlive()); } public void testReadSuccessiveMessages() throws Exception { StubProcessor stub = new StubProcessor(); DataWindow window = new DataWindow(8, 1); stub.setReadWindow(window); UDPSocketChannel channel = new UDPSocketChannel(stub, Role.UNDEFINED); ByteBuffer buffer = ByteBuffer.allocate(1000); assertEquals(0, channel.read(buffer)); Random rnd = new Random(); byte[] data = new byte[300]; rnd.nextBytes(data); window.addData(MessageHelper.createDataMessage((byte)0, 1, data, data.length)); assertEquals(300, channel.read(buffer)); assertEquals(data, buffer.array(), 0, 300); data = new byte[200]; rnd.nextBytes(data); window.addData(MessageHelper.createDataMessage((byte)0, 2, data, data.length)); assertEquals(200, channel.read(buffer)); assertEquals(data, buffer.array(), 300, 200); assertFalse(stub.isSentKeepAlive()); } public void testReadTriggersKeepAlive() throws Exception { StubProcessor stub = new StubProcessor(); DataWindow window = new DataWindow(4, 1); stub.setReadWindow(window); UDPSocketChannel channel = new UDPSocketChannel(stub, Role.UNDEFINED); ByteBuffer buffer = ByteBuffer.allocate(1000); assertEquals(0, channel.read(buffer)); Random rnd = new Random(); byte[] data1 = new byte[101]; byte[] data2 = new byte[102]; byte[] data3 = new byte[103]; byte[] data4 = new byte[104]; rnd.nextBytes(data1); rnd.nextBytes(data2); rnd.nextBytes(data3); rnd.nextBytes(data4); assertEquals(4, window.getWindowSpace()); window.addData(MessageHelper.createDataMessage((byte)0, 1, data1, data1.length)); window.addData(MessageHelper.createDataMessage((byte)0, 2, data2, data2.length)); window.addData(MessageHelper.createDataMessage((byte)0, 3, data3, data3.length)); window.addData(MessageHelper.createDataMessage((byte)0, 4, data4, data4.length)); assertEquals(0, window.getWindowSpace()); assertFalse(stub.isSentKeepAlive()); assertEquals(410, channel.read(buffer)); assertTrue(stub.isSentKeepAlive()); assertEquals(data1, buffer.array(), 0, 101); assertEquals(data2, buffer.array(), 101, 102); assertEquals(data3, buffer.array(), 203, 103); assertEquals(data4, buffer.array(), 306, 104); } public void testReadNoSpaceDoesntTriggerKeepAlive() throws Exception { StubProcessor stub = new StubProcessor(); DataWindow window = new DataWindow(4, 1); stub.setReadWindow(window); UDPSocketChannel channel = new UDPSocketChannel(stub, Role.UNDEFINED); ByteBuffer buffer = ByteBuffer.allocate(0); assertEquals(0, channel.read(buffer)); byte[] data1 = new byte[101]; byte[] data2 = new byte[102]; byte[] data3 = new byte[103]; byte[] data4 = new byte[104]; assertEquals(4, window.getWindowSpace()); window.addData(MessageHelper.createDataMessage((byte)0, 1, data1, data1.length)); window.addData(MessageHelper.createDataMessage((byte)0, 2, data2, data2.length)); window.addData(MessageHelper.createDataMessage((byte)0, 3, data3, data3.length)); window.addData(MessageHelper.createDataMessage((byte)0, 4, data4, data4.length)); assertEquals(0, window.getWindowSpace()); assertFalse(stub.isSentKeepAlive()); assertEquals(0, channel.read(buffer)); assertFalse(stub.isSentKeepAlive()); } public void testWrite() throws Exception { StubProcessor stub = new StubProcessor(); UDPSocketChannel channel = new UDPSocketChannel(stub, Role.UNDEFINED); byte[] data = new byte[100]; Random rnd = new Random(); rnd.nextBytes(data); assertEquals(0, channel.getNumberOfPendingChunks()); ByteBuffer wrapped = ByteBuffer.wrap(data); assertEquals(100, channel.write(wrapped)); assertFalse(wrapped.hasRemaining()); assertEquals(1, channel.getNumberOfPendingChunks()); ByteBuffer written = channel.getNextChunk(); assertEquals(0, written.position()); assertEquals(100, written.limit()); assertEquals(data, written.array(), 0, 100); assertEquals(null, channel.getNextChunk()); assertEquals(0, channel.getNumberOfPendingChunks()); } public void testWritesCoalesced() throws Exception { StubProcessor stub = new StubProcessor(); UDPSocketChannel channel = new UDPSocketChannel(stub, Role.UNDEFINED); byte[] data = new byte[100]; Random rnd = new Random(); rnd.nextBytes(data); assertEquals(0, channel.getNumberOfPendingChunks()); ByteBuffer wrapped = ByteBuffer.wrap(data); assertEquals(100, channel.write(wrapped)); assertFalse(wrapped.hasRemaining()); assertEquals(1, channel.getNumberOfPendingChunks()); byte[] data2 = new byte[300]; rnd.nextBytes(data2); wrapped = ByteBuffer.wrap(data2); assertEquals(300, channel.write(wrapped)); assertFalse(wrapped.hasRemaining()); assertEquals(1, channel.getNumberOfPendingChunks()); ByteBuffer written = channel.getNextChunk(); assertEquals(0, written.position()); assertEquals(400, written.limit()); assertEquals(data, written.array(), 0, 100); assertEquals(data2, written.array(), 100, 300); assertEquals(null, channel.getNextChunk()); assertEquals(0, channel.getNumberOfPendingChunks()); } public void testWriteStopsAtChunkLimit() throws Exception { StubProcessor stub = new StubProcessor(); UDPSocketChannel channel = new UDPSocketChannel(stub, Role.UNDEFINED); assertEquals(0, stub.getChunkLimit()); byte[] data = new byte[1000]; Random rnd = new Random(); rnd.nextBytes(data); assertEquals(0, channel.getNumberOfPendingChunks()); ByteBuffer wrapped = ByteBuffer.wrap(data); assertEquals(512, channel.write(wrapped)); assertTrue(wrapped.hasRemaining()); assertEquals(488, wrapped.remaining()); assertEquals(1, channel.getNumberOfPendingChunks()); ByteBuffer written = channel.getNextChunk(); assertEquals(0, written.position()); assertEquals(512, written.limit()); assertEquals(512, written.capacity()); assertEquals(data, 0, 512, written.array()); assertEquals(null, channel.getNextChunk()); assertEquals(0, channel.getNumberOfPendingChunks()); assertEquals(488, channel.write(wrapped)); assertFalse(wrapped.hasRemaining()); assertEquals(1, channel.getNumberOfPendingChunks()); written = channel.getNextChunk(); assertEquals(0, written.position()); assertEquals(488, written.limit()); assertEquals(512, written.capacity()); assertEquals(data, 512, 488, written.array(), 0, 488); } public void testWriteUpToChunkLimit() throws Exception { StubProcessor stub = new StubProcessor(); UDPSocketChannel channel = new UDPSocketChannel(stub, Role.UNDEFINED); stub.setChunkLimit(7); // writes up to limit + 1 byte[] data = new byte[512 * 8 + 400]; Random rnd = new Random(); rnd.nextBytes(data); assertEquals(0, channel.getNumberOfPendingChunks()); ByteBuffer wrapped = ByteBuffer.wrap(data); assertEquals(512 * 8, channel.write(wrapped)); assertTrue(wrapped.hasRemaining()); assertEquals(400, wrapped.remaining()); assertEquals(8, channel.getNumberOfPendingChunks()); for(int i = 0; i < 8; i++) { ByteBuffer written = channel.getNextChunk(); assertEquals(0, written.position()); assertEquals(512, written.limit()); assertEquals(512, written.capacity()); assertEquals(data, i * 512, 512, written.array()); } assertEquals(null, channel.getNextChunk()); assertEquals(0, channel.getNumberOfPendingChunks()); assertEquals(400, channel.write(wrapped)); assertFalse(wrapped.hasRemaining()); assertEquals(1, channel.getNumberOfPendingChunks()); ByteBuffer written = channel.getNextChunk(); assertEquals(0, written.position()); assertEquals(400, written.limit()); assertEquals(512, written.capacity()); assertEquals(data, 512 * 8, 400, written.array(), 0, 400); } public void testWriteWakesupEvents() throws Exception { StubProcessor stub = new StubProcessor(); UDPSocketChannel channel = new UDPSocketChannel(stub, Role.UNDEFINED); byte[] data = new byte[100]; Random rnd = new Random(); rnd.nextBytes(data); assertFalse(stub.isWokeupWriteEvent()); assertFalse(stub.isWokeupWriteEventForced()); assertEquals(0, channel.getNumberOfPendingChunks()); ByteBuffer wrapped = ByteBuffer.wrap(data); assertEquals(100, channel.write(wrapped)); assertTrue(stub.isWokeupWriteEvent()); assertTrue(stub.isWokeupWriteEventForced()); assertFalse(wrapped.hasRemaining()); assertEquals(1, channel.getNumberOfPendingChunks()); stub.clear(); byte[] data2 = new byte[100]; rnd.nextBytes(data2); wrapped = ByteBuffer.wrap(data2); assertEquals(100, channel.write(wrapped)); assertFalse(stub.isWokeupWriteEvent()); assertFalse(stub.isWokeupWriteEventForced()); assertEquals(1, channel.getNumberOfPendingChunks()); ByteBuffer written = channel.getNextChunk(); assertEquals(0, written.position()); assertEquals(200, written.limit()); assertEquals(data, written.array(), 0, 100); assertEquals(data2, written.array(), 100, 100); assertEquals(null, channel.getNextChunk()); assertEquals(0, channel.getNumberOfPendingChunks()); stub.clear(); data = new byte[100]; rnd.nextBytes(data); wrapped = ByteBuffer.wrap(data); assertEquals(100, channel.write(wrapped)); assertTrue(stub.isWokeupWriteEvent()); assertFalse(stub.isWokeupWriteEventForced()); assertEquals(1, channel.getNumberOfPendingChunks()); written = channel.getNextChunk(); assertEquals(0, written.position()); assertEquals(100, written.limit()); assertEquals(data, written.array(), 0, 100); } public void testWriteClosed() throws Exception { StubProcessor stub = new StubProcessor(); UDPSocketChannel channel = new UDPSocketChannel(stub, Role.UNDEFINED); ByteBuffer buffer = ByteBuffer.allocate(0); assertEquals(0, channel.write(buffer)); stub.close(); try { channel.write(buffer); fail("should have failed"); } catch(ClosedChannelException expected) {} stub.setClosed(false); assertEquals(0, channel.write(buffer)); channel.close(); try { channel.write(buffer); fail("should have failed"); } catch(ClosedChannelException expected) {} } public void testHasBufferedOutput() throws Exception { StubProcessor stub = new StubProcessor(); DataWindow window = new DataWindow(8, 1); stub.setReadWindow(window); UDPSocketChannel channel = new UDPSocketChannel(stub, Role.UNDEFINED); assertFalse(channel.hasBufferedOutput()); channel.write(ByteBuffer.allocate(1000)); assertTrue(channel.hasBufferedOutput()); channel.handleWrite(); assertTrue(channel.hasBufferedOutput()); while (channel.getNextChunk() != null); assertFalse(channel.hasBufferedOutput()); channel.write(ByteBuffer.allocate(0)); assertFalse(channel.hasBufferedOutput()); } private static class StubProcessor extends UDPConnectionProcessor { private boolean closed; private boolean connected; private InetSocketAddress addr; private DataWindow readWindow; private boolean preparedOpenConnection; private boolean prepareOpenConnectionRetValue; private boolean sentKeepAlive; private boolean wokeupWriteEvent; private boolean wokeupWriteEventWasForced; private int chunkLimit; private boolean connecting; void clear() { closed = false; connected = false; addr = null; readWindow = null; preparedOpenConnection = false; prepareOpenConnectionRetValue = false; sentKeepAlive = false; wokeupWriteEvent = false; wokeupWriteEventWasForced = false; chunkLimit = 0; connecting = false; } StubProcessor() { super(new StubUDPSocketChannel(), new DefaultRUDPContext(), Role.UNDEFINED, new EventListenerList<UDPSocketChannelConnectionEvent>()); } @Override protected void close() throws IOException { closed = true; } @Override protected void connect(InetSocketAddress addr) throws IOException { connected = true; this.addr = addr; } @Override protected DataWindow getReadWindow() { return readWindow; } @Override protected InetSocketAddress getSocketAddress() { return addr; } @Override protected boolean isClosed() { return closed; } @Override protected boolean isConnected() { return connected; } @Override protected boolean isConnecting() { return connecting; } @Override protected boolean prepareOpenConnection() throws IOException { preparedOpenConnection = true; return prepareOpenConnectionRetValue; } @Override protected void sendKeepAlive() { sentKeepAlive = true; } @Override protected void wakeupWriteEvent(boolean force) { wokeupWriteEvent = true; wokeupWriteEventWasForced = force; } @Override protected int getChunkLimit() { return chunkLimit; } public boolean isPreparedOpenConnection() { return preparedOpenConnection; } public boolean isSentKeepAlive() { return sentKeepAlive; } public boolean isWokeupWriteEvent() { return wokeupWriteEvent; } public boolean isWokeupWriteEventForced() { return wokeupWriteEventWasForced; } public void setAddr(InetSocketAddress addr) { this.addr = addr; } public void setChunkLimit(int chunkLimit) { this.chunkLimit = chunkLimit; } public void setClosed(boolean closed) { this.closed = closed; } public void setConnected(boolean connected) { this.connected = connected; } public void setPrepareOpenConnectionRetValue(boolean prepareOpenConnectionRetValue) { this.prepareOpenConnectionRetValue = prepareOpenConnectionRetValue; } public void setReadWindow(DataWindow readWindow) { this.readWindow = readWindow; } public void setConnecting(boolean connecting) { this.connecting = connecting; } } private static StubListener listener = new StubListener(); private static RUDPContext context = new DefaultRUDPContext(listener); private static Executor executor = ExecutorsHelper.newProcessingQueue("TestEventThread"); private static UDPSelectorProvider provider = new UDPSelectorProvider(context, new AsynchronousMulticasterImpl<UDPSocketChannelConnectionEvent>(executor)); private static class StubUDPSocketChannel extends UDPSocketChannel { StubUDPSocketChannel() { super(provider, context, Role.UNDEFINED, new EventListenerList<UDPSocketChannelConnectionEvent>()); } } private static class StubListener implements TransportListener { public void eventPending() { } } }