@Test
  public void testLeaseTimeout() throws Exception {
    final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);

    final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
    Mockito.when(conn1.isOpen()).thenReturn(true);
    Mockito.when(connFactory.create(Mockito.eq("somehost"))).thenReturn(conn1);

    final LocalConnPool pool = new LocalConnPool(connFactory, 1, 1);

    final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
    final GetPoolEntryThread t1 = new GetPoolEntryThread(future1);
    t1.start();

    t1.join(GRACE_PERIOD);
    Assert.assertTrue(future1.isDone());
    final LocalPoolEntry entry1 = t1.getEntry();
    Assert.assertNotNull(entry1);

    final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
    final GetPoolEntryThread t2 = new GetPoolEntryThread(future2, 50, TimeUnit.MICROSECONDS);
    t2.start();

    t2.join(GRACE_PERIOD);
    Assert.assertTrue(t2.getException() instanceof TimeoutException);
    Assert.assertFalse(future2.isDone());
    Assert.assertFalse(future2.isCancelled());
  }
  @Test
  public void testLeaseIOException() throws Exception {
    final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
    Mockito.doThrow(new IOException("Oppsie")).when(connFactory).create("somehost");

    final LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);

    final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
    final GetPoolEntryThread t1 = new GetPoolEntryThread(future1);
    t1.start();

    t1.join(GRACE_PERIOD);
    Assert.assertTrue(future1.isDone());
    Assert.assertTrue(t1.getException() instanceof ExecutionException);
    Assert.assertTrue(t1.getException().getCause() instanceof IOException);
    Assert.assertFalse(future1.isCancelled());
  }
  @Test
  public void testStatefulConnectionRedistributionOnPerRouteMaxLimit() throws Exception {
    final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);

    final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
    Mockito.when(conn1.isOpen()).thenReturn(true);
    final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
    Mockito.when(conn2.isOpen()).thenReturn(true);
    final HttpConnection conn3 = Mockito.mock(HttpConnection.class);
    Mockito.when(conn3.isOpen()).thenReturn(true);
    Mockito.when(connFactory.create(Mockito.eq("somehost"))).thenReturn(conn1, conn2, conn3);

    final LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
    pool.setMaxPerRoute("somehost", 2);
    pool.setMaxTotal(2);

    final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
    final GetPoolEntryThread t1 = new GetPoolEntryThread(future1);
    t1.start();

    t1.join(GRACE_PERIOD);
    Assert.assertTrue(future1.isDone());
    final LocalPoolEntry entry1 = t1.getEntry();
    Assert.assertNotNull(entry1);

    final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
    final GetPoolEntryThread t2 = new GetPoolEntryThread(future2);
    t2.start();

    t2.join(GRACE_PERIOD);
    Assert.assertTrue(future2.isDone());
    final LocalPoolEntry entry2 = t2.getEntry();
    Assert.assertNotNull(entry2);

    PoolStats totals = pool.getTotalStats();
    Assert.assertEquals(0, totals.getAvailable());
    Assert.assertEquals(2, totals.getLeased());
    Assert.assertEquals(0, totals.getPending());

    entry1.setState("some-stuff");
    pool.release(entry1, true);
    entry2.setState("some-stuff");
    pool.release(entry2, true);

    Mockito.verify(connFactory, Mockito.times(2)).create(Mockito.eq("somehost"));

    final Future<LocalPoolEntry> future3 = pool.lease("somehost", "some-other-stuff");
    final GetPoolEntryThread t3 = new GetPoolEntryThread(future3);
    t3.start();

    t3.join(GRACE_PERIOD);
    Assert.assertTrue(future3.isDone());
    final LocalPoolEntry entry3 = t3.getEntry();
    Assert.assertNotNull(entry3);

    Mockito.verify(connFactory, Mockito.times(3)).create(Mockito.eq("somehost"));

    Mockito.verify(conn1).close();
    Mockito.verify(conn2, Mockito.never()).close();

    totals = pool.getTotalStats();
    Assert.assertEquals(1, totals.getAvailable());
    Assert.assertEquals(1, totals.getLeased());
  }
  @Test
  public void testConnectionRedistributionOnTotalMaxLimit() throws Exception {
    final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);

    final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
    Mockito.when(conn1.isOpen()).thenReturn(true);
    final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
    Mockito.when(conn2.isOpen()).thenReturn(true);
    final HttpConnection conn3 = Mockito.mock(HttpConnection.class);
    Mockito.when(conn3.isOpen()).thenReturn(true);
    Mockito.when(connFactory.create(Mockito.eq("somehost"))).thenReturn(conn1, conn2, conn3);

    final HttpConnection conn4 = Mockito.mock(HttpConnection.class);
    Mockito.when(conn4.isOpen()).thenReturn(true);
    final HttpConnection conn5 = Mockito.mock(HttpConnection.class);
    Mockito.when(conn5.isOpen()).thenReturn(true);
    Mockito.when(connFactory.create(Mockito.eq("otherhost"))).thenReturn(conn4, conn5);

    final LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
    pool.setMaxPerRoute("somehost", 2);
    pool.setMaxPerRoute("otherhost", 2);
    pool.setMaxTotal(2);

    final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
    final GetPoolEntryThread t1 = new GetPoolEntryThread(future1);
    t1.start();
    final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
    final GetPoolEntryThread t2 = new GetPoolEntryThread(future2);
    t2.start();

    t1.join(GRACE_PERIOD);
    Assert.assertTrue(future1.isDone());
    final LocalPoolEntry entry1 = t1.getEntry();
    Assert.assertNotNull(entry1);
    t2.join(GRACE_PERIOD);
    Assert.assertTrue(future2.isDone());
    final LocalPoolEntry entry2 = t2.getEntry();
    Assert.assertNotNull(entry2);

    final Future<LocalPoolEntry> future3 = pool.lease("otherhost", null);
    final GetPoolEntryThread t3 = new GetPoolEntryThread(future3);
    t3.start();
    final Future<LocalPoolEntry> future4 = pool.lease("otherhost", null);
    final GetPoolEntryThread t4 = new GetPoolEntryThread(future4);
    t4.start();

    Assert.assertFalse(t3.isDone());
    Assert.assertFalse(t4.isDone());

    Mockito.verify(connFactory, Mockito.times(2)).create(Mockito.eq("somehost"));
    Mockito.verify(connFactory, Mockito.never()).create(Mockito.eq("otherhost"));

    PoolStats totals = pool.getTotalStats();
    Assert.assertEquals(0, totals.getAvailable());
    Assert.assertEquals(2, totals.getLeased());

    pool.release(entry1, true);
    pool.release(entry2, true);

    t3.join(GRACE_PERIOD);
    Assert.assertTrue(future3.isDone());
    final LocalPoolEntry entry3 = t3.getEntry();
    Assert.assertNotNull(entry3);
    t4.join(GRACE_PERIOD);
    Assert.assertTrue(future4.isDone());
    final LocalPoolEntry entry4 = t4.getEntry();
    Assert.assertNotNull(entry4);

    Mockito.verify(connFactory, Mockito.times(2)).create(Mockito.eq("somehost"));
    Mockito.verify(connFactory, Mockito.times(2)).create(Mockito.eq("otherhost"));

    totals = pool.getTotalStats();
    Assert.assertEquals(0, totals.getAvailable());
    Assert.assertEquals(2, totals.getLeased());

    final Future<LocalPoolEntry> future5 = pool.lease("somehost", null);
    final GetPoolEntryThread t5 = new GetPoolEntryThread(future5);
    t5.start();
    final Future<LocalPoolEntry> future6 = pool.lease("otherhost", null);
    final GetPoolEntryThread t6 = new GetPoolEntryThread(future6);
    t6.start();

    pool.release(entry3, true);
    pool.release(entry4, true);

    t5.join(GRACE_PERIOD);
    Assert.assertTrue(future5.isDone());
    final LocalPoolEntry entry5 = t5.getEntry();
    Assert.assertNotNull(entry5);
    t6.join(GRACE_PERIOD);
    Assert.assertTrue(future6.isDone());
    final LocalPoolEntry entry6 = t6.getEntry();
    Assert.assertNotNull(entry6);

    Mockito.verify(connFactory, Mockito.times(3)).create(Mockito.eq("somehost"));
    Mockito.verify(connFactory, Mockito.times(2)).create(Mockito.eq("otherhost"));

    totals = pool.getTotalStats();
    Assert.assertEquals(0, totals.getAvailable());
    Assert.assertEquals(2, totals.getLeased());

    pool.release(entry5, true);
    pool.release(entry6, true);

    totals = pool.getTotalStats();
    Assert.assertEquals(2, totals.getAvailable());
    Assert.assertEquals(0, totals.getLeased());
  }