/**
   * Unit test for OPENDJ-1247: a locally timed out request which is not a bind or startTLS should
   * result in a client side timeout error, but the connection should remain valid. In addition, no
   * abandon request should be sent.
   */
  @Test
  public void testClientSideTimeoutForSearchRequest() throws Exception {
    resetState();
    registerSearchEvent();
    registerAbandonEvent();

    for (int i = 0; i < ITERATIONS; i++) {
      final Connection connection = factory.getConnection();
      try {
        waitForConnect();
        final ConnectionEventListener listener = mock(ConnectionEventListener.class);
        connection.addConnectionEventListener(listener);
        final PromiseImpl<LdapException, NeverThrowsException> promise = PromiseImpl.create();
        final LdapPromise<SearchResultEntry> connectionPromise =
            connection.readEntryAsync(DN.valueOf("cn=test"), null);
        connectionPromise.onFailure(getFailureHandler(promise));
        waitForSearch();

        LdapException e = promise.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS);
        verifyResultCodeIsClientSideTimeout(e);
        // Wait for the request to timeout.
        try {
          connectionPromise.getOrThrow(TEST_TIMEOUT, TimeUnit.SECONDS);
          fail("The search request succeeded unexpectedly");
        } catch (TimeoutResultException te) {
          verifyResultCodeIsClientSideTimeout(te);
        }

        // The connection should still be valid.
        assertThat(connection.isValid()).isTrue();
        verifyZeroInteractions(listener);
        /*
         * FIXME: The search should have been abandoned (see comment in
         * LDAPConnection for explanation).
         */
        // waitForAbandon();
      } finally {
        connection.close();
      }
    }
  }
  /**
   * This unit test exposes the bug raised in issue OPENDJ-1156: NPE in ReferenceCountedObject after
   * shutting down directory.
   */
  @Test
  public void testResourceManagement() throws Exception {
    resetState();

    for (int i = 0; i < ITERATIONS; i++) {
      final Connection connection = factory.getConnection();
      try {
        waitForConnect();
        final MockConnectionEventListener listener = new MockConnectionEventListener();
        connection.addConnectionEventListener(listener);

        // Perform remote disconnect which will trigger a client side connection error.
        context.get().disconnect();

        // Wait for the error notification to reach the client.
        listener.awaitError(TEST_TIMEOUT, TimeUnit.SECONDS);
      } finally {
        connection.close();
      }
    }
  }
  /**
   * Unit test for OPENDJ-1247: as per previous test, except this time verify that the connection
   * failure removes the connection from a connection pool.
   */
  @Test
  public void testClientSideTimeoutForBindRequestInConnectionPool() throws Exception {
    resetState();
    registerBindEvent();
    registerCloseEvent();

    for (int i = 0; i < ITERATIONS; i++) {
      final Connection connection = pool.getConnection();
      try {
        waitForConnect();
        final MockConnectionEventListener listener = new MockConnectionEventListener();
        connection.addConnectionEventListener(listener);

        // Now bind with timeout.
        final PromiseImpl<LdapException, NeverThrowsException> promise = PromiseImpl.create();
        final LdapPromise<BindResult> bindPromise = connection.bindAsync(newSimpleBindRequest());
        bindPromise.onFailure(getFailureHandler(promise));
        waitForBind();

        // Wait for the request to timeout and check the handler was invoked.
        TimeoutResultException e = (TimeoutResultException) promise.getOrThrow(5, TimeUnit.SECONDS);
        verifyResultCodeIsClientSideTimeout(e);

        // Now check the promise was completed as expected.
        try {
          bindPromise.getOrThrow(5, TimeUnit.SECONDS);
          fail("The bind request succeeded unexpectedly");
        } catch (TimeoutResultException te) {
          verifyResultCodeIsClientSideTimeout(te);
        }

        /*
         * The connection should no longer be valid, the event listener
         * should have been notified, but no abandon should have been
         * sent.
         */
        listener.awaitError(TEST_TIMEOUT, TimeUnit.SECONDS);
        assertThat(connection.isValid()).isFalse();
        verifyResultCodeIsClientSideTimeout(listener.getError());
        connection.close();
        waitForClose();
        verifyNoAbandonSent();
      } finally {
        connection.close();
      }
    }
  }