package org.limewire.rudp; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.channels.CancelledKeyException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.nio.channels.spi.SelectorProvider; import java.util.Set; import java.util.concurrent.Executor; import org.limewire.listener.AsynchronousMulticasterImpl; import org.limewire.listener.EventListenerList; import org.limewire.nio.observer.TransportListener; import org.limewire.rudp.messages.RUDPMessage; import org.limewire.rudp.messages.SynMessage; import org.limewire.rudp.messages.SynMessage.Role; import org.limewire.rudp.messages.impl.DefaultMessageFactory; import org.limewire.util.BaseTestCase; import org.limewire.concurrent.ExecutorsHelper; import junit.framework.Test; public class UDPMultiplexorTest extends BaseTestCase { public UDPMultiplexorTest(String name) { super(name); } public static Test suite() { return buildTestSuite(UDPMultiplexorTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } 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)); public void testRegister() throws Exception { Selector selector = provider.openSelector(); assertInstanceof(UDPMultiplexor.class, selector); SocketChannel channels[] = new SocketChannel[256]; SelectionKey keys[] = new SelectionKey[256]; for(int i = 0; i < channels.length; i++) { channels[i] = provider.openSocketChannel(); assertFalse(channels[i].isRegistered()); keys[i] = channels[i].register(selector, 1); assertTrue(channels[i].isRegistered()); assertSame(keys[i], channels[i].keyFor(selector)); assertEquals(1, keys[i].interestOps()); assertSame(selector, keys[i].selector()); } Set allKeys = selector.keys(); assertEquals(256, allKeys.size()); for(int i = 0; i < keys.length; i++) assertContains(allKeys, keys[i]); // All could register except the last (they were all full) int n = selector.selectNow(); assertEquals(1, n); Set selected = selector.selectedKeys(); assertEquals(1, selected.size()); SelectionKey key = (SelectionKey)selected.iterator().next(); assertFalse(key.isValid()); try { assertEquals(0, key.readyOps()); fail("should have failed"); } catch(CancelledKeyException expected) {} assertSame(keys[255], key); assertNotContains(selector.keys(), key); } public void testClosedChannelsRemoved() throws Exception { Selector selector = provider.openSelector(); assertInstanceof(UDPMultiplexor.class, selector); SocketChannel channel = provider.openSocketChannel(); SelectionKey key = channel.register(selector, 0); assertSame(key, channel.keyFor(selector)); Set keys = selector.keys(); assertEquals(1, keys.size()); assertContains(keys, key); assertTrue(key.isValid()); channel.close(); assertFalse(key.isValid()); assertSame(key, channel.keyFor(selector)); // Selector still has it because no select has been performed. keys = selector.keys(); assertEquals(1, keys.size()); assertContains(keys, key); // Now do a select and it'll return remove the channel int n = selector.selectNow(); assertEquals(0, n); assertEquals(0, selector.keys().size()); } public void testSelectedKeys() throws Exception { Selector selector = provider.openSelector(); assertInstanceof(UDPMultiplexor.class, selector); StubUDPSocketChannel channel = new StubUDPSocketChannel(); SelectionKey key = channel.register(selector, 0); assertEquals(0, selector.selectNow()); key.interestOps(SelectionKey.OP_CONNECT); assertEquals(0, selector.selectNow()); channel.setReadyOps(SelectionKey.OP_CONNECT); assertEquals(1, selector.selectNow()); Set selectedKeys = selector.selectedKeys(); assertEquals(1, selectedKeys.size()); assertContains(selectedKeys, key); assertEquals(SelectionKey.OP_CONNECT, key.readyOps()); key.interestOps(0); assertEquals(0, selector.selectNow()); key.interestOps(SelectionKey.OP_READ); channel.setReadyOps(SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT); assertEquals(0, selector.selectNow()); channel.setReadyOps(SelectionKey.OP_WRITE | SelectionKey.OP_READ); assertEquals(1, selector.selectNow()); selectedKeys = selector.selectedKeys(); assertEquals(1, selectedKeys.size()); assertContains(selectedKeys, key); assertEquals(SelectionKey.OP_READ, key.readyOps()); key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); assertEquals(1, selector.selectNow()); selectedKeys = selector.selectedKeys(); assertEquals(1, selectedKeys.size()); assertContains(selectedKeys, key); assertEquals(SelectionKey.OP_READ | SelectionKey.OP_WRITE, key.readyOps()); } /** * Tests that if a channel becomes ready for * some event, a provided listener is notified **/ public void testTransportEventGenerated() throws Exception { Selector selector = provider.openSelector(); assertInstanceof(UDPMultiplexor.class, selector); StubUDPSocketChannel channel = new StubUDPSocketChannel(); channel.addr = new InetSocketAddress(InetAddress.getLocalHost(),1); SelectionKey key = channel.register(selector, 0); assertEquals(0, selector.selectNow()); key.interestOps(SelectionKey.OP_CONNECT); SynMessage syn = new DefaultMessageFactory().createSynMessage((byte)1, Role.UNDEFINED); UDPMultiplexor plexor = (UDPMultiplexor) selector; StubProcessor processor = (StubProcessor)channel.getProcessor(); assertNull(processor.msg); listener.notified = false; // send a message, do not change readiness. No notification plexor.routeMessage(syn, channel.addr); assertSame(syn,processor.msg); assertFalse(listener.notified); // send a message, change readiness. Should be notified channel.setReadyOps(1); plexor.routeMessage(syn, channel.addr); assertTrue(listener.notified); } private static class StubUDPSocketChannel extends UDPSocketChannel { private StubProcessor stubProcessor = new StubProcessor(this); InetSocketAddress addr; StubUDPSocketChannel() { super((SelectorProvider)null, context, Role.UNDEFINED, new EventListenerList<UDPSocketChannelConnectionEvent>()); } @Override UDPConnectionProcessor getProcessor() { return stubProcessor; } void setReadyOps(int readyOps) { stubProcessor.setReadyOps(readyOps); } @Override public InetSocketAddress getRemoteSocketAddress() { return addr; } @Override public boolean isConnectionPending() { return true; } } private static class StubProcessor extends UDPConnectionProcessor { private int readyOps; RUDPMessage msg; StubProcessor(UDPSocketChannel channel) { super(channel, context, Role.UNDEFINED, new EventListenerList<UDPSocketChannelConnectionEvent>()); } @Override protected int readyOps() { return readyOps; } protected void setReadyOps(int readyOps) { this.readyOps = readyOps; } @Override protected void handleMessage(RUDPMessage msg) { this.msg = msg; } } private static class StubListener implements TransportListener { boolean notified; public void eventPending() { notified = true; } } }