/**
 * Normally I would not separate out one class for testing, but when this is integrated with the
 * ChanMgr, testing becomes non-deterministic with packets being separated and such. This allows
 * more deterministic testing to fully test AsynchSSLEngine.
 *
 * @author dean.hiller
 */
public class TestNewAsynchSSLEngine2 extends TestCase {

  private static final Logger log = Logger.getLogger(TestNewAsynchSSLEngine2.class.getName());

  private BufferHelper helper = ChannelServiceFactory.bufferHelper(null);
  private MockObject serverList = MockObjectFactory.createMock(SSLListener.class);
  private MockObject clientList = MockObjectFactory.createMock(SSLListener.class);
  private AsyncSSLEngine serverEngine;
  private AsyncSSLEngine clientEngine;

  @Override
  protected void setUp() throws Exception {
    HandlerForTests.setupLogging();

    serverList.setDefaultBehavior("packetEncrypted", new CloneByteBuffer());
    clientList.setDefaultBehavior("packetEncrypted", new CloneByteBuffer());
    SSLEngineFactory sslEngineFactory = new MockSSLEngineFactory();
    FactoryCreator creator = FactoryCreator.createFactory(null);

    SSLEngine wrappedSvr = sslEngineFactory.createEngineForServerSocket();
    serverEngine = creator.createSSLEngine("[serverAsynch] ", wrappedSvr, null);
    serverEngine.setListener((SSLListener) serverList);

    SSLEngine wrappedClient = sslEngineFactory.createEngineForSocket();
    clientEngine = creator.createSSLEngine("[clientEngine] ", wrappedClient, null);
    clientEngine.setListener((SSLListener) clientList);
  }

  @Override
  protected void tearDown() throws Exception {
    if (!clientEngine.isClosed()) closeWithExpects(clientEngine, clientList);
    if (!serverEngine.isClosed()) closeWithExpects(serverEngine, serverList);

    HandlerForTests.checkForWarnings();
    clientList.expect(MockObject.NONE);
    serverList.expect(MockObject.NONE);
  }

  private void closeWithExpects(AsyncSSLEngine engine, MockObject sslListener) throws IOException {
    TestNewAsynchSSLEngine.closeWithExpects(engine, sslListener);
    //		engine.close();
    //
    //		String[] methodNames = new String[2];
    //		methodNames[0] = "packetEncrypted";
    //		methodNames[1] = "closed";
    //		sslListener.expect(methodNames);
  }

  /**
   * This tests the Runnable task being run in between packets such that it should not cause packet
   * feeds to create new Runnables, and can run before all packets are in.
   */
  public void testDelayedRunTask() throws Exception {
    log.fine("B*******************************************");
    clientEngine.beginHandshake();
    CalledMethod m = clientList.expect("packetEncrypted");
    ByteBuffer b = (ByteBuffer) m.getAllParams()[0];

    serverEngine.feedEncryptedPacket(b, null);
    m = serverList.expect("runTask");
    Runnable r = (Runnable) m.getAllParams()[0];

    r.run();
    m = serverList.expect("packetEncrypted");
    b = (ByteBuffer) m.getAllParams()[0];

    clientEngine.feedEncryptedPacket(b, null);
    m = clientList.expect("runTask");
    r = (Runnable) m.getAllParams()[0];

    r.run();
    String[] methodNames = new String[3];
    methodNames[0] = "packetEncrypted";
    methodNames[1] = "packetEncrypted";
    methodNames[2] = "packetEncrypted";
    CalledMethod[] methods = clientList.expect(methodNames);

    ByteBuffer b0 = (ByteBuffer) methods[0].getAllParams()[0];

    serverEngine.feedEncryptedPacket(b0, null);
    ByteBuffer b1 = (ByteBuffer) methods[1].getAllParams()[0];
    m = serverList.expect("runTask");
    r = (Runnable) m.getAllParams()[0];

    serverEngine.feedEncryptedPacket(b1, null);
    ByteBuffer b2 = (ByteBuffer) methods[2].getAllParams()[0];

    // THIS IS THE DELAYED RUN TASK run after second feed of data to sslEngine...
    r.run();

    serverEngine.feedEncryptedPacket(b2, null);
    String[] methodNames1 = new String[3];
    methodNames1[0] = "packetEncrypted";
    methodNames1[1] = "packetEncrypted";
    methodNames1[2] = "encryptedLinkEstablished";
    CalledMethod[] methods1 = serverList.expect(methodNames1);

    ByteBuffer b01 = (ByteBuffer) methods1[0].getAllParams()[0];
    clientEngine.feedEncryptedPacket(b01, null);
    ByteBuffer b11 = (ByteBuffer) methods1[1].getAllParams()[0];
    clientEngine.feedEncryptedPacket(b11, null);

    clientList.expect("encryptedLinkEstablished");
    log.fine("E*******************************************");
  }

  /**
   * This tests the situation where the Runnable must tell the engine to reprocess the buffer
   * itself.
   */
  public void testReallyDelayedRunTask() throws Exception {
    log.fine("B*******************************************");
    clientEngine.beginHandshake();
    CalledMethod m = clientList.expect("packetEncrypted");
    ByteBuffer b = (ByteBuffer) m.getAllParams()[0];

    serverEngine.feedEncryptedPacket(b, null);
    m = serverList.expect("runTask");
    Runnable r = (Runnable) m.getAllParams()[0];

    r.run();
    m = serverList.expect("packetEncrypted");
    b = (ByteBuffer) m.getAllParams()[0];

    clientEngine.feedEncryptedPacket(b, null);
    m = clientList.expect("runTask");
    r = (Runnable) m.getAllParams()[0];

    r.run();
    String[] methodNames = new String[3];
    methodNames[0] = "packetEncrypted";
    methodNames[1] = "packetEncrypted";
    methodNames[2] = "packetEncrypted";
    CalledMethod[] methods = clientList.expect(methodNames);

    ByteBuffer b0 = (ByteBuffer) methods[0].getAllParams()[0];

    serverEngine.feedEncryptedPacket(b0, null);
    ByteBuffer b1 = (ByteBuffer) methods[1].getAllParams()[0];
    m = serverList.expect("runTask");
    r = (Runnable) m.getAllParams()[0];

    serverEngine.feedEncryptedPacket(b1, null);
    ByteBuffer b2 = (ByteBuffer) methods[2].getAllParams()[0];

    serverEngine.feedEncryptedPacket(b2, null);
    String[] methodNames1 = new String[3];

    // THIS IS THE REALLY DELAYED RUN TASK run after all 3 packets are fed
    // to ssl engine
    r.run();

    methodNames1[0] = "packetEncrypted";
    methodNames1[1] = "packetEncrypted";
    methodNames1[2] = "encryptedLinkEstablished";
    CalledMethod[] methods1 = serverList.expect(methodNames1);

    ByteBuffer b01 = (ByteBuffer) methods1[0].getAllParams()[0];
    clientEngine.feedEncryptedPacket(b01, null);
    ByteBuffer b11 = (ByteBuffer) methods1[1].getAllParams()[0];
    clientEngine.feedEncryptedPacket(b11, null);

    clientList.expect("encryptedLinkEstablished");
    log.fine("E*******************************************");
  }

  public void testHalfPackets() throws Exception {
    clientEngine.beginHandshake();

    CalledMethod m = clientList.expect("packetEncrypted");
    ByteBuffer b = (ByteBuffer) m.getAllParams()[0];

    feedPacket(serverEngine, b);

    m = serverList.expect("runTask");
    Runnable r = (Runnable) m.getAllParams()[0];
    r.run();

    m = serverList.expect("packetEncrypted");
    b = (ByteBuffer) m.getAllParams()[0];
    log.fine("remain1=" + b.remaining());

    feedPacket(clientEngine, b);

    m = clientList.expect("runTask");
    r = (Runnable) m.getAllParams()[0];
    r.run();

    String[] methodNames = new String[3];
    methodNames[0] = "packetEncrypted";
    methodNames[1] = "packetEncrypted";
    methodNames[2] = "packetEncrypted";
    CalledMethod[] methods = clientList.expect(methodNames);

    ByteBuffer b0 = (ByteBuffer) methods[0].getAllParams()[0];
    feedPacket(serverEngine, b0);

    m = serverList.expect("runTask");
    r = (Runnable) m.getAllParams()[0];
    r.run();

    ByteBuffer b1 = (ByteBuffer) methods[1].getAllParams()[0];
    feedPacket(serverEngine, b1);

    ByteBuffer b2 = (ByteBuffer) methods[2].getAllParams()[0];
    feedPacket(serverEngine, b2);

    methodNames = new String[3];
    methodNames[0] = "packetEncrypted";
    methodNames[1] = "packetEncrypted";
    methodNames[2] = "encryptedLinkEstablished";
    methods = serverList.expect(methodNames);

    b0 = (ByteBuffer) methods[0].getAllParams()[0];
    feedPacket(clientEngine, b0);

    b1 = (ByteBuffer) methods[1].getAllParams()[0];
    feedPacket(clientEngine, b1);

    clientList.expect("encryptedLinkEstablished");
  }

  public void testCombinedPackets() throws Exception {
    clientEngine.beginHandshake();

    CalledMethod m;
    ByteBuffer b;

    CalledMethod m1 = clientList.expect("packetEncrypted");
    ByteBuffer b3 = (ByteBuffer) m1.getAllParams()[0];
    serverEngine.feedEncryptedPacket(b3, null);

    m = serverList.expect("runTask");
    Runnable r = (Runnable) m.getAllParams()[0];
    r.run();

    m = serverList.expect("packetEncrypted");
    b = (ByteBuffer) m.getAllParams()[0];
    clientEngine.feedEncryptedPacket(b, null);

    m = clientList.expect("runTask");
    r = (Runnable) m.getAllParams()[0];
    r.run();

    String[] methodNames = new String[3];
    methodNames[0] = "packetEncrypted";
    methodNames[1] = "packetEncrypted";
    methodNames[2] = "packetEncrypted";
    CalledMethod[] methods = clientList.expect(methodNames);

    ByteBuffer b0 = (ByteBuffer) methods[0].getAllParams()[0];
    ByteBuffer b1 = (ByteBuffer) methods[1].getAllParams()[0];
    ByteBuffer b2 = (ByteBuffer) methods[2].getAllParams()[0];

    int total = b0.remaining() + b1.remaining() + b2.remaining();
    ByteBuffer combined = ByteBuffer.allocate(total);
    combined.put(b0);
    combined.put(b1);
    combined.put(b2);
    helper.doneFillingBuffer(combined);
    serverEngine.feedEncryptedPacket(combined, null);

    m = serverList.expect("runTask");
    r = (Runnable) m.getAllParams()[0];
    r.run();

    methodNames = new String[3];
    methodNames[0] = "packetEncrypted";
    methodNames[1] = "packetEncrypted";
    methodNames[2] = "encryptedLinkEstablished";
    methods = serverList.expect(methodNames);

    b0 = (ByteBuffer) methods[0].getAllParams()[0];
    b1 = (ByteBuffer) methods[1].getAllParams()[0];
    total = b0.remaining() + b1.remaining();
    combined = ByteBuffer.allocate(total);
    combined.put(b0);
    combined.put(b1);
    helper.doneFillingBuffer(combined);

    clientEngine.feedEncryptedPacket(combined, null);

    clientList.expect("encryptedLinkEstablished");
  }

  public void testOneAndHalfPackets() throws Exception {
    clientEngine.beginHandshake();

    CalledMethod m = clientList.expect("packetEncrypted");
    ByteBuffer b = (ByteBuffer) m.getAllParams()[0];
    serverEngine.feedEncryptedPacket(b, null);

    m = serverList.expect("runTask");
    Runnable r = (Runnable) m.getAllParams()[0];
    r.run();

    m = serverList.expect("packetEncrypted");
    b = (ByteBuffer) m.getAllParams()[0];
    clientEngine.feedEncryptedPacket(b, null);

    m = clientList.expect("runTask");
    r = (Runnable) m.getAllParams()[0];
    r.run();

    String[] methodNames = new String[3];
    methodNames[0] = "packetEncrypted";
    methodNames[1] = "packetEncrypted";
    methodNames[2] = "packetEncrypted";
    CalledMethod[] methods = clientList.expect(methodNames);

    ByteBuffer b0 = (ByteBuffer) methods[0].getAllParams()[0];
    ByteBuffer b1 = (ByteBuffer) methods[1].getAllParams()[0];
    ByteBuffer b2 = (ByteBuffer) methods[2].getAllParams()[0];

    int total = b0.remaining() + b1.remaining() + b2.remaining();
    ByteBuffer combined = ByteBuffer.allocate(total);
    combined.put(b0);

    int lim = b1.limit();
    b1.limit(3); // we only want to put part of b1 in payload
    combined.put(b1);
    helper.doneFillingBuffer(combined);
    serverEngine.feedEncryptedPacket(combined, null);

    combined.clear();
    b1.limit(lim);
    combined.put(b1);
    combined.put(b2);

    helper.doneFillingBuffer(combined);
    serverEngine.feedEncryptedPacket(combined, null);

    m = serverList.expect("runTask");
    r = (Runnable) m.getAllParams()[0];
    r.run();

    methodNames = new String[3];
    methodNames[0] = "packetEncrypted";
    methodNames[1] = "packetEncrypted";
    methodNames[2] = "encryptedLinkEstablished";
    methods = serverList.expect(methodNames);

    b0 = (ByteBuffer) methods[0].getAllParams()[0];
    b1 = (ByteBuffer) methods[1].getAllParams()[0];
    total = b0.remaining() + b1.remaining();
    combined = ByteBuffer.allocate(total);
    combined.put(b0);
    combined.put(b1);
    helper.doneFillingBuffer(combined);

    clientEngine.feedEncryptedPacket(combined, null);

    clientList.expect("encryptedLinkEstablished");
  }

  public void testRunInMiddleOfPacket() throws Exception {
    log.fine("B*******************************************");
    clientEngine.beginHandshake();
    CalledMethod m = clientList.expect("packetEncrypted");
    ByteBuffer b = (ByteBuffer) m.getAllParams()[0];

    serverEngine.feedEncryptedPacket(b, null);
    m = serverList.expect("runTask");
    Runnable r = (Runnable) m.getAllParams()[0];

    r.run();
    m = serverList.expect("packetEncrypted");
    b = (ByteBuffer) m.getAllParams()[0];

    clientEngine.feedEncryptedPacket(b, null);
    m = clientList.expect("runTask");
    r = (Runnable) m.getAllParams()[0];

    r.run();
    String[] methodNames = new String[3];
    methodNames[0] = "packetEncrypted";
    methodNames[1] = "packetEncrypted";
    methodNames[2] = "packetEncrypted";
    CalledMethod[] methods = clientList.expect(methodNames);

    ByteBuffer b0 = (ByteBuffer) methods[0].getAllParams()[0];
    ByteBuffer b1 = (ByteBuffer) methods[1].getAllParams()[0];
    ByteBuffer b2 = (ByteBuffer) methods[2].getAllParams()[0];

    serverEngine.feedEncryptedPacket(b0, null);
    m = serverList.expect("runTask");
    r = (Runnable) m.getAllParams()[0];

    int total = b1.remaining() + b2.remaining();
    ByteBuffer combined = ByteBuffer.allocate(total);

    int lim = b1.limit();
    b1.limit(3); // we only want to put part of b1 in payload
    combined.put(b1);
    helper.doneFillingBuffer(combined);
    serverEngine.feedEncryptedPacket(combined, null);

    // run the task after some of the previous packet fed, then feed rest of packet
    r.run();

    combined.clear();
    b1.limit(lim);
    combined.put(b1);
    combined.put(b2);

    helper.doneFillingBuffer(combined);
    serverEngine.feedEncryptedPacket(combined, null);

    String[] methodNames1 = new String[3];
    methodNames1[0] = "packetEncrypted";
    methodNames1[1] = "packetEncrypted";
    methodNames1[2] = "encryptedLinkEstablished";
    CalledMethod[] methods1 = serverList.expect(methodNames1);

    b0 = (ByteBuffer) methods1[0].getAllParams()[0];
    clientEngine.feedEncryptedPacket(b0, null);
    b1 = (ByteBuffer) methods1[1].getAllParams()[0];
    clientEngine.feedEncryptedPacket(b1, null);

    clientList.expect("encryptedLinkEstablished");
  }

  private void feedPacket(AsyncSSLEngine engine, ByteBuffer b) throws Exception {
    TestNewAsynchSSLEngine.feedPacket(engine, b);
  }
}
public abstract class ZNioSuperclassTest extends TestCase {

  private static final Logger log = Logger.getLogger(ZNioSuperclassTest.class.getName());

  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.fine("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.
   *
   * <p>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.fine("***********************************************");
    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;
  }
}