package org.webpieces.nio.test.tcp; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import org.webpieces.util.logging.Logger; import junit.framework.TestCase; import org.webpieces.nio.api.channels.NioException; import org.webpieces.nio.api.channels.TCPChannel; import org.webpieces.nio.api.deprecated.ChannelService; import org.webpieces.nio.api.deprecated.ChannelServiceFactory; import org.webpieces.nio.api.deprecated.ConnectionCallback; import org.webpieces.nio.api.deprecated.Settings; import org.webpieces.nio.api.handlers.DataListener; import org.webpieces.nio.api.libs.BufferFactory; import org.webpieces.nio.api.libs.BufferHelper; import org.webpieces.nio.api.libs.FactoryCreator; import org.webpieces.nio.api.testutil.CloneByteBuffer; import org.webpieces.nio.api.testutil.HandlerForTests; import org.webpieces.nio.api.testutil.MockDataHandler; import org.webpieces.nio.api.testutil.MockNIOServer; import biz.xsoftware.mock.CalledMethod; import biz.xsoftware.mock.MockObject; import biz.xsoftware.mock.MockObjectFactory; public abstract class ZNioSuperclassTest extends TestCase { private static final Logger log = LoggerFactory.getLogger(ZNioSuperclassTest.class); private BufferFactory bufFactory; private InetSocketAddress svrAddr; private ChannelService chanMgr; private InetAddress loopBack; private InetSocketAddress loopBackAnyPort; private BufferHelper helper = ChannelServiceFactory.bufferHelper(null); private MockObject mockHandler; private MockObject mockConnect; private TCPChannel client1; private MockNIOServer mockServer; protected abstract ChannelService getClientChanMgr(); protected abstract ChannelService getServerChanMgr(); protected abstract Settings getServerFactoryHolder(); protected abstract Settings getClientFactoryHolder(); protected abstract String getChannelImplName(); protected abstract String getServerChannelImplName(); public ZNioSuperclassTest() { if(bufFactory == null) { Map<String, Object> map = new HashMap<String, Object>(); map.put(FactoryCreator.KEY_IS_DIRECT, false); FactoryCreator creator = FactoryCreator.createFactory(null); bufFactory = creator.createBufferFactory(map); } } protected void setUp() throws Exception { HandlerForTests.setupLogging(); //here I keep using the same channel manager on purpose, just //so we get testing between tests that the channel manager shutdown //and started back up cleanly..... if(chanMgr == null) { chanMgr = getClientChanMgr(); } if(mockServer == null) { ChannelService svcChanMgr = getServerChanMgr(); mockServer = new MockNIOServer(svcChanMgr, getServerFactoryHolder()); } chanMgr.start(); svrAddr = mockServer.start(); log.trace("server port ="+svrAddr); loopBack = InetAddress.getByName("127.0.0.1"); loopBackAnyPort = new InetSocketAddress(loopBack, 0); mockHandler = MockObjectFactory.createMock(DataListener.class); mockHandler.setDefaultBehavior("incomingData", new CloneByteBuffer()); mockConnect = MockObjectFactory.createMock(ConnectionCallback.class); client1 = chanMgr.createTCPChannel("ClientChannel", getClientFactoryHolder()); } protected void tearDown() throws Exception { log.info("CHAN MGR STOP"); chanMgr.stop(); chanMgr = null; log.info("MOCK SERVER STOP"); mockServer.stop(); log.info("check for warns"); HandlerForTests.checkForWarnings(); log.info("done"); } //TODO: write test that proves if data comes between calls to Selector.select, //the next selector.select will still trigger even if we cleared the key from //the list of selected keys. public void testDataComesBetweenSelects() throws Exception { } // public void testClientThrowsIntoConnectCallback() throws Exception { // //make sure we are testing the right one.... // Class c = Class.forName(getChannelImplName()); // assertEquals("should be instance of correct channel type", c, client1.getClass()); // // String msg = "some exception message"; // IOException e = new IOException(msg); // mockConnect.addThrowException("connected", e); // // client1.bind(loopBackAnyPort); // client1.registerForReads((DataHandler)mockHandler); // client1.connect(svrAddr, (ConnectCallback)mockConnect); // // mockConnect.expect("connected"); // TCPChannel svrChan = (TCPChannel)mockServer.expect(MockNIOServer.CONNECTED).getAllParams()[0]; // assertEquals("should be instance of correct channel type", c, svrChan.getClass()); // // verifyDataPassing(svrChan); // verifyTearDown(); // } /** * This cannot pass on linux right now as warnings end up in the log * from reads firing even though there should be none if not connected. * We can fix this later if it is needed. * * Order between TCPChannel.connect and TCPChannel.registerForRead * results in different code paths...this is one of the tests. * @throws Exception */ public void xtestRegisterForReadsBeforeConnect() throws Exception { //make sure we are testing the right one.... Class c = Class.forName(getChannelImplName()); assertEquals("should be instance of correct channel type", c, client1.getClass()); client1.bind(loopBackAnyPort); client1.registerForReads((DataListener)mockHandler); client1.oldConnect(svrAddr, (ConnectionCallback)mockConnect); mockConnect.expect("connected"); boolean isConnected = client1.isConnected(); assertTrue("Client should be connected", isConnected); TCPChannel svrChan = ZNioFailureSuperclass.expectServerChannel(mockServer, c); verifyDataPassing(svrChan); verifyTearDown(); } /** * */ public void testConnectClose() throws Exception { //make sure we are testing the right one.... // Class c = Class.forName(getChannelImplName()); // assertEquals("should be instance of secure channel", c, client1.getClass()); //no bind, just do connect to test port is not zero client1.oldConnect(svrAddr, (ConnectionCallback)mockConnect); mockConnect.expect("connected"); mockServer.expect(MockNIOServer.CONNECTED); verifyTearDown(); } /** * Order between TCPChannel.connect and TCPChannel.registerForRead * results in different code paths...this is one of the tests. * @throws Exception */ public void testRegisterForReadsAfterConnect() throws Exception { //make sure we are testing the right one.... Class c = Class.forName(getChannelImplName()); assertEquals("should be instance of secure channel", c, client1.getClass()); //no bind, just do connect to test port is not zero client1.oldConnect(svrAddr, (ConnectionCallback)mockConnect); mockConnect.expect("connected"); log.info("connected"); boolean isConnected = client1.isConnected(); assertTrue("Client should be connected", isConnected); InetSocketAddress localAddr = client1.getLocalAddress(); assertTrue("Port should not be 0", localAddr.getPort() != 0); TCPChannel svrChan = ZNioFailureSuperclass.expectServerChannel(mockServer, c); client1.registerForReads((DataListener)mockHandler); log.info("data passing"); verifyDataPassing(svrChan); log.info("teardown"); verifyTearDown(); log.info("done"); } /** * Test closing socket before ChannelManager shutdown works. * @throws Exception */ public void testCloseSvrSocketBeforeChannelMgrShutdown() throws Exception { Class c = Class.forName(getChannelImplName()); client1.bind(loopBackAnyPort); client1.oldConnect(svrAddr); boolean isConnected = client1.isConnected(); assertTrue("Client should be connected", isConnected); TCPChannel svrChan = ZNioFailureSuperclass.expectServerChannel(mockServer, c); client1.registerForReads((DataListener)mockHandler); verifyDataPassing(svrChan); svrChan.oldClose(); //shutdown channel manager first mockServer.stop(); mockHandler.expect(MockNIOServer.FAR_END_CLOSED); } /** * There was a bug where calling ChannelManager.shutdown before closing any sockets * registered with that ChannelManager cannot be closed...well, at least this test * proves when we close the test, the other side should receive that -1 indicating * the far end closed the socket. * * @throws Exception */ public void testCloseSocketAfterChannelMgrShutdown() throws Exception { Class c = Class.forName(getChannelImplName()); client1.bind(loopBackAnyPort); client1.oldConnect(svrAddr); TCPChannel svrChan = ZNioFailureSuperclass.expectServerChannel(mockServer, c); client1.registerForReads((DataListener)mockHandler); verifyDataPassing(svrChan); //shutdown channel manager first....should all sockets be closed? Right now //someone has to manually close all accepted sockets...ie. client responsibility. svrChan.oldClose(); mockServer.stop(); //notice the Channelmanager on the client side has not shut down so we should //see a close event.... mockHandler.expect(MockNIOServer.FAR_END_CLOSED); } public void testUnregisterReregisterForReads() throws Exception { Class c = Class.forName(getChannelImplName()); client1.bind(loopBackAnyPort); client1.oldConnect(svrAddr); TCPChannel svrChan = ZNioFailureSuperclass.expectServerChannel(mockServer, c); client1.registerForReads((DataListener)mockHandler); ByteBuffer b = verifyDataPassing(svrChan); client1.unregisterForReads(); b.rewind(); svrChan.oldWrite(b); Thread.sleep(5000); mockHandler.expect(MockObject.NONE); client1.registerForReads((DataListener)mockHandler); CalledMethod m = mockHandler.expect(MockNIOServer.INCOMING_DATA); ByteBuffer actualBuf = (ByteBuffer)m.getAllParams()[1]; String result = helper.readString(actualBuf, actualBuf.remaining()); assertEquals("de", result); verifyTearDown(); } /** * the first 1.5.0 jdk threw Errors on this test case * instead of BindException or SocketException. Our ChannelManager * converts the Error back if it is a SocketException. * @throws Exception */ public void testAlreadyBound150Jdk() throws Exception { client1.bind(loopBackAnyPort); try { client1.bind(loopBackAnyPort); fail("Should have thrown SocketException"); } catch(NioException e) {} } /** * This fixes the bug in the jdk where SocketChannel.getLocalPort * return 0 instead of the port that was bound. * * @throws Exception */ public void testTwoBindsOnPortZero() throws Exception { TCPChannel chan1 = chanMgr.createTCPChannel("chan1", getClientFactoryHolder()); TCPChannel chan2 = chanMgr.createTCPChannel("chan2", getClientFactoryHolder()); InetSocketAddress addr = new InetSocketAddress(loopBack, 0); chan1.bind(addr); chan2.bind(addr); int port1 = chan1.getLocalAddress().getPort(); int port2 = chan2.getLocalAddress().getPort(); assertTrue("port1 is zero, this is bad", port1 != 0); assertTrue("port2 is zero, this is bad", port2 != 0); assertTrue("port1==port2, this is bad port1="+port1, port1!=port2); } public void testBindThroughConnect() throws Exception { client1.oldConnect(svrAddr); int port = client1.getLocalAddress().getPort(); assertTrue("port is zero, this is bad", port != 0); mockServer.expect("connected"); //verifyTearDown(); } private ByteBuffer verifyDataPassing(TCPChannel svrChan) throws Exception { ByteBuffer b = ByteBuffer.allocate(10); helper.putString(b, "de"); helper.doneFillingBuffer(b); int expectedWrote = b.remaining(); log.trace("***********************************************"); int actualWrite = client1.oldWrite(b); assertEquals(expectedWrote, actualWrite); CalledMethod m = mockServer.expect(MockNIOServer.INCOMING_DATA); TCPChannel actualChannel = (TCPChannel)m.getAllParams()[0]; Class c = Class.forName(getChannelImplName()); assertEquals("should be correct type of channel", c, actualChannel.getClass()); ByteBuffer actualBuf = (ByteBuffer)m.getAllParams()[1]; String result = helper.readString(actualBuf, actualBuf.remaining()); assertEquals("de", result); b.rewind(); svrChan.oldWrite(b); m = mockHandler.expect(MockDataHandler.INCOMING_DATA); actualBuf = (ByteBuffer)m.getAllParams()[1]; result = helper.readString(actualBuf, actualBuf.remaining()); assertEquals("de", result); return b; } private void verifyTearDown() throws IOException { log.info("local="+client1.getLocalAddress()+" remote="+client1.getRemoteAddress()); log.info("CLIENT1 CLOSE"); client1.oldClose(); mockServer.expect(MockNIOServer.FAR_END_CLOSED); } protected Object getBufFactory() { return bufFactory; } }