/** Tests that when a connection succeeds, the retry limit is reset. */
  @Test
  @Feature({"GCore"})
  public void connectionAttemptsResetTest() {
    GoogleApiClientHelper helper = new GoogleApiClientHelper(mMockClient);

    // Connection attempts
    for (int i = 0; i < ConnectedTask.RETRY_NUMBER_LIMIT - 1; i++) {
      helper.onConnectionFailed(new ConnectionResult(ConnectionResult.SERVICE_UPDATING));
      ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
    }

    // Should have tried to connect every time.
    verify(mMockClient, times(ConnectedTask.RETRY_NUMBER_LIMIT - 1)).connect();

    // Connection successful now
    helper.onConnected(null);
    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();

    for (int i = 0; i < ConnectedTask.RETRY_NUMBER_LIMIT; i++) {
      helper.onConnectionFailed(new ConnectionResult(ConnectionResult.SERVICE_UPDATING));
      ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
    }

    // A success should allow for more connection attempts.
    verify(mMockClient, times(ConnectedTask.RETRY_NUMBER_LIMIT * 2 - 1)).connect();

    // This should not result in a connection attempt, the limit is still there.
    helper.onConnectionFailed(new ConnectionResult(ConnectionResult.SERVICE_UPDATING));
    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();

    // The connection handler should have given up, no new call.
    verify(mMockClient, times(ConnectedTask.RETRY_NUMBER_LIMIT * 2 - 1)).connect();
  }
  @Test
  @Feature({"GCore"})
  public void lifecycleManagementDelayTest() {
    GoogleApiClientHelper helper = new GoogleApiClientHelper(mMockClient);
    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
    Activity mockActivity = mock(Activity.class);
    ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.CREATED);
    helper.setDisconnectionDelay(5000);

    // We have a connected client
    when(mMockClient.isConnected()).thenReturn(true);

    // Should not be disconnected when we go in the background
    ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STOPPED);
    ShadowLooper.runUiThreadTasks();
    verify(mMockClient, times(0)).disconnect();

    // Should be disconnected when we wait.
    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
    verify(mMockClient, times(1)).disconnect();

    // Should be reconnected when we come in the foreground
    ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STARTED);
    verify(mMockClient).connect();

    // Should not disconnect when we became visible during the delay
    ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STOPPED);
    ShadowLooper.runUiThreadTasks();
    verify(mMockClient, times(1)).disconnect();
    ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STARTED);
    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
    verify(mMockClient, times(1)).disconnect();
  }
  /** Tests that the connection handler gives up after a number of connection attempts. */
  @Test
  @Feature({"GCore"})
  public void connectionFailureTest() {
    GoogleApiClientHelper helper = new GoogleApiClientHelper(mMockClient);

    helper.onConnectionFailed(new ConnectionResult(ConnectionResult.DEVELOPER_ERROR));
    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();

    // Should not retry on unrecoverable errors
    verify(mMockClient, never()).connect();

    // Connection attempts
    for (int i = 0; i < ConnectedTask.RETRY_NUMBER_LIMIT; i++) {
      helper.onConnectionFailed(new ConnectionResult(ConnectionResult.SERVICE_UPDATING));
      ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
    }

    // Should have tried to connect every time.
    verify(mMockClient, times(ConnectedTask.RETRY_NUMBER_LIMIT)).connect();

    // Try again
    helper.onConnectionFailed(new ConnectionResult(ConnectionResult.SERVICE_UPDATING));
    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();

    // The connection handler should have given up, no new call.
    verify(mMockClient, times(ConnectedTask.RETRY_NUMBER_LIMIT)).connect();
  }
  @Test
  public void shouldNotCallOnSubscribeWhenSubscriptionUnsubscribedBeforeDelay() {
    Observable.OnSubscribe<Integer> onSubscribe = mock(Observable.OnSubscribe.class);
    Handler handler = spy(new Handler());

    final Worker worker = spy(new HandlerScheduler.HandlerWorker(handler));
    Scheduler scheduler =
        new Scheduler() {
          @Override
          public Worker createWorker() {
            return worker;
          }
        };

    Subscription subscription =
        Observable.create(onSubscribe).delaySubscription(1, MINUTES, scheduler).subscribe();

    verify(worker).schedule(any(Action0.class), eq(1L), eq(MINUTES));
    verify(handler).postDelayed(any(Runnable.class), eq(MINUTES.toMillis(1)));

    subscription.unsubscribe();

    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();

    verify(onSubscribe, never()).call(any(Subscriber.class));
    verify(handler).removeCallbacks(any(Runnable.class));
  }
  @Test
  @Feature({"GCore"})
  public void willUseConnectionBackgroundTest() {
    int disconnectionTimeout = 5000;
    int arbitraryNumberOfSeconds = 42;
    GoogleApiClientHelper helper = new GoogleApiClientHelper(mMockClient);
    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
    Activity mockActivity = mock(Activity.class);
    ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.CREATED);
    helper.setDisconnectionDelay(disconnectionTimeout);

    // We have a connected client
    when(mMockClient.isConnected()).thenReturn(true);

    // We go in the background and extend the delay
    ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STOPPED);
    ShadowLooper.idleMainLooper(disconnectionTimeout - arbitraryNumberOfSeconds);
    helper.willUseConnection();

    // The client should not have been disconnected.
    ShadowLooper.idleMainLooper(disconnectionTimeout - arbitraryNumberOfSeconds);
    verify(mMockClient, never()).disconnect();

    // After the full timeout it should still disconnect though
    ShadowLooper.idleMainLooper(arbitraryNumberOfSeconds);
    verify(mMockClient).disconnect();

    // The client is now disconnected then
    when(mMockClient.isConnected()).thenReturn(false);

    // The call should reconnect a disconnected client
    helper.willUseConnection();
    verify(mMockClient).connect();
  }
  @Test
  @Feature({"GCore"})
  public void willUseConnectionForegroundTest() {
    GoogleApiClientHelper helper = new GoogleApiClientHelper(mMockClient);
    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
    Activity mockActivity = mock(Activity.class);
    ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.CREATED);
    helper.setDisconnectionDelay(5000);

    // We have a connected client
    when(mMockClient.isConnected()).thenReturn(true);

    // We are in the foreground
    ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STARTED);
    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();

    // Disconnections should not be scheduled when in the foreground.
    helper.willUseConnection();
    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
    verify(mMockClient, never()).disconnect();
  }
  /** Tests that connection attempts are delayed. */
  @Test
  @Feature({"GCore"})
  public void connectionAttemptDelayTest() {
    GoogleApiClientHelper helper = new GoogleApiClientHelper(mMockClient);

    ShadowLooper.pauseMainLooper();
    helper.onConnectionFailed(new ConnectionResult(ConnectionResult.SERVICE_UPDATING));
    verify(mMockClient, times(0)).connect();
    ShadowLooper.unPauseMainLooper();

    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
    verify(mMockClient, times(1)).connect();
  }
  @Test
  @Feature({"GCore"})
  public void disconnectionCancellingTest() {
    int disconnectionTimeout = 5000;
    GoogleApiClientHelper helper = new GoogleApiClientHelper(mMockClient);
    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
    Activity mockActivity = mock(Activity.class);
    ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.CREATED);
    helper.setDisconnectionDelay(disconnectionTimeout);

    // We have a connected client
    when(mMockClient.isConnected()).thenReturn(true);

    // We go in the background and come back before the end of the timeout.
    ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STOPPED);
    ShadowLooper.idleMainLooper(disconnectionTimeout - 42);
    ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STARTED);
    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();

    // The client should not have been disconnected, which would drop requests otherwise.
    verify(mMockClient, never()).disconnect();
  }
  @Test
  @Feature({"GCore"})
  public void lifecycleManagementTest() {
    GoogleApiClientHelper helper = new GoogleApiClientHelper(mMockClient);
    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
    Activity mockActivity = mock(Activity.class);
    ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.CREATED);

    // The helper should have been registered to handle connectivity issues.
    verify(mMockClient).registerConnectionCallbacks(helper);
    verify(mMockClient).registerConnectionFailedListener(helper);

    // Client was not connected. Coming in the foreground should not change that.
    ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STARTED);
    verify(mMockClient, never()).connect();

    // We now say we are connected
    when(mMockClient.isConnected()).thenReturn(true);

    // Should be disconnected when we go in the background
    ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STOPPED);
    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
    verify(mMockClient, times(1)).disconnect();

    // Should be reconnected when we come in the foreground
    ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STARTED);
    verify(mMockClient).connect();

    helper.disable();

    // The helper should have been unregistered from handling connectivity issues.
    verify(mMockClient).unregisterConnectionCallbacks(helper);
    verify(mMockClient).unregisterConnectionFailedListener(helper);

    // Should not be interacted with anymore when we stop managing it.
    ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STOPPED);
    verify(mMockClient).disconnect();
  }