  @Test public void twoAddressesReconnect() {

    SocketAddress addr1 = mock(SocketAddress.class);
    SocketAddress addr2 = mock(SocketAddress.class);
    createTransportSet(addr1, addr2);

    // Invocation counters
    int transportsAddr1 = 0;
    int transportsAddr2 = 0;
    int backoff1Consulted = 0;
    int backoff2Consulted = 0;
    int backoff3Consulted = 0;
    int backoffReset = 0;
    int onAllAddressesFailed = 0;

    // First attempt
    DelayedClientTransport delayedTransport1 =
        (DelayedClientTransport) transportSet.obtainActiveTransport();
    verify(mockBackoffPolicyProvider, times(++backoffReset)).get();
    verify(mockTransportFactory, times(++transportsAddr1)).newClientTransport(addr1, authority);
    // Let this one fail without success
    transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
    assertNull(delayedTransport1.getTransportSupplier());
    verify(mockTransportSetCallback, times(onAllAddressesFailed)).onAllAddressesFailed();

    // Second attempt will start immediately. Keep back-off policy.
    DelayedClientTransport delayedTransport2 =
        (DelayedClientTransport) transportSet.obtainActiveTransport();
    assertSame(delayedTransport1, delayedTransport2);
    verify(mockBackoffPolicyProvider, times(backoffReset)).get();
    verify(mockTransportFactory, times(++transportsAddr2)).newClientTransport(addr2, authority);
    // Fail this one too
    transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
    // All addresses have failed. Delayed transport will see an error.
    assertTrue(delayedTransport2.getTransportSupplier().get() instanceof FailingClientTransport);
    verify(mockTransportSetCallback, times(++onAllAddressesFailed)).onAllAddressesFailed();

    // Third attempt is the first address, thus controlled by the first back-off interval.
    DelayedClientTransport delayedTransport3 =
        (DelayedClientTransport) transportSet.obtainActiveTransport();
    assertNotSame(delayedTransport2, delayedTransport3);
    verify(mockBackoffPolicy1, times(++backoff1Consulted)).nextBackoffMillis();
    verify(mockBackoffPolicyProvider, times(backoffReset)).get();
    fakeClock.forwardMillis(9);
    verify(mockTransportFactory, times(transportsAddr1)).newClientTransport(addr1, authority);
    fakeClock.forwardMillis(1);
    verify(mockTransportFactory, times(++transportsAddr1)).newClientTransport(addr1, authority);
    // Fail this one too
    transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
    assertNull(delayedTransport3.getTransportSupplier());
    verify(mockTransportSetCallback, times(onAllAddressesFailed)).onAllAddressesFailed();

    // Forth attempt will start immediately. Keep back-off policy.
    DelayedClientTransport delayedTransport4 =
        (DelayedClientTransport) transportSet.obtainActiveTransport();
    assertSame(delayedTransport3, delayedTransport4);
    verify(mockBackoffPolicyProvider, times(backoffReset)).get();
    verify(mockTransportFactory, times(++transportsAddr2)).newClientTransport(addr2, authority);
    // Fail this one too
    transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
    // All addresses have failed again. Delayed transport will see an error
    assertTrue(delayedTransport4.getTransportSupplier().get() instanceof FailingClientTransport);
    verify(mockTransportSetCallback, times(++onAllAddressesFailed)).onAllAddressesFailed();

    // Fifth attempt for the first address, thus controlled by the second back-off interval.
    DelayedClientTransport delayedTransport5 =
        (DelayedClientTransport) transportSet.obtainActiveTransport();
    assertNotSame(delayedTransport4, delayedTransport5);
    verify(mockBackoffPolicy1, times(++backoff1Consulted)).nextBackoffMillis();
    verify(mockBackoffPolicyProvider, times(backoffReset)).get();
    fakeClock.forwardMillis(99);
    verify(mockTransportFactory, times(transportsAddr1)).newClientTransport(addr1, authority);
    fakeClock.forwardMillis(1);
    verify(mockTransportFactory, times(++transportsAddr1)).newClientTransport(addr1, authority);
    // Let it through
    transports.peek().listener.transportReady();
    // Delayed transport will see the connected transport.
    assertSame(transports.peek().transport, delayedTransport5.getTransportSupplier().get());
    verify(mockTransportSetCallback, never()).onConnectionClosedByServer(any(Status.class));
    // Then close it.
    transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
    verify(mockTransportSetCallback).onConnectionClosedByServer(same(Status.UNAVAILABLE));
    verify(mockTransportSetCallback, times(onAllAddressesFailed)).onAllAddressesFailed();

    // First attempt after a successful connection. Reset back-off policy, and start from the first
    // address.
    DelayedClientTransport delayedTransport6 =
        (DelayedClientTransport) transportSet.obtainActiveTransport();
    assertNotSame(delayedTransport5, delayedTransport6);
    verify(mockBackoffPolicyProvider, times(++backoffReset)).get();
    verify(mockTransportFactory, times(++transportsAddr1)).newClientTransport(addr1, authority);

    // Final checks on invocations on back-off policies
    verify(mockBackoffPolicy1, times(backoff1Consulted)).nextBackoffMillis();
    verify(mockBackoffPolicy2, times(backoff2Consulted)).nextBackoffMillis();
    verify(mockBackoffPolicy3, times(backoff3Consulted)).nextBackoffMillis();
    verifyNoMoreInteractions(mockTransportSetCallback);
  }